NarraLeaf

ISoundUserConfig

type VoiceIdMap = Record<string | number, string | Sound>;
type VoiceSrcGenerator = (id: string | number) => string | Sound;

interface ISoundUserConfig {
    /**
     * Sound source should be a URL or a base64 string
     */
    src: string;
    /**
     * Whether the clip repeats when it reaches its end
     * @default false
     */
    loop: boolean;
    /**
     * Initial volume, between 0 and 1
     * @default 1
     */
    volume: number;
    /**
     * Playback rate, 0.5 to 4
     * @default 1
     */
    rate: number;
    /**
     * Set to `true` to force HTML5 Audio.
     * This should be used for large audio files
     * so that you don't have to wait for the full file to be downloaded and decoded before playing.
     * @default false
     */
    streaming: boolean;
    /**
     * Initial position in seconds - the clip's **in point**.
     *
     * When `loop` is set together with `endTime`, this is also where each repeat restarts from,
     * so the two together describe a loop region rather than just a starting offset - unless
     * `loopStart` moves the repeat's in point somewhere else.
     * @default 0
     */
    seek: number;
    /**
     * Position in seconds where the clip ends - its **out point**.
     * Omit to play through to the end of the file.
     *
     * Without `loop` the clip simply stops there. With `loop` it jumps back to `loopStart`,
     * or to `seek` when no loop in point was given.
     * @default undefined
     */
    endTime?: number;
    /**
     * Position in seconds the clip returns to on every repeat - the **loop in point**.
     *
     * Only meaningful together with `loop` and `endTime`.
     * A value outside `[seek, endTime)` falls back to `seek`.
     * @default undefined
     */
    loopStart?: number;
    /**
     * The audio bus this clip plays on
     * @default SoundType.Sound
     */
    type: SoundBusId;
}

seek, endTime and loopStart

seek is the clip's in point — the position the first pass begins at. endTime is its out point. Given both, playback covers only that region of the file:

Sound.bgm({src: "theme.mp3", seek: 4.2, endTime: 92.5});

Without loop the clip stops at endTime. With loop it repeats, and loopStart decides where each repeat returns to. Leaving loopStart out returns every repeat to seek, which is the behaviour of a clip that has no separate intro.

Separating the two is what expresses the standard "intro then loop" piece of background music — play from the top once, then repeat only the body forever:

Sound.bgm({src: "theme.mp3", loop: true, seek: 0, loopStart: 12, endTime: 90});

The repeat is the Web Audio node's own loop region, so it is sample-accurate: no gap at the seam, and no drift over a session that runs for hours.

A region whose end is not after its start describes nothing playable and is ignored rather than played. A loopStart outside [seek, endTime) falls back to seek.

A streaming clip has no loop region — only a plain repeat — so a loop region does not apply to it.

Restoring a save resumes from the position stored in the save, but the loop still returns to the loop in point rather than to wherever the player happened to save.

type

The audio bus the clip plays on. One of the three buses the engine always seeds — voice, bgm, sound — or the id of any bus declared in GameConfig.audioBuses:

Sound.voice({src: "alice-01.mp3", type: "alice"});

The bus decides which volume control governs this clip, and nothing else — a clip on any bus can be played, stopped, faded and seeked the same way.

On this page