better state reporting

This commit is contained in:
2026-05-19 14:30:57 +02:00
parent ef68672203
commit 2daaafb229
3 changed files with 56 additions and 9 deletions
+1 -1
View File
@@ -392,7 +392,7 @@
(let ((m-id (hash-ref h 'at-music-id))) (let ((m-id (hash-ref h 'at-music-id)))
(unless (and (null? force) (or (eq? m-id #f) (= m-id 0))) (unless (and (null? force) (or (eq? m-id #f) (= m-id 0)))
(cb (list 'state h)))) (cb (list 'state (list h player-state)))))
) )
) )
+5 -4
View File
@@ -130,7 +130,7 @@
(cmd-put (cons cmd args)) (ret-get)))) (cmd-put (cons cmd args)) (ret-get))))
(let* ((handle #f) (let* ((handle #f)
(cb-state* (λ (st) (cb-state handle st))) (cb-state* (λ (st st-hash) (cb-state handle st st-hash)))
(cb-eof* (λ () (cb-eof-stream handle)))) (cb-eof* (λ () (cb-eof-stream handle))))
(set! handle (make-audio-play #t (set! handle (make-audio-play #t
cb-state* cb-eof* cb-state* cb-eof*
@@ -144,10 +144,11 @@
(let loop () (let loop ()
(if (audio-play-valid? handle) (if (audio-play-valid? handle)
(let ((e (evt-get 500))) (let ((e (evt-get 500)))
(cond ((eq? e #f) (loop)) (cond ((eq? e #f) (void))
((is-event? e 'state) ((is-event? e 'state)
(set-audio-play-state! handle (evt-data e)) (let ((data (evt-data e)))
(cb-state* (evt-data e))) (set-audio-play-state! handle (car data))
(cb-state* (cadr data) (car data))))
((is-event? e 'audio-done) (cb-eof*)) ((is-event? e 'audio-done) (cb-eof*))
((is-event? e 'exception) ((is-event? e 'exception)
(err-sound "audio-player: exception event: ~a" e)) (err-sound "audio-player: exception event: ~a" e))
+50 -4
View File
@@ -36,11 +36,54 @@ all other procedures in this module.
The @racket[cb-state] callback is called as: The @racket[cb-state] callback is called as:
@racketblock[ @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 where @racket[player] is the player handle,
recent state snapshot received from the worker side. The callback is called @racket[current-player-state] is the logical player state reported by the
from the event thread created by @racket[make-audio-player]. 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: 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 have buffered samples to play, and the logical player state may move to
@racket['stopped] slightly later when the output queue has drained. @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 When @racket[use-place] is true, @racket[make-audio-player] starts
@racket[placed-player] with @racket[dynamic-place] and communicates with it @racket[placed-player] with @racket[dynamic-place] and communicates with it
through place channels. When @racket[use-place] is false, the same command loop 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 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.} protocol and callbacks, but it is not the preferred mode for robust playback.}
@defproc[(audio-play? [v any/c]) boolean?]{ @defproc[(audio-play? [v any/c]) boolean?]{
Returns @racket[#t] when @racket[v] is a currently valid audio player handle. Returns @racket[#t] when @racket[v] is a currently valid audio player handle.