diff --git a/audio-decoder.rkt b/audio-decoder.rkt new file mode 100644 index 0000000..9b46883 --- /dev/null +++ b/audio-decoder.rkt @@ -0,0 +1,159 @@ +(module audio-decoder racket/base + + (require "flac-decoder.rkt" + racket/contract + racket/string + racket/path + ) + + (provide audio-open + audio-read + audio-stop + audio-seek + audio-kind + audio-valid-ext? + audio-file-valid? + audio-known-exts? + audio-register-reader! + make-audio-reader + audio-handle? + ) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Audio readers + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + (define-struct audio-reader + (exts open reader seeker stopper)) + + ;; audiotype, audio-reader + (define audio-readers (make-hash)) + + (hash-set! audio-readers + 'flac + (make-audio-reader '("flac") + flac-open + flac-read + flac-seek + flac-stop)) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Known extensions + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + (define known-extensions + '("flac")) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Register audio reader + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + (define/contract (audio-register-reader! type reader) + (-> symbol? audio-reader? void?) + (set! known-extensions (append known-extensions (audio-reader-exts reader))) + (hash-set! audio-readers type reader)) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Generic audio reader + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + (define-struct audio-handle + ((kind #:mutable) + (cb-stream-info #:mutable) + (cb-audio #:mutable) + (driver #:mutable) + (driver-handle #:mutable) + ) + ) + + (define (audio-known-exts?) + known-extensions) + + (define/contract (audio-kind handle) + (-> audio-handle? symbol?) + (audio-handle-kind handle)) + + (define (audio-valid-ext? ext) + (set! ext (format "~a" ext)) + (when (string-prefix? ext ".") + (set! ext (substring ext 1))) + (not (null? (filter (λ (e) (string-ci=? ext e)) known-extensions))) + ) + + (define/contract (audio-file-valid? file) + (-> (or/c string? path?) boolean?) + (let ((f (build-path file))) + (let ((e (format "~a" (path-get-extension f)))) + (audio-valid-ext? e)))) + + (define/contract (audio-open audio-file cb-stream-info cb-audio) + (-> (or/c string? path?) procedure? procedure? audio-handle?) + (let ((file (if (path? audio-file) + (path->string audio-file) + audio-file))) + (unless (audio-file-valid? audio-file) + (error (format "Not a valid audio file '~a'" audio-file))) + (unless (file-exists? audio-file) + (error (format "File '~a' does not exist" audio-file))) + (let ((reader* (find-reader audio-file))) + (when (eq? reader* #f) + (error (format "Cannot find reader for '~a'" audio-file))) + (let* ((reader-type (car reader*)) + (reader (cadr reader*)) + (handle (make-audio-handle reader-type cb-stream-info cb-audio reader #f)) + ) + (set-audio-handle-driver-handle! + handle + ((audio-reader-open reader) + file + (λ (meta) + (cb-stream-info reader-type handle meta)) + (λ (buf-info audio-buffer buf-len) + (cb-audio reader-type handle buf-info audio-buffer buf-len))) + ) + handle) + ) + ) + ) + + (define/contract (audio-read handle) + (-> audio-handle? void?) + (let ((reader (audio-reader-reader (audio-handle-driver handle)))) + (void? (reader (audio-handle-driver-handle handle))))) + + (define/contract (audio-seek handle percentage) + (-> audio-handle? number? void?) + (let ((seeker (audio-reader-seeker (audio-handle-driver handle)))) + (void (seeker (audio-handle-driver-handle handle) percentage)))) + + (define/contract (audio-stop handle) + (-> audio-handle? void?) + (let ((stopper (audio-reader-stopper (audio-handle-driver handle)))) + (void (stopper (audio-handle-driver-handle handle))))) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; Utils + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + (define (right-reader? reader ext) + (not (null? (filter (λ (e) (string-ci=? ext e)) (audio-reader-exts reader))))) + + (define (find-reader audio-file) + (let* ((f (build-path audio-file)) + (ext (substring (format "~a" (path-get-extension audio-file)) 1))) + (letrec ((f (λ (keys) + (if (null? keys) + #f + (let ((reader (hash-ref audio-readers (car keys)))) + (if (right-reader? reader ext) + (list (car keys) reader) + (f (cdr keys)))))))) + (f (hash-keys audio-readers)) + ) + ) + ) + +) ; end of module + + + \ No newline at end of file diff --git a/flac-decoder.rkt b/flac-decoder.rkt index 142617c..f068485 100644 --- a/flac-decoder.rkt +++ b/flac-decoder.rkt @@ -48,6 +48,8 @@ (channels (hash-ref h 'channels)) (block-size (hash-ref h 'blocksize))) (hash-set! h 'duration (flac-duration handle)) + (let ((sample (hash-ref h 'number))) + (hash-set! h 'sample sample)) (set! last-buffer buffer) (set! last-buf-len block-size) (hash-set! kinds type #t) @@ -70,12 +72,17 @@ (hash-ref mh 'channels) (hash-ref mh 'bits-per-sample) (hash-ref mh 'total-samples)))) + (let ((duration (exact->inexact + (/ (hash-ref mh 'total-samples) + (hash-ref mh 'sample-rate))))) + (hash-set! mh 'duration duration)) (set-flac-handle-stream-info! handle si) (let ((cb (flac-handle-cb-stream-info handle))) (when (procedure? cb) - (cb si)))))) + (cb mh)))))) ) - )) + ) + ) (define (flac-read handle) (let* ((ffi-handler (flac-handle-ffi-decoder-handler handle)) diff --git a/play-test.rkt b/play-test.rkt index e123664..d887e58 100644 --- a/play-test.rkt +++ b/play-test.rkt @@ -1,7 +1,8 @@ #lang racket/base (require "libao.rkt" - "flac-decoder.rkt" + "audio-decoder.rkt" simple-log + "private/utils.rkt" ;data/queue ;racket-sound ) @@ -25,20 +26,20 @@ (sl-log-to-display) -(define (flac-play frame buffer buf-len) - (let* ((sample (hash-ref frame 'number)) - (rate (hash-ref frame 'sample-rate)) +(define (audio-play type handle buf-info buffer buf-len) + (let* ((sample (hash-ref buf-info 'sample)) + (rate (hash-ref buf-info 'sample-rate)) (second (/ (* sample 1.0) (* rate 1.0))) - (bits-per-sample (hash-ref frame 'bits-per-sample)) + (bits-per-sample (hash-ref buf-info 'bits-per-sample)) (bytes-per-sample (/ bits-per-sample 8)) - (channels (hash-ref frame 'channels)) + (channels (hash-ref buf-info 'channels)) (bytes-per-sample-all-channels (* channels bytes-per-sample)) - (duration (hash-ref frame 'duration)) + (duration (hash-ref buf-info 'duration)) ) (when (eq? ao-h #f) (set! ao-h (ao-open-live bits-per-sample rate channels 'big-endian))) ;(displayln 'ao-play) - (ao-play ao-h test-file3-id second duration buffer buf-len 'flac) + (ao-play ao-h test-file3-id second duration buffer buf-len type) ;(displayln 'done) (let ((second-printer (λ (buf-seconds) (let ((s (inexact->exact (round (ao-at-second ao-h))))) @@ -87,13 +88,14 @@ ) ) -(define (flac-meta meta) - (displayln meta)) +(define (audio-meta type handle meta) + (dbg-sound "type: ~a" type) + (dbg-sound "meta: ~a" meta)) (define (play) (set! ao-h #f) - (let ((flac-h (flac-open test-file3 flac-meta flac-play))) - (flac-read flac-h) + (let ((audio-h (audio-open test-file3 audio-meta audio-play))) + (audio-read audio-h) (ao-close ao-h) (set! ao-h #f))) ;(sleep 1.0)