diff --git a/audio-placed-player.rkt b/audio-placed-player.rkt index cb035ab..bb8306a 100644 --- a/audio-placed-player.rkt +++ b/audio-placed-player.rkt @@ -392,7 +392,7 @@ (let ((m-id (hash-ref h 'at-music-id))) (unless (and (null? force) (or (eq? m-id #f) (= m-id 0))) - (cb (list 'state h)))) + (cb (list 'state (list h player-state))))) ) ) diff --git a/audio-player.rkt b/audio-player.rkt index 55c518f..fd07437 100644 --- a/audio-player.rkt +++ b/audio-player.rkt @@ -130,7 +130,7 @@ (cmd-put (cons cmd args)) (ret-get)))) (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)))) (set! handle (make-audio-play #t cb-state* cb-eof* @@ -144,10 +144,11 @@ (let loop () (if (audio-play-valid? handle) (let ((e (evt-get 500))) - (cond ((eq? e #f) (loop)) + (cond ((eq? e #f) (void)) ((is-event? e 'state) - (set-audio-play-state! handle (evt-data e)) - (cb-state* (evt-data e))) + (let ((data (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 'exception) (err-sound "audio-player: exception event: ~a" e)) diff --git a/scrbl/audio-player.scrbl b/scrbl/audio-player.scrbl index 127b5a4..6a76353 100644 --- a/scrbl/audio-player.scrbl +++ b/scrbl/audio-player.scrbl @@ -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.