pub struct PairedIo<'a> { /* private fields */ }Expand description
A single borrow over one domain’s whole-block input and this view’s output range.
Audio comes from audio_io /
RenderContext::audio_io, analog from
analog_io /
RenderContext::analog_io.
input always covers the whole block, the same
as audio_in(). output and
frames are confined to
output_range instead: the whole block
from a BlockContext, this thread’s share from a
RenderContext — the same range audio_frame_range() and its
analog counterpart already describe there. That asymmetry is
RenderContext’s, not this type’s; on a BlockContext input and
output cover the same frames.
There is no digital_io. BelaContext::digital is one combined
input/output word buffer — the pin directions and values share it —
so a paired view over it would be two aliasing references to the
same memory, which is exactly what this type exists to avoid handing
out. BlockContext::digital / BlockContext::digital_mut (and
their RenderContext counterparts) already reach that buffer.
Implementations§
Source§impl<'a> PairedIo<'a>
impl<'a> PairedIo<'a>
Sourcepub const fn input(&self) -> &[f32]
pub const fn input(&self) -> &[f32]
The whole block’s input samples, interleaved. Length is
frames * in_channels(), where frames is whichever of
audio_frames() / analog_frames() matches this view’s domain.
Sourcepub const fn in_channels(&self) -> usize
pub const fn in_channels(&self) -> usize
Number of input channels; see input for the
layout it multiplies into.
Sourcepub const fn output(&mut self) -> &mut [f32]
pub const fn output(&mut self) -> &mut [f32]
This view’s output samples, interleaved — the frames of
output_range, so index 0 is channel
0 of output_range().start, not of block frame 0.
Sourcepub const fn out_channels(&self) -> usize
pub const fn out_channels(&self) -> usize
Number of output channels; see output for
the layout it multiplies into.
Sourcepub fn frames(&mut self) -> impl Iterator<Item = (&[f32], &mut [f32])> + '_
pub fn frames(&mut self) -> impl Iterator<Item = (&[f32], &mut [f32])> + '_
Reads and writes one frame at a time: for each frame in
output_range, the input channels at
that same block frame paired with the output channels to fill
in.
This is the aligned path the module documentation promises:
input() indexes from block frame 0 and output() from
output_range().start, and frames() walks both origins
together so a caller never subtracts the offset by hand — the
mistake a RenderContext view invites, since its input and
output do not start at the same block frame.
let mut io = context.audio_io();
for (input, output) in io.frames() {
for (sample, value) in output.iter_mut().zip(input) {
*sample = *value;
}
}Examples found in repository?
54 fn render(&self, _state: &mut (), context: &mut RenderContext) {
55 let channels = context
56 .audio_in_channels()
57 .min(context.audio_out_channels());
58 let range = context.audio_frame_range();
59
60 // Ground truth: the indexed path, exactly as `passthrough.rs`
61 // uses it.
62 for frame in range {
63 for channel in 0..channels {
64 let sample = context.audio_read(frame, channel);
65 context.audio_write(frame, channel, sample);
66 }
67 }
68
69 // The paired path, over the same channels — compared against
70 // what the indexed path already wrote, before it overwrites it.
71 let mut io = context.audio_io();
72 for (input, output) in io.frames() {
73 for channel in 0..channels {
74 let expected = output[channel];
75 let paired = input[channel];
76 self.checked.fetch_add(1, Ordering::Relaxed);
77 if paired.to_bits() != expected.to_bits() {
78 self.mismatches.fetch_add(1, Ordering::Relaxed);
79 }
80 output[channel] = paired;
81 }
82 }
83 }