You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

strad.rb 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. # strad.rb -- Translation CLM --> Snd-Ruby
  2. #
  3. # Bowed string physical model with stiffness. CLM version adapted
  4. # from the Matlab and C versions courtesy of JOS and Stefania Serafin
  5. # from code revised on 7/14/01
  6. #
  7. # CLM version by Juan Reyes
  8. #
  9. # Translator/Author: Michael Scholz <mi-scholz@users.sourceforge.net>
  10. # Created: Sun Mar 16 02:46:52 CET 2003
  11. # Changed: Wed Nov 17 23:56:56 CET 2010
  12. require "ws"
  13. class DCBlock
  14. def initialize(input = 0.0, output = 0.0)
  15. @dc_input = input
  16. @dc_output = output
  17. end
  18. def dcblock(sample)
  19. @dc_output = sample + (0.99 * @dc_output - @dc_input)
  20. @dc_input = sample
  21. @dc_output
  22. end
  23. def inspect
  24. format("#<%s: input: %1.3f, output: %1.3f>", self.class, @dc_input, @dc_output)
  25. end
  26. end
  27. add_help(:bow, "bow(start = 0, dur = 1, freq = 440, amp = 0.5, *args)
  28. :bufsize 2205 @srate 22050
  29. :fb 0.2 bow force (0.0..1.0)
  30. :vb 0.05 bow velocity (0.0..0.8)
  31. :bp 0.08 bow position (0.0 bridge, 0.5 middle of string, 1.0 nut)
  32. :inharm 0.1 inharmonicity (0.0 harmonic, 1.0 not harmonic)
  33. :ampenv [0, 0, 20, 1, 48, 1, 92, 0, 100, 0]
  34. :degree 45
  35. :dist 0.0025
  36. :reverb 0.005")
  37. def bow(start = 0, dur = 1, freq = 440, amp = 0.5, *args)
  38. bufsize, fb, vb, bp, inharm, ampenv, degree, dist, reverb = nil
  39. optkey(args, binding,
  40. [:bufsize, 2205],
  41. [:fb, 0.2],
  42. [:vb, 0.05],
  43. [:bp, 0.08],
  44. [:inharm, 0.1],
  45. [:ampenv, [0, 0, 20, 1, 48, 1, 92, 0, 100, 0]],
  46. [:degree, 45],
  47. [:dist, 0.0025],
  48. [:reverb, 0.005])
  49. len = seconds2samples(dur)
  50. ampf = make_env(:envelope, ampenv, :scaler, amp, :length, len)
  51. dcblocker = DCBlock.new
  52. vinut = make_vct(bufsize)
  53. vinbridge = make_vct(bufsize)
  54. vinutt = make_vct(bufsize)
  55. vinbridget = make_vct(bufsize)
  56. vib = vin = vibt = vint = 0.0
  57. mus = 0.8 # friction coeff
  58. twavespeedfactor = 5.2
  59. posl = posr = poslt = posrt = indexl = indexr = indexlt = indexrt = 0
  60. indexl_1 = indexr_1 = indexlt_1 = indexrt_1 = indexl_2 = indexr_2 = indexlt_2 = indexrt_2 = 0
  61. updl = updr = updlt = updrt = 0
  62. # biquad coefficients from jos follow
  63. b0b = 0.859210
  64. b1b = -0.704922
  65. b2b = 0.022502
  66. a1b = -0.943639
  67. a2b = 0.120665
  68. b0n = 7.0580050e-001
  69. b1n = -5.3168461e-001
  70. b2n = 1.4579750e-002
  71. a1n = -9.9142489e-001
  72. a2n = 1.8012052e-001
  73. # bridge side, torsional waves
  74. b0bt = 9.9157155e-001
  75. b1bt = -8.2342890e-001
  76. b2bt = 8.8441749e-002
  77. a1bt = -8.3628218e-001
  78. a2bt = 9.2866585e-002
  79. # finger side, torsional waves
  80. b0nt = 4.3721359e-001
  81. b1nt = -2.7034968e-001
  82. b2nt = -5.7147560e-002
  83. a1nt = -1.2158343e+000
  84. a2nt = 3.2555068e-001
  85. # initializations for the filters
  86. xm1bt = xm2bt = xm1nt = xm2nt = ym1bt = ym2bt = ym1nt = ym2nt = xm1b = xm2b = ym1b = ym2b = 0.0
  87. xm1n = xm2n = ym1n = ym2n = ynb = ynbt = ynn = ynnt = ya1nb = ynba1 = y1nb = 0.0
  88. # friedlander friction inits
  89. aa = bb1 = cc1 = delta1 = bb2 = cc2 = delta2 = v = v1 = v2 = rhs = lhs = vtemp = f = 0.0
  90. string_impedance = 0.55
  91. string_impedancet = 1.8
  92. stick = 0
  93. zslope = 1.0 / ((1.0 / (2.0 * string_impedance)) + (1.0 / (2.0 * string_impedancet)))
  94. xnn = xnb = xnnt = xnbt = 0.0
  95. alphar = alphal = alphart = alphalt = 0
  96. l = (mus_srate() / freq) - 2
  97. lt = l / twavespeedfactor
  98. del_right = l * bp
  99. del_left = l * (1 - bp)
  100. del_leftt = lt * (1 - bp)
  101. del_rightt = lt * bp
  102. samp_rperiod = del_right.floor
  103. samp_lperiod = del_left.floor
  104. samp_lperiodt = del_leftt.floor
  105. samp_rperiodt = del_rightt.floor
  106. samp_rperiod = [samp_rperiod, 0].max
  107. samp_rperiod = [samp_rperiod, bufsize - 1].min
  108. samp_lperiod = [samp_lperiod, 0].max
  109. samp_lperiod = [samp_lperiod, bufsize - 1].min
  110. alphar = (del_right - samp_rperiod).to_f
  111. alphal = (del_left - samp_lperiod).to_f
  112. samp_rperiodt = [samp_rperiodt, 0].max
  113. samp_rperiodt = [samp_rperiodt, bufsize - 1].min
  114. samp_lperiodt = [samp_lperiodt, 0].max
  115. samp_lperiodt = [samp_lperiodt, bufsize - 1].min
  116. alphart = (del_rightt - samp_rperiodt).to_f
  117. alphalt = (del_leftt - samp_lperiodt).to_f
  118. posr = (len + posr) % bufsize
  119. posl = (len + posl) % bufsize
  120. posrt = (len + posrt) % bufsize
  121. poslt = (len + poslt) % bufsize
  122. bowfilt = lambda do |inharmon|
  123. # initialize coefficients
  124. ynb = ((b0b * vib + b1b * xm1b + b2b * xm2b) - a1b * ym1b) - a2b * ym2b
  125. xm2b, xm1b = xm1b, vib
  126. ym2b, ym1b = ym1b, ynb
  127. ynn = ((b0n * vin + b1n * xm1n + b2n * xm2n) - a1n * ym1n) - a2n * ym2n
  128. xm2n, xm1n = xm1n, vin
  129. ym2n, ym1n = ym1n, ynn
  130. # filters for torsional waves
  131. ynbt = ((b0bt * vibt + b1bt * xm1bt + b2bt * xm2bt) - a1bt * ym1bt) - a2bt * ym2bt
  132. xm2bt, xm1bt = xm1bt, vibt
  133. ym2bt, ym1bt = ym1bt, ynbt
  134. ynnt = ((b0nt * vint + b1nt * xm1nt + b2nt * xm2nt) - a1nt * ym1nt) - a2nt * ym2nt
  135. xm2nt, xm1nt = xm1nt, vint
  136. ym2nt, ym1nt = ym1nt, ynnt
  137. inharmon = [inharmon, 0.00001].max
  138. inharmon = [inharmon, 0.9999].min
  139. ya1nb = y1nb = -(inharmon * ynb) + ynba1 + inharmon * ya1nb
  140. ynba1 = ynb
  141. y1nb = -y1nb
  142. ynn = -ynn
  143. ynbt = -ynbt
  144. end
  145. friedlander = lambda do |vh|
  146. aa = zslope
  147. bb1 = ((0.2 * zslope + 0.3 * fb) - zslope * vb) - zslope * vh
  148. cc1 = ((0.06 * fb + zslope * vh * vb) - 0.2 * zslope * vh) - 0.3 * vb * fb
  149. delta1 = bb1 * bb1 - 4 * aa * cc1
  150. bb2 = ((-0.2 * zslope - 0.3 * fb) - zslope * vb) - zslope * vh
  151. cc2 = (((0.06 * fb + zslope * vh * vb) + 0.2 * zslope * vh) + 0.3 * vb * fb) + 0.1 * fb
  152. delta2 = bb2 * bb2 - 4 * aa * cc2
  153. if vb.zero? or fb.zero?
  154. v = vh
  155. else
  156. if vh == vb
  157. v, stick = vb, 1
  158. else
  159. if vh > vb
  160. lhs, rhs = 0.0, 1.0
  161. else
  162. rhs, lhs = 0.0, 1.0
  163. end
  164. if rhs == 1.0
  165. if delta1 < 0
  166. v, stick = vb, 1
  167. else
  168. if stick == 1
  169. vtemp = vb
  170. f = 2.0 * zslope * (vtemp - vh)
  171. if f >= (-(mus * fb))
  172. v = vtemp
  173. else
  174. v1 = (-bb1 + sqrt(delta1)) / (2.0 * aa)
  175. v2 = (-bb1 - sqrt(delta1)) / (2.0 * aa)
  176. v = [v1, v2].min
  177. stick = 0
  178. end
  179. else
  180. v1 = (-bb1 + sqrt(delta1)) / (2.0 * aa)
  181. v2 = (-bb1 - sqrt(delta1)) / (2.0 * aa)
  182. v = [v1, v2].min
  183. stick = 0
  184. end
  185. end
  186. elsif lhs == 1.0
  187. if delta2 < 0
  188. v, stick = vb, 1
  189. else
  190. if stick == 1
  191. vtemp = vb
  192. f = zslope * (vtemp - vh)
  193. if f <= (mus * fb) and f > 0
  194. v = vtemp
  195. else
  196. v1 = (-bb2 - sqrt(delta2)) / (2.0 * aa)
  197. v2 = (-bb2 + sqrt(delta2)) / (2.0 * aa)
  198. vtemp = [v1, v2].min
  199. stick = 0
  200. if vtemp > vb
  201. v, stick = vb, 1
  202. else
  203. v = vtemp
  204. f = zslope * (v - vh)
  205. end
  206. end
  207. else
  208. v1 = (-bb2 - sqrt(delta2)) / (2.0 * aa)
  209. v2 = (-bb2 + sqrt(delta2)) / (2.0 * aa)
  210. v = [v1, v2].min
  211. stick = 0
  212. end
  213. end
  214. if v > vb
  215. v, stick = vb, 1
  216. end
  217. end
  218. end
  219. f = zslope * (v - vh)
  220. xnn = y1nb + (f / (2.0 * string_impedance))
  221. xnb = ynn + (f / (2.0 * string_impedance))
  222. end
  223. end
  224. i = 0
  225. run_instrument(start, dur, :distance, dist, :reverb_amount, reverb, :degree, degree) do
  226. indexl = ((i + posl + bufsize) - samp_lperiod) % bufsize
  227. indexr = ((i + posr + bufsize) - samp_rperiod) % bufsize
  228. indexlt = ((i + poslt + bufsize) - samp_lperiodt) % bufsize
  229. indexrt = ((i + posrt + bufsize) - samp_rperiodt) % bufsize
  230. indexl_1 = (((i + posl + bufsize) - samp_lperiod) - 1) % bufsize
  231. indexr_1 = (((i + posr + bufsize) - samp_rperiod) - 1) % bufsize
  232. indexlt_1 = (((i + poslt + bufsize) - samp_lperiodt) - 1) % bufsize
  233. indexrt_1 = (((i + posrt + bufsize) - samp_rperiodt) - 1) % bufsize
  234. indexl_2 = (((i + posl + bufsize) - samp_lperiod) - 2) % bufsize
  235. indexr_2 = (((i + posr + bufsize) - samp_rperiod) - 2) % bufsize
  236. indexlt_2 = (((i + poslt + bufsize) - samp_lperiodt) - 2) % bufsize
  237. indexrt_2 = (((i + posrt + bufsize) - samp_rperiodt) - 2) % bufsize
  238. # fractional delay lines
  239. vib = ((vinbridge[indexl_2] * (alphal - 1) * (alphal - 2)) / 2.0) +
  240. (vinbridge[indexl_1] * -alphal * (alphal - 2)) +
  241. ((vinbridge[indexl] * alphal * (alphal - 1)) / 2.0)
  242. vin = ((vinut[indexr_2] * (alphar - 1) * (alphar - 2)) / 2.0) +
  243. (vinut[indexr_1] * -alphar * (alphar - 2)) +
  244. ((vinut[indexr] * alphar * (alphar - 1)) / 2.0)
  245. vibt = ((vinbridget[indexlt_2] * (alphalt - 1) * (alphalt - 2)) / 2.0) +
  246. (vinbridget[indexlt_1] * -alphalt * (alphalt - 2)) +
  247. ((vinbridget[indexlt] * alphalt * (alphalt - 1)) / 2.0)
  248. vint = ((vinutt[indexrt_2] * (alphart - 1) * (alphart - 2)) / 2.0) +
  249. (vinutt[indexrt_1] * -alphart * (alphart - 2)) +
  250. ((vinutt[indexrt] * alphart * (alphart - 1)) / 2.0)
  251. # biquad filters
  252. bowfilt.call(inharm)
  253. vh1 = ynn + y1nb + ynnt + ynbt
  254. # now solve set of simultaneous equations for v and f
  255. friedlander.call(vh1)
  256. f = zslope * (v - vh1)
  257. xnnt = ynbt + (f / (2.0 * string_impedancet))
  258. xnbt = ynnt + (f / (2.0 * string_impedancet))
  259. updl = (i + posl + bufsize) % bufsize
  260. updr = (i + posr + bufsize) % bufsize
  261. updlt = (i + poslt + bufsize) % bufsize
  262. updrt = (i + posrt + bufsize) % bufsize
  263. vinbridge[updl] = xnb
  264. vinut[updr] = xnn
  265. vinbridget[updlt] = xnbt
  266. vinutt[updrt] = xnnt
  267. out_val = env(ampf) * dcblocker.dcblock(xnb)
  268. lhs = rhs = 0.0
  269. i += 1
  270. out_val
  271. end
  272. end
  273. =begin
  274. with_sound(:channels, 2) do bow(0, 3, 400, 0.5, :vb, 0.15, :fb, 0.1, :inharm, 0.25) end
  275. with_sound(:channels, 2) do bow(0, 2, 440, 0.5, :fb, 0.25) end
  276. with_sound(:channels, 2) do bow(0, 4, 600, 0.8) end
  277. with_sound(:channels, 2) do bow(0, 6, 147, 2, :fb, 0.035, :vb, 0.1) end
  278. with_sound(:channels, 2) do bow(0, 3, 1100, 0.5, :vb, 0.45, :fb, 0.9, :inharm, 0.3) end
  279. with_sound(:channels, 2) do bow(0, 3, 1500, 0.5, :vb, 0.25, :fb, 0.9, :inharm, 0.3) end
  280. with_sound(:channels, 2) do bow(0, 3, 1525, 0.5, :vb, 0.25, :fb, 0.9, :inharm, 0.3) end
  281. with_sound(:channels, 2, :reverb, :jc_reverb_rb) do bow(0, 1, 400, 0.5, :reverb, 0.0051) end
  282. with_sound(:channels, 2, :reverb, :jc_reverb_rb) do
  283. bow(0, 3, 366, 0.5, :degree, 0)
  284. bow(0, 3, 422, 0.5, :degree, 90)
  285. bow(4, 6, 147, 2, :fb, 0.035, :vb, 0.1, :reverb, 0.051)
  286. end
  287. =end
  288. # strad.rb ends here