Files
racket-sound/scrbl/flac-decoder.scrbl
2026-04-09 11:56:37 +02:00

155 lines
5.0 KiB
Racket

#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["racket-sound/flac-decoder"]
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.