選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

562 行
18KB

  1. ;;; various envelope functions
  2. ;;;
  3. ;;; window-envelope (beg end env) -> portion of env lying between x axis values beg and end
  4. ;;; map-envelopes (func env1 env2) maps func over the breakpoints in env1 and env2 returning a new envelope
  5. ;;; multiply-envelopes (env1 env2) multiplies break-points of env1 and env2 returning a new envelope
  6. ;;; add-envelopes (env1 env2) adds break-points of env1 and env2 returning a new envelope
  7. ;;; max-envelope (env) -> max y value in env, min-envelope
  8. ;;; integrate-envelope (env) -> area under env
  9. ;;; envelope-last-x (env) -> max x axis break point position
  10. ;;; stretch-envelope env old-attack new-attack old-decay new-decay -> divseg-like envelope mangler
  11. ;;; scale-envelope (env scaler offset) scales y axis values by 'scaler' and optionally adds 'offset'
  12. ;;; reverse-envelope (env) reverses the breakpoints in 'env'
  13. ;;; concatenate-envelopes (:rest envs) concatenates its arguments into a new envelope
  14. ;;; repeat-envelope env repeats (reflected #f) (normalized #f) repeats an envelope
  15. ;;; power-env: generator for extended envelopes (each segment has its own base)
  16. ;;; envelope-exp: interpolate segments into envelope to give exponential curves
  17. ;;; rms-envelope
  18. ;;; normalize-envelope
  19. ;;; simplify-envelope
  20. (provide 'snd-env.scm)
  21. ;;; -------- window-envelope (a kinda brute-force translation from the CL version in env.lisp)
  22. (define window-envelope
  23. (let ((documentation "(window-envelope beg end e) -> portion of e lying between x axis values beg and
  24. end: (window-envelope 1.0 3.0 '(0.0 0.0 5.0 1.0)) -> '(1.0 0.2 3.0 0.6)"))
  25. (lambda (beg end e)
  26. (let ((nenv ())
  27. (lasty (if (pair? e) (cadr e) 0.0))
  28. (len (length e)))
  29. (call-with-exit
  30. (lambda (return-early)
  31. (do ((i 0 (+ i 2)))
  32. ((>= i len))
  33. (let ((x (e i))
  34. (y (e (+ i 1))))
  35. (set! lasty y)
  36. (cond ((null? nenv)
  37. (when (>= x beg)
  38. (set! nenv (append nenv (list beg (envelope-interp beg e))))
  39. (if (not (= x beg))
  40. (if (>= x end)
  41. (return-early (append nenv (list end (envelope-interp end e))))
  42. (set! nenv (append nenv (list x y)))))))
  43. ((<= x end)
  44. (set! nenv (append nenv (list x y)))
  45. (if (= x end) (return-early nenv)))
  46. ((> x end)
  47. (return-early
  48. (append nenv (list end (envelope-interp end e))))))))
  49. (append nenv (list end lasty))))))))
  50. ;;; -------- map-envelopes like map-across-envelopes in env.lisp
  51. (define map-envelopes
  52. (let ((documentation "(map-envelopes func env1 env2) maps func over the breakpoints in env1 and env2 returning a new envelope"))
  53. (lambda (op e1 e2)
  54. (let ((xs ()))
  55. (let ((at0
  56. (lambda (e)
  57. (let* ((diff (car e))
  58. (len (length e))
  59. (lastx (e (- len 2)))
  60. (newe (copy e)))
  61. (do ((i 0 (+ i 2)))
  62. ((>= i len) newe)
  63. (let ((x (/ (- (newe i) diff) lastx)))
  64. (set! xs (cons x xs))
  65. (set! (newe i) x))))))
  66. (remove-duplicates
  67. (lambda (lst)
  68. (let rem-dup ((lst lst)
  69. (nlst ()))
  70. (cond ((null? lst) nlst)
  71. ((member (car lst) nlst) (rem-dup (cdr lst) nlst))
  72. (else (rem-dup (cdr lst) (cons (car lst) nlst))))))))
  73. (if (null? e1)
  74. (at0 e2)
  75. (if (null? e2)
  76. (at0 e1)
  77. (let ((ee1 (at0 e1))
  78. (ee2 (at0 e2))
  79. (newe ()))
  80. (set! xs (sort! (remove-duplicates xs) <))
  81. (do ((len (length xs))
  82. (i 0 (+ i 1)))
  83. ((= i len) newe)
  84. (let ((x (xs i)))
  85. (set! newe (append newe (list x (op (envelope-interp x ee1) (envelope-interp x ee2)))))))))))))))
  86. ;;; -------- multiply-envelopes, add-envelopes
  87. (define multiply-envelopes
  88. (let ((documentation "(multiply-envelopes env1 env2) multiplies break-points of env1 and env2 returning a new
  89. envelope: (multiply-envelopes '(0 0 2 .5) '(0 0 1 2 2 1)) -> '(0 0 0.5 0.5 1.0 0.5)"))
  90. (lambda (e1 e2)
  91. (map-envelopes * e1 e2))))
  92. (define add-envelopes
  93. (let ((documentation "(add-envelopes env1 env2) adds break-points of env1 and env2 returning a new envelope"))
  94. (lambda (e1 e2)
  95. (map-envelopes + e1 e2))))
  96. ;;; -------- max-envelope
  97. (define max-envelope
  98. (let ((documentation "(max-envelope env) -> max y value in env"))
  99. (lambda (env1)
  100. (let max-envelope-1 ((e (cddr env1))
  101. (mx (cadr env1)))
  102. (if (null? e)
  103. mx
  104. (max-envelope-1 (cddr e) (max mx (cadr e))))))))
  105. ;;; -------- min-envelope
  106. (define min-envelope
  107. (let ((documentation "(min-envelope env) -> min y value in env"))
  108. (lambda (env1)
  109. (let min-envelope-1 ((e (cddr env1))
  110. (mx (cadr env1)))
  111. (if (null? e)
  112. mx
  113. (min-envelope-1 (cddr e) (min mx (cadr e))))))))
  114. ;;; -------- integrate-envelope
  115. (define integrate-envelope
  116. (let ((documentation "(integrate-envelope env) -> area under env"))
  117. (lambda (env1)
  118. (let integrate-envelope-1 ((e env1)
  119. (sum 0.0000))
  120. (if (or (null? e) (null? (cddr e)))
  121. sum
  122. (integrate-envelope-1 (cddr e) (+ sum (* (+ (cadr e) (cadddr e)) 0.5 (- (caddr e) (car e))))))))))
  123. ;;; -------- envelope-last-x
  124. (define envelope-last-x
  125. (let ((documentation "(envelope-last-x env) -> max x axis break point position"))
  126. (lambda (e)
  127. (if (null? (cddr e))
  128. (car e)
  129. (envelope-last-x (cddr e))))))
  130. ;;; -------- stretch-envelope
  131. (define stretch-envelope
  132. (let ((documentation "(stretch-envelope env old-attack new-attack old-decay new-decay) takes 'env' and
  133. returns a new envelope based on it but with the attack and optionally decay portions stretched
  134. or squeezed; 'old-attack' is the original x axis attack end point, 'new-attack' is where that
  135. section should end in the new envelope. Similarly for 'old-decay' and 'new-decay'. This mimics
  136. divseg in early versions of CLM and its antecedents in Sambox and Mus10 (linen).
  137. (stretch-envelope '(0 0 1 1) .1 .2) -> (0 0 0.2 0.1 1.0 1)
  138. (stretch-envelope '(0 0 1 1 2 0) .1 .2 1.5 1.6) -> (0 0 0.2 0.1 1.1 1 1.6 0.5 2.0 0)"))
  139. (lambda* (fn old-att new-att old-dec new-dec)
  140. (cond ((not new-att)
  141. (if old-att
  142. (error 'wrong-number-of-args "stretch-envelope: ~A, old-attack but no new-attack?" old-att)
  143. fn))
  144. ((and old-dec (not new-dec))
  145. (error 'wrong-number-of-args "stretch-envelope:~A ~A ~A, old-decay but no new-decay?" old-att new-att old-dec))
  146. (else
  147. (let ((x0 (car fn))
  148. (y0 (cadr fn)))
  149. (let ((new-x x0)
  150. (last-x (fn (- (length fn) 2)))
  151. (new-fn (list y0 x0))
  152. (scl (/ (- new-att x0) (max .0001 (- old-att x0)))))
  153. (if (and (number? old-dec)
  154. (= old-dec old-att))
  155. (set! old-dec (* 1e-06 last-x)))
  156. (reverse
  157. (let stretch-envelope-1 ((new-fn new-fn)
  158. (old-fn (cddr fn)))
  159. (if (null? old-fn)
  160. new-fn
  161. (let ((x1 (car old-fn))
  162. (y1 (cadr old-fn)))
  163. (when (and (< x0 old-att) (>= x1 old-att))
  164. (set! y0 (if (= x1 old-att)
  165. y1
  166. (+ y0 (* (- y1 y0) (/ (- old-att x0) (- x1 x0))))))
  167. (set! x0 old-att)
  168. (set! new-x new-att)
  169. (set! new-fn (cons y0 (cons new-x new-fn)))
  170. (set! scl (if old-dec
  171. (/ (- new-dec new-att) (- old-dec old-att))
  172. (/ (- last-x new-att) (- last-x old-att)))))
  173. (when (and (real? old-dec)
  174. (< x0 old-dec)
  175. (>= x1 old-dec))
  176. (set! y0 (if (= x1 old-dec)
  177. y1
  178. (+ y0 (* (- y1 y0) (/ (- old-dec x0) (- x1 x0))))))
  179. (set! x0 old-dec)
  180. (set! new-x new-dec)
  181. (set! new-fn (cons y0 (cons new-x new-fn)))
  182. (set! scl (/ (- last-x new-dec) (- last-x old-dec))))
  183. (unless (= x0 x1)
  184. (set! new-x (+ new-x (* scl (- x1 x0))))
  185. (set! new-fn (cons y1 (cons new-x new-fn)))
  186. (set! x0 x1)
  187. (set! y0 y1))
  188. (stretch-envelope-1 new-fn (cddr old-fn)))))))))))))
  189. ;;; -------- scale-envelope
  190. (define scale-envelope
  191. (let ((documentation "(scale-envelope env scaler (offset 0)) scales y axis values by 'scaler' and optionally adds 'offset'"))
  192. (lambda* (e scl (offset 0))
  193. (if (null? e)
  194. ()
  195. (cons (car e) (cons (+ offset (* scl (cadr e))) (scale-envelope (cddr e) scl offset)))))))
  196. ;;; -------- reverse-envelope
  197. (define reverse-envelope
  198. (let ((documentation "(reverse-envelope env) reverses the breakpoints in 'env'"))
  199. (lambda (e)
  200. (define (reverse-env-1 e newe xd)
  201. (if (null? e)
  202. newe
  203. (reverse-env-1 (cddr e)
  204. (cons (- xd (car e))
  205. (cons (cadr e)
  206. newe))
  207. xd)))
  208. (let ((len (length e)))
  209. (if (memv len '(0 2))
  210. e
  211. (reverse-env-1 e () (e (- len 2))))))))
  212. ;;; -------- concatenate-envelopes
  213. (define concatenate-envelopes
  214. (let ((documentation "(concatenate-envelopes :rest envs) concatenates its arguments into a new envelope"))
  215. (lambda envs
  216. (define (cat-1 e newe xoff x0)
  217. (if (null? e)
  218. newe
  219. (cat-1 (cddr e)
  220. (cons (cadr e)
  221. (cons (- (+ (car e) xoff) x0)
  222. newe))
  223. xoff
  224. x0)))
  225. (let ((ne ())
  226. (xoff 0.0))
  227. (for-each
  228. (lambda (e)
  229. (if (and (pair? ne)
  230. (= (car ne) (cadr e)))
  231. (begin
  232. (set! xoff (- xoff .01))
  233. (set! ne (cat-1 (cddr e) ne xoff (car e))))
  234. (set! ne (cat-1 e ne xoff (car e))))
  235. (set! xoff (+ xoff .01 (cadr ne))))
  236. envs)
  237. (reverse ne)))))
  238. (define repeat-envelope
  239. (let ((documentation "(repeat-envelope env repeats (reflected #f) (normalized #f)) repeats 'env' 'repeats'
  240. times. (repeat-envelope '(0 0 100 1) 2) -> (0 0 100 1 101 0 201 1).
  241. If the final y value is different from the first y value, a quick ramp is
  242. inserted between repeats. 'normalized' causes the new envelope's x axis
  243. to have the same extent as the original's. 'reflected' causes every other
  244. repetition to be in reverse."))
  245. (lambda* (ur-env repeats reflected normalized)
  246. (let ((e (if (not reflected)
  247. ur-env
  248. (let ((lastx (ur-env (- (length ur-env) 2)))
  249. (rev-env (cddr (reverse ur-env)))
  250. (new-env (reverse ur-env)))
  251. (while (pair? rev-env)
  252. (set! new-env (cons (- (+ lastx lastx) (cadr rev-env)) new-env))
  253. (set! new-env (cons (car rev-env) new-env))
  254. (set! rev-env (cddr rev-env)))
  255. (reverse new-env)))))
  256. (let ((first-y (cadr e))
  257. (x (car e)))
  258. (let ((x-max (e (- (length e) 2)))
  259. (new-env (list first-y x)))
  260. (let ((len (length e))
  261. (times (if reflected (floor (/ repeats 2)) repeats))
  262. (first-y-is-last-y (= first-y (e (- (length e) 1)))))
  263. (do ((i 0 (+ i 1)))
  264. ((= i times))
  265. (do ((j 2 (+ j 2)))
  266. ((>= j len))
  267. (set! x (- (+ x (e j)) (e (- j 2))))
  268. (set! new-env (cons (e (+ j 1)) (cons x new-env))))
  269. (if (and (< i (- times 1)) (not first-y-is-last-y))
  270. (begin
  271. (set! x (+ x (/ x-max 100.0)))
  272. (set! new-env (cons first-y (cons x new-env)))))))
  273. (set! new-env (reverse new-env))
  274. (if normalized
  275. (do ((scl (/ x-max x))
  276. (new-len (length new-env))
  277. (i 0 (+ i 2)))
  278. ((>= i new-len))
  279. (set! (new-env i) (* scl (new-env i)))))
  280. new-env))))))
  281. ;;; -------- power-env
  282. ;;;
  283. ;;; (this could also be done using multi-expt-env (based on env-any) in generators.scm)
  284. (if (provided? 'snd)
  285. (require snd-ws.scm)
  286. (require sndlib-ws.scm))
  287. ;;; (define pe (make-power-env '(0 0 1 1 2 0) :duration 1.0))
  288. ;;; :(power-env pe)
  289. ;;; 0.0
  290. ;;; :(power-env pe)
  291. ;;; 4.5352502324316e-05
  292. ;;; :(power-env pe)
  293. ;;; 9.0705004648631e-05
  294. ;;; :(power-env pe)
  295. ;;; 0.00013605750697295
  296. (defgenerator penv (envs #f) (total-envs 0) (current-env 0) (current-pass 0))
  297. (define (power-env pe)
  298. (with-let pe
  299. (let ((val (env (vector-ref envs current-env))))
  300. (set! current-pass (- current-pass 1))
  301. (when (and (= current-pass 0)
  302. (< current-env (- total-envs 1)))
  303. (set! current-env (+ current-env 1))
  304. (set! current-pass (- (length (vector-ref envs current-env)) 1)))
  305. val)))
  306. (define* (make-power-env envelope (scaler 1.0) (offset 0.0) duration)
  307. (let* ((len (- (floor (/ (length envelope) 3)) 1))
  308. (pe (make-penv :envs (make-vector len)
  309. :total-envs len
  310. :current-env 0
  311. :current-pass 0))
  312. (xext (- (envelope (- (length envelope) 3)) (car envelope))))
  313. (do ((i 0 (+ i 1))
  314. (j 0 (+ j 3)))
  315. ((= i len))
  316. (let ((x0 (envelope j))
  317. (x1 (envelope (+ j 3)))
  318. (y0 (envelope (+ j 1)))
  319. (y1 (envelope (+ j 4)))
  320. (base (envelope (+ j 2))))
  321. (vector-set! (pe 'envs) i (make-env (list 0.0 y0 1.0 y1)
  322. :base base :scaler scaler :offset offset
  323. :duration (* duration (/ (- x1 x0) xext))))))
  324. (set! (pe 'current-pass) (- (length (vector-ref (pe 'envs) 0)) 1))
  325. pe))
  326. (define* (power-env-channel pe (beg 0) snd chn edpos (edname "power-env-channel"))
  327. ;; split into successive calls on env-channel
  328. (let ((curbeg beg)) ; sample number
  329. (as-one-edit
  330. (lambda ()
  331. (do ((i 0 (+ i 1)))
  332. ((= i (pe 'total-envs)))
  333. (let* ((e (vector-ref (pe 'envs) i))
  334. (len (length e)))
  335. (env-channel e curbeg len snd chn edpos)
  336. (set! curbeg (+ curbeg len)))))
  337. edname)))
  338. ;;; here's a simpler version that takes the breakpoint list, rather than the power-env structure:
  339. (define powenv-channel
  340. (let ((documentation "(powenv-channel envelope (beg 0) dur snd chn edpos) returns an envelope with a separate base for \
  341. each segment: (powenv-channel '(0 0 .325 1 1 32.0 2 0 32.0))"))
  342. (lambda* (envelope (beg 0) dur snd chn edpos)
  343. (let ((len (length envelope))
  344. (x1 (car envelope)))
  345. (let ((curbeg beg)
  346. (fulldur (or dur (framples snd chn edpos)))
  347. (xrange (- (envelope (- len 3)) x1))
  348. (y1 (cadr envelope))
  349. (base (caddr envelope))
  350. (x0 0.0)
  351. (y0 0.0))
  352. (if (= len 3)
  353. (scale-channel y1 beg dur snd chn edpos)
  354. (as-one-edit
  355. (lambda ()
  356. (do ((i 3 (+ i 3)))
  357. ((= i len))
  358. (set! x0 x1)
  359. (set! y0 y1)
  360. (set! x1 (envelope i))
  361. (set! y1 (envelope (+ i 1)))
  362. (let ((curdur (round (* fulldur (/ (- x1 x0) xrange)))))
  363. (xramp-channel y0 y1 base curbeg curdur snd chn edpos)
  364. (set! curbeg (+ curbeg curdur)))
  365. (set! base (envelope (+ i 2))))))))))))
  366. ;;; by Anders Vinjar:
  367. ;;;
  368. ;;; envelope-exp can be used to create exponential segments to include in
  369. ;;; envelopes. Given 2 or more breakpoints, it approximates the
  370. ;;; curve between them using 'xgrid linesegments and 'power as the
  371. ;;; exponent.
  372. ;;;
  373. ;;; env is a list of x-y-breakpoint-pairs,
  374. ;;; power applies to whole envelope,
  375. ;;; xgrid is how fine a solution to sample our new envelope with.
  376. (define envelope-exp
  377. (let ((documentation "(envelope-exp e (power 1.0) (xgrid 100)) approximates an exponential curve connecting the breakpoints"))
  378. (lambda* (e (power 1.0) (xgrid 100))
  379. (let ((mn (min-envelope e)))
  380. (let ((largest-diff (* 1.0 (- (max-envelope e) mn)))
  381. (x-min (car e))
  382. (x-max (e (- (length e) 2))))
  383. (do ((x-incr (* 1.0 (/ (- x-max x-min) xgrid)))
  384. (new-e ())
  385. (x x-min (+ x x-incr)))
  386. ((>= x x-max)
  387. (reverse new-e))
  388. (let ((y (envelope-interp x e)))
  389. (set! new-e (cons (if (= largest-diff 0.0)
  390. y
  391. (+ mn
  392. (* largest-diff
  393. (expt (/ (- y mn) largest-diff) power))))
  394. (cons x new-e))))))))))
  395. ;;; rms-envelope
  396. (define rms-envelope
  397. (let ((documentation "(rms-envelope file (beg 0.0) (dur #f) (rfreq 30.0) (db #f)) returns an envelope of RMS values in 'file'"))
  398. (lambda* (file (beg 0.0) dur (rfreq 30.0) db)
  399. ;; based on rmsenv.ins by Bret Battey
  400. (let* ((fsr (srate file))
  401. (start (round (* beg fsr)))
  402. (end (if dur (min (* 1.0 (+ start (round (* fsr dur))))
  403. (mus-sound-framples file))
  404. (mus-sound-framples file))))
  405. (let ((incrsamps (round (/ fsr rfreq)))
  406. (len (- (+ end 1) start)))
  407. (let ((reader (make-sampler start file))
  408. (rms (make-moving-average incrsamps)) ; this could use make-moving-rms from dsp.scm
  409. (e ())
  410. (rms-val 0.0)
  411. (jend 0)
  412. (data (make-float-vector len)))
  413. (do ((i 0 (+ i 1)))
  414. ((= i len))
  415. (float-vector-set! data i (next-sample reader)))
  416. (float-vector-multiply! data data)
  417. (do ((i 0 (+ i incrsamps)))
  418. ((>= i end)
  419. (reverse e))
  420. (set! jend (min end (+ i incrsamps)))
  421. (do ((j i (+ j 1)))
  422. ((= j jend))
  423. (moving-average rms (float-vector-ref data j)))
  424. (set! e (cons (* 1.0 (/ i fsr)) e))
  425. (set! rms-val (sqrt (* (mus-scaler rms) (mus-increment rms))))
  426. (set! e (cons (if db
  427. (if (< rms-val 1e-05) -100.0 (* 20.0 (log rms-val 10.0)))
  428. rms-val)
  429. e)))))))))
  430. (define* (normalize-envelope env1 (new-max 1.0))
  431. (scale-envelope env1
  432. (/ new-max
  433. (let abs-max-envelope-1 ((e (cddr env1))
  434. (mx (abs (cadr env1))))
  435. (if (null? e)
  436. mx
  437. (abs-max-envelope-1 (cddr e) (max mx (abs (cadr e)))))))))
  438. ;;; simplify-envelope
  439. ;;;
  440. ;;; this is not very good...
  441. (define* (simplify-envelope env1 (ygrid 10) (xgrid 100))
  442. ;; grid = how fine a fluctuation we will allow.
  443. ;; the smaller the grid, the less likely a given bump will get through
  444. ;; original x and y values are not changed, just sometimes omitted.
  445. (define (point-on-line? px py qx qy tx ty)
  446. ;; is point tx ty on line defined by px py and qx qy --
  447. ;; #f if no, :before if on ray from p, :after if on ray from q, :within if between p and q
  448. ;; (these are looking at the "line" as a fat vector drawn on a grid)
  449. ;; taken from "Graphics Gems" by Glassner, code by A Paeth
  450. (if (or (= py qy ty)
  451. (= px qx tx))
  452. :within
  453. (and (< (abs (- (* (- qy py) (- tx px))
  454. (* (- ty py) (- qx px))))
  455. (max (abs (- qx px))
  456. (abs (- qy py))))
  457. (if (or (< qx px tx) (< qy py ty) (< tx px qx) (< ty py qy))
  458. :before
  459. (if (or (< px qx tx) (< py qy ty) (< tx qx px) (< ty qy py))
  460. :after
  461. :within)))))
  462. (if (not (and env1
  463. (> (length env1) 4)))
  464. env1
  465. (let ((new-env (list (cadr env1) (car env1)))
  466. (ymax (max-envelope env1))
  467. (ymin (min-envelope env1))
  468. (xmax (env1 (- (length env1) 2)))
  469. (xmin (car env1)))
  470. (if (= ymin ymax)
  471. (list xmin ymin xmax ymax)
  472. (do ((y-scl (/ ygrid (- ymax ymin)))
  473. (x-scl (/ (or xgrid ygrid) (- xmax xmin)))
  474. (px #f) (py #f)
  475. (qx #f) (qy #f)
  476. (tx #f) (ty #f)
  477. (qtx #f) (qty #f)
  478. (i 0 (+ i 2)))
  479. ((>= i (length env1))
  480. (set! new-env (cons qty (cons qtx new-env)))
  481. (reverse new-env))
  482. (let ((ttx (env1 i))
  483. (tty (env1 (+ i 1))))
  484. (set! tx (round (* ttx x-scl)))
  485. (set! ty (round (* tty y-scl)))
  486. (if px
  487. (if (not (point-on-line? px py qx qy tx ty))
  488. (begin
  489. (set! new-env (cons qty (cons qtx new-env)))
  490. (set! px qx)
  491. (set! py qy)))
  492. (begin
  493. (set! px qx)
  494. (set! py qy)))
  495. (set! qx tx)
  496. (set! qy ty)
  497. (set! qtx ttx)
  498. (set! qty tty)))))))