pub struct RingBuffer {
pub buffer: Option<Buffer>,
pub format: Option<Format>,
pub producer_bytes: Option<u64>,
pub consumer_bytes: Option<u64>,
pub reference_clock: Option<Clock>,
pub reference_clock_domain: Option<u32>,
/* private fields */
}
Expand description
A ring buffer of audio data.
Each ring buffer has a producer (who writes to the buffer) and a consumer (who reads from the buffer). Additionally, each ring buffer is associated with a reference clock that keeps time for the buffer.
§PCM Data
A ring buffer of PCM audio is a window into a potentially-infinite sequence
of frames. Each frame is assigned a “frame number” where the first frame in
the infinite sequence is numbered 0. Frame X
can be found at ring buffer
offset (X % RingBufferFrames) * BytesPerFrame
, where RingBufferFrames
is
the size of the ring buffer in frames and BytesPerFrame
is the size of a
single frame.
§Concurrency Protocol
Each ring buffer has a single producer and a single consumer which are synchronized by time. At each point in time T according to the ring buffer’s reference clock, we define two functions:
-
SafeWritePos(T)
is the lowest (oldest) frame number the producer is allowed to write. The producer can write to this frame or to any higher-numbered frame. -
SafeReadPos(T)
is the highest (youngest) frame number the consumer is allowed to read. The consumer can read this frame or any lower-numbered frame.
To prevent conflicts, we define these to be offset by one:
SafeWritePos(T) = SafeReadPos(T) + 1
To avoid races, there must be a single producer, but there may be multiple consumers. Additionally, since the producer and consumer(s) are synchronized by time, we require explicit fences to ensure cache coherency: the producer must insert an appropriate fence after each write (to flush CPU caches and prevent compiler reordering of stores) and the consumer(s) must insert an appropriate fence before each read (to invalidate CPU caches and prevent compiler reordering of loads).
Since the buffer has finite size, the producer/consumer cannot write/read
infinitely in the future/past. We allocate P
frames to the producer and
C
frames to the consumer(s), where P + C <= RingBufferFrames
and P
and
C
are both chosen by whoever creates the ring buffer.
§Deciding on P
and C
In practice, producers/consumers typically write/read batches of frames
on regular periods. For example, a producer might wake every Dp
milliseconds to write Dp*FrameRate
frames, where FrameRate
is the PCM
stream’s frame rate. If a producer wakes at time T, it will spend up to the
next Dp
period writing those frames. This means the lowest frame number it
can safely write to is SafeWritePos(T+Dp)
, which is equivalent to
SafeWritePos(T) + Dp*FrameRate
. The producer writes Dp*FrameRate
frames
from the position onwards. This entire region, from SafeWritePos(T)
through 2*Dp*FrameRate
must be allocated to the producer at time T. Making
a similar argument for consumers, we arrive at the following constraints:
P >= 2*Dp*FrameRate
C >= 2*Dc*FrameRate
RingBufferFrames >= P + C
Hence, in practice, P
and C
can be derived from the batch sizes used by
the producer and consumer, where the maximum batch sizes are limited by the
ring buffer size.
§Defining SafeWritePos
The definition of SafeWritePos
(and, implicitly, SafeReadPos
) must be
provided out-of-band.
§Non-PCM Data
Non-PCM data is handled similarly to PCM data, except positions are expressed as “byte offsets” instead of “frame numbers”, where the infinite sequence starts at byte offset 0.
Fields§
§buffer: Option<Buffer>
The actual ring buffer. The sum of producer_bytes
and consumer_bytes
must be <= buffer.size
.
Required.
format: Option<Format>
Encoding of audio data in the buffer. Required.
producer_bytes: Option<u64>
The number of bytes allocated to the producer.
For PCM encodings, P = producer_bytes / BytesPerFrame(format)
, where P
must be integral.
For non-PCM encodings, there are no constraints, however individual encodings may impose stricter requirements.
Required.
consumer_bytes: Option<u64>
The number of bytes allocated to the consumer.
For PCM encodings, C = consumer_bytes / BytesPerFrame(format)
, where C
must be integral.
For non-PCM encodings, there are no constraints, however individual encodings may impose stricter requirements.
Required.
reference_clock: Option<Clock>
Reference clock for the ring buffer.
Required.
reference_clock_domain: Option<u32>
Domain of reference_clock
. See fuchsia.hardware.audio.ClockDomain
.
TODO(https://fxbug.dev/42066209): If fuchsia.hardware.audio doesn’t need to import
fuchsia.audio, we can use that type directly below.
Optional. If not specified, defaults to CLOCK_DOMAIN_EXTERNAL
.
Trait Implementations§
Source§impl Debug for RingBuffer
impl Debug for RingBuffer
Source§impl Decode<RingBuffer, DefaultFuchsiaResourceDialect> for RingBuffer
impl Decode<RingBuffer, DefaultFuchsiaResourceDialect> for RingBuffer
Source§impl Default for RingBuffer
impl Default for RingBuffer
Source§fn default() -> RingBuffer
fn default() -> RingBuffer
Source§impl Encode<RingBuffer, DefaultFuchsiaResourceDialect> for &mut RingBuffer
impl Encode<RingBuffer, DefaultFuchsiaResourceDialect> for &mut RingBuffer
Source§impl PartialEq for RingBuffer
impl PartialEq for RingBuffer
Source§impl ResourceTypeMarker for RingBuffer
impl ResourceTypeMarker for RingBuffer
Source§type Borrowed<'a> = &'a mut RingBuffer
type Borrowed<'a> = &'a mut RingBuffer
Encode<Self>
type cheaply obtainable from &mut Self::Owned
. There are three cases: Read moreSource§fn take_or_borrow<'a>(
value: &'a mut <Self as TypeMarker>::Owned,
) -> Self::Borrowed<'a>
fn take_or_borrow<'a>( value: &'a mut <Self as TypeMarker>::Owned, ) -> Self::Borrowed<'a>
&mut Self::Owned
to Self::Borrowed
. For
HandleBased
types this is “take” (it returns an owned handle and
replaces value
with Handle::invalid
), and for all other types it is
“borrow” (just converts from one reference to another).Source§impl TypeMarker for RingBuffer
impl TypeMarker for RingBuffer
Source§type Owned = RingBuffer
type Owned = RingBuffer
Source§fn inline_align(_context: Context) -> usize
fn inline_align(_context: Context) -> usize
Source§fn inline_size(_context: Context) -> usize
fn inline_size(_context: Context) -> usize
inline_align
.§fn encode_is_copy() -> bool
fn encode_is_copy() -> bool
Self::Owned
matches the FIDL wire
format and encoding requires no validation. When true, we can optimize
encoding arrays and vectors of Self::Owned
to a single memcpy. Read more§fn decode_is_copy() -> bool
fn decode_is_copy() -> bool
Self::Owned
matches the FIDL wire
format and decoding requires no validation. When true, we can optimize
decoding arrays and vectors of Self::Owned
to a single memcpy.