Listener and Sources
Basic Listener and Source Attributes
This section introduces basic attributes that can be set both for
the Listener object and for Source objects.
RFC: attribute grouping
JM: "These attributes are of
two types: non-positional and positional.
Non-positional properties include gain control and Environment Name."
I said: (low pass) Filters are applied to the sound during processing
at various stages. The exact sequence in which Filters are applied is
determined based on the location of the Objects they are
set for - spatial arrangement of Objects determines the
sequence unless invariance is guaranteed, or invariance
violation is permitted by the specification and current &AL;
configuration state.
Is there a required order of application, i.e. a pipeline?
Filter Parameters vs. Non-positional properties.
Spatialization vs. Positional properties.
Spatial attributes?
Let's postpone grouping of attributes.
]]>
The &AL; Listener and Sources have attributes to describe
their position, velocity and orientation in three dimensional space.
&AL; like &OGL;, uses a right-handed Cartesian coordinate system (RHS),
where in a frontal default view X (thumb) points right,
Y (index finger) points up, and Z (middle finger) points towards
the viewer/camera. To switch from a left handed coordinate system (LHS)
to a right handed coordinate systems, flip the sign on the Z coordinate.
Listener/Source Position
&Par;>
&Sig;>
&Val>
&Def;>
POSITION>
3fv, 3f>
any except NaN >
{ 0.0f, 0.0f, 0.0f } >
Description:
POSITION specifies the current location of the Object in the
world coordinate system. Any 3-tuple of valid float/double values
is allowed. Implementation behavior on encountering &NaN; and &Infty;
is not defined. The Object position is always defined in the
world coordinate system.
Annotation (No Transformation)
&AL; does not support transformation operations on Objects.
Support for transformation matrices is not planned.
]]>
Listener/Source Velocity
&Par;>
&Sig;>
&Val>
&Def;>
VELOCITY>
3fv, 3f>
any except NaN >
{ 0.0f, 0.0f, 0.0f } >
Description:
VELOCITY specifies the current velocity (speed and direction) of
the Object, in the world coordinate system. Any 3-tuple of valid
float/double values is allowed. The Object VELOCITY does not affect
its position.
&AL; does not calculate the velocity from subsequent position
updates, nor does it adjust the position over time based on
the specified velocity. Any such calculation is left to the
application. For the purposes of sound processing, position and
velocity are independent parameters affecting different aspects
of the sounds.
VELOCITY is taken into account by the driver to synthesize the
Doppler effect perceived by the Listener for each source, based
on the velocity of both Source and Listener, and the Doppler
related parameters.
Listener/Source Gain (logarithmic)
&Par;>
&Sig;>
&Val>
&Def;>
GAIN>
f>
0.0f, (0.0f, any>
1.0f>
Description:
GAIN defines a scalar amplitude multiplier. As a Source attribute, it applies
to that particular source only. As a Listener attribute, it effectively
applies to all Sources in the current Context. The default 1.0 means
that the sound is un-attenuated. A GAIN value of 0.5 is equivalent to
an attenuation of 6 dB. The value zero equals silence (no output). Driver
implementations are free to optimize this case and skip mixing and
processing stages where applicable. The implementation is in charge of
ensuring artifact-free (click-free) changes of gain values and is free
to defer actual modification of the sound samples, within the limits of
acceptable latencies.
GAIN larger than 1 (amplification) is permitted for Source and
Listener. However, the implementation is free to clamp the
total gain (effective gain per source times listener gain)
to 1 to prevent overflow.
Annotation/ Effective Minimal Distance
Presuming that the sample uses the entire dynamic range of
the encoding format, an effective gain of 1 represents the
maximum volume at which a source can reasonably be played.
During processing, the implementation combines the Source
GAIN (or MIN_GAIN, if set and larger) with distance based
attenuation. The distance at which the effective gain is 1
is equivalent to the DirectSound3D MIN_DISTANCE parameter.
Once the effective gain has reached the maximum possible
value, it will not increase with decreasing distance anymore.
]]>
Annotation (Muting a Context)
To mute the current context, simply set Listener GAIN to zero.
The implementation is expected to optimize for this case,
calculating necessary (offset) updates but bypassing the
mixing and releasing hardware resources.
The specification does not guarantee that the implementation
will release hardware resources used by a muted context.
]]>
Annotation (Muting a Source)
To mute a Source, set Source GAIN to zero. The &AL; implementation
is encouraged to optimize for this case.
]]>
RFC: GAIN > 1?
GAIN could exceed 1 (to compensate attenuation elsewhere, or to account
for grater dynamic range of the hardware? No guarantees are made with
respect to range overflows? Precision loss? Culling by effective gain?
Does &AL; clip values during processing, and when/at what stages?
RFC: Doppler
JM wrote: "VELOCITY is used by the driver
to synthesize the Doppler effect perceived by the listener for each
source, based on the relative velocity of this source with respect
to the listener."
Doppler is calculated using Source and Listener velocities measured
with respect to the medium. Do we have to account for the medium
to move (offsetting listener/source) in later revisions (air/water currents)?
RFC:
JM removed: "For the purposes of sound processing, position and
velocity are independent parameters affecting different paths
in the sound synthesis." I think the "different aspects of sounds"
is ambiguous. Is there a problem with describing &AL; as a
multichannel processing machine?
]]>
Listener Object
The Listener Object defines various properties that affect processing of
the sound for the actual output. The Listener is unique for an &AL; Context,
and has no Name. By controlling the listener, the application controls
the way the user experiences the virtual world, as the listener defines
the sampling/pickup point and orientation, and other parameters that
affect the output stream.
It is entirely up to the driver and hardware configuration, i.e.
the installation of &AL; as part of the operating system and
hardware setup, whether the output stream is generated for
headphones or 2 speakers, 4.1 speakers, or other arrangements,
whether (and which) HRTF's are applied, etc..
Annotation (Listener Anatomy)
The API is ignorant with respect to the real world
listener, it does not need to make assumptions on the
listening capabilities of the user, its species or its
number of ears. It only describes a scene and the position
of the listener in this scene. It is the &AL; implementation
that is designed for humans with ears on either side of the
head.
]]>
Annotation (Listener State Evaluation)
Some Listener state (GAIN) affects only the very last
stage of sound synthesis, and is thus applied to the sound stream
as sampled at the Listener position. Other Listener state is
applied earlier. One example is Listener velocity as used to
compute the amount of Doppler pitch-shifting applied to each source:
In a typical implementation, pitch-shifting (sample-rate conversion)
might be the first stage of the audio processing for each source.
]]>
Listener Attributes
Several Source attributes also apply to Listener: e.g. POSITION, VELOCITY,
GAIN. In addition, some attributes are listener specific.
Listener Orientation
&Par;>
&Sig;>
&Val>
&Def;>
ORIENTATION>
fv >
any except NaN >
{ { 0.0f, 0.0f, -1.0f }, { 0.0f, 1.0f, 0.0f } } >
Description:
ORIENTATION is a pair of 3-tuples representing the 'at' direction vector
and 'up' direction of the Object in Cartesian space. &AL; expects two
vectors that are orthogonal to each other. These
vectors are not expected to be normalized. If one or more vectors
have zero length, implementation behavior is undefined. If the two
vectors are linearly dependent, behavior is undefined.
RFC: Orientation Paranoia
Watch LHS vs. RHS (sign on 'at').
Confirm sequence is (up, at) not vice versa.
Do we want to allow for different representations down the road?
]]>
Changing Listener Attributes
Listener attributes are changed using the Listener group of commands.
void Listener{n}{sifd}{v}
&enum; paramName
&type; values
Querying Listener Attributes
Listener state is maintained inside the &AL; implementation and can be
queried in full. See Querying Object Attributes. The valid values for
paramName are identical to the ones for the Listener* command.
void GetListener{sifd}v
&enum; paramName
&type;* values
Source Objects
Sources specify attributes like position, velocity, and a buffer with
sample data. By controlling a Source's attributes the
application can modify and parameterize the static sample data provided
by the Buffer referenced by the Source.
Sources define a localized sound, and encapsulate
a set of attributes applied to a sound at its origin, i.e. in the
very first stage of the processing on the way to the listener.
Source related effects have to be applied
before Listener related effects unless the output is invariant
to any collapse or reversal of order.
&AL; also provides additional functions to manipulate and query the
execution state of Sources: the current playing status of a
source (started, stopped, paused), including access to the current
sampling position within the associated Buffer.
RFC: Mike on Source Types
AL_SOURCE_ABSOLUTE and AL_SOURCE_AMBIENT have been
deprecated. AL_SOURCE_ABSOLUTE was simply the converse of the
AL_SOURCE_RELATIVE pname, and as such was unnecessary. The
effect of AL_SOURCE_AMBIENT is trivially emulated by either
querying the Listener position and setting the Source position
accordingly, or setting the Source position to (0,0,0) and the
type to AL_SOURCE_RELATIVE, and is therefore also unnecessary.
]]>
RFC: Bernd on Source Types
Mike seems to miss a few problems here. W/o a converse we can't
reset source attributes to ABSOLUTE. Ambient sounds are not
necessarily trivial. A3D manual suggested some magic number
to fake the effect of ambient (i.e. sound that ideally
can't be localized by listener). If we can get away with such magic
numbers in a tutorial in a driver-independent way, fine. If there is any
risk that the impression of ambient sound requires driver specific
hacks, then we need AMBIENT. As soon as we have a third localization
type, ABSOLUTE and RELATIVE are needed as there is no unambiguous
converse.
From the A3D 2.0 Optimize.doc:
"Adding some ambient background noise is a great way to fill in the gaps
when the audio content is reduced. A great way to make an ambient sound
seem like it is coming from everywhere is to load up two buffers with the
same sound, and position them about 2 meters behind the listener at
about 4 and 8 o\rquote clock. The waves have to be looping (make sure
there is no beating when you play them back). Starting the sounds 180
degrees out of phase can help, as will playing them with slightly different
pitch-shift values."
]]>
RFC: Bernd on Source Types (2)
There is a point to be made in using POSITION_RELATIVE and
VELOCITY_RELATIVE iff we do not have AMBIENT to consider.
This makes it a call-by-call choice when setting Source3f{v}
vectors, as it is applied when dereferencing.
]]>
RFC: Bernd on Source Types (3)
Semantically, AMBIENT has nothing to do with coordinate systems,
it is a qualifier just like multichannel direct passthru.
]]>
RFC: Source Attenuation Clamping
Using AL_SOURCE_ATTENUATION_MIN and AL_SOURCE_ATTENUATION_MAX
to specify the clamping values for the normalized attenuation
factor (which is a function of distance) is in contradiction
to the distance based model that Creative is pushing for
(DirectSound). As driver-interall culling of source and other
processing might be based on the effective (overall, ultimate)
gain composed of amplifications and attenuations accumulated
over the entire processing, I raise the question whether a sound
designer might not want to control the effective GAIN ranges
instead of the distance attenuation itself. Samples commonly
use the entire dynamic range provided by the format, which is
mapped to the entire dynamic range of the output device. An
effective gain exceeding 1 does not make sense, an amplification
during processing might.
]]>
Managing Source Names
&AL; provides calls to request and release Source Names handles.
Calls to control Source Execution State are also provided.
Requesting a Source Name
The application requests a number of Sources using GenSources.
&void; GenSources
&sizei; n
&uint;* sources
Releasing Source Names
The application requests deletion of a number of Sources
by DeleteSources.
&void; DeleteSources
&sizei; n
&uint;* sources
Validating a Source Name
The application can verify whether a source name is valid
using the IsSource query.
&bool; IsSource
&uint; sourceName
Source Attributes
This section lists the attributes that are set per Source,
affecting the processing of the current buffer. Some of
these attributes can also be set for buffer queue entries.
Annotation (No Priorities)
There are no per Source priorities, and no explicit priority
handling, defined at this point. A mechanism that lets the
application express preferences in case that the implementation
provides culling and prioritization mechanisms might be added
at some later time. This topic is under discussion for GL as
well, which already has one exlicit priority API along with
internally used MRU heuristics (for resident texture memory).
]]>
Source Positioning
SOURCE_RELATIVE Attribute
&Par;>
&Sig;>
&Val>
&Def;>
SOURCE_RELATIVE >
&bool; >
FALSE, TRUE>
FALSE >
SOURCE_RELATIVE set to TRUE indicates that the values
specified by POSITION are to be interpreted relative
to the listener position.
Annotation (Position only)
SOURCE_RELATIVE does not affect velocity or orientation
calculation.
]]>
Buffer Looping
Source LOOPING Attribute
&Par;>
&Sig;>
&Val>
&Def;>
LOOPING >
&uint; >
TURE, FALSE>
FALSE >
Description:
LOOPING is a flag that indicates that the Source will not
be in STOPPED state once it reaches the end of last buffer
in the buffer queue. Instead, the Source will immediately
promote to INITIAL and PLAYING. The default value is FALSE.
LOOPING can be changed on a Source in any execution state.
In particular, it can be changed on a PLAYING Source.
Annotation (Finite Repetition)
Finite reptition is implemented by buffer queueing.
]]>
Annotation (Loop Control)
To implement a 3 stage "loop point" solution, the
application has to queue the FadeIn buffer first,
then queue the buffer it wants to loop, and set
LOOPING to TRUE once the FadeIn buffer has been
processed and unqueued. To fade from looping, the
application can queue a FadeOut buffer, then
set LOOPING to false on the PLAYING source. Alternatively,
the application can decide to not use the LOOPING
attribute at all, and just continue to queue the buffer
it wants repeated.
]]>
Annotation (Rejected alternatives)
A finite loop counter was rejected because it is
ambiguous with respect to persistent (initial counter)
vs. transient (current counter). For similar reasons,
a Play-equivalent command with a (transient) loop counter
was rejected.
]]>
Current Buffer
Source BUFFER Attribute
&Par;>
&Sig;>
&Val>
&Def;>
BUFFER>
&uint; >
any valid bufferName >
&NONE; >
Description:
Specifies the current Buffer object, making it the
head entry in the Source's queue. Using BUFFER on a
STOPPED or INITIAL Source empties the entire queue,
then appends the one Buffer specified.
For a PLAYING or PAUSED Source, using the Source command
with BUFFER is an INVALID_OPERATION.
It can be applied to INITIAL and STOPPED Sources only.
Specifying an invalid bufferName will
result in an INVALID_VALUE error while specifying an
invalid sourceName results in an INVALID_NAME error.
NONE, i.e. 0, is a valid buffer Name.
Source( sName, BUFFER, 0 ) is a legal way to release the
current buffer queue on an INITIAL or STOPPED Source,
whether it has just one entry (current buffer) or more.
The Source( sName, BUFFER, NONE) call still causes an
INVALID_OPERATION for any source PLAYING or PAUSED,
consequently it cannot be abused to mute or stop a source.
Annotation (repeated Source+BUFFER does not queue)
Using repeated Source(BUFFER) calls to queue a buffer on
an active source would imply that there is no way to
release the current buffer e.g. by setting it to 0.
On the other hand read-only queues do not allow for
releasing a buffer without releasing the entire queue.
We can not require BUFFER state to be transient and lost
as soon as a Source is implicitely or explicitely stopped.
This contradicts queue state being part of the Source's
configuration state that is preserved through Stop()
operations and available for Play().
]]>
Queue State Queries
BUFFERS_QUEUED Attribute
&Par;>
&Sig;>
&Val>
&Def;>
BUFFERS_QUEUED >
&uint; >
[0, any]>
none >
Query only. Query the number of buffers in the queue
of a given Source. This includes those not yet played,
the one currently playing, and the ones that have been
played already. This will return 0 if the current and
only bufferName is 0.
BUFFERS_PROCESSED Attribute
&Par;>
&Sig;>
&Val>
&Def;>
BUFFERS_PROCESSED >
&uint; >
[0, any]>
none >
Query only. Query the number of buffers that have
been played by a given Source.
Indirectly, this gives the index of the buffer
currently playing. Used to determine how much
slots are needed for unqueueing them.
On an STOPPED Source, all buffers are processed.
On an INITIAL Source, no buffers are processed,
all buffers are pending.
This will return 0 if the current and
only bufferName is 0.
Annotation (per-Source vs. Buffer State)
BUFFERS_PROCESSED is only defined within the scope of a given
Source's queue. It indicates that the given number of buffer names
can be unqueued for this Source. It does not guarantee that the
buffers can safely be deleted or refilled, as they might still be
queued with other Sources. One way to keep track of this is to
store, per buffer, the Source for which a given buffer was most
recently scheduled (this will not work if Sources sharing buffers
might be paused by the application). If necessary an explicit
query for a given buffer name can be added in later revisions.
]]>
Annotation (No Looping Queues)
Unqueueing requires nonzero BUFFERS_PROCESSED,
which necessitates no looping on entire queues,
unless we accept that no unqueueing is possible
from Source looping over the entire queue.
Currently not supported, as queueing is
primarily meant for streaming, which implies
unqueue-refill-requeue operations.
]]>
Bounds on Gain
Source Minimal Gain
&Par;>
&Sig;>
&Val>
&Def;>
MIN_GAIN>
f>
0.0f, (0.0f, 1.0f]>
0.0f>
Description:
MIN_GAIN is a scalar amplitude threshold. It indicates the minimal GAIN
that is always guaranteed for this Source. At the end of the processing
of various attenuation factors such as distance based attenuation and
Source GAIN, the effective gain calculated is compared to this value.
If the effective gain is lower than MIN_GAIN, MIN_GAIN is applied.
This happens before the Listener GAIN is applied. If a zero MIN_GAIN
is set, then the effective gain will not be corrected.
Annotation (Effective Maximal Distance)
By setting MIN_GAIN, the application implicitely defines a
maximum distance for a given distance attenuation model and
Source GAIN. The distance at which the effective gain is MIN_GAIN
can be used as a replacement to the DirectSound3D MAX_DISTANCE parameter.
Once the effective gain has reached the MIN_GAIN value, it will
no longer decrease with increasing distance.
]]>
Source Maximal Gain (logarithmic)
&Par;>
&Sig;>
&Val>
&Def;>
MAX_GAIN>
f>
0.0f, (0.0f, 1.0f]>
1.0f>
Description:
MAX_GAIN defines a scalar amplitude threshold. It indicates the maximal
GAIN permitted for this Source. At the end of the processing
of various attenuation factors such as distance based attenuation and
Source GAIN, the effective gain calculated is compared to this value.
If the effective gain is higher than MAX_GAIN, MAX_GAIN is applied.
This happens before the Listener GAIN is applied. If the Listener gain
times MAX_GAIN still exceeds the maximum gain the implementation can
handle, the implementation is free to clamp. If a zero MAX_GAIN
is set, then the Source is effectively muted. The implementation is free
to optimize for this situation, but no optimization is required or
recommended as setting GAIN to zero is the proper way to mute a Source.
Annotation (Un-attenuated Source)
Setting MIN_GAIN and MAX_GAIN to the GAIN value will effectively
make the Source amplitude independent of distance. The
implementation is free to optimize for this situation. However, the
recommended way to accomplish this effect is using a ROLLOFF_FACTOR
of zero.
]]>
Annotation (Internal GAIN threshold)
The &AL; implementation is free to use an internally chosen
threshold level below which a Source is ignored for mixing.
Reasonable choices would set this threshold low enough so
that the user will not perceive a difference. Setting MIN_GAIN
for a source will override any implementation defined test.
]]>
Distance Model Attributes
REFERENCE_DISTANCE Attribute
&Par;>
&Sig;>
&Val>
&Def;>
REFERENCE_DISTANCE >
&float; >
[0, any]>
1.0f >
This is used for distance attenuation calculations
based on inverse distance with rolloff. Depending
on the distance model it will also act as a distance
threshold below which gain is clamped. See the
section on distance models for details.
ROLLOFF_FACTOR Attribute
&Par;>
&Sig;>
&Val>
&Def;>
ROLLOFF_FACTOR >
&float; >
[0, any]>
1.0f >
This is used for distance attenuation calculations
based on inverse distance with rolloff. For
distances smaller than MAX_DISTANCE (and, depending
on the distance model, larger than REFERENCE_DISTANCE),
this will scale the distance attenuation over the
applicable range. See section on distance models for
details how the attenuation is computed as a function
of the distance.
In particular, ROLLOFF_FACTOR can be set to zero for
those Sources that are supposed to be exempt from
distance attenuation. The implementation is encouraged
to optimize this case, bypassing distance attenuation
calculation entirely on a per-Source basis.
MAX_DISTANCE Attribute
&Par;>
&Sig;>
&Val>
&Def;>
MAX_DISTANCE >
&float; >
[0, any]>
MAX_FLOAT >
This is used for distance attenuation calculations
based on inverse distance with rolloff, if the
Inverse Clamped Distance Model is used. In this case,
distances greater than MAX_DISTANCE will
be clamped to MAX_DISTANCE.
MAX_DISTANCE based clamping is applied before MIN_GAIN clamping,
so if the effective gain at MAX_DISTANCE is larger than MIN_GAIN,
MIN_GAIN will have no effect. No culling is supported.
Annotation (No Culling)
This is a per-Source attribute supported for DS3D compatibility
only. Other API features might suffer from side effects due to
the clamping of distance (instead of e.g. clamping to an effective
gain at MAX_DISTANCE).
]]>
Frequency Shift by Pitch
Source PITCH Attribute
&Par;>
&Sig;>
&Val>
&Def;>
PITCH>
f>
(0.0f, 2.0f]>
1.0f>
Description:
Desired pitch shift, where 1.0 equals identity. Each reduction by 50 percent
equals a pitch shift of -12 semitones (one octave reduction). Zero is not
a legal value.
Direction and Cone
Each Source can be directional, depending on the settings for
CONE_INNER_ANGLE and CONE_OUTER_ANGLE. There are three zones
defined: the inner cone, the outside zone, and the transitional
zone in between.
The angle-dependent gain for a directional source is constant
inside the inner cone, and changes over the transitional zone
to the value specified outside the outer cone.
Source GAIN is applied for the inner cone,
with an application selectable CONE_OUTER_GAIN factor to
define the gain in the outer zone. In the transitional
zone implementation-dependent interpolation between
GAIN and GAIN times CONE_OUTER_GAIN is applied.
Annotation (Interpolation Restrictions)
The specification does not specify the exact interpolation
applied in the transitional zone, to calculate gain as a
function of angle. The implementation is free to use
linear or other interpolation, as long as the values
are monotonically decreasing from GAIN to GAIN times CONE_OUTER_GAIN.
]]>
Source DIRECTION Attribute
&Par;>
&Sig;>
&Val>
&Def;>
DIRECTION>
3fv, 3f>
any except NaN >
{ 0.0f, 0.0f, 0.0f } >
Description:
If DIRECTION does not equal the zero vector, the Source is directional.
The sound emission is presumed to be symmetric
around the direction vector (cylinder symmetry). Sources are not
oriented in full 3 degrees of freedom, only two angles are effectively
needed.
The zero vector is default, indicating that a Source is not directional.
Specifying a non-zero vector will make the Source directional.
Specifying a zero vector for a directional Source will effectively
mark it as nondirectional.
RFC: Oriented Sources
Do we want an alternative AZIMUTH/ALTITUDE parametrization?
Do we need ORIENTATION later? Is this superimposable? Can we mix both?
]]>
Annotation (All Sources Directional)
From the point of view of the &AL; implementation, all
Sources are directional. Certain choices for cone angles
as well as a direction vector with zero length are treated
equivalent to an omnidirectional source. The &AL;
implementation is free to flag and optimize these cases.
]]>
RFC: Separate GenDirectionSource?
Is there any risk that directional sources require different
resources that have to be allocated from the beginning, and
that we can not change an omnidirectional source to a
bidirectional source at runtime?
]]>
Source CONE_INNER_ANGLE Attribute
&Par;>
&Sig;>
&Val>
&Def;>
CONE_INNER_ANGLE>
i,f>
any except NaN>
360.0f>
Description:
Inside angle of the sound cone, in degrees. The default of 360 means that the
inner angle covers the entire world, which is equivalent to an omnidirectional
source.
RFC: inconsistent cone angles?
Is (inner <= outer) required? Do we generate an error?
Shouldn't this be a CONE_ANGLES 2f call specifying both angles at once?
]]>
Source CONE_OUTER_ANGLE Attribute
&Par;>
&Sig;>
&Val>
&Def;>
CONE_OUTER_ANGLE>
i,f>
any except NaN>
360.0f>
Description: Outer angle of the sound cone, in degrees. The default of 360 means that the
outer angle covers the entire world. If the inner angle is also 360, then
the zone for angle-dependent attenuation is zero.
RFC: addition?
More generally, we could specify:
"If the sum of inner and outer angles is larger than 360,
CONE_OUTER_ANGLE is clamped to (360-CONE_INNER_ANGLE) and
there is no transition zone."
]]>
Source CONE_OUTER_GAIN Attribute
&Par;>
&Sig;>
&Val>
&Def;>
CONE_OUTER_GAIN>
i,f>
[0.0f, 1.0f]>
0.0f>
Description: the factor with which GAIN is multiplied to
determine the effective gain outside the cone defined by
the outer angle. The effective gain applied outside the
outer cone is GAIN times CONE_OUTER_GAIN. Changing
GAIN affects all directions, i.e. the source is attenuated
in all directions, for any position of the listener.
The application has to change CONE_OUTER_GAIN as well if
a different behavior is desired.
Annotation (GAIN calculation)
The angle-dependend gain DGAIN is multiplied with the
gain determined by the source's GAIN and any distance
attenuation as applicable. Let theta be the angle
between the source's direction vector, and the vector
connection the source and the listener. This multiplier
DGAIN is calculated as:
OUTER = CONE_OUTER_ANGLE/2;
INNER = CONE_INNER_ANGLE/2;
if ( theta less/equal INNER )
DGAIN = 1
else if ( theta greater/equal OUTER )
DGAIN = CONE_OUTER_GAIN
else
DGAIN = 1 - (1-CONE_OUTER_GAIN)*((theta-INNER)/(OUTER-INNER))
GAIN *= DGAIN
in the case of linear interpolation. The implementation
is free to use a different interplation across the (INNER,OUTER)
range as long as it is monotone.
]]>
Annotation (CONE_OUTER_GAIN always less than GAIN)
CONE_OUTER_GAIN is not an absolute value, but (like all GAIN
parameters) a scaling factor. This avoids a possible error
case (implementations can count on effective gain outside the
outer cone being smaller than GAIN), and ensures the common
case in which changing GAIN should affect inner, transitional,
and outer zone simultaneously.
In case that the application desires to have an outer zone
volume exceeding that of the inner cone, the mapping to
&AL; will require to rotate the Source direction to the
opposite direction (negate vector), and swapping
inner and outer angle.
]]>
Changing Source Attributes
The Source specifies the position and other properties as
taken into account during sound processing.
void Source{n}{sifd}
&uint; sourceName
&enum; paramName
&type; value
void Source{n}{sifd}v
&uint; sourceName
&enum; paramName
&type;* values
Querying Source Attributes
Source state is maintained inside the &AL; implementation, and the
current attributes can be queried. The performance of such queries is
implementation dependent, no performance guarantees are made. The
valid values for the paramName parameter are identical to the ones
for Source*.
void GetSource{n}{sifd}{v}
&uint; sourceName
&enum; paramName
&type;* values
Old signature: T GetSource{sifd}{v}( uint id, enum param );
]]>
Queueing Buffers with a Source
&AL; does not specify a built-in streaming mechanism. There
is no mechanism to stream data e.g. into a Buffer object.
Instead, the API introduces a more flexible and versatile
mechanism to queue Buffers for Sources.
There are many ways to use this feature, with
streaming being only one of them.
Streaming is replaced by queuing static
buffers. This effectively moves any multi-buffer
caching into the application and allows the
application to select how many buffers it wants
to use, whether these are re-used in cycle,
pooled, or thrown away.
Looping (over a finite number of repititions) can be
implemented by explicitely repeating buffers
in the queue. Infinite loops can (theoretically)
be accomplished by sufficiently large repetition counters.
If only a single buffer is supposed to be repeated
infinitely, using the respective Source attribute is
recommended.
Loop Points for restricted looping inside a buffer
can in many cases be replaced by splitting the
sample into several buffers, queueing the sample
fragments (including repetitions) accordingly.
Buffers can be queued, unqueued after they have been
used, and either be deleted, or refilled and queued again.
Splitting large samples over several buffers maintained
in a queue has a distinct advantages over approaches that
require explicit management of samples and sample indices.
RFC: Unified Handling
Jonathan Blow has proposed removing the distinction between
streaming and non-streaming buffers. An existing example is
the unified for directional and omnidirectional sources, where
all sources are treated as directional.
]]>
Queueing command
The application can queue up one or multiple buffer names
using SourceQueueBuffers. The buffers will be queued in the sequence
in which they appear in the array.
&void; alSourceQueueBuffers
&uint; sourceName
&sizei; numBuffers
&uint; * bufferNames
This command is legal on a Source in any state (to allow for
streaming, queueing has to be possible on a PLAYING Source).
Queues are read-only with exception of the unqueue operation.
The Buffer Name NONE (i.e. 0) can be queued.
Annotation (BUFFER vs. SourceQueueBuffers)
A Sourcei( sname, BUFFER, bname ) command is an immediate
command, and executed immediately. It effectively unqueues
all buffers, and then adds the specified buffer to the
then empty queue as its single entry. Consequently, this
call is only legal if SourceUnqueueBuffers is legal.
In particular, the Source has to be STOPPED or INITIAL.
The application is still obliged to delete all
buffers as were contained in the queue.
Sourcei( sname, BUFFER, NONE ) is a legal command,
effectively wiping the queue without specifying an
actually playable buffer.
]]>
Annotation (Buffer Repetition)
To accomplish a finite number of repetitions of a buffer name multiple times,
the buffer has to be queued multiple times. If the need occurs, the
API could be extended by SourceQueueBuffer( sname, bname, repetitions )
call for brevity.
]]>
RFC: Duration of bName==0?
The buffer is considered empty, it should have zero length,
thus zero duration for consistency. If an application wants to
schedule a pause, specifying duration for a gain==0 queue entry
might be a cleaner solution.
]]>
Annotation (Backwards Compatiblity)
Sourcei( sname, BUFFER, bname ) has been rejected as
a queueing command, as it would make semantics dependent on
source state (queueing if PLAYING, immediate else).
The command is not legal on a PLAYING or PAUSED Source.
]]>
Annotation (No BUFFER_QUEUE)
Duplication of one entry point is preferable to
duplicating token enums, and tokens do not express
commands, but specify the attribute/state affected.
From the same reason, there is no BUFFER_UNQUEUE
token-as-command.
]]>
Unqueueing command
Once a queue entry for a buffer has been appended to a queue
and is pending processing, it should not be changed.
Removal of a given queue entry is not possible unless
either the Source is STOPPED (in which case then entire queue
is considered processed), or if the queue entry has already
been processed (PLAYING or PAUSED Source).
The Unqueue command removes a number of buffers entries that
have finished processing, in the order of appearance, from
the queue. The operation will fail if more buffers are
requested than available, leaving the destination arguments
unchanged. An INVALID_VALUE error will be thrown.
If no error, the destination argument will have been updated
accordingly.
void SourceUnqueueBuffers
&uint; sourceName
&sizei; numEntries
&uint;* bufferNames
Annotation (Unqueueing shared buffers)
If a buffer is queued with more than one source, it might have
been processed for some not all of them. With the current
interface, the application is forced to maintain its own list
of consumers (Sources) for a buffer it wishes to unqueue.
For groups of Sources that are never individually PAUSED
nor STOPPED, the application can save the MRU Source for
which the buffer was scheduled last.
]]>
Annotation (Looping a Queue vs. Unqueue):
If a Source is playing repeatedly, it will traverse
the entire Queue repeatedly. Consequently, no buffer
in the queue can be considered processed until
there is no further repetition scheduled.
]]>
Annotation (No Name based access)
No interface is provided to access a queue entry by name,
due to ambiguity (same buffer name scheduled several times
in a sequence).
]]>
Annotation (No Index based access)
No interface is provided for random access to a queue entry
by index.
]]>
More Annotation on Queueing
Annotation (No Queue Copying)
The current queue of a source could be copied to another source,
as repetition and traversal parameters are stored unless the
queue entry is unqueued, or the queue is replaced using
AL_BUFFER. Copying a queue is a special case of
copying Source state in one sense, and a special case of
a synching problem in another. Due to these unresolved issues
no such command is included in the current specification.
To share queues, the application can keep buffer names
and the selected attributes that define the queue entries
in an array or other lookup table.
]]>
Annotation (No Explicit QueueClear)
Sourcei( sname, BUFFER, NONE ) serves the
same purpose. The operation is also redundant
with respect to Unqueue for a STOPPED Source.
]]>
Annotation (Queueing vs. AppendData):
Buffer queueing does not solve the synchronization and timing
issues raised by possible underflow, as these are inherent
to application-driven (pushed) streaming. However, it turns
an internal AL error condition (offset exceeds valid data)
into an audible artifact (Source stops).
Its main advantage is that it allows the application coder
to operate at a scale of her own choice, selecting the
number and size of buffers used for caching the stream,
and to schedule buffer refill and queueing according to
preferences and constraints. Queueing effectively moves
all problems related to replacing or appending Buffer data
to the scale of entire arrays istead of single samples and
indices.
]]>
Annotation (Multiple Sources on a stream)
Queueing allows for the application to determine how much of
a backlog of the data stream is preserved. The application can
keep buffers, and queue them with other Sources after they have
been used already by the original Source. Unlike the mechanism
for appending data to a buffer, the backlog is visible to the
application and under its control, and no synchronization of
Sources using the stream is required.
]]>
Annotation (Loop Points and Compressed Data)
For compressed data, uncompression by the application might be
impossible or undesireable. In consequence, splitting the sample
into several buffers is not possible without explicit support
by the API. Buffer-Buffer operations will be added as needed,
for the time being applications should not try to use compressed
samples if more than full looping is required.
]]>
Annotation (No Explicit Queue Objects)
Explicit Queue objects have been considered and rejected,
as they introduce another producer-consumer dependency with
another level of indirection. Further, e.g. QUEUE would
also require deprecating BUFFER (breaking backwards
compatibility) as an alSource argument, or would introduce
a confusing set of precedence and overide rules if both
are used in sequence. However, in the absence of explicit
queue objects the application will be forced to keep track
where buffers have been queued in case it intends to
unqueue them for refill or deletion. If several sources
use the same buffers (e.g. for synchronous or
asynchronous streaming) the buffer will have to be
unqueued from each single one.
]]>
Annotation (Queue no Display List)
An interface resembling &OGL; display-lists has been
considered and rejected. The problem with this approach
is that not only commands would have to be prohibited
(similarly, not all GL calls are legal within a display
list), but also parameters (enumerations).
In particular, only a small set of operations is meant
to be legal for a queue at this point, and appending
to a queue has to be possible at any time.
Within a hypothetical AL display list, only relative
timing/conditionals are allowed as arguments. This
might necessitate to have multiple forms for deferred
commands, or to not allow for absolute timing.
Example:
// lock this queue for overwriting/appending
alBeginQueue( qname, APPEND | REPLACE );
// queue a buffer in sequence, with parameters
// boolean: never skip? never bail?
alQueue( AL_BUFFER, bid, loopdir, repetitions );
...
// end lock.
// Existing queue content will be replaced
// or appended at this point.
alEndQueue();
]]>
Queue Then Delete
create source
queue buffer1
queue buffer2
queue buffer3
play
request deletion of buffer1,2,3
]]>
Queue and Refill with Dual Buffering
create source
fill buffer1
queue buffer1
play
fill buffer2
queue buffer2
check for unused buffers
unqueue buffer1
fill buffer1
queue buffer1
...
]]>
Queue for Loop Points
create source
read sample data
split sample data into pre/main/post
queue pre
queue main with repetitions
queue post
play
set repetitions to 0 on main when needed
wait till post has been played
]]>
]]>
Attributes Specific to Queueing
Buffer Traversal
The Buffer traversal attribute specifies the direction
in which the sample in the buffer is supposed to be
processed. To account for the 3 basic modes of traversal that
can be implemented in software and hardware, the following
tokens are defined:
LOOP_DIRECTION /* traversal direction */
FORWARD /* linear forward (increment) */
BACKWARD /* linear backward (decrement) */
FORWARD_AND_BACK /* RESERVED: ping-pong-looping */
The first and the next two tokens are legal with a buffer queue command.
They are not legal for a Source command, in any possible
Source state. The last token is reserved, but not yet legal to use.
Annotation (Ping-Pong postponed)
Definition and implementation of ping-pong looping
has been postponed. Applications can fake it at doubling
memory expense by reverse copying the buffer (2nd buffer queued
or in a double size single buffer). If there is hardware support
for this feature, AL will have to support it eventually. A boolean
flag is not acceptable because of this possibility.
]]>
]]>
Managing Source Execution
The execution state of a source can be queried. &AL; provides
a set of functions that initiate state transitions causing
Sources to start and stop execution.
TBA: State Transition Diagram.
Annotation/ Source Config/Exec State
Sources have configuration state and execution state.
Configuration state is directly set by the application using
AL commands, starting with the INITIAL configuration. Execution
state (e.g. the offset to the current sample) is not under direct
application control and not exposed.
]]>
Source State Query
The application can query the current state of any Source
using GetSource with the parameter Name SOURCE_STATE.
Each Source can be in one of four possible execution states:
INITIAL, PLAYING, PAUSED, STOPPED. Sources that are either
PLAYING or PAUSED are considered active. Sources that are
STOPPED or INITIAL are considered inactive. Only PLAYING
Sources are included in the processing. The implementation
is free to skip those processing stages for Sources that
have no effect on the output (e.g. mixing for a Source
muted by zero GAIN, but not sample offset increments).
Depending on the current state of a Source certain (e.g. repeated)
state transition commands are legal NOPs: they will be ignored,
no error is generated.
State Transition Commands
The default state of any Source is INITIAL. From this state
it can be propagated to any other state by appropriate use
of the commands below. There are no irreversible state
transitions.
void SourcePlay
&uint; sName
void SourcePause
&uint; sName
void SourceStop
&uint; sName
void SourceRewind
&uint; sName
The functions are also available as a vector variant,
which guarantees synchronized operation on a set of
Sources.
void SourcePlayv
&sizei; n
&uint;* sNames
void SourcePausev
&sizei; n
&uint;* sNames
void SourceStopv
&sizei; n
&uint;* sNames
void SourceRewindv
&sizei; n
&uint;* sNames
The following state/command/state transitions are defined:
Play() applied to an INITIAL Source will promote the Source
to PLAYING, thus the data found in the Buffer will be fed
into the processing, starting at the beginning.
Play() applied to a PLAYING Source will restart the Source
from the beginning. It will not affect the configuration,
and will leave the Source in PLAYING state, but reset the
sampling offset to the beginning.
Play() applied to a PAUSED Source will
resume processing using the Source state
as preserved at the Pause() operation.
Play() applied to a STOPPED Source will propagate it
to INITIAL then to PLAYING immediately.
Pause() applied to an INITIAL Source is a legal NOP.
Pause() applied to a PLAYING Source will change its state to
PAUSED. The Source is exempt from processing, its current
state is preserved.
Pause() applied to a PAUSED Source is a legal NOP.
Pause() applied to a STOPPED Source is a legal NOP.
Stop() applied to an INITIAL Source is a legal NOP.
Stop() applied to a PLAYING Source will change its state to
STOPPED. The Source is exempt from processing, its current
state is preserved.
Stop() applied to a PAUSED Source will change its state
to STOPPED, with the same consequences as on a PLAYING
Source.
Stop() applied to a STOPPED Source is a legal NOP.
Rewind() applied to an INITIAL Source is a legal NOP.
Rewind() applied to a PLAYING Source will change its state to
STOPPED then INITIAL. The Source is exempt from processing:
its current state is preserved, with the exception of the
sampling offset, which is reset to the beginning.
Rewind() applied to a PAUSED Source will change its state
to INITIAL, with the same consequences as on a PLAYING
Source.
Rewind() applied to a STOPPED Source promotes the Source
to INITIAL, resetting the sampling offset to the beginning.
Annotation (SourceNext)
The specification does not provide any means to
immediately skip from the current Buffer to the
next in the queue. A conditional stop (following
the next complete traversal) is available.
If necessary an additonal entry point could be
provided in future revisions.
]]>
Annotation (Rewind() optional)
The INITIAL state is not identical to the STOPPED state.
Applications that want to verify whether a Source
has indeed been PLAYING before becoming STOPPED can
use Rewind() to reset the Source state to INITIAL.
This is an optional operation that can safely be
omitted by application without this constraint.
Applications that want to guard against Play() on
a Source that is INITIAL can query the Source state
first.
]]>
Annotation (Play() on a PLAYING Source)
Repeated Play() commands applied a PLAYING Source are
interpreted as an (atomic) sequence to stop and restart a
Source. This can be used by applications that want to restart
a sound but do not care whether the Source has finished or not,
and do not want an audible pause. One example is the DOOM
chaingun repeatedly abbreviating the pistol sound. To guard
against redundant Play() commands, an application can query
the current state before executing Play(). If the application
coder wants to be sure that the Source will play the buffer
again, she can either increment PLAY_COUNT, or queue the buffer.
]]>
Annotation (redundant commands)
The simple variant (e.g. SourcePlay) is redundant to
the vector variant (e.g. SourcePlayv). However, these
calls will be used frequently, and the simple variant
is provided for convenience. However, &AL; does not
enable applications to use literals as source names.
]]>
Resetting Configuration
The INITIAL state is not necessarily identical to the
default state in which Source is created. INITIAL merely
indicates that the Source can be executed using the
SourcePlay command. A STOPPED or INITIAL Source can
be reset into the default configuration by using a
sequence Source commands as necessary. As the application
has to specify all relevant state anyway to create a
useful Source configuration, no reset command is provided.
RFC: remove INITIAL
INITIAL is identical to STOPPED. The only additional information
conveyed is that INITIAL indicates a source has never been played.
Once a Source is STOPPED, it is not possible by state query alone
to decide whether it has played again. If Sources are used only
once, an application can use INITIAL to verify a Source has been
played.
The problem that I have with this is that if we acknowledge that
the application might need to verify a Source has played once,
why force the application to throw away Sources to accomplish
this? An explicit state PLAYABLE replacing INITIAL and its
inauspicious connotations (default state) and a state transition
function Rewind() that makes a STOPPED Source PLAYABLE again would
be one possibility to address this need. The obvious drawback is
that it breaks backwards compatibility.
]]>
RFC: state issues
A Source is active if it is PAUSED or PLAYING.
A Source that is STOPPED preserves configuration state,
including buffer/queue information.
Only a Source that is Reset() to INITIAL looses all
buffer and queue information. In this case, the INITIAL
Sources will be stopped implicitely when reaching the
end of a non-repeating (non-looping) buffer traversal.
Sources can be stopped explicitely by the application
with either Stop() or Reset().
Stop() propagates
the source to STOPPED preserving its configuration state,
setting its execution state to the same as if it reached
the end of execution.
]]>
Annotation (illegal NOPs)
In the current specification there are no illegal NOPs.
In other words, no sequence of commands affecting the
execution state will generate an INVALID_OPERATION error.
]]>
RFC/bk000504:
No UNDEFINED state. Always valid state. I.e. we have a default Buffer
that is used for sources where the application doesn't specify,
and what's in it? Default gain is zero? We need to specify
INITIAL.
RFC/bk000504:
Potential ambiguity: how to we distinguish STOPPED as
requested by the application from INACTIVE for
non-looping sounds once the buffer has been iterated?
Related: handling of Sources using an underflowing
streaming buffer? IMO not recommended, make this
undefined on error.
RFC/bk000504:
Possible redundancy: the only reason for STOP seems to
be resetting the play positions. Redundant if we
ever manipulate offsets directly (rewind/set).
RFC/bk000504:
Possible redundancy:
If we ever want to support explicit setting of the start
position/offset into Buffer, START is equivalent to Set(0).
Also see LOOP (implies has to be STOPPED). Fade-Out and
Fade-In control - always manually?
]]>