Compare commits

...

2 Commits

Author SHA1 Message Date
c1efdca680 - 2026-04-07 15:34:11 +02:00
17a4ddb661 - 2026-04-07 15:33:20 +02:00
4 changed files with 170 additions and 3 deletions

View File

@@ -8,10 +8,10 @@
(define scribblings (define scribblings
'( '(
("scrbl/racket-sound.scrbl" () (library) "racket-sound") ;("scrbl/racket-sound.scrbl" () (library) "racket-sound")
("scrbl/liboa.scrbl" () (library) "racket-sound/liboa/libao.rkt") ;("scrbl/liboa.scrbl" () (library) "racket-sound/liboa/libao.rkt")
("scrbl/flac-decoder.scrbl" () (library) "racket-sound/libflac/flac-decoder.rkt") ("scrbl/flac-decoder.scrbl" () (library) "racket-sound/libflac/flac-decoder.rkt")
("scrbl/taglib.scrbl" () (library) "racket-sound/libtag/taglib.rkt") ;("scrbl/taglib.scrbl" () (library) "racket-sound/libtag/taglib.rkt")
) )
) )

3
scrbl/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*.html
*.js
*.css

155
scrbl/flac-decoder.scrbl Normal file
View File

@@ -0,0 +1,155 @@
#lang scribble/manual
@(require racket/base
(for-label racket/base
racket/path
"../flac-decoder.rkt"
"../flac-definitions.rkt"))
@title{flac-decoder}
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
@defmodule["flac-decoder.rkt"]
This module provides a small decoder interface on top of the FLAC
FFI layer. It opens a decoder for a file, reads stream metadata,
reads audio frames, exposes the current decoder state, and allows
an active read loop to be stopped. It also re-exports the bindings
from @racketmodname["flac-definitions.rkt"].
A decoder handle stores the native decoder handler together with
optional callbacks for stream metadata and decoded audio.
@section{Procedures}
@defproc[(flac-open [flac-file* (or/c path? string?)]
[cb-stream-info (or/c procedure? #f)]
[cb-audio (or/c procedure? #f)])
(or/c flac-handle? #f)]{
Opens a FLAC decoder for @racket[flac-file*]. If a path is given,
it is converted with @racket[path->string]. If the file does not
exist, the result is @racket[#f].
Otherwise a native decoder handler is created with
@racket[flac-ffi-decoder-handler], initialized with the file, and
wrapped in a @racket[flac-handle]. The given callbacks are stored
in the handle.
When metadata of type @racket['streaminfo] is processed and
@racket[cb-stream-info] is a procedure, it is called with a
@racket[flac-stream-info] value.
When decoded audio data is processed and @racket[cb-audio] is a
procedure, it is called as
@racket[(cb-audio header buffers)], where @racket[header] is a
mutable hash containing the frame header fields plus
@racket['duration], and @racket[buffers] is the decoded channel
data returned by the FFI layer.
}
@defproc[(flac-stream-state [handle flac-handle?])
(or/c 'search-for-metadata
'read-metadata
'search-for-frame-sync
'read-frames
'end-of-stream
'ogg-error
'seek-error
'aborted
'memory-allocation-error
'uninitialized
'end-of-link)]{
Returns the current decoder state reported by the native decoder
handler.
}
@defproc[(flac-read [handle flac-handle?])
(or/c 'stopped-reading
'end-of-stream)]{
Reads the stream by repeatedly calling the native decoder with
@racket['process-single].
Before reading starts, the handle fields @racket[stop-reading]
and @racket[reading] are set to @racket[#f] and @racket[#t]. If a
stop has been requested with @racket[flac-stop], reading ends
with @racket['stopped-reading] and @racket[reading] is reset to
@racket[#f].
Whenever pending metadata is available, it is processed with
@racket[process-meta]. For metadata of type
@racket['streaminfo], a @racket[flac-stream-info] value is
constructed, stored in the handle, and passed to the
stream-info callback.
Whenever pending frame data is available, it is processed with
@racket[process-frame]. The frame header is converted to a
mutable hash, extended with a @racket['duration] entry taken
from @racket[flac-duration], and passed together with the
decoded buffers to the audio callback.
For each processed frame, the module also updates
@racket[last-buffer], @racket[last-buf-len], and @racket[kinds].
The procedure prints diagnostic messages for state changes,
metadata, stream errors, and stop handling.
}
@defproc[(flac-read-meta [handle flac-handle?])
(or/c flac-stream-info? #f)]{
Advances the decoder until the state becomes one of
@racket['read-metadata], @racket['end-of-stream],
@racket['aborted], @racket['memory-allocation-error], or
@racket['uninitialized].
If the resulting state is @racket['read-metadata], pending
metadata is processed and the stored stream info is returned.
Otherwise the result is @racket[#f].
Only metadata of type @racket['streaminfo] is converted into a
@racket[flac-stream-info] value by this module.
}
@defproc[(flac-stop [handle flac-handle?]) void?]{
Requests termination of an active @racket[flac-read] loop by
setting the handle field @racket[stop-reading] to @racket[#t].
The procedure then waits until the handle field
@racket[reading] becomes @racket[#f], sleeping for 10 ms between
checks.
The procedure prints timing information before and after the
wait.
}
@section{Diagnostic bindings}
@defthing[kinds hash?]{
A mutable hash used to record the frame number kinds encountered
during decoding. The keys are the values found in the
frame-header field @racket['number-type].
}
@defthing[last-buffer (or/c #f list?)]{
The most recently decoded buffer set produced by frame
processing.
}
@defthing[last-buf-len (or/c #f exact-integer?)]{
The block size of the most recently processed frame.
}
@section{Notes}
The frame-header hash passed to the audio callback is produced
by @racket[flac-ffi-frame-header]. In this module it is extended
with a @racket['duration] field before the callback is called.
All bindings from @racketmodname["flac-definitions.rkt"] are
re-exported.

View File

@@ -16,6 +16,9 @@
tags-year tags-year
tags-genre tags-genre
tags-track tags-track
tags-composer
tags-disc-number
tags-album-artist
tags-length tags-length
tags-sample-rate tags-sample-rate
@@ -152,6 +155,8 @@
[(eq? v 'bit-rate) bit-rate] [(eq? v 'bit-rate) bit-rate]
[(eq? v 'channels) channels] [(eq? v 'channels) channels]
[(eq? v 'keys) (hash-keys key-store)] [(eq? v 'keys) (hash-keys key-store)]
[(eq? v 'album-artist) album-artist]
[(eq? v 'disc-number) disc-number]
[(eq? v 'val) [(eq? v 'val)
(if (null? args) (if (null? args)
#f #f
@@ -207,6 +212,8 @@
(tags-comment 'comment) (tags-comment 'comment)
(tags-genre 'genre) (tags-genre 'genre)
(tags-composer 'composer) (tags-composer 'composer)
(tags-album-artist 'album-artist)
(tags-disc-number 'disc-number)
(tags-year 'year) (tags-year 'year)
(tags-track 'track) (tags-track 'track)
@@ -246,6 +253,8 @@
(define (tags-picture->ext tags) (define (tags-picture->ext tags)
(let ((mt (tags-picture->mimetype tags))) (let ((mt (tags-picture->mimetype tags)))
(cond (cond
((eq? mt #f)
#f)
((or (string-suffix? mt "/jpeg") (string-suffix? mt "/jpg")) ((or (string-suffix? mt "/jpeg") (string-suffix? mt "/jpg"))
'jpg) 'jpg)
((string-suffix? mt "/png") ((string-suffix? mt "/png")