Server exports
Call these from a server script with exports.tm_sound:Name(...). Each name also works with the first letter's case flipped.
Every server export takes a source first: one player id, a list of ids ({ 3, 8 }) or -1 for everyone. The server keeps a record of each sound it starts, so players who join later hear it at the right point, and the sound is removed when its only target leaves.
Playing
exports.tm_sound:PlayUrl(source, id, url, volume?, loop?, bucket?, playback?)
exports.tm_sound:PlayUrlPos(source, id, url, volume?, position, loop?)
exports.tm_sound:PlayUrlPos(source, id, url, volume?, position, distance?, loop?, bucket?)
exports.tm_sound:PlayOnEntity(source, id, url, volume?, netId, distance?, loop?, bucket?, playback?)playback is { timeStamp = seconds, paused = boolean } and starts the sound at a point, or paused, with no delayed seek.
PlayUrl and the first form of PlayUrlPos are xsound's signatures. The longer PlayUrlPos form adds a distance and a routing bucket. The two are told apart by the argument after the position: a boolean is loop, a number is distance. A table after loop may carry distance and bucket.
Play
Play takes one table and is the way to start a sound with everything set up front:
exports.tm_sound:Play({
source = -1, id = 'speaker', url = url, volume = 0.5,
entityId = netId, distance = 8.0, loop = true,
timeStamp = 42.0, paused = true,
rate = 1.05, eq = { low = -6, mid = 0, high = 3 },
filter = { type = 'lowpass', frequency = 800 },
fadeInMs = 1500, bucket = 12,
})Use position instead of entityId for a sound at a point. A paused start does not fade (the fade begins when you fadeIn it before Resume).
Changing a sound
| Export | Description |
|---|---|
Destroy(source, id) | Stops the sound and removes the server's record of it. |
Pause(source, id), Resume(source, id) | Pause and resume. The server keeps counting the position correctly. |
setVolume(source, id, volume) | Sets the volume. Replaces a running fade. |
Position(source, id, position), Distance(source, id, distance) | Move a sound, change its range. |
setSoundLoop(source, id, loop) | Turns looping on or off. |
setTimeStamp(source, id, seconds) | Seeks. |
destroyOnFinish(source, id, bool) | Whether the sound is removed when it ends. |
setRate(source, id, rate) | Pitch and speed. Clamped to the Mixer limits settings. |
setEQ(source, id, band, gainDb), setEQAll(source, id, low, mid, high) | The three band EQ in dB. |
setFilter(source, id, filter) | A biquad filter, or nil to remove it. An unusable filter is ignored and never clears the current one. |
fadeIn(source, id, durationMs, targetVolume?) | Fade in from silence. |
fadeOut(source, id, durationMs) | Fade to silence. The sound keeps playing, destroy it after the fade. |
Pitch, EQ, filter and fades are tracked on the server like volume. They reach every client in the sound's bucket, survive a re-created sound and are part of what a late joiner receives: someone who joins in the middle of a fade starts at the level the fade has reached and finishes the ramp.
Every value is clamped on the server before it is sent, so a script passing nan or an enormous number cannot reach the player.
Information
| Export | Returns |
|---|---|
getServerInfo(id) | The server's record of a sound, or nil. |
serverSoundExists(id) | boolean |
getAllServerSounds() | Every tracked sound, by id. |
getSoundsByEntity(netId) | Ids of the sounds attached to an entity. |
getSoundsByPlayer(source) | Ids of the sounds a player hears (aimed at them, and global ones). |
syncPlayer(source) | Pushes every sound a player should hear. Joining players already do this themselves. |
The record holds id, url, volume, loop, dynamic, position, distance, entityId, playing, paused, rate, eq, filter, startedAt, timeStamp, targets and bucket. timeStamp is the position at the moment it was last updated, about four times a second.
Routing buckets
A sound with a bucket is only played for, and synced to, players currently in that routing bucket. A sound without one is heard everywhere.
- A sound attached to an entity takes the entity's bucket when you give none (the Inherit the entity's bucket setting).
- When a player changes bucket, the sounds of the old instance stop and the new instance's start, checked every Bucket check interval while bucketed sounds exist.
SetBucket(id, bucket)moves a sound,GetBucket(id)reads it.nilmeans every bucket.
exports.tm_sound:PlayUrlPos(-1, 'shell_radio', url, 1.0, coords, 15.0, true, GetPlayerRoutingBucket(src))Feature detection
The manifest lists the optional features it has, so a script written against a newer build can check before it calls an export an older build lacks (calling a missing export stalls the caller):
local features = GetResourceMetadata('tm_sound', 'sound_features', 0) or ''
local hasFilter = features:find('filter', 1, true) ~= nileq is setEQ and setEQAll, rate is the server setRate, fade is the server fades and Play's fadeInMs, filter is the server setFilter and Play's filter. Do not probe an export handle for this: the proxy answers for every name.