(module mp3-decoder racket/base (require ffi/unsafe "libmpg123-ffi.rkt" "private/utils.rkt" (prefix-in fin: finalizer) ) (provide mp3-open mp3-valid? mp3-read mp3-stop mp3-seek ) (define-struct mp3-handle (if cb-info cb-audio (stop #:mutable) (seek #:mutable) (reading #:mutable) (format #:mutable) ) #:transparent ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Functions to do the good stuff ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (mp3-valid? mp3-file) #t) (define audio-type 'mp3) (define last-rate 44200) ; An assumption for if we've got nothing (define last-channels 2) ; An assumption for if we've got nothing (define last-bits 16) ; An assumption for if we've got nothing (define (correct-format-hash h) (let ((rate (hash-ref h 'sample-rate #f))) (when (eq? rate #f) (hash-set! h 'sample-rate last-rate))) (let ((channels (hash-ref h 'channels #f))) (when (eq? channels #f) (hash-set! h 'channels last-channels))) (let ((bits (hash-ref h 'bits-per-sample #f))) (when (eq? bits #f) (hash-set! h 'bits-per-sample last-bits))) (let ((total-samples (hash-ref h 'total-samples #f))) (when (eq? total-samples #f) (hash-set! h 'total-samples 0) (hash-set! h 'duration 0))) ) (define (report-format handle current-pcm-pos) (dbg-sound "Reporting format at pcm-pos: ~a" current-pcm-pos) (let ((h (mp3-handle-format handle))) (set! last-rate (hash-ref h 'sample-rate)) (set! last-channels (hash-ref h 'channels)) (set! last-bits (hash-ref h 'bits-per-sample))) ((mp3-handle-cb-info handle) (mp3-handle-format handle))) (define (give-audio handle info pos buffer size) (let ((h (mp3-handle-format handle))) (correct-format-hash h) (hash-set! h 'sample pos) (let ((sample-rate (hash-ref h 'sample-rate last-rate))) (hash-set! h 'current-time (exact->inexact (/ pos sample-rate)))) ((mp3-handle-cb-audio handle) h buffer size))) (define (mp3-open mp3-file* cb-stream-info cb-audio) (let ((mp3-file (if (path? mp3-file*) (path->string mp3-file*) mp3-file*))) (if (file-exists? mp3-file) (let ((handler (mpg123-ffi-decoder-handler))) (handler 'new) (handler 'init mp3-file) (let ((h (make-mp3-handle handler cb-stream-info cb-audio #f #f #f (make-hash) ))) h)) #f))) (define (handle-format handle pcm-pos rate channels sample-bits sample-bytes pcm-length) (let ((f (make-hash))) (hash-set! f 'duration (exact->inexact (/ pcm-length rate))) (hash-set! f 'sample-rate rate) (hash-set! f 'channels channels) (hash-set! f 'bits-per-sample sample-bits) (hash-set! f 'bytes-per-sample sample-bytes) (hash-set! f 'total-samples pcm-length) (set-mp3-handle-format! handle f) ) (report-format handle pcm-pos) ) (define (mp3-read handle) (let* ((ffi-handler (mp3-handle-if handle)) (cb-info (mp3-handle-cb-info handle)) (cb-audio (mp3-handle-cb-audio handle)) ) (set-mp3-handle-reading! handle #t) (let loop () (if (eq? (mp3-handle-stop handle) #t) (begin (dbg-sound "Stopping mp3 decoding") (set-mp3-handle-reading! handle #f) 'stopped-reading ) (begin (unless (eq? (mp3-handle-seek handle) #f) (dbg-sound "Seeking to ~a" (mp3-handle-seek handle)) (ffi-handler 'seek (mp3-handle-seek handle)) (set-mp3-handle-seek! handle #f)) (ffi-handler 'read (λ (info pos buffer size) (if (eq? info 'done) (set-mp3-handle-stop! handle #t) (give-audio handle info pos buffer size) )) (λ (pcm-pos rate channels sample-bits sample-bytes pcm-length) (handle-format handle pcm-pos rate channels sample-bits sample-bytes pcm-length)) ) (loop) ) )) (ffi-handler 'close) (ffi-handler 'delete) ) ) (define (mp3-seek handle percentage) (let ((fmt (mp3-handle-format handle))) (let ((total-samples (hash-ref fmt 'total-samples 0))) (unless (or (eq? total-samples #f) (= total-samples -1)) (let ((sample (inexact->exact (round (* (exact->inexact (/ percentage 100.0)) total-samples))))) (set-mp3-handle-seek! handle sample)) ) ) ) ) (define (mp3-stop handle) (set-mp3-handle-stop! handle #t) (while (mp3-handle-reading handle) (sleep 0.01)) ) ); end of module