-
Notifications
You must be signed in to change notification settings - Fork 0
Playback Engine
Relevant source files
The following files were used as context for generating this wiki page:
The Aurora playback engine is a custom streaming architecture built on top of rodio for audio output and symphonia for decoding. It utilizes a buffered, seekable source implementation that allows for seamless track transitions, high-performance seeking, and background decoding.
The StateStruct serves as the primary controller for the audio pipeline. It manages the rodio::Sink, the current track metadata, and the active SeekableAudio instance.
| Method | Description | Implementation Details |
|---|---|---|
add() |
Loads and plays the current song. | Clears the sink, initializes SeekableAudio, and spawns a background task to save history. daemon/src/types/state_impl/mod.rs:55-79
|
pause() |
Toggles the playback state. | Calls sink.play() or sink.pause() based on current state. daemon/src/types/state_impl/mod.rs:81-87
|
clear() |
Stops playback and resets state. | Clears the sink, empties the queue, and sets current_song to None. daemon/src/types/state_impl/mod.rs:89-95
|
is_paused() |
Returns current sink state. | Direct proxy to self.sink.is_paused(). daemon/src/types/state_impl/mod.rs:97-99
|
The playback.rs module extends StateStruct to handle queue navigation. The next() function supports standard linear progression, "Repeat One" logic, and a shuffle mechanism that swaps the next index with a random queue position. daemon/src/types/state_impl/playback.rs:31-61
Sources: daemon/src/types/state_impl/mod.rs:16-30, daemon/src/types/state_impl/mod.rs:55-99, daemon/src/types/state_impl/playback.rs:7-62
Aurora does not load entire files into memory before playback. Instead, it uses a streaming architecture where a background thread decodes samples into an AudioBuffer while the rodio::Sink consumes them.
SeekableAudio manages the lifecycle of a single track's audio data. Upon initialization via SeekableAudio::new, it:
- Reads metadata (sample rate, channels, duration) using
symphonia. daemon/src/types/state_impl/source.rs:53-58 - Spawns a background thread to run
decode_streaming. daemon/src/types/state_impl/source.rs:63-66 - Wraps the shared
AudioBufferin aBufferSourceand appends it to therodio::Sink. daemon/src/types/state_impl/source.rs:67-69
Seeking is performed by stopping the current sink, calculating the new sample index based on the target Duration, and creating a new BufferSource starting at that index. daemon/src/types/state_impl/source.rs:192-208
This diagram maps the high-level playback concepts to the specific structs and threads in the aurora-daemon.
graph TD
subgraph "Natural Language Space"
A["Playback Request"]
B["Audio Stream"]
C["Track Buffer"]
end
subgraph "Code Entity Space (aurora-daemon)"
direction LR
A1["StateStruct::add()"]
A2["SeekableAudio::new()"]
A3["decode_streaming() thread"]
A4["AudioBuffer"]
A5["BufferSource (rodio::Source)"]
A6["rodio::Sink"]
A1 --> A2
A2 -.-> A3
A3 -- "pushes samples" --> A4
A4 -- "shared Arc/Mutex" --> A5
A5 -- "consumed by" --> A6
end
A --- A1
B --- A3
C --- A4
Sources: daemon/src/types/state_impl/source.rs:45-76, daemon/src/types/state_impl/source.rs:121-182, daemon/src/types/state_impl/source.rs:192-208, daemon/src/types/state_impl/source.rs:212-234
The watcher_thread is a background loop responsible for track progression. It monitors the rodio::Sink to detect when the current audio source has been exhausted.
The thread polls every 100ms. If the sink is empty and the queue is not, it triggers the transition to the next song.
-
Detection: Checks
state_locked.sink.empty(). daemon/src/watcher_thread.rs:11 -
Transition: Calls
state_locked.next(1)followed bystate_locked.add(). daemon/src/watcher_thread.rs:12-13 -
Synchronization: Broadcasts the updated
Response::Queueto all connected clients viasend_to_all. daemon/src/watcher_thread.rs:15-17
sequenceDiagram
participant W as watcher_thread.rs
participant S as StateStruct (Locked)
participant SK as rodio::Sink
participant C as Connected Clients
loop Every 100ms
W->>S: lock()
S->>SK: empty()?
alt Sink is Empty AND Queue not Empty
SK-->>S: true
S->>S: next(1)
S->>S: add() (Load next track)
S->>W: Return updated Queue
W->>C: send_to_all(Response::Queue)
else Sink not Empty
SK-->>S: false
end
W->>S: drop(lock)
end
Sources: daemon/src/watcher_thread.rs:6-21, daemon/src/types/state_impl/playback.rs:31-61
The daemon handles album art extraction and caching to ensure the GUI can display high-resolution covers without re-parsing audio files.
-
Extraction: Uses
loftyto read primary tags and extract thePicturedata. daemon/src/types/state_impl/mod.rs:121-129 -
Processing: Decodes the image using the
imagecrate and resizes it to 100x100 pixels using aNearestfilter. daemon/src/types/state_impl/mod.rs:132-141 -
Caching: Saves the resulting JPEG to the system cache directory (e.g.,
~/.cache/aurora-player/) named by the song's UUID. daemon/src/types/state_impl/mod.rs:147-163 -
Persistence: Spawns a background
tokio::task::spawn_blockingto update theart_pathin the SQLite database. daemon/src/types/state_impl/mod.rs:171-183
Overview
aurora-daemon
- Background Service
- Startup & State Init
- Request Handlers & Dispatch
- Playback Engine & Audio
- Library Indexing & Watching
- Playlists, Liked & History
- Background Threads
aurora-protocol
aurora-player
Reference