#lang racket
(require (only-in ffi/unsafe memcpy _sint16 memset ptr-add)
ffi/vector
"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 (rs-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)
(unless (rsound? r1)
(raise-type-error 'rsound-equal? "rsound" 0 r1 r2))
(unless (rsound? r2)
(raise-type-error 'rsound-equal? "rsound" 1 r1 r2))
(and (= (rs-frames r1)
(rs-frames r2))
(= (rsound-sample-rate r1)
(rsound-sample-rate r2))
(for/and ([i (in-range (rs-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 (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 #f))
(define (signal/block-play signal/block sample-rate #:buffer-time [buffer-time #f])
(rc:signal/block-play signal/block sample-rate buffer-time))
(define (signal/block-play/unsafe signal/block sample-rate #:buffer-time [buffer-time #f])
(rc:signal/block-play/unsafe signal/block sample-rate buffer-time))
(define ((rsound-play/helper loop?) sound)
(unless (rsound? sound)
(raise-type-error 'play "rsound" 0 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 (= (rs-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 real->s16))
(define (set-rs-ith/right! sound frame new-val)
(rsound-mutator sound frame #f new-val real->s16))
(define (set-rs-ith/left/s16! sound frame new-val)
(rsound-mutator sound frame #t new-val (lambda (x) x)))
(define (set-rs-ith/right/s16! sound frame new-val)
(rsound-mutator sound frame #f new-val (lambda (x) x)))
(define (rsound-mutator rsound frame left? new-val scale-fun)
(s16vector-set! (rsound-data rsound)
(frame->sample (+ (rsound-start rsound) frame) left?)
(scale-fun new-val)))
(define (frame->sample f left?)
(+ (* f rc:channels) (if left? 0 1)))
(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 rs-frames los)))
(define cblock (make-s16vector (* rc:channels total-frames)))
(for/fold ([offset-samples 0])
([sound (in-list los)])
(let ([sound-samples (* rc:channels (rs-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×)
(unless (and (list? sound×)
(andmap (lambda (x) (and (list? x) (= (length x) 2)
(rsound? (first x))
(nonnegative-integer? (second x))))
sound×))
(raise-type-error 'assemble "list of (list <rsound> <frame>)"
0 sound×))
(same-sample-rate-check (map car sound×))
(let* ([total-frames (inexact->exact (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/i) s&t)
(define offset (inexact->exact offset/i))
(match-define (rsound s16vec start stop sample-rate) sound)
(define frames (rs-frames sound))
(define dst-offset (* rc:channels offset))
(define src-offset (* rc:channels start))
(define num-samples (* rc:channels frames))
(define p1 (ptr-add (s16vector->cpointer cblock)
(* s16-size dst-offset)))
(define p2 (ptr-add (s16vector->cpointer s16vec)
(* s16-size src-offset)))
(s16buffer-add!/c p1 p2 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×)])
(+ (rs-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 (floor frames))]
[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))
(define int-frames (inexact->exact frames))
(let* ([cblock (make-s16vector (* rc:channels int-frames))])
(for ([i (in-range int-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 int-frames sample-rate)))
(define (silence frames)
(unless (nonnegative-integer? frames)
(raise-type-error 'make-silence "non-negative integer" 0 frames sample-rate))
(define sample-rate (default-sample-rate))
(define int-frames (inexact->exact frames))
(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 (check-below-threshold buffer frames threshold)
(when (> (buffer-largest-sample buffer frames) threshold)
(error 'check-below-threshold "sound contains samples above threshold ~s." threshold)))