Audio Overview
OpenBOR uses a unified sample, stream, and channel system for sound effects, ambience, voice, music, and WebM audio. WAV and Ogg Vorbis files may be retained in memory or streamed from module data. BOR ADPCM is streamed through the same channel machinery. Music commands route WAV, Ogg, or BOR playback to soft-reserved channel 0.
The audio system has three main parts:
- Sample - Loaded audio data or streaming metadata identified by a sample ID.
- Playback - One use of a sample, identified by a unique play ID.
- Channel - Mixer slot that owns the current playback state.
Keeping these concepts separate is important. One loaded sample may play on several channels at once, and every playback receives its own play ID.
Formats
| Format | Channels | Source quality | Resident sample | Streamed sample |
|---|---|---|---|---|
| PCM WAV | Mono or stereo | 8, 16, or 24-bit PCM 11.025 through 48 kHz supported |
Yes | Yes |
Ogg Vorbis (.ogg or .oga)
|
Mono or stereo | 11.025 through 48 kHz Decoded to 16-bit PCM |
Yes | Yes |
BOR ADPCM (.bor)
|
Mono or stereo | 11.025 through 48 kHz Decoded to 16-bit PCM |
No | Yes |
WAV, Ogg, and BOR sample types are identified by their file signatures rather than the filename extension alone. Valid WAV data begins with RIFF, valid Ogg data begins with OggS, and valid BOR data begins with BOR music. This permits alternate extensions or extensionless paths when the containing command supports them.
Output
OpenBOR outputs stereo audio at 16-bit and 44.1 kHz (CD quality). Source files up to 24-bit and 48 kHz are retained at their source quality when loaded, then converted by the mixer during output.
Recommendations
- Use WAV for short effects, low decoding overhead, or exact uncompressed PCM.
- Use Ogg for long sounds where compressed module size is useful.
- Use 22.05 kHz for compact general-purpose effects when high-frequency detail is unnecessary.
- Use 44.1 or 48 kHz for music, voice, or effects that benefit from higher-frequency detail.
- Use mono when a sound will be positioned with separate left and right channel volumes.
- Use stereo when the source itself contains important left and right information.
Resident and streamed samples
WAV and Ogg samples support resident or streamed storage. BOR ADPCM supports streamed storage only. The stream argument of loadsample() selects the mode.
| Mode | stream
|
Load behavior | Playback behavior | Best use |
|---|---|---|---|---|
| Resident | 0
|
Loads PCM into memory. Ogg data is decoded during loading. | Reads directly from the sample cache. | Short or frequently reused effects. |
| Streamed | 1
|
Loads and validates metadata only. | Reads or decodes incrementally through rotating PCM buffers. | Music, ambience, speech, and long or infrequently used effects. |
The same WAV or Ogg source file may exist in both modes at once. Resident and streamed forms have separate cache identities and therefore separate sample IDs. Loading the same file repeatedly in the same mode returns its existing sample ID. BOR has only a streamed cache identity.
Stream behavior
Each active streamed playback owns its file position and, for Ogg or BOR, its decoder state. Several channels may therefore stream the same or different files independently.
Each streamed channel retains four rotating buffers of 16 KiB each. At the largest supported WAV input format - 48 kHz, stereo, 24-bit - each buffer holds approximately 57 milliseconds of audio. Total reserve is therefore approximately 228 milliseconds per active stream at that format.
Producer-fed streams use the same rotating queue when the final frame count is not known in advance. WebM audio follows this path: its decoder thread publishes 16-bit PCM blocks while the ordinary channel mixer consumes them.
File reads, seeks, decoding setup, allocation, and cleanup occur outside the audio callback. Streamed channels are refilled fairly in round-robin order under a per-update work budget. If storage cannot supply data in time, the mixer outputs silence without advancing past unheard audio.
Stream capacity is determined by available memory, storage throughput, platform file resources, and the sound channel pool.
Capacity
With the modern PAK64 format, OpenBOR does not impose a universal fixed size limit on sound assets. Internal stream positions, frame counts, and file offsets use 64-bit values wherever the source decoder supports them, providing theoretical bounds measured in exabytes - far beyond practical hardware limits. Actual capacity is therefore determined by the source format, decoder interface, available memory, storage space, and platform resources.
Cached samples are decoded and retained in memory, so their practical size is limited by available RAM. Streamed samples retain only metadata and a fixed set of rotating PCM buffers, allowing very large assets to play without loading the entire file into memory.
Classic RIFF/WAVE format uses 32-bit chunk sizes, limiting WAV files to just under 4 GiB.
OGG and OGA files have a platform-dependent limit. As of Windows 11, the Vorbis decoder reports file positions through a 32-bit long. This limits OGG and OGA assets to 2,147,483,647 bytes - one byte under 2 GiB. The 2 GiB Vorbis limit does not apply to LP64 targets such as 64-bit Linux, where long is 64-bit.
OpenBOR imposes no limit on the combined size of all sound assets; total capacity is constrained only by available RAM and storage hardware.
Sound channels
OpenBOR exposes channel numbers from 0 through 4095. Internally, channels use 64 banks of 64 channels each.
- Bank 0, containing channels 0 through 63, is allocated during sound startup.
- Additional banks are allocated in groups of 64 when existing automatic channels are occupied.
- Allocated banks remain available until sound shutdown.
- Finished or stopped playback immediately frees its channel for reuse.
- 64-bit state masks allow the mixer to skip inactive banks and channels.
Ordinary workloads therefore pay for active playback rather than the full 4,096-channel capacity.
Channel allocation
Automatic playback selects the lowest available non-reserved channel. If every allocated bank is full, OpenBOR allocates the next bank and continues. Priority replacement is considered only when no further automatic channel can be supplied, including allocation failure or exhaustion of the 4,096-channel address space.
When replacement is required, OpenBOR finds the lowest-priority active non-reserved channel:
- Playback replaces that channel when the new priority is equal to or greater than the existing priority.
- Playback fails and returns
-1when every replaceable channel has a greater priority.
Priority normally has no effect because almost all projects remain below the available channel capacity.
Channel 0 and music
Channel 0 is soft-reserved as conventional music and media space.
- Automatic sample allocation skips channel 0.
- Priority replacement skips channel 0.
- WAV, Ogg, and BOR music explicitly target channel 0.
- Starting new music replaces the current channel 0 playback.
- WebM files with an audio track stop channel 0 and all active non-reserved sample channels before opening a producer-fed PCM stream on channel 0.
- WebM files without an audio track leave existing channel 0 playback running.
- Stopping music leaves channel 0 idle but still reserved from automatic allocation.
- Engine playback paths that explicitly target a channel may use channel 0 because the reservation is an allocator exclusion, not ownership protection.
- The next music request or WebM audio stream replaces anything explicitly placed on channel 0.
Bank 0 therefore provides one conventional music channel plus 63 ordinary automatic channels.
WAV, Ogg, BOR, and WebM audio all use the general channel mixer. BOR retains channel-owned ADPCM decoder state, while WebM supplies decoded PCM through a generic live-producer queue.
Practical Limits
The 4,096-channel capacity was neither chosen as a game-design goal nor intended to represent a realistic level of polyphonic mixing. It is simply a byproduct of OpenBOR's highly efficient, mask-friendly organization of 64 banks with 64 channels each. This layout maps naturally to CPU-friendly 64-bit state masks and also happens to produce an absurdly large sound-channel ceiling.
Practical polyphonic mixing is inherently constrained by frequency overlap, auditory masking, finite output headroom, and the limits of human hearing. Once even a few dozen sounds play simultaneously, they may collapse into an indecipherable, noise-like audio slurry.
This is not a limitation of OpenBOR's mixer, but an inherent property of sound reproduction and human perception. The large channel pool eliminates arbitrary channel exhaustion and accommodates unusual workloads, while effective sound design should still prioritize clarity.
Music
Music commands use streamed playback on channel 0. OpenBOR first tries the supplied path exactly. If no playable file is found, it appends extensions in this order:
.bor.ogg.oga.wav
Providing an extension explicitly avoids fallback ambiguity.
Loop offsets
Music offsets select the position used when automatic looping returns from the end of the track. The initial pass still begins at the start.
| Music format | Offset unit | Behavior |
|---|---|---|
| WAV or Ogg | PCM frame | Restarts automatic looping at the selected frame. |
| BOR ADPCM | Encoded data byte | Restarts the channel-owned ADPCM stream at the selected byte offset. |
One PCM frame contains every spatial sample for one moment in time. Mono contains one scalar sample per frame, while stereo contains a left and right pair. To convert seconds to a PCM-frame offset:
frame offset = seconds * sample rate
For example, one second into a 48 kHz WAV or Ogg file is frame 48000. BOR offsets supplied to the music interface use encoded data bytes. BOR sample playback through playsample() uses PCM-frame offsets.
BOR conversion
BOR is an ADPCM format with low decoding overhead. The wav2bor utility included with the OpenBOR development tools converts WAV sources to BOR music.
Use 16-bit PCM, mono or stereo source WAV files. The decoder accepts sample rates from 11.025 through 48 kHz, with 22.05 kHz providing a practical compact option.
Use Ogg when compressed module size is more important than BOR's lower decoding overhead.
Native sound playback
Native model and engine sounds load as resident samples. WAV and Ogg sources are accepted through the same sample loader.
Predefined sounds
OpenBOR loads the following conventional sound paths during startup. Replace a file to customize the effect. Use a silent file when an engine-triggered sound should remain inaudible.
| Path | Typical use |
|---|---|
data/sounds/beat1.wav
|
Default attack impact. Playback speed may vary with damage unless noslowfx is enabled.
|
data/sounds/block.wav
|
Default blocked-attack impact. |
data/sounds/fall.wav
|
Entity landing after a knockdown. |
data/sounds/get.wav
|
Normal item pickup. |
data/sounds/money.wav
|
Score or money pickup. |
data/sounds/jump.wav
|
Jump action. |
data/sounds/indirect.wav
|
Indirect collision caused by a thrown or blasted entity. |
data/sounds/punch.wav
|
Default standing attack-chain sound, commonly heard on a miss. |
data/sounds/1up.wav
|
Extra life. |
data/sounds/go.wav
|
Wait completion and movement prompt. |
data/sounds/timeover.wav
|
Timer expiration or loss of all credits. |
data/sounds/beep.wav
|
Menu navigation. |
data/sounds/beep2.wav
|
Menu selection. |
data/sounds/pause.wav
|
Pause action. beep2.wav is used as fallback when unavailable.
|
data/sounds/bike.wav
|
Biker engine effect. |
Tip: OpenBOR handles missing predefined sounds without interrupting play, though failed loads are written to the log. Use a valid silent WAV when a predefined sound should remain inaudible without producing a load warning.
Model commands
diesound
diesound <path>
# Default
diesound none
Sets the resident sample played when the entity is defeated by damage, including special defeat sources such as pits or lifespan expiration.
Animation frame sounds
Animation frames support up to 64 indexed resident sound entries. Sound commands placed before a frame configure that frame only. Entering the frame processes every configured entry according to its delay, chance, and random-selection settings.
sound.index {index}
sound {path|none}
sound.delay {ticks}
sound.chance {chance}
sound.random {minimum index} {maximum index}
| Command | Default | Description |
|---|---|---|
sound.index {index}
|
0
|
Selects the sound entry modified by subsequent sound, sound.delay, and sound.chance commands. Valid indexes are 0 through 63.
|
| none} | No active entry | Assigns a resident WAV or Ogg sample to the selected index. none creates an explicit silent entry that can participate in random selection.
|
sound.delay {ticks}
|
0
|
Waits the supplied unsigned number of logical clock ticks before the sound becomes eligible to play. The mixer channel owns the delay after the frame is entered. |
sound.chance {chance}
|
100
|
Sets an integer playback chance from 0 through 100 percent. Chance is evaluated once when the sound's delay expires. A failed roll releases the channel without emitting audio. |
sound.random {minimum index} {maximum index}
|
No random range | Selects one configured entry uniformly from the inclusive index range. Configured entries outside the range play normally. |
The selected index begins at 0 for each frame. sound, sound.delay, and sound.chance may appear in any order after selecting an index. Repeating a command for the same entry replaces that property. sound.random does not depend on the selected index, and the last range supplied for the frame takes precedence. All entry data, the random range, and the selected index reset after the frame command.
Only successfully loaded paths and explicit sound none entries count as configured random candidates. Unused indexes inside the range are ignored. Selecting none produces no audio, allowing silent entries to control the probability distribution without combining that distribution with sound.chance.
Random selection and playback chance are independent. sound.random first chooses which indexed entry is submitted. The selected entry's sound.chance is then evaluated after its sound.delay expires. Every configured sound outside the random range is submitted normally and applies its own delay and chance.
Multiple sound example
Both sounds are processed when frame 0 is entered. Index 0 plays immediately. Index 1 waits 10 logical ticks, then has a 50 percent chance to play.
anim attack1
sound.index 0
sound data/sounds/swing.wav
sound.index 1
sound data/sounds/voice.ogg
sound.delay 10
sound.chance 50
frame data/chars/example/attack1_0.png
Random sound example
One configured entry from indexes 0 through 3 is selected whenever the frame is entered. Each of the four entries has equal selection weight, so index 1 provides a 25 percent silent result. Index 4 lies outside the random range and always plays.
sound.random 0 3
sound.index 0
sound data/sounds/step1.wav
sound.index 1
sound none
sound.index 2
sound data/sounds/step2.wav
sound.index 3
sound data/sounds/step3.wav
sound.index 4
sound data/sounds/cloth.wav
frame data/chars/example/walk_0.png
hitfx and blockfx
hitfx <path>
blockfx <path>
These commands assign resident samples to attack collision data. hitfx plays when the attack hits, while blockfx plays when the attack is blocked. Collision-property command names expose the same sound assignments.
Other native systems may select sounds for their own events. Relevant feature articles document those event-specific commands.
Playing hit sounds through a flash entity provides centralized control when several attacks share the same effects.
Script playback
loadsample()
Loads a resident sample or streaming metadata and returns its sample ID.
int sample_id = loadsample(
string filename,
int log_errors,
int stream
);
| Argument | Required | Default | Description |
|---|---|---|---|
filename
|
Yes | None | Full module path to a WAV, Ogg, or BOR source. BOR requires streamed mode. |
log_errors
|
No | 0
|
Nonzero writes failed load attempts to the log. |
stream
|
No | 0
|
0 loads resident PCM. Nonzero loads metadata for streamed playback. BOR accepts only nonzero.
|
Returns -1 when loading fails. Valid sample IDs are zero or greater.
Repeated calls for the same filename and storage mode return the existing sample ID. Resident and streamed forms of the same WAV or Ogg filename receive different IDs. Attempting to load BOR as a resident sample fails.
playsample()
Starts a sample and returns the selected channel.
int channel = playsample(
int sample_id,
unsigned int priority,
int volume_left,
int volume_right,
unsigned int speed,
int loop,
mixed start_offset,
mixed loop_offset
);
| Argument | Required | Default | Description |
|---|---|---|---|
sample_id
|
Yes | None | Sample ID returned by loadsample() or another engine source.
|
priority
|
No | 0
|
Replacement priority used only when no automatic channel can be supplied. |
volume_left
|
No | Current effect volume | Left output level. Values are clamped from 0 through 100. |
volume_right
|
No | Current effect volume | Right output level. Values are clamped from 0 through 100. |
speed
|
No | 100
|
Playback speed and pitch percentage. Values below 1 use 100. |
loop
|
No | 0
|
Nonzero repeats playback until stopped. |
start_offset
|
No | Channel-based start | PCM frame used once when playback begins. |
loop_offset
|
No | 0
|
PCM frame used when automatic looping restarts after the end. |
Returns -1 when playback cannot start. Otherwise, the result is a flattened channel number from 0 through 4095.
Offsets apply equally to resident and streamed WAV or Ogg samples, as well as streamed BOR samples. Every playsample() offset uses PCM frames. Mono BOR seek positions must be even frames because one encoded byte contains two mono frames. start_offset never changes later loop behavior, while loop_offset never changes the initial start. Passing an explicit start offset of 0 guarantees playback begins at the first frame. Omitting it uses channel-based phase staggering.
The loop offset matters only when loop is nonzero. Both offsets must be smaller than the sample's PCM frame count.
Resident example
int sample_id = loadsample("data/sounds/voice.ogg", 1, 0);
int channel = playsample(sample_id, 0, 100, 100, 100, 0, 0, 0);
Streamed loop example
This example begins at frame zero and loops to one second into a 48 kHz ambience track.
int sample_id = loadsample("data/sounds/ambience.ogg", 1, 1);
int channel = playsample(sample_id, 0, 100, 100, 100, 1, 0, 48000);
Playback and channel control
isactivesample()
int active = isactivesample(int channel);
Returns 1 when the channel contains active playback, or 0 otherwise.
sampleid()
int play_id = sampleid(int channel);
Returns the channel's unique play ID, or -1 when the channel is inactive. Use SOUND_PROPERTY_SAMPLE when the cached sample ID is required.
querychannel()
int channel = querychannel(int play_id);
Returns the active channel containing the supplied play ID, or -1 when that playback is no longer active. This is the inverse lookup for sampleid().
stopchannel()
stopchannel(int channel);
Stops playback on the supplied channel. Stream handles and decoder state are closed outside the audio callback.
pausesample()
pausesample(int toggle, int channel);
Pauses a single active channel when toggle is nonzero and resumes it when zero.
pausesamples()
pausesamples(int toggle);
Pauses or resumes all active non-reserved sample channels. Soft-reserved channel 0 is controlled explicitly with pausemusic() or the sound object API.
unloadsample()
unloadsample(int sample_id);
Releases resident PCM or streamed metadata for the supplied sample ID. The cache identity remains available, so a later loadsample() call for the same filename and mode restores the sample under the same ID when possible.
Avoid unloading a sample while it is still needed by active playback.
Music script interface
Music functions control streamed WAV, Ogg, and BOR playback on channel 0.
playmusic()
playmusic(string filename, int loop, int loop_offset);
Starts music using the extension search order described above. Calling playmusic() without arguments stops current music.
The optional offset is an automatic-loop position. WAV and Ogg use PCM frames, while BOR uses encoded data bytes.
fademusic()
fademusic(float fade);
fademusic(float fade, string next_music, int loop, int loop_offset);
Reduces the current music volume by fade during each music fade update. The four-argument form begins the supplied music after volume reaches zero.
setmusicvolume()
setmusicvolume(int volume_left, int volume_right);
Sets music left and right volume. Omitting volume_right uses the left value for both sides. Normal project values are 0 through 100, while supplied values are clamped from 0 through 800.
setmusictempo()
setmusictempo(int tempo);
Sets music playback speed and pitch as a percentage. 100 is normal playback.
pausemusic()
pausemusic(int toggle);
Pauses current music when toggle is nonzero and resumes it when zero.
WebM audio
WebM files with an embedded Vorbis audio track decode into a generic producer-fed PCM stream on channel 0. Before opening the stream, WebM stops current channel 0 playback and all active non-reserved sample channels.
Each WebM stream receives a play ID. Queue publication and cleanup verify that ID so a finishing decoder cannot close later replacement playback on channel 0.
WebM files without an audio track leave existing channel 0 playback running and continue its stream updates during video playback. WebM audio targets channel 0 automatically.
Sound object script API
Advanced scripts may inspect the channel pool, obtain a stable sound object pointer, and read or change playback properties.
Music and WebM playback use ordinary sound channel objects. Scripts inspect conventional music space with get_sound_channel_object(0).
Channel banks are retained after allocation, so a sound object pointer remains structurally valid until sound shutdown. The channel may be reused for later playback, however. Store the play ID when a script must verify that a pointer still represents the same playback instance.
Use named constants from openborconstant(). Raw numeric property and mask IDs are implementation details.
Bank masks
mixed mask = get_sound_channel_bank_mask(int mask_type);
Each returned bit represents one bank. Bit 0 represents bank 0, bit 1 represents bank 1, and so on.
| Mask constant | Description |
|---|---|
SOUND_CHANNEL_BANK_MASK_ALLOCATED
|
Banks whose 64-channel storage has been allocated. |
SOUND_CHANNEL_BANK_MASK_ACTIVE
|
Banks containing at least one active channel. |
SOUND_CHANNEL_BANK_MASK_AVAILABLE
|
Allocated banks containing at least one channel available to automatic playback. |
SOUND_CHANNEL_BANK_MASK_STREAMING
|
Banks containing at least one channel with live streaming state. |
Channel masks
mixed mask = get_sound_channel_mask(
int bank,
int mask_type
);
Each returned bit represents one slot in the selected 64-channel bank. Convert a bank and local bit position to the public channel number with:
channel = bank * 64 + bit
Unallocated banks return zero for every channel mask.
| Mask constant | Description |
|---|---|
SOUND_CHANNEL_MASK_ACTIVE
|
Channels currently playing or looping. |
SOUND_CHANNEL_MASK_PAUSED
|
Active channels currently paused. |
SOUND_CHANNEL_MASK_RESERVED
|
Channels excluded from automatic allocation and priority replacement. Channel 0 is reserved by default. |
SOUND_CHANNEL_MASK_STREAMING
|
Channels owning a live stream queue, handle, or decoder. A finished stream may retain this bit briefly until safe main-thread cleanup. |
Mask example
int active_id = openborconstant("SOUND_CHANNEL_MASK_ACTIVE");
mixed bank_zero_active = get_sound_channel_mask(0, active_id);
Channel objects
get_sound_channel_object()
void sound = get_sound_channel_object(int channel);
Returns the stable sound object at a flattened channel index. Valid indexes are 0 through 4095. An unallocated channel returns an empty value.
Allocated but inactive channels still have objects. Test SOUND_PROPERTY_ACTIVE or the active mask before treating an object as current playback.
get_sound_channel_index()
int channel = get_sound_channel_index(void sound);
Validates a sound object pointer and returns its flattened channel index.
Property access
mixed value = get_sound_property(
void sound,
int property
);
set_sound_property(
void sound,
int property,
mixed value
);
Playback-sensitive changes use synchronization helpers. Changing the position or loop offset of an active sample-backed stream discards and rebuilds prefetched buffers so playback resumes from coherent data.
Producer-fed streams such as WebM do not expose a cached sample or seekable source buffer. Their common channel state remains readable, but sample-dependent position or loop writes are rejected. Internal rotating buffers are never exposed through the script API.
| Property constant | Type | Access | Description |
|---|---|---|---|
SOUND_PROPERTY_ACTIVE
|
Integer | Read | Activity state: 0 inactive, 1 one-shot playback, 2 looping playback.
|
SOUND_PROPERTY_CHANNEL
|
Integer | Read | Flattened channel index from 0 through 4095. |
SOUND_PROPERTY_CHANNELS
|
Integer | Read | Source channel count: 1 mono or 2 stereo.
|
SOUND_PROPERTY_LOOP_OFFSET
|
Unsigned 64-bit integer | Read/write | PCM frame used when automatic looping restarts. New values must be within a sample-backed source. Producer-fed streams reject writes. |
SOUND_PROPERTY_PAUSED
|
Integer | Read/write | Pause state. Writes update the authoritative paused mask. |
SOUND_PROPERTY_PERIOD
|
Unsigned 64-bit integer | Read/write | Raw fixed-point PCM frames advanced per output frame, with 16 fractional bits. Zero is rejected. This is lower-level than the playsample() speed percentage.
|
SOUND_PROPERTY_PLAY_ID
|
Integer | Read | Unique ID for the current playback instance. |
SOUND_PROPERTY_PRIORITY
|
Unsigned 64-bit integer | Read/write | Nonnegative replacement priority. Writes must fit the channel's unsigned integer priority storage. |
SOUND_PROPERTY_SAMPLE
|
Integer | Read | Cached sample ID used by the channel, or -1 for a producer-fed stream such as WebM.
|
SOUND_PROPERTY_SAMPLE_POSITION
|
Unsigned 64-bit integer | Read/write | Current PCM frame. Writing seeks resident or sample-backed streamed playback without changing the loop offset. Producer-fed streams reject writes. |
SOUND_PROPERTY_VOLUME_DIVISOR
|
Integer | Read/write | Gain divisor. Values must be 1 or greater. Lower values increase gain and may cause clipping. |
SOUND_PROPERTY_VOLUME_LEFT
|
Integer | Read/write | Left volume. Property writes clamp values from 0 through 800. |
SOUND_PROPERTY_VOLUME_RIGHT
|
Integer | Read/write | Right volume. Property writes clamp values from 0 through 800. |
Property example
void sound = get_sound_channel_object(channel);
if(sound)
{
int active = get_sound_property(
sound,
openborconstant("SOUND_PROPERTY_ACTIVE")
);
if(active)
{
set_sound_property(
sound,
openborconstant("SOUND_PROPERTY_VOLUME_LEFT"),
30
);
set_sound_property(
sound,
openborconstant("SOUND_PROPERTY_VOLUME_RIGHT"),
100
);
}
}
Channel 0 access
Use get_sound_channel_object(0) and the SOUND_PROPERTY_* API to inspect or control current channel 0 playback. This applies equally to WAV, Ogg, BOR, and producer-fed WebM audio where the requested property is applicable.
Generic stream buffers remain engine-internal rather than script-visible playback state.
Related global variants
The following values are available through openborvariant():
| Variant | Description |
|---|---|
effectvol
|
Current effect volume used as the default left and right value for playsample().
|
musicvol
|
Current music volume. |
soundvol
|
Current master sound volume. |
maxsoundchannels
|
Maximum flattened sound channel capacity. Current value is 4096.
|
Practical guidance
- Preload frequently used resident effects before timing-sensitive gameplay.
- Stream long voice, ambience, and music that would waste memory when retained as decoded PCM.
- Keep short, frequently repeated effects resident to avoid unnecessary file and decoder work.
- Use explicit start offset
0when exact synchronization to the first PCM frame matters. - Store both channel and play ID when later logic must confirm the same playback is still present.
- Prefer
stopchannel(),pausesample(), and property setters over direct assumptions about channel record state. - Treat channel masks as fast state summaries. Treat sound objects as reusable channel slots rather than permanent playback objects.
- Remember that channel 0 is conventional music space, not protected music space.