Jump to content

Audio Overview: Difference between revisions

From OpenBOR
No edit summary
Line 266: Line 266:
==== Animation frame sounds ====
==== Animation frame sounds ====


Animation frames support up to 64 indexed resident sound entries. Sound commands placed before a <code>frame</code> configure that frame only. Entering the frame processes every configured entry according to its delay, chance, and random-selection settings.
Each animation frame supports up to 64 indexed resident sound entries. Sound commands placed before a <code>frame</code> configure that frame only. Entering the frame processes every configured entry according to its delay, chance, and random-selection settings.


<syntaxhighlight lang="text">
<syntaxhighlight lang="text">

Revision as of 00:02, 8 August 2026

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 -1 when 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:

  1. .bor
  2. .ogg
  3. .oga
  4. .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

Each animation frame supports 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.

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 0 when 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.