From 3fd5fdbdd585fc4e03601c4c80b80848d96774b2 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Thu, 23 Apr 2026 11:03:47 +0200 Subject: [PATCH] trying to overcome gapless problems with mpg123 (very small tick left) and facilitate output to wav --- libao-async-ffi.rkt | 2 +- libao.rkt | 60 ++++++++++++++++++++-------------- libmpg123-ffi.rkt | 40 ++++++++++++++--------- mp3-decoder.rkt | 80 +++++++++++++++++++++++++++------------------ play-test.rkt | 15 ++++++--- 5 files changed, 120 insertions(+), 77 deletions(-) diff --git a/libao-async-ffi.rkt b/libao-async-ffi.rkt index 2a0ca2a..ae0a295 100644 --- a/libao-async-ffi.rkt +++ b/libao-async-ffi.rkt @@ -64,7 +64,7 @@ (define-libao-async ao_async_version (_fun -> _int)) ;extern void *ao_create_async(int bits, int rate, int channel, int byte_format); -(define-libao-async ao_create_async(_fun _int _int _int _Endian_t -> _libao-async-handle-pointer)) +(define-libao-async ao_create_async(_fun _int _int _int _Endian_t _string/utf-8 -> _libao-async-handle-pointer)) ;extern void ao_stop_async(void *handle); (define-libao-async ao_stop_async(_fun _libao-async-handle-pointer -> _void)) diff --git a/libao.rkt b/libao.rkt index 11143f7..c2f15d0 100644 --- a/libao.rkt +++ b/libao.rkt @@ -10,6 +10,7 @@ ) (provide ao-open-live + ao-open-file ao-play ao-close ao-at-second @@ -81,36 +82,47 @@ (rc:define/contract (ao-open-live bits rate channels byte-format) (rc:-> ao-valid-bits? ao-valid-rate? ao-valid-channels? ao-valid-format? ao-handle?) - (let ((handle (make-ao-handle device-number))) + (ao-open-file bits rate channels byte-format #f)) - (fin:register-finalizer handle - (lambda (handle) - (ao-close handle))) +(define (valid-file-kind? f) + (or (string? f) (path? f) (eq? f #f))) - (set-ao-handle-bits! handle bits) - (set-ao-handle-bytes-per-sample! handle (bytes-for-bits bits)) - (set-ao-handle-byte-format! handle byte-format) - (set-ao-handle-channels! handle channels) - (set-ao-handle-rate! handle rate) +(rc:define/contract (ao-open-file bits rate channels byte-format filename) + (rc:-> ao-valid-bits? ao-valid-rate? ao-valid-channels? ao-valid-format? valid-file-kind? ao-handle?) + (let* ((handle (make-ao-handle device-number)) + (file (if (eq? filename #f) + #f + (format "~a" filename))) + ) + (fin:register-finalizer handle + (lambda (handle) + (ao-close handle))) - (info-sound "ao-open-live ~a ~a ~a ~a" bits rate channels byte-format) + (set-ao-handle-bits! handle bits) + (set-ao-handle-bytes-per-sample! handle (bytes-for-bits bits)) + (set-ao-handle-byte-format! handle byte-format) + (set-ao-handle-channels! handle channels) + (set-ao-handle-rate! handle rate) + + (info-sound "ao-open-live ~a ~a ~a ~a ~a" bits rate channels byte-format file) - (let ((player (ffi:ao_create_async bits rate channels byte-format))) - (set-ao-handle-async-player! handle player) - (if (eq? player #f) - (begin - (err-sound "ao-open-live - cannote create player") - (set-ao-handle-closed! handle #t) - handle) - (begin - (info-sound "ao-open-live - created player") - (set-ao-handle-closed! handle #f) - handle + (let ((player (ffi:ao_create_async bits rate channels byte-format file))) + (set-ao-handle-async-player! handle player) + (if (eq? player #f) + (begin + (err-sound "ao-open-live - cannote create player") + (set-ao-handle-closed! handle #t) + handle) + (begin + (info-sound "ao-open-live - created player") + (set-ao-handle-closed! handle #f) + handle + ) + ) ) - ) ) - ) - ) + ) + (rc:define/contract (ao-close handle) (rc:-> ao-handle? void?) diff --git a/libmpg123-ffi.rkt b/libmpg123-ffi.rkt index 454c878..ae87f91 100644 --- a/libmpg123-ffi.rkt +++ b/libmpg123-ffi.rkt @@ -329,6 +329,7 @@ int main(int argc, char *argv[]) (define pcm-length -1) (define encoding -1) (define mp3-file "") + (define current-pcm-pos 0) (define (new) (if (eq? mh #f) @@ -386,11 +387,8 @@ int main(int argc, char *argv[]) (define (set-param p val) (mpg123_param2 mh p val (exact->inexact val))) - (define (init file) - (let ((r (mpg123_open mh file))) - (unless (eq? r 'MPG123_OK) - (error (format "mpg123_open: ~a" (mpg123_plain_strerror r)))) - ) + (define (do-format) + (dbg-sound "do-format called, got an MPG123_NEW_FORMAT message") (let-values ([(fr rate* channels* encoding*) (mpg123_getformat mh)]) (unless (eq? fr 'MPG123_OK) (error (format "mpg123_format: ~a" (mpg123_plain_strerror fr)))) @@ -404,11 +402,19 @@ int main(int argc, char *argv[]) (unless (eq? sr 'MPG123_OK) (error (format "mpg123_scan: ~a" (mpg123_plain_strerror sr)))) (set! pcm-length (mpg123_length64 mh))) + ) + + (define (init file) + (let ((r (mpg123_open mh file))) + (unless (eq? r 'MPG123_OK) + (error (format "mpg123_open: ~a" (mpg123_plain_strerror r)))) + ) (set! mp3-file (format "~a" file)) + (set! current-pcm-pos 0) #t) (define (mp3-format cb) - (cb rate channels sample-bits sample-bytes pcm-length)) + (cb current-pcm-pos rate channels sample-bits sample-bytes pcm-length)) (define (close) (let ((r (mpg123_close mh))) @@ -423,16 +429,18 @@ int main(int argc, char *argv[]) (set! mp3-file "") #t)) - (define (read cb) + (define (read cb format-cb) (let-values ([(r done) (mpg123_read mh buffer buf-size)]) - (if (eq? r 'MPG123_DONE) - (cb 'done -1 buffer done) - (if (eq? r 'MPG123_OK) - (let ((pcm-pos (mpg123_tell64 mh))) - (cb 'data pcm-pos buffer done)) - (error (format "mpg123_read: ~a" (mpg123_plain_strerror r))) - ) - ) + (cond + ((eq? r 'MPG123_DONE) (cb 'done -1 buffer done)) + ((eq? r 'MPG123_NEW_FORMAT) (do-format) + (mp3-format format-cb) + (read cb format-cb)) + ((eq? r 'MPG123_OK) (let ((pcm-pos (mpg123_tell64 mh))) + (set! current-pcm-pos pcm-pos) + (cb 'data pcm-pos buffer done))) + (else (error (format "mpg123_read: ~a" (mpg123_plain_strerror r)))) + ) ) #t) @@ -453,7 +461,7 @@ int main(int argc, char *argv[]) [(eq? cmd 'close) (close)] [(eq? cmd 'format) (mp3-format (car args))] [(eq? cmd 'info) (info)] - [(eq? cmd 'read) (read (car args))] + [(eq? cmd 'read) (read (car args) (cadr args))] [(eq? cmd 'seek) (seek (car args))] [(eq? cmd 'tell) (tell)] [(eq? cmd 'file) mp3-file] diff --git a/mp3-decoder.rkt b/mp3-decoder.rkt index 2744cd6..fce0859 100644 --- a/mp3-decoder.rkt +++ b/mp3-decoder.rkt @@ -28,37 +28,52 @@ ;; 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 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) - (hash-set! h 'current-time (exact->inexact (/ pos (hash-ref h 'sample-rate)))) + (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 (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)))) + (let ((handler (mpg123-ffi-decoder-handler))) + (handler 'new) (handler 'init mp3-file) (let ((h (make-mp3-handle handler cb-stream-info @@ -66,26 +81,25 @@ #f #f #f - #f + (make-hash) ))) - (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 (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)) @@ -110,18 +124,20 @@ (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) - (set! mp3-handles (cons ffi-handler mp3-handles)) + (ffi-handler 'delete) ) ) (define (mp3-seek handle percentage) (let ((fmt (mp3-handle-format handle))) - (let ((total-samples (hash-ref fmt 'total-samples))) + (let ((total-samples (hash-ref fmt 'total-samples 0))) (unless (or (eq? total-samples #f) (= total-samples -1)) diff --git a/play-test.rkt b/play-test.rkt index b5ce341..c3a0f87 100644 --- a/play-test.rkt +++ b/play-test.rkt @@ -19,8 +19,10 @@ (set! test-file4 "/tmp/test1.mp3") ) (when (eq? os 'windows) - (set! test-file3 "C:\\Muziek\\Klassiek-Strijkkwartet\\Quatuor Zaïde\\Franz\\01 Erlkönig, D. 328 (Arr. For String Quartet by Eric Mouret).flac") + ;(set! test-file3 "C:\\Muziek\\Klassiek-Strijkkwartet\\Quatuor Zaïde\\Franz\\01 Erlkönig, D. 328 (Arr. For String Quartet by Eric Mouret).flac") ;(set! test-file3 "C:\\Muziek\\Klassiek-Viool\\Janine Jansen\\Janine Jansen - Sibelius en Prokovief 1 (2024)\\02 - Violin Concerto in D Minor, Op. 47 II. Adagio di molto.flac") + (set! test-file3 "c:\\tmp\\test.mp3") + (set! test-file4 "c:\\tmp\\test1.mp3") ) ) @@ -33,6 +35,7 @@ (define current-audio-h #f) (sl-log-to-display) +(define wav-output-file #f) (define (audio-play type ao-type handle buf-info buffer buf-len) (let* ((sample (hash-ref buf-info 'sample)) @@ -46,7 +49,7 @@ ) ;(displayln buf-info) (when (eq? ao-h #f) - (set! ao-h (ao-open-live bits-per-sample rate channels 'native-endian))) + (set! ao-h (ao-open-file bits-per-sample rate channels 'native-endian wav-output-file))) ;(displayln 'ao-play) (ao-play ao-h current-file-id second duration buffer buf-len ao-type) (set! duration (inexact->exact (round duration))) @@ -71,13 +74,16 @@ (let* ((buf-size (ao-bufsize-async ao-h)) (buf-seconds (exact->inexact (/ buf-size bytes-per-sample-all-channels rate)))) (second-printer buf-seconds) - (when (> buf-seconds 10) + (when (= (round current-seconds) 10) + (when (= current-file-id 3) + (audio-seek current-audio-h 97.0))) + (when (> buf-seconds 5) (letrec ((waiter (λ () (let ((buf-seconds-left (exact->inexact (/ (ao-bufsize-async ao-h) bytes-per-sample-all-channels rate)))) - (if (< buf-seconds-left 2.0) + (if (< buf-seconds-left 3.0) (info-sound "Seconds in buffer left: ~a" buf-seconds-left) (begin (sleep 0.5) @@ -114,6 +120,7 @@ (set! current-audio-h audio-h) (audio-read audio-h) ) + (info-sound "Opening next file: ~a" test-file4) (let ((audio-h (audio-open test-file4 audio-meta audio-play))) (set! current-file-id test-file4-id) (set! current-audio-h audio-h)