initial import from racket-sound -> racket-audio
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
#lang scribble/manual
|
||||
|
||||
@(require racket/base
|
||||
(for-label racket/base
|
||||
racket/path
|
||||
racket/contract
|
||||
"../mp3-decoder.rkt"))
|
||||
|
||||
@title{mp3-decoder}
|
||||
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
||||
|
||||
@defmodule[(file "../mp3-decoder.rkt")]
|
||||
|
||||
This module provides an MP3 decoder backend. It opens an MP3 file,
|
||||
reports stream information through a callback, streams decoded PCM
|
||||
buffers, and supports stopping and seeking.
|
||||
|
||||
The module is intended to be used through
|
||||
@racketmodname[racket-sound/audio-decoder], but its procedures can also
|
||||
be used directly.
|
||||
|
||||
@section{Validation}
|
||||
|
||||
@defproc[(mp3-valid? [mp3-file any/c]) boolean?]{
|
||||
|
||||
Returns #t.
|
||||
|
||||
The current implementation does not inspect mp3-file. This procedure
|
||||
exists to satisfy the reader interface used by
|
||||
@racketmodname[racket-sound/audio-decoder].
|
||||
|
||||
Basic validation such as file existence and extension matching is
|
||||
performed in the higher-level module. This procedure therefore acts as
|
||||
an additional hook and currently accepts all inputs.
|
||||
}
|
||||
|
||||
@section{Opening}
|
||||
|
||||
@defproc[(mp3-open [mp3-file* (or/c path? string?)]
|
||||
[cb-stream-info procedure?]
|
||||
[cb-audio procedure?])
|
||||
(or/c struct? #f)]{
|
||||
|
||||
Opens an MP3 decoder for mp3-file*.
|
||||
|
||||
If mp3-file* is a path, it is converted with path->string. If the file
|
||||
does not exist, the result is #f.
|
||||
|
||||
Otherwise a decoder handle is created and initialized. During
|
||||
initialization, stream information is collected and stored in a mutable
|
||||
hash in the handle.
|
||||
|
||||
The stream-info callback is invoked once, immediately after
|
||||
initialization:
|
||||
|
||||
@racketblock[
|
||||
(cb-stream-info info)
|
||||
]
|
||||
|
||||
where info is a mutable hash containing at least:
|
||||
|
||||
@itemlist[#:style 'compact
|
||||
@item{'duration}
|
||||
@item{'sample-rate}
|
||||
@item{'channels}
|
||||
@item{'bits-per-sample}
|
||||
@item{'bytes-per-sample}
|
||||
@item{'total-samples}]
|
||||
}
|
||||
|
||||
@section{Reading}
|
||||
|
||||
@defproc[(mp3-read [handle struct?]) any/c]{
|
||||
|
||||
Starts the decode loop for handle.
|
||||
|
||||
The loop repeatedly decodes audio chunks and invokes the audio
|
||||
callback:
|
||||
|
||||
@racketblock[
|
||||
(cb-audio info buffer size)
|
||||
]
|
||||
|
||||
Before each callback, the info hash is updated in place with:
|
||||
|
||||
@itemlist[#:style 'compact
|
||||
@item{'sample}
|
||||
@item{'current-time}]
|
||||
|
||||
The loop also checks for a pending seek request. If a seek has been
|
||||
requested, the stored target sample position is forwarded to the
|
||||
decoder backend and the request is cleared.
|
||||
|
||||
The loop terminates when either:
|
||||
|
||||
@itemlist[#:style 'compact
|
||||
@item{the backend reports end-of-stream}
|
||||
@item{a stop has been requested via mp3-stop}]
|
||||
|
||||
If a stop is detected, the procedure returns 'stopped-reading.
|
||||
|
||||
After termination, the underlying decoder is closed and released.
|
||||
|
||||
The return value is otherwise unspecified.
|
||||
}
|
||||
|
||||
@section{Seeking}
|
||||
|
||||
@defproc[(mp3-seek [handle struct?]
|
||||
[percentage number?])
|
||||
void?]{
|
||||
|
||||
Requests a seek within the stream.
|
||||
|
||||
The percentage argument represents a position relative to the full
|
||||
audio stream, where 0 is the start and 100 is the end. The value may be
|
||||
fractional.
|
||||
|
||||
If the total number of samples is available in the handle, the
|
||||
procedure computes an absolute target sample and stores it in the
|
||||
handle as a pending seek request.
|
||||
|
||||
The actual seek operation is performed later by mp3-read in its decode
|
||||
loop.
|
||||
|
||||
If the total number of samples is unavailable or equal to -1, this
|
||||
procedure has no effect.
|
||||
}
|
||||
|
||||
@section{Stopping}
|
||||
|
||||
@defproc[(mp3-stop [handle struct?]) void?]{
|
||||
|
||||
Requests termination of an active mp3-read loop.
|
||||
|
||||
The procedure sets an internal stop flag and waits until the read loop
|
||||
has terminated, sleeping briefly between checks.
|
||||
}
|
||||
|
||||
@section{Notes}
|
||||
|
||||
The stream-info hash is shared between initialization and decoding and
|
||||
is updated in place during playback.
|
||||
|
||||
The audio buffer passed to the callback is managed by the decoder and
|
||||
should be treated as transient data.
|
||||
|
||||
Seeking is implemented as a request stored in the handle and executed
|
||||
by the decode loop, not directly by mp3-seek.
|
||||
Reference in New Issue
Block a user