#lang racket
(require "../rsound.rkt"
         rackunit
         rackunit/text-ui
         racket/runtime-path
         ffi/vector)
(define twopi (* 2 pi))
(define (sine-wave pitch sample-rate volume)
  (let ([scalar (* twopi pitch)])
    (lambda (i)
      (let ([t (/ i sample-rate)])
        (* volume (sin (* scalar t)))))))
(define (make-tone pitch volume frames)
  (mono-signal->rsound frames (sine-wave pitch (default-sample-rate) volume)))
(define-runtime-path short-test-wav "./short-test.wav")
(define-runtime-path kick-wav "./kick_01.wav")
(define-runtime-path short-with-pad-wav "./short-with-pad.wav")
(run-tests
(test-suite "rsound"
(let ()
(let ([t (mono-signal->rsound 100 (lambda (i) (sin (* twopi 13/44100 i))))])
  (check-equal? (rs-ith/left/s16 t 0) 0)
  (check-equal? (rs-ith/right/s16 t 1) (inexact->exact
                                            (round (* s16max (sin (* twopi 13/44100)))))))
(let ([s (signals->rsound 100 (lambda (i) (/ i 100)) (lambda (i) (- 1 (/ i 100))))])
  (check-= (rs-ith/left s 13) 13/100 1e-4)
  (check-= (rs-ith/right s 89) 11/100 1e-4))
(let ([t (mono-signal->rsound 100 (lambda (i) (sin (* twopi 13/44100 i))))])
  (check-= (rs-ith/left t 0) 0 1e-4)
  (check-= (rs-ith/right t 1) (sin (* twopi 13/44100)) 1e-4))
(let ([v1 (mono-signal->rsound 100 (lambda (i) (/ i 100)))]
      [v2 (mono-signal->rsound 100 (lambda (i) (/ i 100)))])
  (check rsound-equal? v1 v2)
  (s16vector-set! (rsound-data v2) 50 -30)
  (check-equal? (rsound-equal? v1 v2) false))
(let ([sample-sound (make-tone 2000 0.15 10)])
  (check-= (/ (rs-largest-sample sample-sound) s16max) 0.15 1))
(let ([sample-sound (rsound-append* (list (silence 10) 
                                          (make-tone 2000 0.15 10)))])
  (check-not-exn (lambda () (check-below-threshold (rsound-data sample-sound) (rsound-frames sample-sound) 0.15)))
  (check-exn exn:fail? (lambda () (check-below-threshold (rsound-data sample-sound) (rsound-frames sample-sound) 0.1))))
(check-exn (lambda (exn)
             (regexp-match #px"^rsound-append\\*: " (exn-message exn)))
           (lambda () (rs-append* 34)))
(let ([sample-sound (make-tone 400 0.15 (* 60 (default-sample-rate)))])
  (time (check-below-threshold sample-sound 0.2)))
(let ([s (silence 100)])
  (for ([i (in-range 100)])
    (check-equal? (rs-ith/left/s16 s i) 0)
    (check-equal? (rs-ith/right/s16 s i) 0)))
(check-equal? (rsound-frames (silence 22050)) 22050)
(check-equal? (rsound-sample-rate (silence 22050)) (default-sample-rate))
(check-equal? (sound-list-total-frames (list (list (silence 22050) 0))) 22050)
(check-equal? (sound-list-total-frames (list (list (silence 22050) 0)
                                             (list (silence 44100) 22050)
                                             (list (silence 20000) 44100)))
              66150)
(check-exn exn:fail? (lambda () (same-sample-rate-check (list (parameterize ([default-sample-rate 22050])
                                                                (silence 10)) 
                                                              (silence 20)))))
(check-exn exn:fail? (lambda () (same-sample-rate-check (list ))))
(check-not-exn       (lambda () (same-sample-rate-check (list (silence 10) 
                                                              (silence 20)))))
(let* ([sample-sound (make-tone 400 0.15 6)]
       [overlaid (assemble (list (list sample-sound 0) (list sample-sound 0)))]
       [doublevol (make-tone 400 0.3 6)])
  (for ([i (in-range 6)])
    (check-= (rs-ith/left/s16 overlaid i) (rs-ith/left/s16 doublevol i) 1.0)
    (check-= (rs-ith/right/s16 overlaid i) (rs-ith/right/s16 doublevol i) 1.0))
  
  (define s2 (assemble (list (list sample-sound 0) (list sample-sound 3.5))))
  (check-equal? (rs-ith/left/s16 s2 5)
                (+ (rs-ith/left/s16 sample-sound 5)
                   (rs-ith/left/s16 sample-sound 2))))
  
  (let* ([sample-sound (mono-signal->rsound 100 (lambda (x) (* 0.2 (random))))]
         [overlaid (assemble (list (list sample-sound 25) 
                                          (list sample-sound 0)
                                          (list sample-sound 75)))])
    (for ([i (in-range 25)])
      (check-equal? (rs-ith/left/s16 overlaid i)
                    (rs-ith/left/s16 sample-sound i)))
    (for ([i (in-range 25 75)])
      (check-equal? (rs-ith/right/s16 overlaid i)
                    (+ (rs-ith/right/s16 sample-sound i)
                       (rs-ith/right/s16 sample-sound (- i 25)))))
    (for ([i (in-range 75 100)])
      (check-equal? (rs-ith/right/s16 overlaid i)
                    (+ (rs-ith/right/s16 sample-sound i)
                       (rs-ith/right/s16 sample-sound (- i 25))
                       (rs-ith/right/s16 sample-sound (- i 75)))))
    (for ([i (in-range 100 125)])
      (check-equal? (rs-ith/left/s16 overlaid i)
                    (+ (rs-ith/left/s16 sample-sound (- i 25))
                       (rs-ith/left/s16 sample-sound (- i 75))))))
(define test-rsound (rs-read short-test-wav))
(check-equal? (rsound-frames test-rsound) 100)
(check-equal? (rs-read-sample-rate short-test-wav) 44100)
(check-equal? (rs-read-frames short-test-wav) 100)
(define (desired-nth-sample n)
  (round (* #x8000 (sin (* 2 pi (/ n 44100) 700)))))
(define first-sample (desired-nth-sample 1))
(define second-sample (desired-nth-sample 2))
(define thirtieth-sample (desired-nth-sample 30))
(define fiftieth-sample (desired-nth-sample 50))
(check-= (rs-ith/left/s16 test-rsound 0) 0.0 1e-4)
(check-= (rs-ith/left/s16 test-rsound 1) first-sample 1e-4)
(check-= (rs-ith/right/s16 test-rsound 2) second-sample 1e-4)
(check-= (rs-ith/right/s16 test-rsound 50) fiftieth-sample 1)
(define test-sub-rsound (rs-read/clip short-test-wav 30 40))
(check-not-exn (lambda () (rs-read/clip short-test-wav 30 40.0)))
(check-equal? (rsound-frames test-sub-rsound) 10)
(check-= (rs-ith/left/s16 test-sub-rsound 0) (desired-nth-sample 30) 1e-4)
(check-= (rs-ith/right/s16 test-sub-rsound 1) (desired-nth-sample 31) 1e-4)
(let ([temp (make-temporary-file)])
  (rs-write test-rsound temp)
  (check rsound-equal? (rs-read temp) test-rsound))
(let ([short-test2 (rs-append test-rsound test-rsound)])
  (check-equal? (rs-ith/left/s16 short-test2 150) 
                (rs-ith/left/s16 short-test2 50))
  (check-equal? (rs-ith/right/s16 short-test2 153) 
                (rs-ith/right/s16 short-test2 53)))
(let ([short-test2 (rs-append* (list (silence 50)
                                         test-rsound
                                         test-rsound))])
  (check-equal? (rs-ith/left/s16 short-test2 200)
                (rs-ith/left/s16 test-rsound 50))
  (check-equal? (rs-ith/right/s16 short-test2 203) 
                (rs-ith/right/s16 test-rsound 53)))
  (check-equal? (rsound-start test-rsound) 0)
  (check-equal? (rsound-stop test-rsound) 100)
(let ([shorter-test (clip test-rsound 30 60)])
  (check-equal? (rsound-start shorter-test) 30)
  (check-equal? (rsound-stop shorter-test) 60)
  (check-equal? (rsound-frames shorter-test) 30)
  (check-equal? (rsound-sample-rate shorter-test) 44100)
  (check-equal? (rs-ith/left/s16 shorter-test 6)
                (rs-ith/left/s16 test-rsound 36)))
(define kick-rsound (rs-read kick-wav))
(check-equal? (rsound-frames kick-rsound) 4410)
(check-equal? (rs-ith/left/s16 kick-rsound 1803) 27532)
(check-equal? (rs-ith/right/s16 kick-rsound 1803) 27532)
(define short-with-pad (rs-read short-with-pad-wav))
(check-equal? (rsound-frames short-with-pad) #x21)
(check-equal? (rs-ith/left/s16 short-with-pad 5) #x892)
(check-equal? (rs-ith/right/s16 short-with-pad 6) #x478)
(check-exn exn:fail?
           (lambda () (rsound-loop (silence 0))))
(check-= (/ (rs-ith/left/s16 (mono-signal->rsound 300
                                                 (lambda (i) (* 1.5 (sin (* twopi 147/44100 i)))))
                                 73)
            #x7fff)
         1.0
         1e-4)
(let ([s (silence 100)])
  (set-rs-ith/left! s 34 0.7)
  (set-rs-ith/right! s 87 0.3)
  (check-= (rs-ith/left s 34) 0.7 1e-5)
  (check-= (rs-ith/right s 34) 0.0 1e-5)
  (check-= (rs-ith/left s 87) 0.0 1e-5)
  (check-= (rs-ith/right s 87) 0.3 1e-5))
)))