XAudio API
The SuperBLT XAudio 'eXtended Audio' API (coincedentally the same as the DirectX audio system, although that was not intended) is an API that allows BLT mods to use 3D audio effects. It is based upon OpenAL, currently using OpenALSoft. HRTF support is planned.
Basics
First, make sure you call blt.xaudio.setup()
before you do anything with XAudio. It doesn't matter if you
call it many times, but it very much matters if you don't call it at all.
XAudio works on the basis of sources and buffers:
Buffers
A piece of audio can be loaded from an OGG file into a buffer. The memory used in the buffer is shared between all sources playing it, so once a source is loaded it's memory consumption does not change.
Buffers are cached, so if you create two buffers with the same filename there will be no difference in memory consumption as if you'd only created one. Additionally, buffers are retained when starting/stopping a heist to speed up loading.
A buffer is created with XAudio.Buffer:new(filename)
, where filename
is the path to a OGG file you
want to load (note the path is the same type as used in dofile, so you should use something
like ModPath .. "sounds/myfile.ogg"
).
When you're done with a buffer, close it with buff:close()
. This tells the system you're done with it. Note
it does not actually destroy the buffer and free up it's memory use (done in case you want to use the buffer
later on) - for that, use buff:close(true)
(note this only has an effect if this is done on the last buffer
object referencing the physical buffer).
You can also find the length in seconds of a buffer with buff:get_length()
.
Reference
close([force])
: Closes this buffer. If you don't close it, the buffer will be automatically be closed when the game exits (however, that is not a reason not to close it.). If theforce
parameter is supplied, it must be a boolean. And if it'strue
, the buffer will be unloaded (assuming there is not another instance of the object open). If not specified orfalse
, the buffer will remain cached to speed up loading for when the source is next loaded.get_length()
: Gets the length of the represented audio track, in seconds.
Sources
A basic source is an instance of XAudio.Source
. You probably won't use these basic sources very much, if
at all. They are Lua objects (via blt_class
), and can be subclassed and extended to perform custom tasks.
There are two basic ways to use a source:
- Create a source with XAudio.Source:new()
, load buffers into it with src:set_buffer(buffer)
, and
close it when you're done. Try to avoid this whenever possible for performance reasons.
- Create a source and supply a buffer as you create it with XAudio.Source:new(buffer)
. The source
will immediately start playing the supplied buffer, and will automatically close itself when it is finished. After
creating one of these, you don't have to pay any further attention to it. This is the recomended way of
using sources.
Note at at present, using OpenALsoft, there is a maximum of 256 sources you can use at any one time. Don't pay too much attention to this particular number, as in the future when hardware acceleration is supported it will vary depending on your motherboard/soundcard drivers.
This means that you should not hold onto open sources. Using the second method presented above will likely clear your mod from having any issues with this, as it is unlikely more than 256 sounds will need to be played at any one time.
If you do have to use the former method for whatever reason, be absolutely sure you close your sources with
the src:close()
method.
You can check if a source is closed with the src:is_closed()
method, and note that trying to call almost any
other method on a closed source will result in a Lua error.
You can get the current state of a source using src:get_state()
. This will return XAudio.Source.PLAYING
,
.PAUSED
, .STOPPED
, .INITIAL
or .OTHER
.
You can set the volume of a source using src:set_volume(val)
where val
is the volume between 0
and 1
.
You can set the position, velocity and direction of a source using src:set_position
, src:set_velocity
,
and src:set_direction
, respectively. These functions accept world positions either as vectors or as x,y,z sets
for their arguments.
You can set the minimum and maximum range at which the sound is audible by using src:set_min_distance
and
src:set_max_distance
. The minimum distance defines the range at which the sound will always play at full
volume and the maximum distance specifies the range at which the sound stops being audible.
Please note that if you play a sterio track, the source's position will be ignored and it will play without any 3D audio effects.
Reference
close()
: Destroy this audio source. Do this as soon as possible, as there is a limit to how many sources can be open at any one time. This does not affect the underlying buffer.set_buffer(buffer)
: Sets this source to play the supplied buffer. This does not start the source playing, only selects which source should be played. It is an error to call this while the source is active (playing or paused states).set_single_sound(enable)
: Sets whether this source is in single-sound mode. You wouldn't normally use this - rather, pass the desired buffer to the constructor.is_active()
: Returnstrue
if this source is in either it's playing or paused state. When a source is active, some methods (such as set_buffer) are disabled.is_closed()
: Returnstrue
if the source is closed,false
otherwise.get_steate()
: Returns the current state of this source. This returns one ofXAudio.Source.PLAYING
,XAudio.Source.PAUSED
,XAudio.Source.STOPPED
, orXAudio.Source.INITIAL
.set_position(pos | x,y,z)
: Sets the position of this audio source, in world units. You can either supply a single vector as an argument, or supply seperate X, Y and Z coordinates.set_velocity(pos | x,y,z)
: Sets the velocity of this object. This is used to calculate the change in pitch due to the doppler effect, although it may be used for other stuff later. Try to set it if you can, but don't worry if you can't.set_direction(pos | x,y,z)
: Sets the direction (facing) of this source. Currently not used, but it may be in the future.set_min_distance(dis)
: Sets the distance at which the sound starts getting quieter.set_max_distance(dis)
: Sets the distance at which the sound stops being audible.set_volume(volume)
: Sets a volume multiplier for this source, between0
(muted) and1
(no attenuation, default).set_type(type)
: Sets the type of this sound. This determines which volume slider controls this source. Valid values areXAudio.Source.SOUND_EFFECT
(default) andXAudio.Source.MUSIC
get_type()
: Gets the type of this source - seeset_type
.get_volume()
: Gets the volume set withset_volume
. This does not include the game's volume sliders.get_raw_volume()
: Gets the raw volume (the value sent to OpenAL) - this includes both the volume set withset_volume
, and the value of the appropriate volume slider (seeset_type
).update(t, dt, paused)
: Updates the audio source. You shouldn't call this yourself, but you're welcome to override it in subclasses. It is called every tick by XAudio.
Unit Sources
A unit source is similar to a basic source, only it is attached to a unit - if you attach a unit source to a cop, then the source will move to follow the cop around.
A unit source is created in a very similar way to a basic source - use XAudio.UnitSource:new(unit, buffer)
to create
the unit source. If you want the source to follow the player, provide XAudio.PLAYER
as the unit argument.
Voiceline Manager
A voiceline manager is a utility to help you with making units speak (and ensuring they don't say two lines at
the same time). They can be created with XAudio.VoicelineManager:new(unit, buffer)
(also accepting XAudio.PLAYER
).
You must constantly call vm:update()
. This function starts new sounds playing, so not calling it will result
in no sounds coming out. Stopping calling it will result in all currently-playing sounds continue to the end, however.
When you've created one, use vm:play(buffer[, channel])
to play a sound. Voiceline manager support using multiple
channels - it will happily play two sounds at once if they're one seperate channels. If channel
is not
speicifed, it defaults to XAudio.VoicelineManager.DEFAULT
. Trying to play a sound while another sound is busy
playing on the same channel will result in it being queued until the first source is done.