ffmpeg support

This commit is contained in:
2026-04-28 15:04:12 +02:00
parent d78a0ae9ff
commit d55f52818d
33 changed files with 157 additions and 22 deletions
+60 -11
View File
@@ -2,6 +2,9 @@
(require "flac-decoder.rkt"
"mp3-decoder.rkt"
"ffmpeg-decoder.rkt"
"audio-sniffer.rkt"
"private/utils.rkt"
racket/contract
racket/string
racket/path
@@ -52,12 +55,38 @@
mp3-stop
'ao))
;; FFmpeg decodere
(hash-set! audio-readers
'ffmpeg
(make-audio-reader '("ogg" "oga" "opus"
"m4a" "mp4" "m4b"
"aac"
"wav"
"aiff" "aif" "aifc"
"wma"
"webm" "mkv" "mka")
ffmpeg-valid?
ffmpeg-open
ffmpeg-read
ffmpeg-seek
ffmpeg-stop
'ao))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Known extensions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define known-extensions
'("flac" "mp3"))
'("flac" ; FLAC decoder
"mp3" ; mp3 decoder
"ogg" "oga" "opus"
"m4a" "mp4" "m4b"
"aac"
"wav"
"aiff" "aif" "aifc"
"wma"
"webm" "mkv" "mka" ; FFMPEG decoder
))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Register audio reader
@@ -92,7 +121,11 @@
(set! ext (format "~a" ext))
(when (string-prefix? ext ".")
(set! ext (substring ext 1)))
(not (null? (filter (λ (e) (string-ci=? ext e)) known-extensions)))
(if (not (null? (filter (λ (e) (string-ci=? ext e)) known-extensions)))
#t
(begin
(warn-sound "extension '~a' not in known-extensions '~a'" ext known-extensions)
#f))
)
(define/contract (audio-file-valid? file)
@@ -189,17 +222,33 @@
(define (right-reader? reader ext)
(not (null? (filter (λ (e) (string-ci=? ext e)) (audio-reader-exts reader)))))
(define reader-for-kind
(make-hash '((mp3 . ffmpeg) ; ffmpeg does a better job on gapless playback...
(flac . flac)
(ogg . ffmpeg)
(vorbis . ffmpeg)
(opus . ffmpeg)
(wav . ffmpeg)
(aiff . ffmpeg)
(mp4 . ffmpeg)
(aac . ffmpeg)
(alac . ffmpeg)
(ac3 . ffmpeg)
(ape . ffmpeg)
(wavpack . ffmpeg)
(wma . ffmpeg)
(matroska . ffmpeg))))
(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)
; First try to sniff the format
(let ((format (audio-sniff-format/extension audio-file)))
(let ((reader-kind (hash-ref reader-for-kind format #f)))
(if (eq? reader-kind #f)
#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))
(let ((reader (hash-ref audio-readers reader-kind)))
(list reader-kind reader))
)
)
)
)
+23 -1
View File
@@ -2,7 +2,9 @@
(require racket/contract
racket/path
racket/string)
racket/string
racket/runtime-path
)
(provide audio-format?
audio-sniff-format
@@ -334,3 +336,23 @@
(define/contract (audio-format-matches? file formats)
(-> path-string? (listof symbol?) boolean?)
(audio-format-matches?* file formats))
(define-runtime-path audio-tests "tests")
(define (sniff-test)
(for-each (λ (ext)
(let* ((dir (build-path audio-tests))
(ext* (format ".~a" ext))
(files (map (λ (f)
(build-path audio-tests f))
(filter (λ (f)
(string-suffix? (format "~a" f) ext*))
(directory-list dir))))
)
(for-each (λ (f)
(displayln (format "~a: ~a" f (audio-sniff-format/extension f))))
files)
)
)
'(aac adts flac mp3 ogg ts ac3 aiff m4a mp4 ogx wav wv mp2))
)
+1 -1
View File
@@ -1,7 +1,7 @@
(module ffmpeg_decoder racket/base
(require ffi/unsafe
"ffmpeg_ffi.rkt"
"ffmpeg-ffi.rkt"
"private/utils.rkt"
(prefix-in fin: finalizer)
)
+10
View File
@@ -118,6 +118,16 @@
(define-ffmpeg-audio fmpg_timecode
(_fun _fmpg_instance -> _double))
(define-ffmpeg-audio fmpg_ffmpeg_version
(_fun -> _string*/utf-8))
(define-ffmpeg-audio fmpg_int_version2string
(_fun _int -> _string*/utf-8))
(define-ffmpeg-audio fmpg_compatible_ffmpeg
(_fun -> _int))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Our interface for decoding to racket
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+5
View File
@@ -8,6 +8,7 @@
)
(provide ao_create_async
ao_real_output_bits_async
ao_stop_async
ao_play_async
ao_is_at_music_id_async
@@ -66,6 +67,10 @@
;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 _string/utf-8 -> _libao-async-handle-pointer))
;extern int ao_real_output_bits(void *handle)
(define-libao-async ao_real_output_bits_async
(_fun _libao-async-handle-pointer -> _int))
;extern void ao_stop_async(void *handle);
(define-libao-async ao_stop_async(_fun _libao-async-handle-pointer -> _void))
+11 -2
View File
@@ -10,6 +10,7 @@
)
(provide ao-open-live
ao-device-bits
ao-open-file
ao-play
ao-close
@@ -35,6 +36,8 @@
(define-struct ao-handle (handle-num
[bits #:auto #:mutable]
[bytes-per-sample #:auto #:mutable]
[dev-bits #:auto #:mutable]
[dev-bytes-per-sample #:auto #:mutable]
[byte-format #:auto #:mutable]
[channels #:auto #:mutable]
[rate #:auto #:mutable]
@@ -113,8 +116,10 @@
(err-sound "ao-open-live - cannote create player")
(set-ao-handle-closed! handle #t)
handle)
(begin
(info-sound "ao-open-live - created player")
(let ((out-bits (ffi:ao_real_output_bits_async player)))
(info-sound "ao-open-live - created player at ~a bits" out-bits)
(set-ao-handle-dev-bits! handle out-bits)
(set-ao-handle-dev-bytes-per-sample! handle (/ out-bits 8))
(set-ao-handle-closed! handle #f)
handle
)
@@ -143,6 +148,10 @@
(define (any? x)
#t)
(rc:define/contract (ao-device-bits handle)
(rc:-> ao-handle? integer?)
(ao-handle-dev-bits handle))
(rc:define/contract (ao-play handle music-id at-time-in-s music-duration-s buffer buf-len buf-type)
(rc:-> ao-handle? integer? number? number? any? integer? ao-supported-music-format? void?)
(let* ((bytes-per-sample (ao-handle-bytes-per-sample handle))
+43 -4
View File
@@ -4,10 +4,13 @@
simple-log
"private/utils.rkt"
racket-sprintf
racket/runtime-path
;data/queue
;racket-sound
)
(define-runtime-path tests "tests")
(define test-file3 #f)
(define test-file4 #f)
(define test-file3-id 3)
@@ -15,8 +18,9 @@
(let ((os (system-type 'os)))
(when (eq? os 'unix)
;(set! test-file3 "/muziek/Klassiek-Viool/Alina Ibragimova/Paganini_24 Caprices (2021)/24. 24 Caprices, Op 1 - No. 24 in A minor- Tema con variazioni. Quasi presto.flac")
(set! test-file3 "/tmp/test.mp3")
(set! test-file4 "/tmp/test1.mp3")
;(set! test-file3 "/tmp/test.mp3")
(set! test-file3 (build-path tests "mahler-1.mp3"))
(set! test-file4 (build-path tests "mahler-2.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")
@@ -34,6 +38,10 @@
(define current-file-id -1)
(define current-audio-h #f)
(define current-bits -1)
(define current-rate -1)
(define current-channels -1)
(sl-log-to-display)
(define wav-output-file #f)
(define seeked #f)
@@ -51,12 +59,43 @@
(when (= (round current-seconds) 10)
(when (and (= current-file-id 3) (not seeked))
(set! seeked #t)
(audio-seek current-audio-h 97.0)))))
(let ((perc (exact->inexact (* (/ (- duration 15) duration) 100.0))))
(info-sound "Seeking to ~a%" perc)
(audio-seek current-audio-h perc))))))
)
(when (not (eq? ao-h #f))
(when (not (and
(= current-bits bits-per-sample)
(= current-rate rate)
(= current-channels channels)))
(ao-close ao-h)
(set! ao-h #f)))
;(displayln buf-info)
(when (eq? ao-h #f)
(set! ao-h (ao-open-file bits-per-sample rate channels 'native-endian wav-output-file)))
(info-sound "Opening ao handle")
(info-sound "bits-per-sample: ~a" bits-per-sample)
(info-sound "rate : ~a" rate)
(info-sound "channels : ~a" channels)
(info-sound "endian : ~a" 'native-endian)
(info-sound "(optional) file: ~a" wav-output-file)
(sync-log-sound)
(set! ao-h (ao-open-file bits-per-sample rate channels 'native-endian wav-output-file))
(set! current-bits bits-per-sample)
(set! current-rate rate)
(set! current-channels channels)
(info-sound "ao bits per sample: ~a" (ao-device-bits ao-h))
(sync-log-sound)
)
;(displayln 'ao-play)
;(dbg-sound "Playing audio at ~a" second)
;(sync-log-sound)
(ao-play ao-h current-file-id second duration buffer buf-len ao-type)
(set! duration (inexact->exact (round duration)))
;(displayln 'done)
+1
View File
@@ -18,6 +18,7 @@
err-sound
warn-sound
fatal-sound
sync-log-sound
)
(sl-def-log racket-sound sound)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.