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? ]]>