#lang racket
(require (only-in ffi/unsafe memcpy _sint16 memset ptr-add cpointer?)
ffi/vector
racket/unsafe/ops
"read-wav.rkt"
"write-wav.rkt"
(prefix-in rc: "rsound-commander.rkt")
"private/s16vector-add.rkt")
(define (positive-integer? n)
(and (integer? n) (< 0 n)))
(define (nonnegative-integer? n)
(and (integer? n) (<= 0 n)))
(struct rsound (data start stop sample-rate)
#:transparent
)
(define s&t-list? (listof (list/c rsound? number?)))
(provide/contract
[sound-list-total-frames (-> s&t-list? number?)])
(provide (except-out (all-defined-out) sound-list-total-frames))
(define s16max #x7fff)
(define -s16max (- s16max))
(define s16max/i (exact->inexact #x7fff))
(define s16-size 2)
(define channels rc:channels)
(define stop rc:stop-playing)
(define default-sample-rate (make-parameter 44100))
(define (rsound-frames rsound)
(- (rsound-stop rsound) (rsound-start rsound)))
(define (rsound/all s16vec sample-rate)
(rsound s16vec 0 (/ (s16vector-length s16vec) channels) sample-rate))
(define (rsound-equal? r1 r2)
(and (= (rsound-frames r1)
(rsound-frames r2))
(= (rsound-sample-rate r1)
(rsound-sample-rate r2))
(for/and ([i (in-range (rsound-frames r1))])
(and (= (rs-ith/left/s16 r1 i) (rs-ith/left/s16 r2 i))
(= (rs-ith/right/s16 r1 i) (rs-ith/right/s16 r2 i))))))
(define (s16vector-equal? v1 v2)
(and (= (s16vector-length v1)
(s16vector-length v2))
(for/and ([i (in-range (s16vector-length v1))])
(= (s16vector-ref v1 i) (s16vector-ref v2 i)))))
(define (rsound-hash-1 x y) 3)
(define (rsound-hash-2 x y) 3)
(define (signal? f)
(and (procedure? f) (procedure-arity-includes? f 1)))
(define (signal/block? f)
(and (procedure? f) (procedure-arity-includes? f 3)))
(define (rs-read path)
(unless (path-string? path)
(raise-type-error 'rsound-read "path-string" 0 path))
(match (read-sound/s16vector path 0 #f)
[(list data sample-rate) (rsound/all data sample-rate)]))
(define (rs-read/clip path start-frame end-frame)
(unless (path-string? path)
(raise-type-error 'rsound-read "path-string" 0 path start-frame end-frame))
(unless (nonnegative-integer? start-frame)
(raise-type-error 'rsound-read "non-negative integer" 1 path start-frame end-frame))
(unless (nonnegative-integer? end-frame)
(raise-type-error 'rsound-read "non-negative integer" 2 path start-frame end-frame))
(match (read-sound/s16vector path (inexact->exact start-frame) (inexact->exact end-frame))
[(list data sample-rate) (rsound/all data sample-rate)]))
(define (rs-read-sample-rate path)
(unless (path-string? path)
(raise-type-error 'rsound-read-sample-rate "path-string" 0 path))
(second (read-sound/formatting path)))
(define (rs-read-frames path)
(unless (path-string? path)
(raise-type-error 'rsound-read-frames "path-string" 0 path))
(first (read-sound/formatting path)))
(define (rs-write sound path)
(unless (rsound? sound)
(raise-type-error 'rsound-write "rsound" 0 sound path))
(unless (path-string? path)
(raise-type-error 'rsound-write "path" 1 sound path))
(match sound
[(struct rsound (data start stop sample-rate))
(write-sound/s16vector data start stop sample-rate path)]))
(define (signal-play signal sample-rate)
(unless (and (procedure? signal)
(procedure-arity-includes? signal 1))
(raise-type-error 'signal-play "signal" 0 signal sample-rate))
(unless (positive-integer? sample-rate)
(raise-type-error 'signal-play "sample rate (nonnegative exact integer)" 1 signal sample-rate))
(rc:signal/block-play/unsafe (rc:signal->signal/block/unsafe signal) sample-rate))
(define (signal/block-play signal/block sample-rate)
(rc:signal/block-play signal/block sample-rate))
(define (signal/block-play/unsafe signal/block sample-rate)
(rc:signal/block-play/unsafe signal/block sample-rate))
(define ((rsound-play/helper loop?) sound)
(match sound
[(struct rsound (data start finish sample-rate))
(if loop?
(error 'rsound-play/helper "not implemented")
(rc:buffer-play data start finish sample-rate))]
[other
(error 'rsound-play/helper "expected an rsound, got: ~e" sound)]))
(define play
(rsound-play/helper #f))
(define (rsound-loop sound)
(when (= (rsound-frames sound) 0)
(error 'rsound-loop "It's a bad idea to loop an empty sound."))
((rsound-play/helper #t) sound))
(define (rsound-play sound)
(let ([filename (make-temporary-file "tmpsound~a.wav")])
(check-below-threshold sound 2.0)
(rsound-write sound filename)
(thread
(lambda ()
(play-sound filename #f)
(delete-file filename)))))
(define (change-loop sound)
(unless (rsound? sound)
(raise-type-error 'change-loop "rsound" 0 sound))
(match sound
[(struct rsound (data frames sample-rate))
(error 'change-loop "not currently implemented")]
[other
(error 'change-loop "expected an rsound, got: ~e" sound)]))
(define (rs-ith/left/s16 sound frame)
(rsound-extractor sound frame #t (lambda (x) x)))
(define (rs-ith/right/s16 sound frame)
(rsound-extractor sound frame #f (lambda (x) x)))
(define (rs-ith/left sound frame)
(rsound-extractor sound frame #t s16->real))
(define (rs-ith/right sound frame)
(rsound-extractor sound frame #f s16->real))
(define (rsound-extractor rsound frame left? scale-fun)
(scale-fun (s16vector-ref (rsound-data rsound) (frame->sample (+ (rsound-start rsound) frame) left?))))
(define (set-rs-ith/left! sound frame new-val)
(rsound-mutator sound frame #t new-val))
(define (set-rs-ith/right! sound frame new-val)
(rsound-mutator sound frame #f new-val))
(define (rsound-mutator rsound frame left? new-val)
(unless (rsound? rsound)
(raise-type-error 'rsound-mutator "rsound" 0 rsound frame new-val))
(unless (nonnegative-integer? frame)
(raise-type-error 'rsound-mutator "nonnegative integer" 1 rsound frame new-val))
(unless (< frame (rsound-frames rsound))
(raise-type-error 'rsound-mutator (format "frame index less than available # of frames ~s" (rsound-frames rsound)) 1 rsound frame new-val))
(unless (real? new-val)
(raise-type-error 'rsound-mutator "real number" 2 rsound frame new-val))
(s16vector-set! (rsound-data rsound)
(frame->sample (+ (rsound-start rsound) frame) left?)
(real->s16 new-val)))
(define (frame->sample f left?)
(+ (* f rc:channels) (if left? 0 1)))
(define (rsound-nth-sample sound sample)
(unless (rsound? sound)
(raise-type-error 'rsound-nth-sample/right "rsound" 0 sound sample))
(unless (positive-integer? sample)
(raise-type-error 'rsound-nth-sample/right "positive integer" 1 sound sample))
(match-let* ([(struct rsound (data frames sample-rate)) sound])
(when (>= sample (* channels frames))
(error 'rsound-nth-sample "requested sample # ~s greater than available # of samples ~s." sample (* channels (inexact->exact frames))))
(s16vector-ref data sample)))
(define (rsound-scale scale sound)
(unless (rsound? sound)
(raise-type-error 'rsound-clip "rsound" 0 sound start finish))
(unless )
(define (left i) (* scale (rs-ith/left sound i)))
(define (right i) (* scale (rs-ith/right sound i)))
(signal->rsound/stereo (rsound-frames sound)
(rsound-sample-rate sound)
left
right))
(define (clip sound start finish)
(unless (rsound? sound)
(raise-type-error 'rsound-clip "rsound" 0 sound start finish))
(unless (nonnegative-integer? start)
(raise-type-error 'rsound-clip "non-negative integer" 1 sound start finish))
(unless (nonnegative-integer? finish)
(raise-type-error 'rsound-clip "non-negative integer" 2 sound start finish))
(unless (and (<= 0 start finish (rsound-frames sound)))
(error 'rsound-clip
frames-out-of-range-msg
start finish (rsound-frames sound)))
(match-define (rsound data old-start old-stop sample-rate) sound)
(rsound data (+ old-start start) (+ old-start finish) sample-rate)
(let* ([cblock (make-s16vector (* rc:channels (- finish start)))])
(memcpy (s16vector->cpointer cblock) 0
(s16vector->cpointer (rsound-data sound)) (* start rc:channels)
(* rc:channels (- finish start)) _sint16)
(rsound cblock (rsound-sample-rate sound))))
(define frames-out-of-range-msg
(string-append "must have 0 < start < end < frames. "
"You provided start ~s and end ~s for a sound with ~s frames."))
(define (rs-append sound-a sound-b)
(rs-append* (list sound-a sound-b)))
(define (rs-append* los)
(unless (and (list? los) (andmap rsound? los))
(raise-type-error 'rsound-append* "list of rsounds" 0 los))
(same-sample-rate-check los)
(define total-frames (apply + (map rsound-frames los)))
(define cblock (make-s16vector (* rc:channels total-frames)))
(for/fold ([offset-samples 0])
([sound (in-list los)])
(let ([sound-samples (* rc:channels (rsound-frames sound))])
(memcpy (s16vector->cpointer cblock) offset-samples
(s16vector->cpointer (rsound-data sound))
(* rc:channels (rsound-start sound))
sound-samples _sint16)
(+ offset-samples sound-samples)))
(rsound cblock 0 total-frames (rsound-sample-rate (car los))))
(define (assemble sound×)
(same-sample-rate-check (map car sound×))
(let* ([total-frames (inexact->exact (round (sound-list-total-frames sound×)))]
[cblock (make-s16vector (* total-frames rc:channels))])
(memset (s16vector->cpointer cblock) 0 #x00 (* total-frames rc:channels) _sint16)
(for ([s&t (in-list sound×)])
(match-define (list sound offset) s&t)
(match-define (rsound s16vec start stop sample-rate) sound)
(define frames (rsound-frames sound))
(define dst-offset (* rc:channels (inexact->exact (round offset))))
(define src-offset (* rc:channels start))
(define num-samples (* rc:channels frames))
(s16buffer-add!/c (ptr-add (s16vector->cpointer cblock)
(* s16-size dst-offset))
(ptr-add (s16vector->cpointer s16vec)
(* s16-size src-offset))
num-samples))
(rsound cblock 0 total-frames (rsound-sample-rate (caar sound×)))))
(define (sound-list-total-frames sound×)
(apply max (for/list ([s&t (in-list sound×)])
(+ (rsound-frames (car s&t)) (cadr s&t)))))
(define (same-sample-rate-check los)
(when (null? los)
(error 'same-sample-rate-check "can't use empty list (what would the sample rate be?)"))
(unless (or (<= (length los) 1) (apply = (map rsound-sample-rate los)))
(error 'same-sample-rate-check "sample rates must all be the same, given: ~s" (map rsound-sample-rate los))))
(define (mono-signal->rsound frames f)
(define sample-rate (default-sample-rate))
(unless (nonnegative-integer? frames)
(raise-type-error 'signal->rsound "non-negative integer" 0 frames sample-rate f))
(unless (and (procedure? f) (procedure-arity-includes? f 1))
(raise-type-error 'signal->rsound "function of one argument" 2 frames sample-rate f))
(let* ([int-frames (inexact->exact (round frames))]
[int-sample-rate (inexact->exact (round sample-rate))]
[cblock (make-s16vector (* rc:channels int-frames))])
(for ([i (in-range int-frames)])
(let* ([offset (* rc:channels i)]
[sample (real->s16 (f i))])
(s16vector-set! cblock offset sample)
(s16vector-set! cblock (+ offset 1) sample)))
(rsound cblock 0 int-frames sample-rate)))
(define (signals->rsound frames fleft fright)
(define sample-rate (default-sample-rate))
(unless (nonnegative-integer? frames)
(raise-type-error 'signal->rsound/stereo "non-negative integer" 0 frames sample-rate fleft fright))
(unless (and (procedure? fleft) (procedure-arity-includes? fleft 1))
(raise-type-error 'signal->rsound/stereo "function of one argument" 2 frames sample-rate fleft fright))
(unless (and (procedure? fright) (procedure-arity-includes? fright 1))
(raise-type-error 'signal->rsound/stereo "function of one argument" 3 frames sample-rate fleft fright))
(let* ([cblock (make-s16vector (* rc:channels frames))])
(for ([i (in-range frames)])
(let* ([offset (* rc:channels i)])
(s16vector-set! cblock offset (real->s16 (fleft i)))
(s16vector-set! cblock (+ offset 1) (real->s16 (fright i)))))
(rsound cblock 0 frames sample-rate)))
(define (signal->rsound/filtered frames filter f)
(define sample-rate (default-sample-rate))
(unless (nonnegative-integer? frames)
(raise-type-error 'fun->filtered-mono-rsound "non-negative integer" 0 frames sample-rate f))
(unless (and (procedure? f) (procedure-arity-includes? f 1))
(raise-type-error 'fun->filtered-mono-rsound "function of one argument" 2 frames sample-rate f))
(let* ([cblock (make-s16vector (* rc:channels frames))])
(for ([i (in-range frames)])
(let* ([offset (* rc:channels i)]
[sample (real->s16 (filter (f i)))])
(s16vector-set! cblock offset sample)
(s16vector-set! cblock (+ offset 1) sample)))
(rsound cblock 0 frames sample-rate)))
(define (silence frames)
(define sample-rate (default-sample-rate))
(define int-frames (inexact->exact (round frames)))
(unless (nonnegative-integer? frames)
(raise-type-error 'make-silence "non-negative integer" 0 frames sample-rate))
(let* ([cblock (make-s16vector (* rc:channels int-frames))])
(memset (s16vector->cpointer cblock) #x0 (* rc:channels int-frames) _sint16)
(rsound cblock 0 int-frames sample-rate)))
(define (s16->real x)
(/ (exact->inexact x) s16max/i))
(define (real->s16 x)
(min s16max (max -s16max (inexact->exact (round (* s16max/i x))))))
(define (rs-largest-sample sound)
(buffer-largest-sample/range (rsound-data sound) (rsound-start sound) (rsound-stop sound)
(rsound-frames sound)))
(define (rs-largest-frame/range/left sound min-frame max-frame)
(buffer-largest-sample/range/left (rsound-data sound) (rsound-frames sound) min-frame max-frame))
(define (rs-largest-frame/range/right sound min-frame max-frame)
(buffer-largest-sample/range/right (rsound-data sound) (rsound-frames sound) min-frame max-frame))
(define (buffer-largest-sample/range buffer start stop frames)
(buffer-largest-sample/range/helper buffer (* rc:channels frames) (* rc:channels start)
(* rc:channels stop) 1))
(define (buffer-largest-sample/range/left buffer frames min-frame max-frame)
(frame-range-checks frames min-frame max-frame)
(buffer-largest-sample/range/helper buffer
(* rc:channels frames)
(* rc:channels min-frame)
(* rc:channels max-frame)
2))
(define (buffer-largest-sample/range/right buffer frames min-frame max-frame)
(frame-range-checks frames min-frame max-frame)
(buffer-largest-sample/range/helper buffer
(* rc:channels frames)
(add1 (* rc:channels min-frame))
(add1 (* rc:channels max-frame))
2))
(define (buffer-largest-sample/range/helper buffer samples min-sample max-sample increment)
(for/fold ([max-so-far 0.0])
([i (in-range min-sample max-sample increment)])
(max max-so-far (abs (s16vector-ref buffer i)))))
(define (frame-range-checks frames min-frame max-frame)
(when (not (and (<= 0 min-frame) (<= 0 max-frame)
(<= min-frame frames) (<= max-frame frames)))
(error 'frame-range-checks "range limits ~v and ~v not in range 0 - ~v" min-frame max-frame frames))
(when (not (< min-frame max-frame))
(error 'frame-range-checks "range limits ~v and ~v not in order and separated by at least 1" min-frame max-frame)))
(define (check-below-threshold buffer frames threshold)
(when (> (buffer-largest-sample buffer frames) threshold)
(error 'check-below-threshold "sound contains samples above threshold ~s." threshold)))