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.

549 line
17KB

  1. ;;; with-sound for a sndlib-only context (no Snd editor)
  2. (provide 'sndlib-ws.scm)
  3. (set! *clm-srate* 44100)
  4. (define *clm-file-name* "test.snd")
  5. (define *clm-channels* 1)
  6. (define *clm-sample-type* mus-lfloat)
  7. (define *clm-header-type* mus-next)
  8. (define *clm-verbose* #f)
  9. (define *clm-play* #f)
  10. (define *clm-statistics* #f)
  11. (define *clm-reverb* #f)
  12. (define *clm-reverb-channels* 1)
  13. (define *clm-reverb-data* ())
  14. (define *clm-locsig-type* mus-interp-linear)
  15. (define *clm-clipped* #t)
  16. (define *clm-array-print-length* 12)
  17. (define *clm-player* #f)
  18. (define *clm-notehook* #f)
  19. (define *clm-with-sound-depth* 0) ; for CM, not otherwise used
  20. (define *clm-delete-reverb* #f) ; should with-sound clean up reverb stream
  21. (set! *clm-file-buffer-size* 65536)
  22. (define (times->samples beg dur)
  23. "(times->samples beg dur) converts beg and (+ beg dur) to samples, returning both in a list"
  24. (list (seconds->samples beg) (seconds->samples (+ beg dur))))
  25. ;;; -------- definstrument --------
  26. ;(define definstrument define*) -- old form 2-Nov-05
  27. (define *definstrument-hook* #f) ; for CM
  28. (define-macro (definstrument args . body)
  29. (let* ((name (car args))
  30. (targs (cdr args))
  31. (utargs (let ((arg-names ()))
  32. (for-each
  33. (lambda (a)
  34. (if (not (keyword? a))
  35. (if (symbol? a)
  36. (set! arg-names (cons a arg-names))
  37. (set! arg-names (cons (car a) arg-names)))))
  38. targs)
  39. (reverse arg-names))))
  40. `(begin
  41. (define* (,name ,@targs)
  42. (if *clm-notehook*
  43. (*clm-notehook* (symbol->string ',name) ,@utargs))
  44. ,@body)
  45. ,@(if *definstrument-hook*
  46. (list (*definstrument-hook* name targs))
  47. (list)))))
  48. ;;; -------- with-sound --------
  49. (define* (with-sound-helper thunk
  50. (output *clm-file-name*)
  51. (channels *clm-channels*)
  52. (srate *clm-srate*)
  53. (sample-type *clm-sample-type*)
  54. (header-type *clm-header-type*)
  55. (comment #f)
  56. (verbose *clm-verbose*)
  57. (reverb *clm-reverb*)
  58. (revfile "test.rev")
  59. (reverb-data *clm-reverb-data*)
  60. (reverb-channels *clm-reverb-channels*)
  61. (continue-old-file #f)
  62. (statistics *clm-statistics*)
  63. (scaled-to #f)
  64. (scaled-by #f)
  65. (play *clm-play*)
  66. (clipped 'unset)
  67. (notehook *clm-notehook*) ; (with-sound (:notehook (lambda args (display args))) (fm-violin 0 1 440 .1))
  68. (ignore-output #f))
  69. "with-sound-helper is the business portion of the with-sound macro"
  70. (let* ((old-srate *clm-srate*)
  71. (old-*output* *output*)
  72. (old-*reverb* *reverb*)
  73. (old-notehook *clm-notehook*)
  74. (old-verbose *clm-verbose*)
  75. (output-to-file (string? output))
  76. (output-1 (if (and output-to-file
  77. (or scaled-to scaled-by))
  78. (string-append output ".temp")
  79. output)) ; protect during nesting
  80. (reverb-1 revfile)
  81. (reverb-to-file (and reverb (string? revfile))))
  82. (if ignore-output
  83. (begin
  84. (set! output-1 *clm-file-name*)
  85. (set! output-to-file (string? output-1))))
  86. (dynamic-wind
  87. (lambda ()
  88. (set! *clm-verbose* verbose)
  89. (set! *clm-notehook* notehook)
  90. (set! (locsig-type) *clm-locsig-type*)
  91. (set! (mus-array-print-length) *clm-array-print-length*)
  92. (if (equal? clipped 'unset)
  93. (if (and (or scaled-by scaled-to)
  94. (member sample-type (list mus-bfloat mus-lfloat mus-bdouble mus-ldouble)))
  95. (set! (mus-clipping) #f)
  96. (set! (mus-clipping) *clm-clipped*))
  97. (set! (mus-clipping) clipped))
  98. (set! *clm-srate* srate))
  99. (lambda ()
  100. (if output-to-file
  101. (begin
  102. (if continue-old-file
  103. (begin
  104. (set! *output* (continue-sample->file output-1))
  105. (set! *clm-srate* (mus-sound-srate output-1)))
  106. (begin
  107. (if (file-exists? output-1)
  108. (delete-file output-1))
  109. (set! *output* (make-sample->file output-1 channels sample-type header-type comment)))))
  110. (begin
  111. (if (and (not continue-old-file)
  112. (vector? output-1))
  113. (fill! output-1 0.0))
  114. (set! *output* output-1)))
  115. (if reverb
  116. (if reverb-to-file
  117. (begin
  118. (if continue-old-file
  119. (set! *reverb* (continue-sample->file reverb-1))
  120. (begin
  121. (if (file-exists? reverb-1)
  122. (delete-file reverb-1))
  123. (set! *reverb* (make-sample->file reverb-1 reverb-channels sample-type header-type)))))
  124. (begin
  125. (if (and (not continue-old-file)
  126. (vector? reverb-1))
  127. (fill! reverb-1 0.0))
  128. (set! *reverb* reverb-1))))
  129. (let ((start (if statistics (get-internal-real-time)))
  130. (flush-reverb #f)
  131. (cycles 0)
  132. (revmax #f))
  133. (catch 'mus-error
  134. thunk
  135. (lambda args
  136. (format () ";~%with-sound mus-error: ~{~A~^ ~}~%" (cdr args))
  137. (set! flush-reverb #t)))
  138. (if (and reverb
  139. (not flush-reverb)) ; i.e. not interrupted by error and trying to jump out
  140. (begin
  141. (if reverb-to-file
  142. (mus-close *reverb*))
  143. (if statistics
  144. (if reverb-to-file
  145. (set! revmax (cadr (mus-sound-maxamp reverb-1)))
  146. (if (float-vector? reverb-1)
  147. (set! revmax (float-vector-peak reverb-1)))))
  148. (if reverb-to-file
  149. (set! *reverb* (make-file->sample reverb-1)))
  150. (apply reverb reverb-data) ; here is the reverb call(!)
  151. (if reverb-to-file
  152. (mus-close *reverb*))
  153. ))
  154. (if output-to-file
  155. (mus-close *output*))
  156. (if statistics
  157. (begin
  158. (set! cycles (- (get-internal-real-time) start))
  159. (format () "~%;~A:~% maxamp~A:~{ ~,4F~}~%~A compute time: ~,3F~%"
  160. (if output-to-file
  161. (if (or scaled-to scaled-by)
  162. (substring output-1 0 (- (length output-1) 5))
  163. output-1)
  164. (if (vector? output-1) "vector" "flush"))
  165. (if (or scaled-to scaled-by)
  166. " (before scaling)"
  167. "")
  168. (if output-to-file
  169. (let ((lst (mus-sound-maxamp output-1)))
  170. (do ((i 0 (+ i 2)))
  171. ((>= i (length lst)))
  172. (list-set! lst i (/ (list-ref lst i) *clm-srate*)))
  173. lst)
  174. (if (float-vector? output-1)
  175. (list (float-vector-peak output-1))
  176. '(0.0)))
  177. (if revmax (format #f " rev max: ~,4F~%" revmax) "")
  178. cycles)))
  179. (if (or scaled-to scaled-by)
  180. (if output-to-file
  181. (let ((scaling
  182. (or scaled-by
  183. (let* ((mx-lst (mus-sound-maxamp output-1))
  184. (mx (if (not (null? mx-lst)) (cadr mx-lst) 1.0)))
  185. (do ((i 1 (+ i 2)))
  186. ((>= i (length mx-lst)) (/ scaled-to mx))
  187. (set! mx (max mx (list-ref mx-lst i)))))))
  188. (out-file (substring output-1 0 (- (length output-1) 5))))
  189. (let ((g (make-sample->file out-file channels sample-type header-type #f)))
  190. (mus-close g))
  191. (mus-file-mix out-file output-1 0 (mus-sound-framples output-1) 0
  192. (let ((mx (make-float-vector (list channels channels) 0.0)))
  193. (do ((i 0 (+ i 1)))
  194. ((= i channels) mx)
  195. (set! (mx i i) scaling))))
  196. (delete-file output-1)
  197. (set! output-1 (substring output-1 0 (- (length output-1) 5))))
  198. (if (float-vector? output-1)
  199. (if scaled-to
  200. (let ((pk (float-vector-peak output-1)))
  201. (if (> pk 0.0)
  202. (float-vector-scale! output-1 (/ scaled-to pk))))
  203. (float-vector-scale! output-1 scaled-by)))))
  204. (if (and *clm-player* play output-to-file)
  205. (*clm-player* output-1)))
  206. output-1)
  207. (lambda ()
  208. (set! *clm-verbose* old-verbose)
  209. (set! *clm-notehook* old-notehook)
  210. (if *reverb*
  211. (begin
  212. (mus-close *reverb*)
  213. (set! *reverb* old-*reverb*)))
  214. (if *output*
  215. (begin
  216. (if (mus-output? *output*)
  217. (mus-close *output*))
  218. (set! *output* old-*output*)))
  219. (set! *clm-srate* old-srate)))))
  220. (define-macro (with-sound args . body)
  221. `(with-sound-helper (lambda () ,@body) ,@args))
  222. ;;; -------- with-temp-sound --------
  223. (define-macro (with-temp-sound args . body)
  224. `(let ((old-file-name *clm-file-name*))
  225. ;; with-sound but using tempnam for output (can be over-ridden by explicit :output)
  226. (dynamic-wind
  227. (lambda ()
  228. (set! *clm-file-name* (tmpnam)))
  229. (lambda ()
  230. (with-sound-helper (lambda () ,@body) ,@args)) ; dynamic-wind returns this as its result
  231. (lambda ()
  232. (set! *clm-file-name* old-file-name)))))
  233. ;;; -------- clm-load --------
  234. (define (clm-load file . args)
  235. "(clm-load file . args) loads 'file' in the context of with-sound"
  236. (apply with-sound-helper (lambda () (load file)) args))
  237. ;;; -------- sound-let --------
  238. ;;;
  239. ;;; (with-sound () (sound-let ((a () (fm-violin 0 .1 440 .1))) (mus-file-mix "test.snd" a)))
  240. (define-macro (sound-let snds . body)
  241. `(let ((temp-files ()))
  242. (begin
  243. (let ((val (let ,(map (lambda (arg)
  244. (if (> (length arg) 2)
  245. `(,(car arg) (with-temp-sound ,(cadr arg) ,@(cddr arg)))
  246. arg))
  247. snds)
  248. ,@body))) ; sound-let body
  249. (for-each (lambda (file) ; clean up all local temps
  250. (if (and (string? file) ; is it a file?
  251. (file-exists? file))
  252. (delete-file file)))
  253. temp-files)
  254. val)))) ; return body result
  255. ;;; -------- Common Music --------
  256. (define* (init-with-sound
  257. (srate *clm-srate*)
  258. (output *clm-file-name*)
  259. (channels *clm-channels*)
  260. (header-type *clm-header-type*)
  261. data-format
  262. (sample-type *clm-sample-type*)
  263. (comment #f)
  264. ;(verbose *clm-verbose*) ; why is this commented out?
  265. (reverb *clm-reverb*)
  266. (revfile "test.rev")
  267. (reverb-data *clm-reverb-data*)
  268. (reverb-channels *clm-reverb-channels*)
  269. (continue-old-file #f)
  270. (statistics *clm-statistics*)
  271. (scaled-to #f)
  272. (play *clm-play*)
  273. (scaled-by #f))
  274. "(init-with-sound . args) is the first half of with-sound; it sets up the CLM output choices, reverb, etc. Use \
  275. finish-with-sound to complete the process."
  276. (let ((old-srate *clm-srate*)
  277. (start (if statistics (get-internal-real-time)))
  278. (output-to-file (string? output))
  279. (reverb-to-file (and reverb (string? revfile))))
  280. (set! *clm-srate* srate)
  281. (if output-to-file
  282. (if continue-old-file
  283. (begin
  284. (set! *output* (continue-sample->file output))
  285. (set! *clm-srate* (mus-sound-srate output)))
  286. (begin
  287. (if (file-exists? output)
  288. (delete-file output))
  289. (set! *output* (make-sample->file output channels (or data-format sample-type) header-type comment))))
  290. (begin
  291. (if (and (not continue-old-file)
  292. (vector output))
  293. (fill! output 0.0))
  294. (set! *output* output)))
  295. (if reverb
  296. (if reverb-to-file
  297. (if continue-old-file
  298. (set! *reverb* (continue-sample->file revfile))
  299. (begin
  300. (if (file-exists? revfile)
  301. (delete-file revfile))
  302. (set! *reverb* (make-sample->file revfile reverb-channels (or data-format sample-type) header-type))))
  303. (begin
  304. (if (and (not continue-old-file)
  305. (vector? revfile))
  306. (fill! revfile 0.0))
  307. (set! *reverb* revfile))))
  308. (list 'with-sound-data
  309. output
  310. reverb
  311. revfile
  312. old-srate
  313. statistics
  314. #f ;to-snd
  315. scaled-to
  316. scaled-by
  317. play
  318. reverb-data
  319. start)))
  320. (define (finish-with-sound wsd)
  321. "(finish-with-sound wsd) closes the notelist process started by init-with-sound"
  322. (if (eq? (car wsd) 'with-sound-data)
  323. (let ((output (list-ref wsd 1))
  324. (reverb (list-ref wsd 2))
  325. (revfile (list-ref wsd 3))
  326. (old-srate (list-ref wsd 4))
  327. ;(statistics (list-ref wsd 5))
  328. ;(to-snd (list-ref wsd 6))
  329. ;(scaled-to (list-ref wsd 7))
  330. ;(scaled-by (list-ref wsd 8))
  331. ;(play (list-ref wsd 9))
  332. (reverb-data (list-ref wsd 10))
  333. ;(start (list-ref wsd 11))
  334. )
  335. (if reverb
  336. (begin
  337. (mus-close *reverb*)
  338. (if (string? revfile)
  339. (set! *reverb* (make-file->sample revfile))
  340. (set! *reverb* revfile))
  341. (apply reverb reverb-data)
  342. (mus-close *reverb*)))
  343. (if (mus-output? *output*)
  344. (mus-close *output*))
  345. (set! *clm-srate* old-srate)
  346. output)
  347. (throw 'wrong-type-arg
  348. (list "finish-with-sound" wsd))))
  349. (define wsdat-play ; for cm
  350. (dilambda
  351. (lambda (w)
  352. "accessor for play field of init-with-sound struct"
  353. (list-ref w 9))
  354. (lambda (w val)
  355. (list-set! w 9 val))))
  356. (define ->frequency
  357. (let ((main-pitch (/ 440.0 (expt 2.0 (/ 57 12)))) ; a4 = 440Hz is pitch 57 in our numbering
  358. (last-octave 0) ; octave number can be omitted
  359. (ratios (vector 1.0 256/243 9/8 32/27 81/64 4/3 1024/729 3/2 128/81 27/16 16/9 243/128 2.0)))
  360. (lambda* (pitch pythagorean) ; pitch can be pitch name or actual frequency
  361. "(->frequency pitch pythagorean) returns the frequency (Hz) of the 'pitch', a CLM/CM style note name as a \
  362. symbol: 'e4 for example. If 'pythagorean', the frequency calculation uses small-integer ratios, rather than equal-tempered tuning."
  363. (if (symbol? pitch)
  364. (let* ((name (string-downcase (symbol->string pitch)))
  365. (base-char (name 0))
  366. (sign-char (and (> (length name) 1)
  367. (not (char-numeric? (name 1)))
  368. (not (char=? (name 1) #\n))
  369. (name 1)))
  370. (octave-char (if (and (> (length name) 1)
  371. (char-numeric? (name 1)))
  372. (name 1)
  373. (if (and (> (length name) 2)
  374. (char-numeric? (name 2)))
  375. (name 2)
  376. #f)))
  377. (base (modulo (+ 5 (- (char->integer base-char) (char->integer #\a))) 7)) ; c-based (diatonic) octaves
  378. (sign (if (not sign-char) 0 (if (char=? sign-char #\f) -1 1)))
  379. (octave (if octave-char (- (char->integer octave-char) (char->integer #\0)) last-octave))
  380. (base-pitch (+ sign (case base ((0) 0) ((1) 2) ((2) 4) ((3) 5) ((4) 7) ((5) 9) ((6) 11))))
  381. (et-pitch (+ base-pitch (* 12 octave))))
  382. (set! last-octave octave)
  383. (if pythagorean
  384. (* main-pitch (expt 2 octave) (ratios base-pitch))
  385. (* main-pitch (expt 2.0 (/ et-pitch 12)))))
  386. pitch))))
  387. (define (->sample beg)
  388. "(->sample time-in-seconds) -> time-in-samples"
  389. (round (* (if (not (null? (sounds))) (srate) *clm-srate*) beg)))
  390. ;;; -------- defgenerator --------
  391. ;;; (defgenerator osc a b)
  392. ;;; (defgenerator (osc :methods (list (cons 'mus-frequency (lambda (obj) 100.0)))) a b)
  393. (define-macro (defgenerator struct-name . fields)
  394. (define (list->bindings lst)
  395. (let ((len (length lst)))
  396. (let ((nlst (make-list (* len 2))))
  397. (do ((old lst (cdr old))
  398. (nsym nlst (cddr nsym)))
  399. ((null? old) nlst)
  400. (if (pair? (car old))
  401. (begin
  402. (set-car! (cdr nsym) (caar old))
  403. (set-car! nsym (list 'quote (caar old))))
  404. (begin
  405. (set-car! (cdr nsym) (car old))
  406. (set-car! nsym (list 'quote (car old)))))))))
  407. (let* ((name (if (pair? struct-name)
  408. (car struct-name)
  409. struct-name))
  410. (sname (if (string? name)
  411. name
  412. (symbol->string name)))
  413. (wrapper (or (and (pair? struct-name)
  414. (or (and (> (length struct-name) 2)
  415. (equal? (struct-name 1) :make-wrapper)
  416. (struct-name 2))
  417. (and (= (length struct-name) 5)
  418. (equal? (struct-name 3) :make-wrapper)
  419. (struct-name 4))))
  420. (lambda (gen) gen)))
  421. (methods (and (pair? struct-name)
  422. (or (and (> (length struct-name) 2)
  423. (equal? (struct-name 1) :methods)
  424. (struct-name 2))
  425. (and (= (length struct-name) 5)
  426. (equal? (struct-name 3) :methods)
  427. (struct-name 4))))))
  428. `(begin
  429. (define ,(string->symbol (string-append sname "?")) #f)
  430. (define ,(string->symbol (string-append "make-" sname)) #f)
  431. (let ((gen-type ',(string->symbol (string-append "+" sname "+")))
  432. (gen-methods (and ,methods (apply inlet ,methods))))
  433. (set! ,(string->symbol (string-append sname "?"))
  434. (lambda (obj)
  435. (and (let? obj)
  436. (eq? (obj 'mus-generator-type) gen-type))))
  437. (set! ,(string->symbol (string-append "make-" sname))
  438. (lambda* ,(map (lambda (n)
  439. (if (pair? n) n (list n 0.0)))
  440. fields)
  441. (,wrapper
  442. (openlet
  443. ,(if methods
  444. `(sublet gen-methods
  445. ,@(list->bindings (reverse fields)) 'mus-generator-type gen-type)
  446. `(inlet 'mus-generator-type gen-type ,@(list->bindings fields)))))))))))
  447. ;;; --------------------------------------------------------------------------------
  448. ;;;
  449. ;;; functions from Snd that are used in some instruments
  450. ;;; these replacements assume that the Snd functions are not present
  451. (define* (file-name name)
  452. (if (string? name)
  453. (mus-expand-filename name)
  454. (mus-file-name name)))
  455. (define srate mus-sound-srate)
  456. (define (channels . args)
  457. (let ((obj (car args)))
  458. (if (string? obj)
  459. (mus-sound-chans obj)
  460. (mus-channels obj))))
  461. ;;; I think length is handled by s7 for all types
  462. (define (framples . args)
  463. (let ((obj (car args)))
  464. (if (string? obj)
  465. (mus-sound-framples obj)
  466. (length obj))))
  467. (define snd-print display)
  468. (define snd-warning display)
  469. (define snd-display (lambda args (apply format (append (list #t) (cdr args)))))
  470. (define (snd-error str) (error 'mus-error str))
  471. (define snd-tempnam tmpnam)