class

Tryst::SDL::Track

Inherits Reference < Object

One playback slot on a mixer: an object the caller holds, carrying a single audio input, its own gain, and its own play/pause/stop state. There is no limit on how many can exist at once.

A track is not usually constructed directly - Sound#play_track and Music make them - but doing so is legal and is how a caller would reuse one slot for a series of different sounds.

Constructors

new(mixer : Mixer = Mixer.default)
Source

Instance methods

audio=(source : AudioSource) : AudioSource

Points the track at some audio. A track with no input assigned cannot be played; assigning while playing swaps what it plays.

Source
clear_on_stopped

Removes the block, and the SDL callback behind it.

Source
deliver_stopped

@api private - Mixer#dispatch_stopped calls this on the main thread. Returns how many stops it delivered, which is more than one when the track stopped several times since the last call.

Source
destroy
Source
destroyed?
Source
gain

This track's gain, multiplied with the mixer's: 1.0 unchanged, 0.0 silent, above 1.0 amplifies.

Source
gain=(value : Float32 | Float64) : Float32
Source
loops_remaining

Loops STILL TO COME, which is not what was asked for at #play: it counts down as they are used up, reads 0 on the final pass or when stopped, and -1 when looping forever.

Source
loops_remaining=(count : Int32) : Int32

Replaces however many loops were left. -1 for forever, 0 to let the current pass be the last - which is how a looping track is brought to a graceful end rather than cut off.

Source
mixer
Source
on_stopped

Runs block after this track finishes - either because it played to the end, or because something stopped it. Pausing does not count, and neither does destroying a playing track.

NOT called from the audio thread. SDL fires its own callback there, where allocating or running arbitrary Crystal is not safe; all that happens then is a counter being bumped. The block runs later, on whichever thread calls Mixer#dispatch_stopped - so an application has to call that periodically, typically from a timer in its event loop:

track.on_stopped { |finished| play_next_after(finished) }
session.every(50) { mixer.dispatch_stopped }

Setting a second block replaces the first.

Source
pause
Source
paused?
Source
play(loops : Int32 = 0, fade_ms : Int32 = 0, start_ms : Int32 = 0) : self

Starts, or restarts, playback.

loops counts EXTRA passes: 0 plays once, 2 plays three times, -1 repeats forever.

Source
playing?

A track is in exactly one of three states: playing, paused or stopped. They are mutually exclusive, so a PAUSED TRACK IS NOT PLAYING. To ask "has this been started at all", which is the usual intent, use !stopped?.

Source
position_3d

Where the track sits in space.

Answers the origin both for a track placed at the origin and for one that was never placed at all - SDL keeps no way to tell those apart, so this cannot be used to ask whether placement is on.

Source
position_3d=(point : Point3D) : Point3D

Places the track in space relative to the listener, who sits at the origin and cannot move. Further away is quieter, and the direction is rendered onto whatever speakers there are.

The track's input is converted to MONO to be placed, so a stereo source loses its own left/right in exchange for a position.

Source
position_ms

How far into its input the track has played, or nil when the input cannot say. A playing track's answer moves; a stopped or paused one reports where it halted.

This is the playback position, not a position in space - see #position_3d for that.

Source
position_ms=(ms : Int) : Int

Seeks. Legal on a stopped track, though #play resets the start position anyway; a paused track resumes from the new spot.

Needs an input that can seek, so not one fed by an audio stream, and some decoders can only land near the requested spot rather than exactly on it.

Source
remaining_ms

How much input is left to mix, or nil when the duration is not known. Zero for a stopped track.

Counts the input only: a track looping forever still reports the remainder of its current pass, and a fade-out in progress does not shorten it.

Source
resume
Source
stereo(left : Number, right : Number) : self

Forces the track to stereo and mixes it only onto the front left and right speakers, with each side scaled by its own gain.

track.stereo(left: 1.0, right: 0.0) # hard left
track.stereo(left: 0.7, right: 0.7) # centred, quieter

Negative gains clamp to zero; there is no ceiling, so above 1.0 makes a side louder. Deliberately no single pan knob wrapping this: turning one number into a pair means picking a pan law, and the two reasonable choices disagree about how loud the centre is.

Resets the 3D position to the origin, since it replaces 3D mode.

Source
stop(fade_ms : Int32 = 0) : self

Halts playback, fading to silence first when asked. Halting an already-stopped track is legal and does nothing.

Source
stop_pending?

@api private - non-allocating check to avoid the dup in Mixer#dispatch_stopped.

Source
stopped?

Neither playing nor paused: never started, or finished, or halted. The third of the three states.

Source
tag(name : String) : self

Adds a tag - an arbitrary label like "sfx", "ui" or "ambient" - so this track can be played, stopped or re-gained along with every other track wearing it. See Mixer#set_tag_gain, which is what makes an independent effects volume possible.

A track may carry any number of tags, and adding one twice is legal and does nothing.

Source
tagged?(name : String) : Bool
Source
tags

The track's tags, in no guaranteed order.

Source
to_unsafe

@api private

Source
unplace

Turns off placement of every kind - forced stereo and 3D alike - and returns the track to mixing normally across all speakers.

Source
untag(name : String) : self

Removes a tag. Removing one the track does not have is fine.

Source

Nested types