The idea behind OpenAL is a 3d positional spatialized sound library analogous to OpenGL: instead of micromanaging each aspect of sound playback and effect, the application writer may limit themselves to placing the sounds in the scene and letting the native OpenAL implementation determine the correct amount of pitch alteration, gain attenuation, phase shift, etc, required to render the sounds correctly.
That's the goal, anyway.
Here are some of the most Frequently Asked Questions, and their answers.
Configure openal with `configure --enable-esd'. make, then install. Then, place the following in your `~/.openalrc' file:
(define devices '(esd))
Dollars-to-donuts your driver doesn't implemented the select call
properly. You can see messages from native_blitbuffer
complaining
about select timeouts when this is a problem.
If you suffer from an unimplemented select call in your driver, put the following in your `~/.openalrc' file:
(define native-use-select #f)
This will tell OpenAL
that it shouldn't rely on select for
information about when to write to a file descriptor.
Use alAttenuationScale_LOKI
. For more info, please see
See section How to extend Loki's OpenAL.
This is the OpenAL API. Sort of.
al calls, either implicitly or explicitly, act upon opaque objects
of the kind Buffers
, Sources
, Contexts
, or
Listeners
. Of these types, the generation and deletion of
Buffers
, Sources
, and Contexts
are explicit
and must be handled by the application.
To give a brief overview:
Buffers
Buffers
contain PCM audio data, and the parameters associated with
the data (length, frequency, sample width etc). Buffers
cannot
be played, a Source
must be associated with them, and the Source
is then played.
Buffers
are created with a call to alGenBuffers
, which creates
a set of buffer ids and binds them to buffers. The buffer id is the only
mechanism by which an application may refer to a buffer. After its
useful lifetime is over, buffer ids should be deleted via the call
alDeleteBuffers
.
Sources
Sources
are independent objects, which, when played, make sound.
Sources
need to be associated with a Buffer
before they
can be played.
Sources
are created with a call to alGenSources
, which creates
a set of source ids and binds them to sources. The source id is the only
mechanism by which an application may refer to a source. After its
useful lifetime is over, source ids should be deleted via the call
alDeleteSources
.
Contexts
Context
abstracts the audio device from the rest of the library
(and the application). You generally only need one. Calling most al
functions before creating a context via alcCreateContext
will result
in either an error or a segfault.
When you're about to quit, call alcDestroyContext
to destroy the
context and don't make any other al calls.
ALboolean
ALbyte
ALubyte
ALshort
ALushort
ALuint
ALint
ALfloat
ALdouble
ALsizei
Here are the al functions, grouped according to object they affect.
Listener functions
void alListenerf( ALenum pname, ALfloat param )
void alListener3f( ALenum pname, ALfloat param1, ALfloat param2, ALfloat param3 )
void alListenerfv( ALenum pname, ALfloat *param )
void alGetListeneri( ALenum pname, ALint *value )
void alGetListenerf( ALenum pname, ALfloat *values )
void alGetListenerfv( ALenum pname, ALfloat *values )
Source functions
void alGenSources( ALsizei n, ALuint* sources )
void alDeleteSources( ALsizei n, ALuint* sources )
ALboolean alIsSource( ALuint sid )
void alSourcei( ALuint sid, ALenum param, ALint value )
void alSourcef( ALuint sid, ALenum param, ALfloat value )
void alSource3f( ALuint sid, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3)
void alSourcefv( ALuint sid, ALenum param, ALfloat* values )
void alGetSourcei( ALuint sid, ALenum pname, ALint* value )
void alGetSourcef( ALuint sid, ALenum pname, ALfloat* value )
void alGetSourcefv( ALuint sid, ALenum pname, ALfloat* values )
void alSourcePlayv( ALuint ns, ALuint *ids )
void alSourceStopv( ALuint ns, ALuint *ids )
void alSourcePlay( ALuint sid )
void alSourcePause( ALuint sid )
void alSourceStop( ALuint sid )
Buffers
void alGenBuffers( ALsizei n, ALuint *samples )
void alDeleteBuffers( ALsizei n, ALuint *samples )
ALboolean alIsBuffer( ALuint buffer )
void alBufferData( ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq )
ALsizei alBufferAppendData( ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq )
void alGetBufferi( ALuint buffer, ALenum param, ALint *value )
void alGetBufferf( ALuint buffer, ALenum param, ALfloat* value )
Extension Support
ALboolean alIsExtensionPresent( const ALubyte* fname )
void *alGetProcAddress( const ALubyte* fname )
ALenum alGetEnumValue( const ALubyte* ename )
Misc
void alEnable( ALenum capability )
void alDisable( ALenum capability )
ALboolean alIsEnabled( ALenum capability )
void alHint( ALenum target, ALenum mode )
ALboolean alGetBoolean( ALenum param )
ALint alGetInteger( ALenum param )
ALfloat alGetFloat( ALenum param )
ALdouble alGetDouble( ALenum param )
void alGetBooleanv( ALenum param, ALboolean* data )
void alGetIntegerv( ALenum param, ALint* data )
void alGetFloatv( ALenum param, ALfloat* data )
void alGetDoublev( ALenum param, ALdouble* data )
const ALubyte *alGetString( ALenum param )
You can't do anything really good in OpenAL without creating at least one context.
void *alcCreateContext( ALint* attrlist )
attrlist
is usually NULL
, but you can pass it an integer array
terminated by ALC_INVALID
in alc enum / integer pairs:
int attrlist[] = { ALC_SYNC, AL_TRUE, ALC_SOURCES, 100, ALC_FREQUENCY, 44100, ALC_INVALID }; void *context = alcCreateContext(attrlist);
ALCenum alcMakeContextCurrent( ALvoid *alcHandle )
void *alcUpdateContext( ALvoid *alcHandle )
ALC_SYNC
context creation flag), this
commits all the changes needed and writes the result to the audio
backend. In asynchronous operation, this is a NOP.
ALCenum alcDestroyContext( ALvoid *alcHandle )
alcHandle
, freeing all
associated memory.
ALCenum alcGetError( ALvoid );
alc
error set.
const ALubyte *alcGetErrorString(ALenum param);
alc
error set.
Description of OpenAL utility functions goes here.
alkludge.h
is a repository for stuff that need to either be
discarded, rewritten, or thought out. You should not rely on
functions in alkludge.h
, although it is unlikely that functions
will be removed from it without some form of equivalent functionality
being introduced in the regular library.
There are two types of errors in OpenAL: alc
errors and al
errors. al
errors are context specific and one deep. Only the
first occurring error will be recorded, subsequent errors will not be
recorded. Errors may be retrieved via the alGetError
call, after
which the error in the current context will be set to
AL_NO_ERROR
, and subsequent errors will again alter the context's
error state.
alc
errors are not context specific, and may be retrieved via the
alcGetError
call. After a call to alcGetError
, the
context-global error variable is set to ALC_NO_ERROR
.
While we may be a bit biased, we are partial to the Loki OpenAL implementation. Loki's implementation is currently an all-software library with support for multiple filters, configuration, and extension support via plugins.
If you retrieve the library via cvs, be sure to run the autogen.sh script provided. It will build configure and config.h.in.
After that, run configure with the options you want. Run configure --help for available options.
Users can give openal hints as to optimal defaults for various
parameters via the openal configuration file. The openal configuration
file should be placed in either /etc
or in one's home
directory, being named openalrc
in the first location or
.openalrc
in the second location.
The configuration language is meant to be lisp-like. This does not mean
that it supports lisp constructs, only that it looks something like
lisp. This hideous language is referred to within the openal sources as
ALRC
(for openAL Resource Configuration language).
ALRC
supports a very small number of primitives required to give
the user a very small amount of control over OpenAL. Generally, the
user can specific default values for things like listener position,
source parameters, etc.
Primitives are responsible for evaluation their own arguments, unlike expressions or functions. Therefore, an argument passed to a primitive may never be evaluated.
The primitives are:
and
(and predicate-1 predicate-2 ... )Performs a logical
and
on parameters, using short circuit
evaluation. Evaluates to true if none of the parameters
evaluate to false.
or
(or predicate-1 predicate-2 ... )Performs a logical
or
on parameters, stopping at the first true
evaluation. Evaluates to true if any of the parameters evaluate to
true.
define
(define identifier value)
define
evaluates value
, and if identifier
is not
already in the symbol table, creates a new symbol named
identifier
with the evaluated value
. If identifier
is already defined, define sets it to the the evaluated value
.
load-extension
(load-extension "/absolute/pathname/plugin.so")
load-extension
informs openal of the presence of a plugin which
conforms to the extension format described in @xref{Making your own}.
By setting certain variables
, a user can change the behavior of
OpenAL without resorting to wholesale recompilations. Loki's OpenAL
implementation respects on certain variables and checks for them.
Usually, the values are used to set default values (such as the default
gain for a source (usually 1.0).
There are some, but I haven't listed them here.
devices
devices
is a list of tokens (either strings or unquoted symbols)
that OpenAL checks for in order to determine the sequence and types of
devices that should be used to render audio to.
The list of available devices at the time of this writing is:
native | Standard OSS (/dev/dsp) backend on linux. |
sdl | Simple DirectMedia Layer backend. |
arts | aRTs backend |
esd | esound daemon backend. |
alsa | ALSA backend. |
waveout | WAVE file output. |
(define devices '(sdl native))...which indicates to OpenAL that it should try to render audio to the SDL backend (if available), and failing that to try the native audio method for the platform in question, which usually means using the OSS drivers and
/dev/dsp
. If each device specified in devices
fails, then the default behavior (native
) is tried. If that
fails, OpenAL will return a NULL
context.
Symbol values have associated type information. Users should be acquainted with the following types in ALRC:
'(0.0 0.0 0.0) ; list which is not an expression (define blah 'blah) ; list which is an expression
Loki's OpenAL implementation include some functions which are specific
to the implementation. These functions all have the suffix _LOKI
to distinguish them.
In order to use these extensions, the application will need to query
their existence via IsExtensionPresent
, and then use
GetProcAddress
to resolve the address of the extension. The
result of GetProcAddress
will need to be cast to the appropriate
function type.
AL_BYTE_LOKI
AL_BYTE_LOKI
is used as a getter to alGetSourcei
, and
reports a source's position, in bytes, into the buffer, or -1 in the
case of a non playing source. There are no guarentees about the
internal representation of data so the value returned using this
token may not correspond with the equivilant offset in the user
supplied data.
AL_SOURCE_LOOPING_LOKI
AL_SOURCE_LOOPING_LOKI
is used as a setter/getter to
al{Get}Sourcei
, and sets/gets the infinite loop flag on a source.
Here's a list of extensions which are built into the Loki OpenAL implementation. The prefix is included in this section for clarify.
void alAttenuationScale_LOKI(ALfloat param)
alAttenuationScale_LOKI
scales the units of the simulation.
Without a call to this function, it is very likely that your application
will not sound correct.
The default scaling factor in Loki's OpenAL is such that a listener
placed at the origin will hear sounds attenuated until that point that
the sounds are ALMAXDISTANCE
units away. ALMAXDISTANCE
is
an arbitrary constant defined when including AL/alkludge.h
.
It is almost assured that the default scaling factor will be incorrect
for most applications. That is why this call is so important. Usage
will generally follow:
/* create context, load data, and define some value radius * to be the radius of your world simulation. */ setScale = (void (*)(ALfloat )) alGetProcAddress("alAttenuationScale_LOKI"); if(setScale != NULL) { setScale(radius / ALMAXDISTANCE); }It is highly recommended you explicitly use this function, and do not assume that the default units will be useful to you.
ALfloat alcGetAudioChannel_LOKI(ALuint channel)
alcGetAudioChannel_LOKI
takes in a channel enumeration from the
set AL_CHAN_MAIN_LOKI
, AL_CHAN_PCM_LOKI
, and
AL_CHAN_CD_LOKI
, and returns a normalized ALfloat, corresponding
to the volume associated with the channel on whatever backend the
library is using.
void alcSetAudioChannel_LOKI(ALuint channel, ALfloat volume)
alcSetAudioChannel_LOKI
takes a channel specification from the
set AL_CHAN_MAIN_LOKI
, AL_CHAN_PCM_LOKI
, and
AL_CHAN_CD_LOKI
and a normalized volume, and sets the hardware
channel associated with the channel argument to the volume (the volume
mapped to the settings appropriate for the backend, that is).
void alMute_LOKI(ALvoid)
alMute_LOKI
returns nothing and takes no argument. After a call
to alMute_LOKI
, the implementation ceased all audio output, while
still updating state (so sources still play, you just can't hear them).
The audio setting is preserved so that a subsequent call to
alUnMute_LOKI
will restore the volume to its value prior to the
alMute_LOKI
call.
This isn't really useful, as you can do the same with by just querying
and setting the listener gain.
void alUnMute_LOKI(ALvoid)
alUnMute_LOKI
performs the inverse operation of
alMute_LOKI
. alUnMute_LOKI restores the volume of the simulation
to that which is was before calling alMute_LOKI
.
void alReverbScale_LOKI(ALuint sid, ALfloat param)
alReverbScale_LOKI
allows you to set a normalized value param,
which represents the gain that is used when reverberating. Don't use
it. It will be removed as soon as the IASIG extension is completed.
void alReverbDelay_LOKI(ALuint sid, ALfloat param)
alReverbScale_LOKI
allows you to set the delay associated with a
reverberating source. Don't use this. It will be removed as soon as
the IASIG extension is completed.
ALboolean alBufferPitchHack_LOKI(ALuint bid, ALfloat pitch)
ALboolean alutBufferAndConvertData_LOKI(ALuint bid, void *data,
alutBufferAndConvertData_LOKI
is like alBufferData
, but it
infers the format / frequency information from the data itself. This is
useful for when the application is handed a chunk of data that it would
like to use for a buffer, but cannot readily determine the type of
data.
alutBufferAndConvertData_LOKI
accepts data in the form of entire
sound files, in their native format. It does not accept raw PCM data.
The should_use
argument is used to inform OpenAL that the data in
question should be used, as opposed to OpenAL making a copy. I don't
suggest you even set it to AL_TRUE.
alutBufferAndConvertData_LOKI
returns AL_TRUE
when the
conversion and alBufferData
was successful, AL_FALSE
otherwise. Unless you specify the should_use
flag, in which case
alutBufferAndConvertData_LOKI
will return AL_FALSE
in the
additional case that it could not use the original data (ie, in-place
conversion would be impossible without overflow).
void alBombOnError_LOKI(void)
alBombOnError_LOKI
, when called, will cause the implementation to
abort on error, instead of just setting the per-context error and
continuing. This is useful only for debugging. alc errors are not
handled.
void alBufferSyncData_LOKI( ALuint bid, ALenum format, ALvoid *data,
alBufferSyncData_LOKI
is like alBufferData
, but will
use the passed data instead of creating a copy. Not recommended for
general use.
void alBufferi_LOKI( ALuint bid, ALenum param, ALint value )
alBufferi_LOKI
can be used to set a buffer's attributes. Not
recommended unless you know what you're doing. Usually, you can use
this to enable a "multichannel" buffer.
void alBufferDataWithCallback_LOKI( ALuint bid, ALint (*callback)(ALuint sid, ALuint bid, ALshort *data, ALenum format, ALuint samples))
alBufferDataWithCallback_LOKI
can be used to specify that the buffer
bid
should, instead of using a static chunk of data specified by
alBufferData
, call the callback callback
to fill a chunk of
data as needed.
The parameters for callback
are as such:
ALuint sid
The source id that this request is associated with. Since many sources may share the same buffer, this sid allows the application to keep track of offsets and other state associated with each source instance.ALuint bid
The buffer id that the callback is associated with.ALshort *data
The memory area that the callback should populateALenum format
The format the the output data should be in.ALuint samples
The number of samples required.
It is possible to extend the functionality of Loki's implementation of OpenAL via plugins (combined with some configuration mojo).
"Plugins" are actually just shared libraries which contain, as an available symbol, a table which contains function name/address pairs. Code necessary for library initialization and exit should be placed in _init, _fini as described in dlopen(3).
The table containing the function name/address pairs should correspond to the following format:
struct { ALubyte *name; void *addr; } alExtension_03282000 [] = { { "alutLoadGIF", (void *) alutLoadGIF }, { NULL, NULL } };
Right now, the end { NULL, NULL }
pair is needed, and the table
needs to be named alExtension_03282000
. This format is likely to
change in the future, in order to avoid a badly written plugin from
crashing each application linked against openal.
In order to take advantage of the extension, openal will have to be informed of its location via the configuration file See section The openal configuration file.
To avoid potential compatibility problems with initialization code, Loki's
OpenAL implementation checks for the presence of the functions
alExtInit_03282000
and alExtFini_03282000
. These functions
are executed at dlopen and dlclose time, respectively.
Here are some examples.
#include <AL/al.h> #include <AL/alc.h> #include <AL/alkludge.h> #include <AL/alut.h> #include <time.h> #include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #define DATABUFFERSIZE (10 * (512 * 3) * 1024) static void iterate(void); static void init(void); static void cleanup(void); static ALuint moving_source = 0; static time_t start; static void *data = (void *) 0xDEADBEEF; static void *context_id; static void iterate( void ) { /* sleep for a bit */ micro_sleep(500000); } static void init( void ) { FILE *fh; ALfloat zeroes[] = { 0.0f, 0.0f, 0.0f }; ALfloat back[] = { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f }; ALfloat front[] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f }; ALuint stereo; ALsizei size; ALsizei bits; ALsizei freq; ALsizei format; int filelen; data = malloc(DATABUFFERSIZE); alListenerfv(AL_POSITION, zeroes ); alListenerfv(AL_ORIENTATION, front ); alGenBuffers( 1, &stereo); fh = fopen("sample.wav", "rb"); if(fh == NULL) { fprintf(stderr, "Couldn't open sample.wav\n"); exit(1); } filelen = fread(data, 1, DATABUFFERSIZE, fh); fclose(fh); talutBufferAndConvertData(stereo, data, AL_TRUE, filelen); alGenSources(1, &moving_source); alSource3f( moving_source, AL_POSITION, 0.0, 0.0, 4.0 ); alSourcefv( moving_source, AL_VELOCITY, zeroes ); alSourcei( moving_source, AL_BUFFER, stereo ); alSourcei( moving_source, AL_SOURCE_LOOPING, AL_FALSE); /* Scale world to 0.7 * ALMAXDISTANCE */ talAttenuationScale(0.7); return; } static void cleanup(void) { free(data); alcDestroyContext(context_id); } int main( int argc, char* argv[] ) { int attrlist[] = { ALC_FREQUENCY, 22050, ALC_INVALID }; time_t shouldend; ALint state = AL_INITIAL; /* Initialize ALUT. */ context_id = alcCreateContext(attrlist); if(context_id == NULL) { return 1; } /* * Setup LOKI extensions */ fixup_function_pointers(); init( ); alSourcePlay( moving_source ); do { iterate(); shouldend = time(NULL); if((shouldend - start) > 10) { /* After 10 seconds, we end */ alSourceStop(moving_source); } alSourcei(moving_source, AL_SOURCE_STATE, &state); } while(state == AL_ACTIVE); cleanup(); return 0; }
Error Handling goes here.
Context Management goes here.
Panning example goes here.
Positional example goes here.
Doppler example goes here.
#include <AL/al.h> #include <AL/alc.h> #include <AL/alkludge.h> #include <AL/alut.h> #include <time.h> #include <stdio.h> #include <unistd.h> static void init( void ); static ALuint reverb_sid = 0; static void *wave = NULL; static void init( void ) { ALfloat zeroes[] = { 0.0f, 0.0f, 0.0f }; ALfloat back[] = { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f }; ALfloat front[] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f }; ALuint boom; ALsizei size; ALsizei bits; ALsizei freq; ALsizei format; alListenerfv(AL_POSITION, zeroes ); alListenerfv(AL_VELOCITY, zeroes ); alListenerfv(AL_ORIENTATION, front ); alGenBuffers( 1, &boom ); alutLoadWAV( "l2.wav", &wave, &format, &size, &bits, &freq); alBufferData( boom, format, wave, size, freq ); free(wave); /* openal makes a local copy of wave data */ alGenSources( 1, &reverb_sid); alSource3f(reverb_sid, AL_POSITION, 2.0, 0.0, 4.0); alSourcefv(reverb_sid, AL_VELOCITY, zeroes); alSourcefv(reverb_sid, AL_ORIENTATION, back); alSourcei (reverb_sid, AL_BUFFER, boom); alReverbScale(reverb_sid, 0.6); alReverbDelay(reverb_sid, 0.3); alAttenuationScale(0.3); } int main(int argc, char *argv[]) { alutInit( &argc, argv ); init( ); alSourcePlay(reverb_sid); sleep(10); alutExit(); return 0; }
Support for streaming sounds rises from the API specification
surrounding alBufferAppendData
. Streaming sources are understood
to be sources whose AL_BUFFER
parameter is a streaming buffer. A
streaming buffer is a buffer created by a call to
alGenStreamingBuffer
, and can only be fed data using
alBufferAppendData
.
Streaming sources have restrictions associated with them, and for the time being require special creation procedures. Further specifications may remove some of these restrictions, but until further notice streaming sources will always be synchronized, and some of the filters may not apply to them.
insert code example
Foo!
Jump to: a
Jump to: a
Jump to: _ - a - b - c - d - e - i - l - o - p - s - u - w
This document was generated on 1 December 2000 using texi2html 1.56k.