This commit is contained in:
2026-06-05 22:17:30 +02:00
6 changed files with 132 additions and 34 deletions
+84 -4
View File
@@ -36,11 +36,54 @@ all other procedures in this module.
The @racket[cb-state] callback is called as:
@racketblock[
(cb-state player state-hash)]
(cb-state player current-player-state state-hash)]
where @racket[player] is the player handle and @racket[state-hash] is the most
recent state snapshot received from the worker side. The callback is called
from the event thread created by @racket[make-audio-player].
where @racket[player] is the player handle,
@racket[current-player-state] is the logical player state reported by the
worker, and @racket[state-hash] is the most recent state snapshot received from
the worker side. The callback is called from the event thread created by
@racket[make-audio-player].
The worker-side player state is one of the following symbols:
@itemlist[
#:style 'compact
@item{@racket['stopped] -- no stream is currently playing. This is the
initial state of the placed player. The player also enters this state after
@racket[audio-stop!] or after the decoder has reached the end of the stream
and the libao output queue has drained.}
@item{@racket['playing] -- a stream is active. The decoder may still be
reading from the input file, or the decoder may already have finished while
libao is still playing queued PCM samples.}
@item{@racket['paused] -- playback is paused. The current stream is retained
and the libao output side is paused. Resuming playback moves the player back
to @racket['playing].}
@item{@racket['quit] -- the placed player has been asked to terminate. This
is the terminal state of the worker.}
]
The wrapper around the placed player may also report these states through
@racket[audio-state]:
@itemlist[
#:style 'compact
@item{@racket['initialized] -- the audio handle has been created, but no
worker-side state snapshot has been received yet.}
@item{@racket['invalid] -- the audio handle is no longer valid. This happens
after @racket[audio-quit!] or when the underlying place or thread has stopped.}
]
The @racket[state-hash] contains the detailed playback state reported by the
worker. It includes values such as the current playback position, stream
duration, buffer status, music id, and libao handle validity. Code that only
needs the logical playback state should use @racket[current-player-state]
instead of extracting it from the hash.
The @racket[cb-eof-stream] callback is called as:
@@ -52,6 +95,8 @@ that the decoder has finished queueing the stream. The audio device may still
have buffered samples to play, and the logical player state may move to
@racket['stopped] slightly later when the output queue has drained.
End-of-stream is not represented as a separate player state.
When @racket[use-place] is true, @racket[make-audio-player] starts
@racket[placed-player] with @racket[dynamic-place] and communicates with it
through place channels. When @racket[use-place] is false, the same command loop
@@ -65,6 +110,7 @@ other active threads in the main VM. Those delays can otherwise be heard as
clicks, gaps, or stuttering playback. Thread mode is useful for debugging the
protocol and callbacks, but it is not the preferred mode for robust playback.}
@defproc[(audio-play? [v any/c]) boolean?]{
Returns @racket[#t] when @racket[v] is a currently valid audio player handle.
@@ -157,6 +203,40 @@ is lowered to @racket[10]. A @racket[max] below @racket[min] is changed to
@racket[30]. The worker side applies its own safe ordering and clamping before
using the values. In normal use the return value is @racket['ok].}
@deftogether[
(@defproc[(audio-ao-buf-ms! [handle audio-play?]
[ms integer?])
(or/c integer? boolean?)]
@defproc[(audio-ao-buf-ms [handle audio-play?])
(or/c integer? boolean?)])
]{
Sets or queries the libao output buffer size, expressed in milliseconds.
The @racket[audio-ao-buf-ms!] procedure forwards @racket[ms] to the audio
player backend by sending the @racket['ao-buf-ms] RPC command. This hooks
into the libao-side buffer configuration and can be used to tune the amount of
audio data that the output layer keeps ahead of playback.
The @racket[audio-ao-buf-ms] procedure queries the currently configured value
by sending the same RPC command without a new value.
The returned value is the value reported by the backend. Normally this is an
integer number of milliseconds. A boolean result indicates that the value could
not be set or queried, or that the backend reported a non-numeric status.
Larger buffer values can make playback more robust against short scheduling
delays, but also increase latency. Smaller values reduce latency, but may make
drop-outs more likely when the decoder or GUI thread is temporarily delayed.
The value is clamped between 50 and 1000ms.
@racketblock[
(audio-ao-buf-ms! player 500)
(audio-ao-buf-ms player)
]
}
@section{State snapshots}
The player keeps a local cache of the most recent state snapshot received from