123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- (library (riff-wave-writer)
- (export pi
- sine*
- make-sine-func
- make-stepper
- make-sine-oscillator
- sample-function
- float->bytevector
- integer->bytevector
- max-amplitude)
- (import (except (rnrs base) let-values vector-for-each)
- (only (guile)
- lambda* λ
- ;; printing
- ;; display
- ;; simple-format
- ;; numbers
- inexact->exact
- ;; input output
- call-with-output-file
- current-output-port)
- (ice-9 exceptions)
- (ice-9 binary-ports)
- (rnrs bytevectors)
- (srfi srfi-43)
- ;; structs
- (srfi srfi-9)
- ;; functional structs
- (srfi srfi-9 gnu)
- (bytevector-utils)
- (model)
- (math)))
- ;;; function utilities
- (define make-stepper
- (λ (func step-size)
- "Make a function, which calls FUNC with an argument, which
- is calculated according to a given STEP-SIZE."
- (λ (n)
- (func (* n step-size)))))
- (define make-sine-oscillator
- (λ (sine-func step-size)
- "Return a function, which takes a factor for a step-index to
- calculate a sine value at that step."
- (make-stepper sine-func step-size)))
- (define sample-function
- (λ (stepped-func num-samples)
- "Given a function FUNC, return a vector of NUM-SAMPLES
- consequtive samples."
- (let ([sample-vector (make-vector num-samples 0)])
- (let iter ([counter 0])
- (cond
- [(< counter num-samples)
- (vector-set! sample-vector
- counter
- ;; Expects a stepped function. A
- ;; stepped function translates the
- ;; input argument to the nth step as
- ;; input argument. Such as produced
- ;; by make-stepper.
- (stepped-func counter))
- (iter (+ counter 1))]
- [else sample-vector])))))
- ;;; calculation
- (define max-amplitude
- (λ (bit-depth)
- (- (expt 2 (- bit-depth 1)) 1)))
- (define write-riff-wave
- (λ (file-name riff-wave bit-depth)
- (define maximum-amplitude (max-amplitude bit-depth))
- (define convert-sample
- (λ (sample)
- (integer->bytevector
- (float->scaled-truncated-integer sample maximum-amplitude)
- 2 #:endianness 'little)))
- (define chunk-header-bytevectors
- (λ (id size)
- "Construct a list of bytevectors for given chunk header
- information and return it."
- (list (ascii-string->bytevector id 4 #:endianness 'big)
- (integer->bytevector size 4 #:endianness 'little))))
- (call-with-output-file file-name
- (λ (out-port)
- (let ([header-chunk (riff-wave-header-chunk riff-wave)]
- [format-chunk (riff-wave-format-chunk riff-wave)]
- [data-chunk (riff-wave-data-chunk riff-wave)])
- ;; Write the header chunk -- The header chunk only
- ;; has a header. Or interpreted differently, its
- ;; data are the other chunks.
- (for-each (λ (bv) (put-bytevector out-port bv))
- (append
- (chunk-header-bytevectors
- (header-chunk-id header-chunk)
- (header-chunk-size header-chunk))
- (list
- (ascii-string->bytevector (header-chunk-riff-type header-chunk)
- 4 #:endianness 'big))))
- ;; Write the format chunk header.
- (for-each (λ (bv) (put-bytevector out-port bv))
- (chunk-header-bytevectors
- (format-chunk-id format-chunk)
- (format-chunk-size format-chunk)))
- ;; Write the format chunk.
- (put-bytevector
- out-port
- (integer->bytevector
- (format-chunk-compression-code format-chunk)
- COMPRESSION-CODE-BYTES #:endianness 'little))
- (put-bytevector
- out-port
- (integer->bytevector
- (format-chunk-num-channels format-chunk)
- NUM-CHANNELS-BYTES #:endianness 'little))
- (put-bytevector
- out-port
- (integer->bytevector
- (format-chunk-sample-rate format-chunk)
- SAMPLE-RATE-BYTES #:endianness 'little))
- (put-bytevector
- out-port
- (integer->bytevector
- (format-chunk-avg-byte-rate format-chunk)
- AVG-BYTE-RATE-BYTES #:endianness 'little))
- (put-bytevector
- out-port
- (integer->bytevector
- (format-chunk-block-align format-chunk)
- BLOCK-ALIGN-BYTES #:endianness 'little))
- (put-bytevector
- out-port
- (integer->bytevector
- (format-chunk-significant-bits-per-sample format-chunk)
- SIGNIFICANT-BITS-PER-SAMPLE-BYTES #:endianness 'little))
- ;; Write the data chunk header.
- (for-each (λ (bv) (put-bytevector out-port bv))
- (chunk-header-bytevectors
- (data-chunk-id data-chunk)
- (data-chunk-size data-chunk)))
- ;; Write the data chunk.
- (vector-for-each
- (λ (_index sample)
- (put-bytevector out-port (convert-sample sample)))
- (data-chunk-samples data-chunk)))))))
- (let* ([file-name "waveform.wav"]
- [duration 2 #|seconds|#]
- [sample-rate 44100]
- [step-size (/ 1 sample-rate)]
- [sine-func (make-sine-func #:amplitude #e0.5 #:frequency 440)]
- [oscillator (make-sine-oscillator sine-func step-size)]
- [num-channels 1] ; mono
- [compression-code 1] ; uncompressed
- [bit-depth 16] ; multiple of 8
- [extra-format-bytes-count 0]
- [samples-count (* duration sample-rate)]
- [samples (sample-function oscillator samples-count)])
- (let ([riff-wave
- (make-riff-wave
- (make-header-chunk (calculate-header-chunk-size
- (calculate-format-chunk-size extra-format-bytes-count)
- (calculate-data-chunk-size samples-count
- bit-depth
- num-channels)))
- (make-format-chunk compression-code
- num-channels
- sample-rate
- bit-depth)
- (make-data-chunk samples
- bit-depth
- num-channels))])
- (write-riff-wave file-name riff-wave bit-depth)))
|