(define rate44kHz (cons 44100 (bytes #xAC #x44 #x00 #x00)))
(define rate22kHz (cons 22255 (bytes #x56 #xEE #x8B #xA3)))
(define A440 440) (define A480 480)
(define (linear-pitch-space p &opt (middleA A440))
(define (log2 n) (/ (log n) (log 2)))
(expt 2
(+ (/ (- p 69)
12)
(log2 middleA))))
(define (sample-to-bytes x)
(integer->integer-bytes (inexact->exact (floor (* x 32000))) 2 #t #t))
(define (enchunk ID buffer &opt (prefix (bytes)))
(let* ((l (+ (bytes-length buffer)
(bytes-length prefix)))
(nobytes (bytes))
(padded (if (odd? l)
(+ 1 l)
l)))
(if (eq? (string-length ID) 4)
(bytes-append (string->bytes/locale ID)
(integer->integer-bytes padded 4 #f #t)
(if prefix prefix nobytes)
buffer
(if (odd? l) (bytes 0) nobytes))
(error "ID must be 4 characters long!"))))
(define (op->buffer operator t &opt (rate rate44kHz))
(let ((buffer (make-bytes (* 2
(+ 1 (* t (car rate))))))
(end t)
(inc (/ 1 (car rate))))
(let loop ((x 0)
(byteoffset 0))
(if (> x end)
buffer
(begin (bytes-copy! buffer
byteoffset
(sample-to-bytes (operator x)))
(loop (+ x inc) (+ byteoffset 2)))))))
(define (buffer->aiff bitstream &opt (rate rate44kHz))
(let ((frames (/ (bytes-length bitstream) 2)))
(enchunk "FORM"
(bytes-append
(enchunk "COMM"
(bytes-append
(integer->integer-bytes 1 2 #t #t) (integer->integer-bytes frames 4 #f #t) (integer->integer-bytes 16 2 #t #t) (bytes #x40 #x0E) (cdr rate) (bytes 0 0 0 0))) (enchunk "SSND"
bitstream
(make-bytes 8 0))) (string->bytes/locale "AIFF"))))
(define (sinewave freq)
(lambda (x) (sin (* x freq 6.2832))))
(define (noise)
(lambda (x) (random)))
(define (ampmod signal mod)
(lambda (x) (* (signal x)
(mod x))))
(define (half-wave op)
(lambda (x)
(let ((b (op x)))
(if (> b 0) b 0))))
(define (FMoperator freq modulation)
(lambda (x)
(sin (+ (* x freq 6.2832)
(modulation x)))))
(define (mix &rest sources)
(lambda (x)
(/ (foldl (lambda (operation sum)
(+ sum
(operation x)))
0
sources)
(length sources))))
(define (amp a op)
(lambda (x)
(let ((b (* a (op x))))
(cond ((< b -1) -1)
((> b 1) 1)
(else b)))))
(define complex-sound (op->buffer (mix (ampmod (mix (sinewave (linear-pitch-space 60))
(sinewave (linear-pitch-space 64))
(sinewave (linear-pitch-space 67))) (half-wave (amp 10 (FMoperator 8
(sinewave 1))))) (amp 0.3 (ampmod (noise)
(sinewave 0.08))) (ampmod (amp 10 (FMoperator (linear-pitch-space 58)
(sinewave (/ (linear-pitch-space 58) 3)))) (sinewave 0.01))) 20
rate22kHz))
(with-output-to-file "test.aiff"
(thunk (display (buffer->aiff complex-sound rate22kHz)))
'replace)