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.

prc95.rb 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. # prc95.rb -- Translation of prc95.scm/prc-toolkit95.lisp to Snd/Ruby
  2. # Perry Cook's Physical Modelling Toolkit
  3. # Translator/Author: Michael Scholz <mi-scholz@users.sourceforge.net>
  4. # Changed: Mon Nov 22 13:28:27 CET 2010
  5. require "ws"
  6. module PRC
  7. def play_all(dur = 1)
  8. with_sound(:clm, false, :play, 1, :statistics, true, :output, "cook.snd") do
  9. beg = 0
  10. plucky(beg, dur, 440, 0.2, 1.0)
  11. beg += dur + 0.2
  12. bow(beg, dur, 440, 0.2, 1.0)
  13. beg += dur + 0.2
  14. brass(beg, dur, 440, 0.2, 1.0)
  15. beg += dur + 0.2
  16. clarinet(beg, dur, 440, 0.2, 1.0)
  17. beg += dur + 0.2
  18. flute(beg, dur, 440, 0.2, 1.0)
  19. end
  20. end
  21. def make_reed(*args)
  22. offset, slope = nil
  23. optkey(args, binding,
  24. [:offset, 0.6],
  25. [:slope, -0.8])
  26. lambda do |samp| [1.0, offset + slope * samp].min end
  27. end
  28. def reedtable(r, sample)
  29. r.call(sample)
  30. end
  31. def make_bowtable(*args)
  32. offset, slope = nil
  33. optkey(args, binding,
  34. [:offset, 0.0],
  35. [:slope, 1.0])
  36. lambda do |samp| [0.0, 1.0 - (slope * (samp + offset)).abs].max end
  37. end
  38. def bowtable(b, sample)
  39. b.call(sample)
  40. end
  41. def jettable(sample)
  42. [-1.0, [1.0, sample * (sample * sample - 1.0)].min].max
  43. end
  44. def make_onezero(*args)
  45. gain, zerocoeff = nil
  46. optkey(args, binding,
  47. [:gain, 0.5],
  48. [:zerocoeff, 1.0])
  49. make_one_zero(gain, gain * zerocoeff)
  50. end
  51. def make_onep(*args)
  52. polecoeff = optkey(args, [:polecoeff, 0.9])
  53. make_one_pole(1.0 - polecoeff, -polecoeff)
  54. end
  55. def set_pole(p, val)
  56. set_mus_b1(p, -val)
  57. set_mus_a0(p, 1.0 - val)
  58. end
  59. def set_gain(p, val)
  60. set_mus_a0(p, mus_a0(p) * val)
  61. end
  62. def lip_set_freq(b, freq)
  63. set_mus_frequency(b, freq)
  64. end
  65. def lip(b, mouthsample, boresample)
  66. temp = formant(b, mouthsample - boresample)
  67. temp = [1.0, temp * temp].min
  68. temp * mouthsample + ((1.0 - temp) * boresample)
  69. end
  70. def make_dc_block
  71. input = output = 0.0
  72. lambda do |samp|
  73. output = samp + (0.99 * output - input)
  74. input = samp
  75. output
  76. end
  77. end
  78. def dc_block(b, sample)
  79. b.call(sample)
  80. end
  81. def make_delaya(len, lag)
  82. lastin = output = 0.0
  83. input = make_delay(len)
  84. outpointer = 2.0 - lag
  85. outpointer += len while outpointer <= 0.0
  86. outpoint = outpointer.floor
  87. alpha = outpointer - outpoint
  88. coeff = (1.0 - alpha) / (1.0 + alpha)
  89. outpoint = -outpoint
  90. lambda do |samp|
  91. delay(input, samp)
  92. temp = tap(input, outpoint)
  93. output = -coeff * output + lastin + temp * coeff
  94. lastin = temp
  95. output
  96. end
  97. end
  98. def delaya(d, sample)
  99. d.call(sample)
  100. end
  101. def make_delayl(len, lag)
  102. input = make_delay(len)
  103. outpointer = 1 - lag
  104. outpointer += len while outpointer <= 0.0
  105. outpoint = outpointer.floor
  106. alpha = outpointer - outpoint
  107. omalpha = 1.0 - alpha
  108. outpoint = -outpoint
  109. lambda do |samp|
  110. delay(input, samp)
  111. tap(input, outpoint - 1) * omalpha + tap(input, outpoint) * alpha
  112. end
  113. end
  114. def delayl(d, sample)
  115. d.call(sample)
  116. end
  117. # sample instruments
  118. def plucky(start, dur, freq, amp, maxa)
  119. len = (mus_srate / 100.0).floor + 1
  120. delayline = make_delaya(len, mus_srate / freq.to_f - 0.5)
  121. filter = make_onezero()
  122. dout = 0.0
  123. len.times do |i| dout = delaya(delayline, 0.99 * dout + maxa * (1.0 - random(2.0))) end
  124. run_instrument(start, dur) do
  125. dout = delaya(delayline, one_zero(filter, dout))
  126. amp * dout
  127. end
  128. end
  129. def bowstr(start, dur, freq, amp, maxa)
  130. len = (mus_srate / 100.0).floor + 1
  131. ratio = 0.8317
  132. temp = mus_srate / freq.to_f - 4.0
  133. neckdelay = make_delayl(len, temp * ratio)
  134. bridgedelay = make_delayl((len / 2.0).floor, temp * (1.0 - ratio))
  135. bowtab = make_bowtable(:slope, 3.0)
  136. filt = make_onep()
  137. rate = 0.001
  138. bowing = true
  139. bowvelocity = rate
  140. maxvelocity = maxa
  141. attackrate = rate
  142. durlen = seconds2samples(dur)
  143. ctr = 0
  144. release = (0.8 * durlen).floor
  145. bridgeout = 0.0
  146. neckout = 0.0
  147. set_pole(filt, 0.6)
  148. set_gain(filt, 0.3)
  149. run_instrument(start, dur) do
  150. bridgerefl = nutrefl = veldiff = stringvel = bowtemp = 0.0
  151. if bowing
  152. unless maxvelocity == bowvelocity
  153. if bowvelocity < maxvelocity
  154. bowvelocity += attackrate
  155. else
  156. bowvelocity -= attackrate
  157. end
  158. end
  159. else
  160. if bowvelocity > 0.0
  161. bowvelocity -= attackrate
  162. end
  163. end
  164. bowtemp = 0.3 * bowvelocity
  165. filt_output = one_pole(filt, bridgeout)
  166. bridgerefl = -filt_output
  167. nutrefl = -neckout
  168. stringvel = bridgerefl + nutrefl
  169. veldiff = bowtemp - stringvel
  170. veldiff = veldiff * bowtable(bowtab, veldiff)
  171. neckout = delayl(neckdelay, bridgerefl + veldiff)
  172. bridgeout = delayl(bridgedelay, nutrefl + veldiff)
  173. result = amp * 10.0 * filt_output
  174. if ctr == release
  175. bowing = false
  176. attackrate = 0.0005
  177. end
  178. ctr += 1
  179. result
  180. end
  181. end
  182. def brass(start, dur, freq, amp, maxa)
  183. len = (mus_srate / 100.0).floor + 1
  184. delayline = make_delaya(len, 1.0 + mus_srate / freq.to_f)
  185. lipfilter = make_formant()
  186. dcblocker = make_dc_block()
  187. blowing = true
  188. rate = 0.001
  189. breathpressure = 0.0
  190. maxpressure = maxa
  191. attackrate = rate
  192. durlen = seconds2samples(dur)
  193. release = (0.8 * durlen).floor
  194. ctr = 0
  195. dout = 0.0
  196. lip_set_freq(lipfilter, freq)
  197. run_instrument(start, dur) do
  198. if blowing
  199. unless maxpressure == breathpressure
  200. if breathpressure < maxpressure
  201. breathpressure += attackrate
  202. else
  203. breathpressure -= attackrate
  204. end
  205. end
  206. else
  207. if breathpressure > 0.0
  208. breathpressure -= attackrate
  209. end
  210. end
  211. dout = delaya(delayline,
  212. dc_block(dcblocker, lip(lipfilter, 0.3 * breathpressure, 0.9 * dout)))
  213. result = amp * dout
  214. if ctr == release
  215. blowing = false
  216. attackrate = 0.0005
  217. end
  218. ctr += 1
  219. result
  220. end
  221. end
  222. def clarinet(start, dur, freq, amp, maxa)
  223. len = (mus_srate / 100.0).floor + 1
  224. delayline = make_delayl(len, 0.5 * (mus_srate / freq.to_f) - 1.0)
  225. rtable = make_reed(:offset, 0.7, :slope, -0.3)
  226. filter = make_onezero()
  227. blowing = true
  228. breathpressure = 0.0
  229. rate = 0.001
  230. maxpressure = maxa
  231. attackrate = rate
  232. durlen = seconds2samples(dur)
  233. release = (0.8 * durlen).floor
  234. ctr = 0
  235. dout = 0.0
  236. run_instrument(start, dur) do
  237. pressurediff = 0.0
  238. if blowing
  239. unless maxpressure == breathpressure
  240. if breathpressure < maxpressure
  241. breathpressure += attackrate
  242. else
  243. breathpressure -= attackrate
  244. end
  245. end
  246. else
  247. if breathpressure > 0.0
  248. breathpressure -= attackrate
  249. end
  250. end
  251. pressurediff = one_zero(filter, -0.95 * dout) - breathpressure
  252. dout = delayl(delayline,
  253. breathpressure + pressurediff * reedtable(rtable, pressurediff))
  254. result = amp * dout
  255. if ctr == release
  256. blowing = false
  257. attackrate = 0.0005
  258. end
  259. ctr += 1
  260. result
  261. end
  262. end
  263. def flute(start, dur, freq, amp, maxa)
  264. len = (mus_srate / 100.0).floor + 1
  265. ratio = 0.8
  266. temp = mus_srate / freq.to_f - 0.5
  267. jetdelay = make_delayl((len / 2.0).floor, temp * (1.0 - ratio))
  268. boredelay = make_delayl(len, ratio * temp)
  269. filter = make_onep()
  270. dcblocker = make_dc_block()
  271. jetrefl = 0.6
  272. sinphase = 0.0
  273. blowing = true
  274. breathpressure = 0.0
  275. rate = 0.0005
  276. maxpressure = maxa
  277. attackrate = rate
  278. durlen = seconds2samples(dur)
  279. release = (0.8 * durlen).floor
  280. ctr = 0
  281. dout = 0.0
  282. set_pole(filter, 0.8)
  283. set_gain(filter, -1.0)
  284. run_instrument(start, dur) do
  285. randpressure = 0.1 * breathpressure * random(1.0)
  286. temp = 0.0
  287. pressurediff = 0.0
  288. sinphase += 0.0007
  289. sinphase -= 6.28 if sinphase > 6.28
  290. randpressure = randpressure + 0.05 * breathpressure * sin(sinphase)
  291. if blowing
  292. unless maxpressure == breathpressure
  293. if breathpressure < maxpressure
  294. breathpressure += attackrate
  295. else
  296. breathpressure -= attackrate
  297. end
  298. end
  299. else
  300. if breathpressure > 0.0
  301. breathpressure -= attackrate
  302. end
  303. end
  304. temp = dc_block(dcblocker, one_pole(filter, dout))
  305. pressurediff = jettable(delayl(jetdelay,
  306. breathpressure + (randpressure - jetrefl * temp)))
  307. dout = delayl(boredelay, pressurediff)
  308. result = 0.3 * amp * dout
  309. if ctr == release
  310. blowing = false
  311. attackrate = 0.0005
  312. end
  313. ctr += 1
  314. result
  315. end
  316. end
  317. end
  318. include PRC
  319. # prc95.rb ends here