Files
racket-sound/mp3-decoder.rkt
T
2026-04-22 15:01:54 +02:00

142 lines
4.7 KiB
Racket

(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-handles '())
(register-finalizer mp3-handles
(λ (handles)
(for-each (λ (handle)
(handle 'delete)) handles)))
(define (mp3-valid? mp3-file)
#t)
(define audio-type 'mp3)
(define (report-format handle current-pcm-pos)
((mp3-handle-cb-info handle) (mp3-handle-format handle)))
(define (give-audio handle info pos buffer size)
(let ((h (mp3-handle-format handle)))
(hash-set! h 'sample pos)
(hash-set! h 'current-time (exact->inexact (/ pos (hash-ref h '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 (if (null? mp3-handles)
(let ((h (mpg123-ffi-decoder-handler)))
(h 'new)
h)
(let ((h (car mp3-handles)))
(set! mp3-handles (cdr mp3-handles))
h))))
(handler 'init mp3-file)
(let ((h (make-mp3-handle handler
cb-stream-info
cb-audio
#f
#f
#f
#f
)))
(handler 'format
(λ (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! h f)
)
)
)
(report-format h 0)
h))
#f)))
(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)
))
)
(loop)
)
))
(ffi-handler 'close)
(set! mp3-handles (cons ffi-handler mp3-handles))
)
)
(define (mp3-seek handle percentage)
(let ((fmt (mp3-handle-format handle)))
(let ((total-samples (hash-ref fmt 'total-samples)))
(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