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.

288 lines
12KB

  1. ;;; SNDWARP
  2. (provide 'snd-sndwarp.scm)
  3. (if (not (provided? 'snd-env.scm)) (load "env.scm")) ; normalize-envelope
  4. ;;;
  5. ;;; CLM 3 implementation of Richard Karpen's SNDWARP Csound Ugen.
  6. ;;; By Bret Battey. http://www.BatHatMedia.com
  7. ;;; translated to Scheme by Bill S Feb-05
  8. ;;; changes for the optimizer 24-Oct-06
  9. ;;;
  10. ;;; Except as noted below, the parameters are modeled directly after
  11. ;;; the Csound version of sndwarp.
  12. ;;; ISSUES
  13. ;;;
  14. ;;; Output in this new CLM version is seeming quite noisy/clipped (?)
  15. ;;; Varying stereo/mono input/output has not been tested in clm2 alterations.
  16. ;;; Hasn't been tested with differing input and output file sampling rates.
  17. ;;; Uses half-sine envelope only; doesn't support alternative windowing envs.
  18. ;;; Csound-style attack doesn't strictly match Csound results
  19. ;;; SNDWARP KEY PARAMETERS
  20. ;;;
  21. ;;; amp = Amplitude
  22. ;;; [number]
  23. ;;;
  24. ;;; amp-env = Amplitude envelope
  25. ;;; [envelope]
  26. ;;;
  27. ;;; stretch = Stretch value or time pointer envelope (see 'time-ptr')
  28. ;;; [number or envelope expressed in either stretch values
  29. ;;; (for stretch mode) or in seconds (in time-ptr mode)]
  30. ;;;
  31. ;;; srate = Resampling scalar (1 = same pitch, .5 = 1 octave lower, etc.)
  32. ;;; A negative srate will read backwards into the soundfile from
  33. ;;; the start of each read window (not available in Csound version).
  34. ;;; [number or envelope]
  35. ;;;
  36. ;;; inputbeg = Source file input offset. In 'stretch' mode (see 'time-ptr'),
  37. ;;; soundfile read will begin at inputbeg. In 'time-ptr' mode,
  38. ;;; inputbeg will be added to the time pointer.
  39. ;;; [number, in seconds]
  40. ;;;
  41. ;;; wsize = Size of the sndwarp windows.
  42. ;;; [number, in seconds]
  43. ;;;
  44. ;;; randw = Range of random values to be added to wsize
  45. ;;; [number, in seconds]
  46. ;;;
  47. ;;; overlaps = number of window overlaps
  48. ;;; [number per second]
  49. ;;;
  50. ;;; time-ptr = Flag to determine whether stretching or time-pointer mode
  51. ;;; is to be used in interpreting the 'stretch' parameter.
  52. ;;; In stretch mode, the value of 'stretch' will scale the time
  53. ;;; of the sound. For example, a value of 2 will stretch the sound
  54. ;;; by 2 times. Note that stretch values of or near 0 are not
  55. ;;; viable since window advance times are determined by dividing
  56. ;;; by the stretch value.
  57. ;;; In time-ptr mode, the value(s) of stretch are readin pointers
  58. ;;; into the soundfile. For example, to read through a file
  59. ;;; backwards from 2 seconds at half speed, one would use a
  60. ;;; stretch envelope like [0 2 1 0] with a 4 second note duration.
  61. ;;; [NIL = stretch mode, T = time-ptr mode]
  62. ;;;
  63. ;;; scale-time-ptr = Flag to determine whether the time-ptr envelope will be
  64. ;;; interpreted in absolute seconds or rescaled to fit the
  65. ;;; duration of the input sound file.
  66. ;;; {not part of csound implementation}
  67. ;;; [NIL = absolute, T = rescale]
  68. ;;;
  69. ;;; zero-start-time-ptr = Flag to determine when in time-ptr mode whether
  70. ;;; the first section of windows will start at
  71. ;;; time-ptr = 0.
  72. ;;; The csound sndwarp makes this assumption, so you
  73. ;;; always get a bit of the attack of the sound even
  74. ;;; if you try to run the time pointer starting in
  75. ;;; the middle or end.
  76. ;;; [NIL = first section starts according to time-ptr,
  77. ;;; T = first section always starts at time-ptr = 0]
  78. ;;;
  79. ;;; sndwarp-window-offset = Flag to determine how the windows are offset
  80. ;;; in time. T = Csound sndwarp style, windows
  81. ;;; in different layers line up.
  82. ;;; NIL = spread evenly.
  83. ;;;
  84. ;;; loc = Stereo panning position, where 0 = left and 1 = right.
  85. ;;; Uses simple sqrt method.
  86. ;;; [number or envelope]
  87. ;;;
  88. ;;; rev = Scalar for reverb sending to a CLM reverb instrument.
  89. ;;; [number or envelope]
  90. ;;;
  91. ;;; status = Flag to control whether SNDWARP prints a window %-complete count
  92. ;;; while working.
  93. ;;; [NIL = no status printing, T = status printing]
  94. ;;;
  95. ;;; srcwidth = width of the sinc function used in the interpolation function of
  96. ;;; CLM's "src" -- which provides the resampling in sndwarp. Defaults to
  97. ;;; 5. If you hear high-frequency artifacts in the output sound, try
  98. ;;; increasing this number.
  99. ;;;
  100. ;;; SNDWARP DEFAULTS
  101. (define sndwarp-amp 1.0)
  102. (define sndwarp-amp-env '(0 1 100 1))
  103. (define sndwarp-stretch 1.0)
  104. (define sndwarp-srate 1.0)
  105. (define sndwarp-inputbeg 0.0)
  106. (define sndwarp-wsize 0.1) ; csound manual recommended start = .1
  107. (define sndwarp-randw 0.02) ; csound manual recommended start = .02
  108. (define sndwarp-overlaps 15) ; csound manual recommended start = 15
  109. (define sndwarp-time-ptr #f)
  110. (define sndwarp-scale-time-ptr #f)
  111. (define sndwarp-zero-start-time-ptr #f) ; #t to match csound
  112. (define sndwarp-window-offset #f) ; #t to match csound
  113. (define sndwarp-loc 0.5)
  114. (define sndwarp-rev 0.1)
  115. (define sndwarp-srcwidth 5)
  116. ;;; UTILITY FUNCTIONS
  117. (define clmsw-2pi (* 2 pi))
  118. ;;; SNDWARP
  119. (define* (sndwarp begtime dur file
  120. (amp sndwarp-amp)
  121. (amp-env sndwarp-amp-env)
  122. (stretch sndwarp-stretch)
  123. (srate sndwarp-srate)
  124. (inputbeg sndwarp-inputbeg)
  125. (wsize sndwarp-wsize)
  126. (randw sndwarp-randw)
  127. (overlaps sndwarp-overlaps)
  128. (time-ptr sndwarp-time-ptr)
  129. (scale-time-ptr sndwarp-scale-time-ptr)
  130. (zero-start-time-ptr sndwarp-zero-start-time-ptr)
  131. (window-offset sndwarp-window-offset)
  132. (loc sndwarp-loc)
  133. (rev sndwarp-rev)
  134. (srcwidth sndwarp-srcwidth))
  135. (define (clmsw-envelope-or-number in)
  136. (if (number? in) (list 0 in 1 in) in))
  137. (let* ((stereo-i (= (mus-sound-chans file) 2))
  138. (f-a (make-readin file :channel 0))
  139. (f-b (and stereo-i
  140. (make-readin file :channel 1))))
  141. (let ((beg (seconds->samples begtime))
  142. (fsr (mus-sound-srate file))
  143. (rdA (make-src :input (lambda (dir) (readin f-a)) :srate 0.0 :width srcwidth))
  144. (rdB (and stereo-i
  145. (make-src :input (lambda (dir) (readin f-b)) :srate 0.0 :width srcwidth)))
  146. (windf (make-oscil))
  147. (wsizef (make-env (clmsw-envelope-or-number wsize) :duration dur))
  148. (ampf (make-env amp-env :scaler amp :duration dur))
  149. (sratef (make-env (clmsw-envelope-or-number srate) :duration dur))
  150. (timef (let ((time-env (clmsw-envelope-or-number stretch))
  151. (fdur (mus-sound-duration file)))
  152. (make-env
  153. (if (and time-ptr scale-time-ptr)
  154. (normalize-envelope time-env (- fdur inputbeg))
  155. time-env)
  156. :duration dur)))
  157. (locf (make-env (clmsw-envelope-or-number loc) :duration dur)))
  158. (let ((end (+ beg (seconds->samples dur)))
  159. (stereo-o #f)
  160. (writestart 0)
  161. (readstart (round (* fsr inputbeg)))
  162. (eow-flag #f)
  163. (overlap-ratio 0.0000)
  164. (overlap-ratio-compl 0.0000)
  165. (outa-val 0.0000)
  166. (outb-val 0.0000))
  167. (do ((overlap 0 (+ 1 overlap)))
  168. ((or eow-flag (= overlap overlaps)))
  169. (set! overlap-ratio (/ overlap overlaps))
  170. (set! overlap-ratio-compl (- 1 overlap-ratio))
  171. (set! eow-flag #f)
  172. (set! writestart beg)
  173. (set! (mus-location ampf) beg)
  174. (set! (mus-location locf) beg)
  175. (do ((section 0 (+ 1 section)))
  176. ((or eow-flag (= overlap overlaps)))
  177. (set! (mus-location timef) writestart)
  178. (set! (mus-location sratef) writestart)
  179. (set! (mus-location wsizef) writestart)
  180. (set! wsize (env wsizef))
  181. (let* ((winlen (if (= overlap 0 section) ; first section of first overlap isn't randomized
  182. wsize
  183. (+ wsize (random randw))))
  184. (winsamps (seconds->samples winlen))
  185. (srate-val (env sratef)))
  186. (let ((time-val (env timef)))
  187. ;; Even for the first section's truncated envelopes, the frequency of the envelope must be as if the envelope were full duration.
  188. (set! (mus-frequency windf) (* .5 (/ fsr winsamps)))
  189. ;; Set windowing oscillator to starting phase and appropriate frequency to provide half-sine envelope over window.
  190. ;; Phase must be altered for first envelope of each overlap stream.
  191. (set! (mus-phase windf)
  192. (if (and (= section 0)
  193. (not (= overlap 0)))
  194. (* .5 clmsw-2pi overlap-ratio-compl)
  195. 0.0))
  196. ;; Either use the absolute time pointer or a scaled increment.
  197. ;; If first section in scaled mode, must initialize section readstart to beginning plus first overlap position.
  198. ;; In both cases, need to alter readstart and length of first section's windows based on phase of overlap
  199. (if time-ptr
  200. ;; TIME-PTR mode
  201. (if (= section 0)
  202. ;; initial section
  203. (let ((overlap-start
  204. (if (and window-offset
  205. (not (= overlap 0)))
  206. ;; Csound style - start each overlap series further into the soundfile
  207. (round (* winlen overlap-ratio-compl))
  208. ;; Alternative style - start each overlap series at 0
  209. 0))
  210. ;; To match csound version, first section must start reading at 0. Using zero-start-time-ptr
  211. ;; flag = #f, however, allows first section to start as determined by time-ptr instead.
  212. (adj-time-val (if zero-start-time-ptr 0.0 time-val)))
  213. (set! readstart (round (* fsr (+ inputbeg overlap-start adj-time-val))))
  214. (if (not (= overlap 0)) (set! winsamps (floor (* winsamps overlap-ratio)))))
  215. ;; remaining sections
  216. (set! readstart (round (* fsr (+ inputbeg time-val)))))
  217. ;; STRETCH mode
  218. (if (= section 0)
  219. ;; initial section
  220. (let ((init-read-start
  221. (if (and window-offset
  222. (not (= overlap 0)))
  223. ;; Csound style - start each overlap series further into the soundfile
  224. (round (* winlen overlap-ratio-compl))
  225. ;; Alternative style - start each overlap series at 0
  226. 0)))
  227. (set! readstart (round (* fsr (+ inputbeg init-read-start))))
  228. (if (not (= overlap 0)) (set! winsamps (floor (* winsamps overlap-ratio)))))
  229. ;; remaining sections
  230. (set! readstart (round (+ readstart (* fsr (/ winlen time-val))))))))
  231. ;; Set readin position and sampling rate
  232. (set! (mus-location f-a) readstart)
  233. (set! (mus-increment rdA) srate-val)
  234. (mus-reset rdA)
  235. (if stereo-i
  236. (begin
  237. (set! (mus-location f-b) readstart)
  238. (set! (mus-increment rdB) srate-val)
  239. (mus-reset rdB)))
  240. ;; Write window out
  241. (do ((k 0 (+ 1 k))
  242. (i writestart (+ i 1)))
  243. ((or eow-flag (= k winsamps)))
  244. (if (> i end)
  245. (begin
  246. (set! eow-flag #t)
  247. (set! overlap (+ 1 overlaps)))
  248. (let* ((amp-val (env ampf))
  249. (loc-val (env locf))
  250. (win-val (oscil windf))
  251. (sampa (* (src rdA) win-val))
  252. (sampb (if stereo-i (* (src rdB) win-val))))
  253. ;; channel panning
  254. (if stereo-o
  255. (let ((apan (sqrt loc-val))
  256. (bpan (sqrt (- 1 loc-val))))
  257. (set! outa-val (* amp-val apan sampa))
  258. (set! outb-val (* amp-val bpan (if stereo-i sampb sampa))))
  259. ;; stereo in, mono out
  260. (set! outa-val (* amp-val (if stereo-i
  261. (* (+ sampa sampb) .75)
  262. ;; mono in, mono out
  263. sampa))))
  264. ;; output
  265. (outa i outa-val)
  266. (if stereo-o
  267. (begin
  268. (outb i outb-val)
  269. (if *reverb* (outa i (* rev outa-val) *reverb*)))))))
  270. (if (and (not eow-flag) ;; For first section, have to backup readstart
  271. (= section 0)
  272. (> overlap 0)
  273. (not time-ptr))
  274. (set! readstart (- readstart (round (* fsr winlen overlap-ratio-compl)))))
  275. (set! writestart (+ writestart winsamps)))))))))