ffmpeg support
This commit is contained in:
+61
-12
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
(require "flac-decoder.rkt"
|
(require "flac-decoder.rkt"
|
||||||
"mp3-decoder.rkt"
|
"mp3-decoder.rkt"
|
||||||
|
"ffmpeg-decoder.rkt"
|
||||||
|
"audio-sniffer.rkt"
|
||||||
|
"private/utils.rkt"
|
||||||
racket/contract
|
racket/contract
|
||||||
racket/string
|
racket/string
|
||||||
racket/path
|
racket/path
|
||||||
@@ -52,12 +55,38 @@
|
|||||||
mp3-stop
|
mp3-stop
|
||||||
'ao))
|
'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
|
;; Known extensions
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(define 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
|
;; Register audio reader
|
||||||
@@ -92,7 +121,11 @@
|
|||||||
(set! ext (format "~a" ext))
|
(set! ext (format "~a" ext))
|
||||||
(when (string-prefix? ext ".")
|
(when (string-prefix? ext ".")
|
||||||
(set! ext (substring ext 1)))
|
(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)
|
(define/contract (audio-file-valid? file)
|
||||||
@@ -189,17 +222,33 @@
|
|||||||
(define (right-reader? reader ext)
|
(define (right-reader? reader ext)
|
||||||
(not (null? (filter (λ (e) (string-ci=? ext e)) (audio-reader-exts reader)))))
|
(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)
|
(define (find-reader audio-file)
|
||||||
(let* ((f (build-path audio-file))
|
; First try to sniff the format
|
||||||
(ext (substring (format "~a" (path-get-extension audio-file)) 1)))
|
(let ((format (audio-sniff-format/extension audio-file)))
|
||||||
(letrec ((f (λ (keys)
|
(let ((reader-kind (hash-ref reader-for-kind format #f)))
|
||||||
(if (null? keys)
|
(if (eq? reader-kind #f)
|
||||||
#f
|
#f
|
||||||
(let ((reader (hash-ref audio-readers (car keys))))
|
(let ((reader (hash-ref audio-readers reader-kind)))
|
||||||
(if (right-reader? reader ext)
|
(list reader-kind reader))
|
||||||
(list (car keys) reader)
|
)
|
||||||
(f (cdr keys))))))))
|
|
||||||
(f (hash-keys audio-readers))
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
+23
-1
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
(require racket/contract
|
(require racket/contract
|
||||||
racket/path
|
racket/path
|
||||||
racket/string)
|
racket/string
|
||||||
|
racket/runtime-path
|
||||||
|
)
|
||||||
|
|
||||||
(provide audio-format?
|
(provide audio-format?
|
||||||
audio-sniff-format
|
audio-sniff-format
|
||||||
@@ -334,3 +336,23 @@
|
|||||||
(define/contract (audio-format-matches? file formats)
|
(define/contract (audio-format-matches? file formats)
|
||||||
(-> path-string? (listof symbol?) boolean?)
|
(-> path-string? (listof symbol?) boolean?)
|
||||||
(audio-format-matches?* file formats))
|
(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
@@ -1,7 +1,7 @@
|
|||||||
(module ffmpeg_decoder racket/base
|
(module ffmpeg_decoder racket/base
|
||||||
|
|
||||||
(require ffi/unsafe
|
(require ffi/unsafe
|
||||||
"ffmpeg_ffi.rkt"
|
"ffmpeg-ffi.rkt"
|
||||||
"private/utils.rkt"
|
"private/utils.rkt"
|
||||||
(prefix-in fin: finalizer)
|
(prefix-in fin: finalizer)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -118,6 +118,16 @@
|
|||||||
(define-ffmpeg-audio fmpg_timecode
|
(define-ffmpeg-audio fmpg_timecode
|
||||||
(_fun _fmpg_instance -> _double))
|
(_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
|
;; Our interface for decoding to racket
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
(provide ao_create_async
|
(provide ao_create_async
|
||||||
|
ao_real_output_bits_async
|
||||||
ao_stop_async
|
ao_stop_async
|
||||||
ao_play_async
|
ao_play_async
|
||||||
ao_is_at_music_id_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);
|
;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))
|
(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);
|
;extern void ao_stop_async(void *handle);
|
||||||
(define-libao-async ao_stop_async(_fun _libao-async-handle-pointer -> _void))
|
(define-libao-async ao_stop_async(_fun _libao-async-handle-pointer -> _void))
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
(provide ao-open-live
|
(provide ao-open-live
|
||||||
|
ao-device-bits
|
||||||
ao-open-file
|
ao-open-file
|
||||||
ao-play
|
ao-play
|
||||||
ao-close
|
ao-close
|
||||||
@@ -35,6 +36,8 @@
|
|||||||
(define-struct ao-handle (handle-num
|
(define-struct ao-handle (handle-num
|
||||||
[bits #:auto #:mutable]
|
[bits #:auto #:mutable]
|
||||||
[bytes-per-sample #:auto #:mutable]
|
[bytes-per-sample #:auto #:mutable]
|
||||||
|
[dev-bits #:auto #:mutable]
|
||||||
|
[dev-bytes-per-sample #:auto #:mutable]
|
||||||
[byte-format #:auto #:mutable]
|
[byte-format #:auto #:mutable]
|
||||||
[channels #:auto #:mutable]
|
[channels #:auto #:mutable]
|
||||||
[rate #:auto #:mutable]
|
[rate #:auto #:mutable]
|
||||||
@@ -113,15 +116,17 @@
|
|||||||
(err-sound "ao-open-live - cannote create player")
|
(err-sound "ao-open-live - cannote create player")
|
||||||
(set-ao-handle-closed! handle #t)
|
(set-ao-handle-closed! handle #t)
|
||||||
handle)
|
handle)
|
||||||
(begin
|
(let ((out-bits (ffi:ao_real_output_bits_async player)))
|
||||||
(info-sound "ao-open-live - created 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)
|
(set-ao-handle-closed! handle #f)
|
||||||
handle
|
handle
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
(rc:define/contract (ao-close handle)
|
(rc:define/contract (ao-close handle)
|
||||||
@@ -143,6 +148,10 @@
|
|||||||
(define (any? x)
|
(define (any? x)
|
||||||
#t)
|
#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: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?)
|
(rc:-> ao-handle? integer? number? number? any? integer? ao-supported-music-format? void?)
|
||||||
(let* ((bytes-per-sample (ao-handle-bytes-per-sample handle))
|
(let* ((bytes-per-sample (ao-handle-bytes-per-sample handle))
|
||||||
|
|||||||
+43
-4
@@ -4,10 +4,13 @@
|
|||||||
simple-log
|
simple-log
|
||||||
"private/utils.rkt"
|
"private/utils.rkt"
|
||||||
racket-sprintf
|
racket-sprintf
|
||||||
|
racket/runtime-path
|
||||||
;data/queue
|
;data/queue
|
||||||
;racket-sound
|
;racket-sound
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(define-runtime-path tests "tests")
|
||||||
|
|
||||||
(define test-file3 #f)
|
(define test-file3 #f)
|
||||||
(define test-file4 #f)
|
(define test-file4 #f)
|
||||||
(define test-file3-id 3)
|
(define test-file3-id 3)
|
||||||
@@ -15,8 +18,9 @@
|
|||||||
(let ((os (system-type 'os)))
|
(let ((os (system-type 'os)))
|
||||||
(when (eq? os 'unix)
|
(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 "/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-file3 "/tmp/test.mp3")
|
||||||
(set! test-file4 "/tmp/test1.mp3")
|
(set! test-file3 (build-path tests "mahler-1.mp3"))
|
||||||
|
(set! test-file4 (build-path tests "mahler-2.mp3"))
|
||||||
)
|
)
|
||||||
(when (eq? os 'windows)
|
(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")
|
||||||
@@ -34,6 +38,10 @@
|
|||||||
(define current-file-id -1)
|
(define current-file-id -1)
|
||||||
(define current-audio-h #f)
|
(define current-audio-h #f)
|
||||||
|
|
||||||
|
(define current-bits -1)
|
||||||
|
(define current-rate -1)
|
||||||
|
(define current-channels -1)
|
||||||
|
|
||||||
(sl-log-to-display)
|
(sl-log-to-display)
|
||||||
(define wav-output-file #f)
|
(define wav-output-file #f)
|
||||||
(define seeked #f)
|
(define seeked #f)
|
||||||
@@ -51,12 +59,43 @@
|
|||||||
(when (= (round current-seconds) 10)
|
(when (= (round current-seconds) 10)
|
||||||
(when (and (= current-file-id 3) (not seeked))
|
(when (and (= current-file-id 3) (not seeked))
|
||||||
(set! seeked #t)
|
(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)
|
;(displayln buf-info)
|
||||||
(when (eq? ao-h #f)
|
(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)
|
;(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)
|
(ao-play ao-h current-file-id second duration buffer buf-len ao-type)
|
||||||
(set! duration (inexact->exact (round duration)))
|
(set! duration (inexact->exact (round duration)))
|
||||||
;(displayln 'done)
|
;(displayln 'done)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
err-sound
|
err-sound
|
||||||
warn-sound
|
warn-sound
|
||||||
fatal-sound
|
fatal-sound
|
||||||
|
sync-log-sound
|
||||||
)
|
)
|
||||||
|
|
||||||
(sl-def-log racket-sound 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.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user