-
Notifications
You must be signed in to change notification settings - Fork 0
Queue Playback
QueuePlayer is the main entry point for products that want Veloura's PCM
output without importing Discord. PCMQueuePlayer remains an alias for
backward compatibility.
from veloura.audio import AudioTrack
track = AudioTrack.from_source(
"./music/song.flac",
title="Song Title",
duration=213.4,
artist="Artist",
album="Album",
artwork_url="https://example.com/cover.jpg",
source_id="catalogue:track:123",
mood="late-night",
)Common fields:
| Field | Meaning |
|---|---|
stream_url |
Local path or directly playable media URL passed to FFmpeg. |
webpage |
Human-facing source page, if different from the media URL. |
duration |
Source duration in seconds. Accurate duration enables correctly timed crossfades. |
source_id |
Stable application identifier used before URL or title for cache identity. |
payload |
Application-owned object; Veloura does not inspect it. |
trim_start / trim_end
|
Seconds removed from the beginning or end. |
gain |
Linear gain multiplier, clamped during playback. |
tempo |
Playback speed ratio used by FFmpeg. |
crossfade_seconds |
Optional per-track cap for the outgoing transition. |
analysis |
Veloura's serializable preparation and planning details. |
Extra keyword arguments passed to from_source() are placed in
track.metadata.
from veloura.audio import QueuePlayer
player = QueuePlayer(
volume=0.65,
crossfade_seconds=5.0,
max_queue_size=100,
)For any queue exposed to users, set max_queue_size. An enqueue beyond that
limit raises OverflowError, allowing your API or bot to return a clear error.
player.enqueue(track)
player.extend([second_track, third_track])
player.set_volume(0.8) # clamped to 0.0 through 1.5
player.set_crossfade(6.0) # clamped to 0 through 15 seconds
current = player.current_track()
up_next = player.next_track()
queued = player.queued_tracks()
player.skip()
player.clear() # keeps the current track, clears pending tracks
player.stop() # stops playback and clears everythingskip() returns whether Veloura had something to skip or promote.
while player.is_active():
frame = player.read_frame()
if not frame:
break
output.write(frame)Each non-empty frame represents 20 ms of 48 kHz stereo signed 16-bit PCM and is 3,840 bytes. A final read returns empty bytes. A temporary decoder stall can produce a complete silence frame while Veloura waits for more PCM.
Only one playback consumer should call read_frame(). Queue controls are
protected by the underlying source lock and can be called from control paths,
but your application should still keep ownership of its higher-level state.
The effective fade is the smallest applicable limit:
- The player's configured crossfade.
- The outgoing track's
crossfade_seconds, when prepared. - One third of the outgoing playable duration.
- The hard realtime maximum of 15 seconds.
When the remaining duration reaches that value, Veloura opens the next stream and mixes both frames using cosine/sine equal-power gains. This avoids the obvious volume dip produced by a simple linear fade.
Reliable duration metadata matters. If a live stream has no known duration, Veloura cannot schedule an ending crossfade and will move on when FFmpeg reaches the end.
snapshot = player.snapshot()
print(snapshot.current)
print(snapshot.elapsed)
print(snapshot.duration)
print(snapshot.queue)
print(snapshot.volume)
print(snapshot.crossfade_seconds)
print(snapshot.error)
print(snapshot.busy)snapshot() is intentionally cheap and non-blocking. If the mixer lock is busy,
the snapshot can report busy=True and return the best immediately available
view.
Always expose snapshot.error in logs or diagnostics. Invalid files, expired
stream URLs, and decoder failures are recorded there instead of being silently
lost.
from veloura.audio import transition_preset
plan = player.prepare_next_transition_pair(
transition_preset("automix"),
analysis_seconds=45,
timeout=8,
)
if plan:
print(plan.crossfade_seconds, plan.reason, plan.confidence)This analyzes the current and next tracks and mutates their transition fields.
It returns None until both tracks are available. Run it away from a strict
audio callback because beat analysis invokes FFmpeg.
Veloura Audio · MIT licensed · Documentation for the 0.6.x API