155 lines
5.0 KiB
Racket
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["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. |