dhewm3/neo/openal/docs/chp-buffers.sgml

692 lines
27 KiB
Text
Raw Normal View History

2011-11-22 21:28:15 +00:00
<chapter id="buffers">
<title>Buffers</title>
<para>
A Buffer encapsulates &AL; state related to storing sample data. The
application can request and
release Buffer objects, and fill them with data. Data can be supplied
compressed and encoded as long as the format is supported.
Buffers can, internally, contain waveform data as uncompressed or
compressed samples.
</para>
<para>
Unlike Sources and Listener, Buffer Objects can be shared among AL contexts.
Buffers are referenced by Sources.
A single Buffer can be referred to by multiple Sources. This separation allows
drivers and hardware to optimize storage and processing where applicable.
</para>
<para>
The simplest supported format for buffer data is PCM.
</para>
<![ %Annote [
<note id="ann-bk000721-01"><title>Annotation/ Compressed Buffers</title><para>
Compressed formats are in no way guaranteed by the implementation
to remain compressed. The driver might have to uncompres in memory
at once, if no hardware-assisted or incremental decoding is possible.
In many cases an implementation has to decompress the buffer,
converting the uncompressed data to a canonical internal format,
and resample it into the format native to the current context.
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000721-18"><title>RFC: Compressed Buffers</title><para>
MikeV suggests: an application can query the amount of memory buffer
is consuming. He suggests using GetBufferi(AL_SIZE, ... ). This seems
a bad idea as (a) the application is not meant to micromanage
driver-internal memory, (b) the memory requirements known to the
application might differ from the actual, (c) there are OS
mechanisms to query free memory, (d) AL_SIZE is now ambiguous as
it announces app-side memory as allocated vs. driver side memory
as used by the driver. For clarity AL_INTERNAL_SIZE (analog to
internal format enums) might be a better choice.
</para></note>
]]>
<![ %Scratch [
<para>
Buffers can be non-streaming (default) or streaming. Non-streaming
buffers are used to store looping and non-looping (single-shot) sound
data. Streaming buffers have to be used to cache streaming
media that the application can not keep in memory for technical
reasons: e.g. data delivered in real time over network. Streaming buffers
can also be used to partially store sound samples that the application
coder consider too large to keep in memory at once.
</para>
]]>
<sect1>
<title>Buffer States</title>
<para>
At this time, Buffer states are defined for purposes of discussion.
The states described in this section are not exposed through the API
(can not be queried, or be set directly), and the state description
used in the implementation might differ from this.
</para>
<para>
A Buffer is considered to be in one of the following States, with respect
to all Sources:
<itemizedlist>
<listitem>
<para>
UNUSED: the Buffer is not included in any queue
for any Source. In particular, the
Buffer is neither pending nor current
for any Source. The Buffer name can be
deleted at this time.
</para>
</listitem>
<listitem>
<para>
PROCESSED: the Buffer is listed in the queue of
at least one Source, but is neither pending
nor current for any Source. The Buffer can
be deleted as soon as it has been unqueued
for all Sources it is queued with.
</para>
</listitem>
<listitem>
<para>
PENDING: there is at least one Source for which the
Buffer has been queued, for which the Buffer
data has not yet been dereferenced. The Buffer
can only be unqueued for those Sources that
have dereferenced the data in the Buffer
in its entirety, and cannot be deleted or
changed.
</para>
</listitem>
</itemizedlist>
The Buffer state is dependent on the state of all Sources
that is has been queued for.
A single queue occurrence of a Buffer propagates the
Buffer state (over all Sources) from UNUSED to PROCESSED
or higher. Sources that are STOPPED or INITIAL still have
queue entries that cause Buffers to be PROCESSED.
</para>
<para>
A single queue entry
with a single Source for which the Buffer is not yet
PROCESSED propagates the buffer's queueing state to
PENDING.
</para>
<para>
Buffers that are PROCESSED for a given Source can be
unqueued from that Source's queue. Buffers that have
been unqueued from all Sources are UNUSED.
Buffers that are UNUSED can be deleted, or changed by
BufferData commands.
</para>
<![ %Annote [
<note><title>Annotation (No CURRENT State)</title><para>
For buffer queueing, it is not relevant whether
the Buffer data is currently dereferenced by any
Source or not. It is therefore not necessary to
distinguish a CURRENT state (being referenced as
current buffer by a single PLAYING or PAUSED Source).
</para></note>
]]>
<![ %Annote [
<note><title>Annotation (State Query and Shared Buffers)</title><para>
A buffer that is unused by one Source might be used
by another. The Unqueue operation is determined by
the number of queue entries already processed by the
given Source.
However, the application has to check whether the
Buffer is still in use by other Sources. For now,
applications have to maintain their own lists of
buffer consumer (source) lists. If necessary, an
explicit call to determine current buffer state
with respect to all Sources might be added in
future revisions.
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000927-02"><title>RFC: IsBufferProcessed? </title><para>
Instead of exposing the internal state, a simple boolean query
whether a buffer can be deleted or refilled can be used.
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000731-04"><title>RFC: BufferData on QUEUED</title><para>
The error on using BufferData in QUEUED buffers is introduced
because the implementation might not be able to guarantee when
the Buffer is dereferenced. Applications have to account for the
possibility that the Buffer is dereferenced at the latest possible
moment, e.g. when it becomes CURRENT. As it is easier to relax
this restricition at a later time (no effect on backwards
compatibility) than doing the reverse, we are conserative here.
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000731-05"><title>RFC: Buffer State Query</title><para>
Buffer State could be queried using alBuffer(), but it can't be
set. Prohibiting deferred deletion of buffers would make such a
state query desirable.
</para></note>
]]>
</sect1>
<sect1>
<title>Managing Buffer Names</title>
<para>
&AL; provides calls to obtain Buffer names, to request
deletion of a Buffer object associated with a valid Buffer name,
and to validate a Buffer name. Calls to control Buffer attributes
are also provided.
</para>
<sect2>
<title>Requesting Buffers Names</title>
<para>
The application requests a number of Buffers using GenBuffers.
<funcsynopsis><funcprototype>
<funcdef> void <function> GenBuffers </function></funcdef>
<paramdef> &sizei; <parameter> n </parameter></paramdef>
<paramdef> &uint;* <parameter> bufferNames </parameter></paramdef>
</funcprototype></funcsynopsis>
</para>
<![ %RFC [
<note id="rfc-bk000803-02"><title>RFC: Buffer Name 0</title><para>
NONE or 0 is a reserved buffer name. What properties does
this buffer have? Does it have content? If there is no content,
what is its duration? 0? 1 microsecond? Should we use this buffer
to schedule a limited duration "silence"?
</para></note>
]]>
</sect2>
<sect2>
<title>Releasing Buffer Names</title>
<para>
The application requests deletion of a number of Buffers
by calling DeleteBuffers.
</para>
<para>
Once deleted, Names are no longer valid for use with AL
function calls. Any such use will cause an INVALID_NAME
error. The implementation is free to defer actual
release of resources.
<funcsynopsis><funcprototype>
<funcdef> &void; <function> DeleteBuffers </function></funcdef>
<paramdef> &sizei; <parameter> n </parameter></paramdef>
<paramdef> &uint;* <parameter> bufferNames </parameter></paramdef>
</funcprototype></funcsynopsis>
IsBuffer(bname) can be used to verify deletion of a buffer.
Deleting bufferName 0 is a legal NOP in both scalar and
vector forms of the command. The same is true for unused
buffer names, e.g. such as not allocated yet, or as
released already.
</para>
<![ %RFC [
<note id="rfc-bk000803-01"><title>RFC: Force Deletion</title><para>
If a buffer name is deleted, we could replace all occurences
in queues with bname 0. This is the GL behavior for deleting
the texture currently bound.
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000731-07"><title>RFC: Relasing used Buffers</title><para>
If a Buffer is USED or QUEUED, it cannot be deleted, and the operation
should fail. We have three possible responses: throw an error, deferr
deletion, or force deletion by replacing every use
of the buffer in question with bname zero.
Throwing an error requires that we lock, verify that all specified
buffers can be deleted, then perform deletion, then unlock. If there
is one buffer that can not be deleted we have to throw an error and
make the entire operation a NOP.
Deferred deletion has its own set of problems (see other RFC).
Forcing deletion makes the mistake obvious to the application
for current buffers (sound artifacts) but still doesn't expose
errors for queued buffers. It also requires complete consumer
book-keeping for each buffer. GL uses this approach for textures
at little expense because it only has one current texture.
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000731-06"><title>RFC: Deferred Buffer Release</title><para>
Buffer deletion could be performed as a deferred operation.
In this case actual deletion would be deferred until a Buffer is
unused, i.e. not QUEUED or CURRENT anymore. The specification
would not guarantee that they are deleted as soon as possible.
However, such a deferred execution would be muddying the borders
between immediate and deferred execution in general (as we
might want to add scheduling and deferred commands at a later time).
Introduced as the default it makes impossible for the application
to force deletion or errors. Errors caused by improper use of
&AL; will be triggered at some distance from the original mistaken
command. Debugging such conditions is usually expensive. This approach
also does not take into account sharing of buffers among contexts.
It might be possible to introduce this behavior as a Hint()
in case that it is not desirable to explicitely introduce
deferred commands.
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000927-03"><title>RFC: sourceName 0 </title><para>
Is there a useful application for this? Do we mark that this
is reserved?
</para></note>
]]>
</sect2>
<sect2>
<title>Validating a Buffer Name</title>
<para>
The application can verify whether a buffer Name is valid
using the IsBuffer query.
<funcsynopsis><funcprototype>
<funcdef> &bool; <function> IsBuffer </function></funcdef>
<paramdef> &uint; <parameter> bufferName</parameter></paramdef>
</funcprototype></funcsynopsis>
</para>
</sect2>
</sect1>
<sect1>
<title>Manipulating Buffer Attributes</title>
<sect2>
<title>Buffer Attributes</title>
<para>
This section lists the attributes that can be set, or
queried, per Buffer. Note that some of these attributes
can not be set using the Buffer commands, but are set
using commands like BufferData.
</para>
<para>
Querying the attributes of a Buffer with a buffer name that
is not valid throws an INVALID_OPERATION. Passing in an
attribute name that is invalid throws an INVALID_VALUE error.
</para>
<para>
<table>
<title>Buffer FREQUENCY Attribute</title>
<tgroup cols="4" align="left" colsep=1 rowsep=1>
<thead>
<row>
<entry>&Par;</>
<entry>&Sig;</>
<entry>&Val</>
<entry>&Def;</>
</row>
</thead>
<tbody>
<row>
<entry> FREQUENCY </>
<entry> float </>
<entry> none </>
<entry> (0, any]</>
</row>
</tbody>
</tgroup>
</table>
Description: Frequency, specified in samples per second,
i.e. units of Hertz [Hz].
Query by GetBuffer. The frequency state of a buffer is set by
BufferData calls.
</para>
<![ %Annote [
<note><title>Annotation (No Frequency enumeration)</title><para>
As the implementation has to support conversion from
one frequency to another to implement pitch, it is
feasible to offer support for arbitrary sample
frequencies, instead of restricting the application
to an enumeration of supported sample frequencies.
Another reason not to limit frequency to an enumerated
set is that future hardware might support variable
frequencies as well (it might be preferable to choose
the sampling frequency according to the PSD of the
signal then).
</para><para>
However, it is desirable to avoid conversions due
to differences between the sample frequency used in
the original data, the frequency supported during the
mixing, and the frequency expected by the output device.
</para></note>
]]>
<![ %Annote [
<note><title>Annotation (Implied Frequency)</title><para>
To account for the possibility of future AL implementations
supporting encoding formats for the application might
not want, or be able, to retrieve the actual frequency
from the encoded sample, the specification will be
amended to guarantee the following behavior: If a nonzero
frequency is specified, it will force a conversion from
the actual to the requested frequency. If the application
specifies a 0 frequency, AL will use the actual frequency.
If there is no frequency information implied by the format
or contained in the encoded data, specifying a 0 frequency
will yield INVALID_VALUE. It is recommended that applications
use NONE instead of the literal value.
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000806-01"><title>RFC: BITS not needed </title><para>
This is not a setter. As a state query it doesn't provide useful
information about the internal canonical format (which could be
queried independent of the buffer).
</para></note>
<para>
<table>
<title>Buffer BITS Attribute</title>
<tgroup cols="4" align="left" colsep=1 rowsep=1>
<thead>
<row>
<entry>&Par;</>
<entry>&Sig;</>
<entry>&Val</>
<entry>&Def;</>
</row>
</thead>
<tbody>
<row>
<entry>BITS</>
<entry>ui</>
<entry>8,16</>
<entry>0</>
</row>
</tbody>
</tgroup>
</table>
Description:
Bits per sample. This is a query-only attribute. The
default value for an (empty) buffer is zero.
</para>
]]>
<![ %Annote [
<note><title>Annotation (No Format query)</title><para>
As of this time there is no query for FORMAT, or format
related state information. Query of the channels or
bits of a given buffer make little sense if the query
the internal (canonical, not buffer specific) format.
Query of the original sample data format makes little
sense unless the implementation is obliged to preserve
the original data.
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000806-02"><title>RFC: CHANNELS needed?</title><para>
No setter. Does this indicate the channels in the original data,
or those in the (canonical) format internally used? Redundant to
querying the buffer type. Should be enums as speaker configurations
might have to be destribed by two integers: 5.1.
</para></note>
<para>
<table>
<title>Buffer CHANNELS Attribute</title>
<tgroup cols="4" align="left" colsep="1" rowsep="1">
<thead>
<row>
<entry>&Par;</>
<entry>&Sig;</>
<entry>&Val;</>
<entry>&Def;</>
</row>
</thead>
<tbody>
<row>
<entry>CHANNELS</>
<entry>ui</>
<entry>RFC: enums or N/A?</>
<entry>0</>
</row>
</tbody>
</tgroup>
</table>
Description: Channels that buffer stores. Query only
attribute. This is almost
always 1, as applications using spatialized sound always
downsample to mono. This depends on the purpose of the
buffer: buffers used for spatialization have to provide
single-channel data. The default value for an (empty)
buffer is zero.
</para>
]]>
<para>
<table>
<title>Buffer SIZE Attribute</title>
<tgroup cols="4" align="left" colsep="1" rowsep="1">
<thead>
<row>
<entry>&Par;</>
<entry>&Sig;</>
<entry>&Val;</>
<entry>&Def;</>
</row>
</thead>
<tbody>
<row>
<entry>SIZE</>
<entry> &sizei; </>
<entry> [0, MAX_UINT]</>
<entry> 0 </>
</row>
</tbody>
</tgroup>
</table>
Description: Size in bytes of the buffer data. Query through
GetBuffer, can be set only using BufferData calls.
Setting a SIZE of 0 is a legal NOP. The number of bytes does
not necessarily equal the number of samples (e.g. for compressed data).
</para>
<![ %RFC [
<note id="rfc-bk000724-15"><title>RFC: buffer overflow/underflow</title><para>
If a SIZE is specified for a buffer and an attempt is made to
write less or more data to the buffer, is this an error? Do we
have SubData updates? Is trying to write data to a zero size buffer
an error? Which error?
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000619-16"><title>RFC: query for samples/duration?</title><para>
Do we need a query for samples (which does not equal memory size)?
Do we prefer a duration query? As integral, for precision? Which,
combined with frequency, has to guarantee accurate sample number?
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000724-01"><title>RFC: memory budgeting</title><para>
SIZE comment said: Useful for memory budgeting with compressed data.
Sounds bogus: the application can only announce how many bytes
of data it intends to provide, it might not be able to estimate
the uncompressed, decoded size in the internal format chosen by
the implementation w/o decompressing, decoding, and querying for
the internal format. Micromanaging memory is usually a bad idea.
Using SIZE to configure a buffer might ge a mistake. Using the
same enum to query the actual size internally (which might
consists of two or more buffer areas and cached decoding state)
will be confusing.
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000724-02"><title>RFC: buffer initial state</title><para>
MikeV added:
"The default attribute values for a Buffer are nonsensical insofar
as a Buffer is incomplete without data having been
specified."
This seems wrong. An AL object will always be complete and valid,
albeit useless. A Buffer in its default state might not produce
any useful outout, but it can be specified and used.
</para></note>
]]>
</sect2>
<sect2>
<title>Querying Buffer Attributes</title>
<para>
Buffer state is maintained inside the &AL; implementation and can be
queried in full. The valid values for paramName are identical to the
ones for Buffer*.
<funcsynopsis><funcprototype>
<funcdef> void <function> GetBuffer{n}{sifd}{v} </function></funcdef>
<paramdef> &uint; <parameter>bufferName</parameter></paramdef>
<paramdef> &enum; <parameter> paramName </parameter></paramdef>
<paramdef> &type;* <parameter> values </parameter></paramdef>
</funcprototype></funcsynopsis>
</para>
</sect2>
<sect2>
<title>Specifying Buffer Content</title>
<para>
A special case of Buffer state is the actual sound sample data stored
in asociation with the Buffer. Applications can specify sample data using
BufferData.
<funcsynopsis><funcprototype>
<funcdef> &void; <function> BufferData </function></funcdef>
<paramdef> &uint; <parameter>bufferName</parameter></paramdef>
<paramdef> &enum; <parameter>format</parameter></paramdef>
<paramdef> &void;* <parameter> data </parameter></paramdef>
<paramdef> &sizei; <parameter> size </parameter></paramdef>
<paramdef> &sizei; <parameter>frequency</parameter></paramdef>
</funcprototype></funcsynopsis>
The data specified is copied to an internal software, or if possible,
hardware buffer. The implementation is free to apply decompression,
conversion, resampling, and filtering as needed. The internal format
of the Buffer is not exposed to the application, and not
accessible. Valid formats are FORMAT_MONO8, FORMAT_MONO16,
FORMAT_STEREO8, and FORMAT_STEREO16. An implementation may
expose other formats, see the chapter on Extensions for
information on determining if additional formats are supported.
</para>
<para>
Applications should always check for an error condition after attempting
to specify buffer data in case an implementation has to generate an
OUT_OF_MEMORY or conversion related INVALID_VALUE error. The application
is free to reuse the memory specified by the data pointer once the
call to BufferData returns. The implementation has to dereference,
e.g. copy, the data during BufferData execution.
</para>
<![ %RFC [
<note id="rfc-bk000724-04"><title>RFC: format enums</title><para>
With the possible exception of sample frequency, all
details of a sample (mono/stero, bit resolution, channels,
encoding,compression) should be specified in a format parameter.
In other words, I opt for an enumeration of formats. GL has a
quite large number of those w/o suffering damage. Allowing
parameters the way we do increases error cases and/or
conversion load. The space is combinatorial to begin with,
but an enumeration of valid formats seems better to impose
restrictions. Using enums helps dealing with formats
opaque to the application (compressed data with compressed
header) where sample size and sampling frequency might not
be available.
There is the related issue of internal formats. A buffer used
for spatialization will have to convert to mono. A buffer used
to pass through to the output hardware will have to
remap an n-channel format to an m-channel format (or let the
hardware do it) including crosstalk handling depending on
the output device (headphones vs. speaker). To prevent that
every buffer has to do both AL needs to know the purpose of a
buffer when dereferencing the data.
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk0000507-17"><title>RFC: Frequency woes</title><para>
Frequency as a uint rises precision issues.
Frequency might not be known for compressed data, we might need
a (yuck) wildcard frequency specifier?
We have a redundancy: frequency per context (mixing quality
desired), frequency internally used by the implementation when
storing the buffers, frequency provided in the data. We need
to specify the latter in some cases and can't in others. Format
enum or frequency enum again.
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000504-01"><title>RFC: data mover mess</title><para>
API to copy data from output buffer to a buffer?
BufferWriteData to supersede BufferData, BufferReadData
as later extensions for buffer readback for those who want it?
Reading the output stream reverses the problems we have
with appendData: the application provides a memory buffer
into which AL copies as much data as available/as fits.
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000724-11"><title>RFC: expose internal format</title><para>
Implementations are free to use whatever internal data format is best
suited for their hardware/software implementation, leaving the actual
sample format and structure opaque to the application. Should applications
be able to influence this? Through context creation? Per Buffer?
Do we use Lowest Common Denominator or highest possible quality as a
default?
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000724-12"><title>RFC: memory management with compressed data</title><para>
If we allow for mixing from compressed data (perfectly reasonable
for hardware) then it seems even more unlikely the application could
estimate memory usage.
If a compressed format is supported by AL, do we require support for mixing from
compressed data? I daresay not - some formats might not allow for
cheap incremental decompression.
</para></note>
]]>
<![ %RFC [
<note id="rfc-bk000724-21"><title>RFC: conversion and sample retrieval</title><para>
To retrieve a sample, the size has to be queried first for allocating a
properly sized memory segment as destination. This is dependent on the
format. Conversion can be implemented as creating a buffer, and then
requesting the data in a different format. Is this desirable? Even if
we restrict reading from buffers to the same format they were written
to, conversion from the internal format might be inevitable. Querying
and setting the internal format however might be desirable for certain
purposes.
</para></note>
]]>
</sect2>
</sect1>
</chapter>