From 25ba4611967786b887f46d9c8120cee8ddbead3b Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Wed, 22 Apr 2026 14:14:26 +0200 Subject: [PATCH] mp3 doc --- scrbl/mp3-decoder.scrbl | 109 ++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 55 deletions(-) diff --git a/scrbl/mp3-decoder.scrbl b/scrbl/mp3-decoder.scrbl index 73b0efc..bd42541 100644 --- a/scrbl/mp3-decoder.scrbl +++ b/scrbl/mp3-decoder.scrbl @@ -10,37 +10,27 @@ @defmodule[racket-sound/mp3-decoder] -This module provides an MP3 decoder implementation for -@racketmodname[racket-sound/audio-decoder]. It opens an MP3 file, +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. -Compared to @racketmodname[racket-sound/flac-decoder], this module -stores its stream information directly in a mutable hash and updates -that hash in place during decoding. - -@section{Handles} - -@defproc[(mp3-handle? [v any/c]) boolean?]{ - -Returns @racket[#t] if @racket[v] is an MP3 decoder handle, and -@racket[#f] otherwise. -} +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? [file any/c]) boolean?]{ +@defproc[(mp3-valid? [mp3-file any/c]) boolean?]{ -Returns @racket[#t]. +Returns #t. -This procedure does not inspect @racket[file]. It exists to satisfy the -reader interface expected by +The current implementation does not inspect mp3-file. This procedure +exists to satisfy the reader interface used by @racketmodname[racket-sound/audio-decoder]. -In practice, basic validation such as file existence and extension -checking is already performed by -@racketmodname[racket-sound/audio-decoder]. This procedure therefore -acts only as an additional hook and currently accepts all inputs. +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} @@ -48,17 +38,16 @@ acts only as an additional hook and currently accepts all inputs. @defproc[(mp3-open [mp3-file* (or/c path? string?)] [cb-stream-info procedure?] [cb-audio procedure?]) - (or/c mp3-handle? #f)]{ + (or/c struct? #f)]{ -Opens an MP3 decoder for @racket[mp3-file*]. +Opens an MP3 decoder for mp3-file*. -If @racket[mp3-file*] is a path, it is converted with -@racket[path->string]. If the file does not exist, the result is -@racket[#f]. +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. +initialization, stream information is collected and stored in a mutable +hash in the handle. The stream-info callback is invoked once, immediately after initialization: @@ -67,22 +56,22 @@ initialization: (cb-stream-info info) ] -where @racket[info] is a mutable hash containing at least: +where info is a mutable hash containing at least: @itemlist[#:style 'compact - @item{@racket['duration] --- stream duration in seconds;} - @item{@racket['sample-rate] --- samples per second per channel;} - @item{@racket['channels] --- number of channels;} - @item{@racket['bits-per-sample] --- bits per sample;} - @item{@racket['bytes-per-sample] --- bytes per sample;} - @item{@racket['total-samples] --- total PCM sample count.}] + @item{'duration} + @item{'sample-rate} + @item{'channels} + @item{'bits-per-sample} + @item{'bytes-per-sample} + @item{'total-samples}] } @section{Reading} -@defproc[(mp3-read [handle mp3-handle?]) any/c]{ +@defproc[(mp3-read [handle struct?]) any/c]{ -Starts the decode loop for @racket[handle]. +Starts the decode loop for handle. The loop repeatedly decodes audio chunks and invokes the audio callback: @@ -91,20 +80,23 @@ callback: (cb-audio info buffer size) ] -Before each callback, the @racket[info] hash is updated in place with: +Before each callback, the info hash is updated in place with: @itemlist[#:style 'compact - @item{@racket['sample] --- current PCM sample position;} - @item{@racket['current-time] --- current playback time in seconds.}] + @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, or} - @item{a stop has been requested via @racket[mp3-stop].}] + @item{the backend reports end-of-stream} + @item{a stop has been requested via mp3-stop}] -If a stop is detected, the procedure returns -@racket['stopped-reading]. +If a stop is detected, the procedure returns 'stopped-reading. After termination, the underlying decoder is closed and released. @@ -113,24 +105,32 @@ The return value is otherwise unspecified. @section{Seeking} -@defproc[(mp3-seek [handle mp3-handle?] +@defproc[(mp3-seek [handle struct?] [percentage number?]) void?]{ -Seeks within the stream by percentage of the total length. +Requests a seek within the stream. -The @racket[percentage] argument is interpreted as a value between -@racket[0] and @racket[100]. The target position is computed from the -value stored under @racket['total-samples] in the stream-info hash. +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 sample count is unavailable, this procedure has no effect. +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 mp3-handle?]) void?]{ +@defproc[(mp3-stop [handle struct?]) void?]{ -Requests termination of an active @racket[mp3-read] loop. +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. @@ -144,6 +144,5 @@ 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. -Although the handle contains a field related to deferred seeking, the -current implementation performs seeking directly in -@racket[mp3-seek]. \ No newline at end of file +Seeking is implemented as a request stored in the handle and executed +by the decode loop, not directly by mp3-seek. \ No newline at end of file