sound package

License

The sound package is marked as Compatible, which means any game can use it.

A word on garbage collection: SubCritical sound devices are expected to do audio processing in a separate thread from the main thread. However, SubCritical (by design) does not provide thread synchronization primitives. Among other things, that means that the audio processing thread can not communicate with the Lua script or manipulate Lua objects. That includes keeping around references for sound buffers and the like. You are responsible for not allowing sound effects, sound streams, etc. that are "in use" to be garbage collected. This is as simple as keeping references around.

Classes

SoundBuffer

An instance of SoundBuffer contains a snippet of audio data—usually a single sound effect.

length = buffer:GetLength()
Returns the length of the sound data in buffer, in (real) seconds.

MonoSoundBuffer : SoundBuffer StereoSoundBuffer : SoundBuffer

MonoSoundBuffer and StereoSoundBuffer are SoundBuffers containing single-channel and dual-channel audio data, respectively. (The distinction only exists to simplify some sound handling code. Lua code should never need to distinguish between them—they are interchangeable.)

SoundLoader

SoundLoader is the audio analog of GraphicLoader—it reads from audio files of various formats and transforms them into SoundBuffers for your game's consumption.

You cannot instantiate a SoundLoader directly, and should not try to Construct one, since you won't know what formats the returned SoundLoader can load. Instead, you should Construct a specific loader (such as VorbisLoader) and use that.

sound = loader:Load(path) sound,othersound,... = loader:Load(path, otherpath, ...) sound = assert(loader:Load(path))
Tries to load the sound located at path (see SCPath) in a format this SoundLoader understands. If this sound is important, you may want to assert it as shown above, but a little fault tolerance here is probably in order. (Sound can add to a game's atmosphere greatly, but should lack thereof break the game?)

SoundMaster

A SoundMaster is a category of object that attaches to a slave SoundStream at construction time and... does something with it. What it does is specific to the implementing object.

SoundMaster has no methods or other useful attributes for Lua code, and is of no use to anyone except someone implementing something that fits its paradigm.

SoundDevice : SoundMaster

SoundDevice is the obvious application of SoundMaster. It plays the attached SoundStream through some kind of sound hardware.

SoundDevice has no methods or other useful attributes for Lua code. Once it is constructed, it must merely be kept around. Putting it in a local that you never touch again won't cut it, as it will probably be garbage collected, causing your sound to mysteriously stop. Tucking it away in a global will work, however.

If you want easily-controlled "sound effects" like you've probably been expecting for the last several classes, attach a SoundDevice to a SoundMixer.

device = SubCritical.Construct("SoundDevice", some_stream)

SoundStream

A SoundStream is a stream of sound. (With me so far?) It produces audio continuously, as long as something is drawing audio out of it (a SoundMaster, probably).

It can represent, for example, a stream of audio (this is how music is intended to be implemented), or even something relatively complicated like a mixer.

samplerate = stream:GetFramerate()
Returns the frames-per-second of this stream. (Common values are 32000, 44100, and 48000.)

SoundMixer : SoundStream

SoundMixer is where the action is. It's very powerful. It has a fixed number of sound channels, each with a fixed-size command queue. At a given moment, a channel can be playing a SoundBuffer, a SoundStream, or silence. It can play buffers/streams at different rates (which can be changed after the play command is issued), and provides full control over both volume and stereo panning with pan matrices.

mixer = SubCritical.Construct("SoundMixer", channels, qlen, samplerate)
Creates a SoundMixer with the given number of channels, channel command queue depth, and sample rate. At a given time, only qlen-1 unexecuted commands can be in a channel's command queue.
channels and qlen must both be powers of 2. channels must be >= 1, and qlen must be >= 2. (Future revisions to SoundMixer may round up to the nearest valid value.)
Making samplerate a user-controllable option is a good idea. Different sound cards may output at 22050Hz, 32000Hz, 44100Hz, 48000Hz, or even higher (or lower) samplerates, and maximum quality can be attained by outputting at the same samplerate as the sound card.
numchannels = mixer:GetNumChannels()
Return the number of channels this mixer has. This is always >= the number you asked for at construction time.
success = mixer:Play(channel, command) success = mixer:Stop(channel, command)
Both of these functions add a command to channel's command queue. (channel is numbered starting from 1, Lua-style.)
Both of these functions return true on success, false on queueing error (always a full queue), and throw an error on a malformed command or an invalid channel.
Command is a table, containing the following fields:
sound
Optional. Ignored by Stop. If present, should be a SoundBuffer or SoundStream, which will replace the currently playing source (if any) when this command is executed. (resetting the pan matrix, playback rate, etc. to defaults unless new ones are specified in the same table.)
DO NOT allow the object in question to be garbage collected while it could still be in use! (This is as simple as keeping a reference around somewhere, such as in a variable.)
repeats
Optional. Ignored unless accompanied by sound. If present, and sound is a SoundBuffer, its effect depends on its type. If it is a number, it is the number of times to repeat playback of sound (numbers < 0 or > 32767 will repeat indefinitely). If it is a boolean, it is whether to repeat the sound or not (if true, repeat indefinitely; this is the preferred way to have an indefinite loop).
loop_left
Optional. Ignored unless accompanied by sound. If present, and sound is a SoundBuffer, and repeats is greater than 0, the sample offset to repeat from. (Specified in seconds.)
loop_right
Optional. Ignored unless accompanied by sound. If present, and sound is a SoundBuffer, the sample offset to treat as the end of the sound. (Specified in seconds.)
pan
Optional. Not ignored by Stop but it's silly to put it in. Changes the pan matrix in use by the channel. (If sound is provided, this will play sound with the provided pan matrix, otherwise it will change the pan matrix of the currently playing sound. This also affects all future sounds on the channel that do not provide their own value for pan.)
pan should be a table with 1, 2, or 4 elements, all numbers >= -4 or so and <= 4 or so. If it contains 1 element, both channels of the source will be amplified by that factor. If it contains 2 elements, the left channel will be amplified by the first factor and the right channel by the second. If it contains 4 elements, the sound output is determined by the following pseudocode:
left_output = left_source * pan[1] + right_source * pan[2]
right_output = left_source * pan[3] + right_source * pan[4]
rate
The playback rate of the source. (If sound is provided, this will play sound at the given rate, otherwise it will change the rate of the currently playing sound.)
Should be > 0 and <= 256. 1 = normal speed, < 1 = slower, > 1 = faster.
delay
The delay, in seconds, between reaching this part of the command queue and actually executing its command. This has precision down to individual samples.
flag1 flag2 flag3 flag4
If set to a non-false value, set the corresponding flag to true upon executing (not reaching) this command. (See TestFlag.)
success = mixer:ClearQueue(channel)
Inserts a ClearQueue command into channel's command queue. channel is constantly searching its entire queue for a ClearQueue command and if it sees one it deletes all commands up to and including that command from its queue. (You can call Play, etc. immediately after the ClearQueue and it will be as if ClearQueue was instantaneous. It is this complicated due to the lockless implementation of threading SubCritical employs.)
Note that this command can actually fail due to an overfull queue!
flag = mixer:TestFlag(channel, flag)
Returns true if flag is set in channel, clearing it in the process.

WAVLoader : SoundLoader

WAVLoader reads Windows WAVE files. Only 1- and 2-channel 8- or 16-bit WAVEs are supported.

WAVLoader has no methods it didn't inherit.

flac_loader = SubCritical.Construct("WAVLoader")

WAVStream : SoundStream

A stream of audio data from a Windows WAVE file. Only uncompressed 1- and 2-channel 8- or 16-bit PCM WAVEs are supported. The stream loops back to the beginning of the WAVE file upon reaching the end.

WAVStream has no methods it didn't inherit.

stream = SubCritical.Construct("WAVStream", path)

Back to index