Store sample count in Buffer
This fixes the sample count not being correct for MIDI-only plugins.
This commit is contained in:
parent
19be530ba3
commit
9e1a888b38
|
@ -17,6 +17,10 @@ pub use samples::{ChannelSamples, ChannelSamplesIter, SamplesIter};
|
||||||
/// this either way. Maybe just get rid of it in favor for raw pointers.
|
/// this either way. Maybe just get rid of it in favor for raw pointers.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Buffer<'a> {
|
pub struct Buffer<'a> {
|
||||||
|
/// The number of samples contained within `output_slices`. This needs to be stored separately
|
||||||
|
/// to be able to handle 0 channel IO for MIDI-only plugins.
|
||||||
|
num_samples: usize,
|
||||||
|
|
||||||
/// Contains slices for the plugin's outputs. You can't directly create a nested slice from a
|
/// Contains slices for the plugin's outputs. You can't directly create a nested slice from a
|
||||||
/// pointer to pointers, so this needs to be preallocated in the setup call and kept around
|
/// pointer to pointers, so this needs to be preallocated in the setup call and kept around
|
||||||
/// between process calls. And because storing a reference to this means a) that you need a lot
|
/// between process calls. And because storing a reference to this means a) that you need a lot
|
||||||
|
@ -31,11 +35,7 @@ impl<'a> Buffer<'a> {
|
||||||
/// Returns the number of samples per channel in this buffer.
|
/// Returns the number of samples per channel in this buffer.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
if self.output_slices.is_empty() {
|
self.num_samples
|
||||||
0
|
|
||||||
} else {
|
|
||||||
self.output_slices[0].len()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of channels in this buffer.
|
/// Returns the number of channels in this buffer.
|
||||||
|
@ -47,7 +47,7 @@ impl<'a> Buffer<'a> {
|
||||||
/// Returns true if this buffer does not contain any samples.
|
/// Returns true if this buffer does not contain any samples.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.output_slices.is_empty() || self.output_slices[0].is_empty()
|
self.num_samples == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain the raw audio buffers.
|
/// Obtain the raw audio buffers.
|
||||||
|
@ -108,17 +108,28 @@ impl<'a> Buffer<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the raw output slice vector. This needs to be resized to match the number of output
|
/// Set the slices in the raw output slice vector. This vector needs to be resized to match the
|
||||||
/// channels during the plugin's initialization. Then during audio processing, these slices
|
/// number of output channels during the plugin's initialization. Then during audio processing,
|
||||||
/// should be updated to point to the plugin's audio buffers.
|
/// these slices should be updated to point to the plugin's audio buffers. The `num_samples`
|
||||||
|
/// argument should match the length of the inner slices.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The stored slices must point to live data when this object is passed to the plugins' process
|
/// The stored slices must point to live data when this object is passed to the plugins' process
|
||||||
/// function. The rest of this object also assumes all channel lengths are equal. Panics will
|
/// function. The rest of this object also assumes all channel lengths are equal. Panics will
|
||||||
/// likely occur if this is not the case.
|
/// likely occur if this is not the case.
|
||||||
pub unsafe fn set_slices(&mut self, update: impl FnOnce(&mut Vec<&'a mut [f32]>)) {
|
pub unsafe fn set_slices(
|
||||||
|
&mut self,
|
||||||
|
num_samples: usize,
|
||||||
|
update: impl FnOnce(&mut Vec<&'a mut [f32]>),
|
||||||
|
) {
|
||||||
|
self.num_samples = num_samples;
|
||||||
update(&mut self.output_slices);
|
update(&mut self.output_slices);
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
for slice in &self.output_slices {
|
||||||
|
nih_debug_assert_eq!(slice.len(), num_samples);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +142,7 @@ mod miri {
|
||||||
let mut real_buffers = vec![vec![0.0; 512]; 2];
|
let mut real_buffers = vec![vec![0.0; 512]; 2];
|
||||||
let mut buffer = Buffer::default();
|
let mut buffer = Buffer::default();
|
||||||
unsafe {
|
unsafe {
|
||||||
buffer.set_slices(|output_slices| {
|
buffer.set_slices(512, |output_slices| {
|
||||||
let (first_channel, other_channels) = real_buffers.split_at_mut(1);
|
let (first_channel, other_channels) = real_buffers.split_at_mut(1);
|
||||||
*output_slices = vec![&mut first_channel[0], &mut other_channels[0]];
|
*output_slices = vec![&mut first_channel[0], &mut other_channels[0]];
|
||||||
})
|
})
|
||||||
|
@ -159,7 +170,7 @@ mod miri {
|
||||||
let mut real_buffers = vec![vec![0.0; 512]; 2];
|
let mut real_buffers = vec![vec![0.0; 512]; 2];
|
||||||
let mut buffer = Buffer::default();
|
let mut buffer = Buffer::default();
|
||||||
unsafe {
|
unsafe {
|
||||||
buffer.set_slices(|output_slices| {
|
buffer.set_slices(512, |output_slices| {
|
||||||
let (first_channel, other_channels) = real_buffers.split_at_mut(1);
|
let (first_channel, other_channels) = real_buffers.split_at_mut(1);
|
||||||
*output_slices = vec![&mut first_channel[0], &mut other_channels[0]];
|
*output_slices = vec![&mut first_channel[0], &mut other_channels[0]];
|
||||||
})
|
})
|
||||||
|
|
|
@ -1788,7 +1788,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
wrapper
|
wrapper
|
||||||
.output_buffer
|
.output_buffer
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.set_slices(|output_slices| {
|
.set_slices(0, |output_slices| {
|
||||||
output_slices.resize_with(bus_config.num_output_channels as usize, || &mut [])
|
output_slices.resize_with(bus_config.num_output_channels as usize, || &mut [])
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1812,7 +1812,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
Buffer::default,
|
Buffer::default,
|
||||||
);
|
);
|
||||||
for buffer in aux_input_buffers.iter_mut() {
|
for buffer in aux_input_buffers.iter_mut() {
|
||||||
buffer.set_slices(|channel_slices| {
|
buffer.set_slices(0, |channel_slices| {
|
||||||
channel_slices
|
channel_slices
|
||||||
.resize_with(bus_config.aux_input_busses.num_channels as usize, || {
|
.resize_with(bus_config.aux_input_busses.num_channels as usize, || {
|
||||||
&mut []
|
&mut []
|
||||||
|
@ -1827,7 +1827,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
Buffer::default,
|
Buffer::default,
|
||||||
);
|
);
|
||||||
for buffer in aux_output_buffers.iter_mut() {
|
for buffer in aux_output_buffers.iter_mut() {
|
||||||
buffer.set_slices(|channel_slices| {
|
buffer.set_slices(0, |channel_slices| {
|
||||||
channel_slices
|
channel_slices
|
||||||
.resize_with(bus_config.aux_output_busses.num_channels as usize, || {
|
.resize_with(bus_config.aux_output_busses.num_channels as usize, || {
|
||||||
&mut []
|
&mut []
|
||||||
|
@ -1987,9 +1987,10 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
// TODO: The audio buffers have a latency field, should we use those?
|
// TODO: The audio buffers have a latency field, should we use those?
|
||||||
// TODO: Like with VST3, should we expose some way to access or set the silence/constant
|
// TODO: Like with VST3, should we expose some way to access or set the silence/constant
|
||||||
// flags?
|
// flags?
|
||||||
|
let block_len = block_end - block_start;
|
||||||
let mut output_buffer = wrapper.output_buffer.borrow_mut();
|
let mut output_buffer = wrapper.output_buffer.borrow_mut();
|
||||||
let mut buffer_is_valid = false;
|
let mut buffer_is_valid = false;
|
||||||
output_buffer.set_slices(|output_slices| {
|
output_buffer.set_slices(block_len, |output_slices| {
|
||||||
// Buffers for zero-channel plugins like note effects should always be allowed
|
// Buffers for zero-channel plugins like note effects should always be allowed
|
||||||
buffer_is_valid = output_slices.is_empty();
|
buffer_is_valid = output_slices.is_empty();
|
||||||
|
|
||||||
|
@ -2024,7 +2025,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
*(audio_outputs.data32 as *mut *mut f32).add(output_channel_idx);
|
*(audio_outputs.data32 as *mut *mut f32).add(output_channel_idx);
|
||||||
*output_channel_slice = std::slice::from_raw_parts_mut(
|
*output_channel_slice = std::slice::from_raw_parts_mut(
|
||||||
channel_ptr.add(block_start),
|
channel_ptr.add(block_start),
|
||||||
block_end - block_start,
|
block_len,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2056,7 +2057,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
ptr::copy_nonoverlapping(
|
ptr::copy_nonoverlapping(
|
||||||
input_channel_ptr.add(block_start),
|
input_channel_ptr.add(block_start),
|
||||||
output_channel_ptr.add(block_start),
|
output_channel_ptr.add(block_start),
|
||||||
block_end - block_start,
|
block_len,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2095,13 +2096,12 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
|
|
||||||
// If the host passes weird data then we need to be very sure that there are
|
// If the host passes weird data then we need to be very sure that there are
|
||||||
// no dangling references to previous data
|
// no dangling references to previous data
|
||||||
buffer.set_slices(|slices| slices.fill_with(|| &mut []));
|
buffer.set_slices(0, |slices| slices.fill_with(|| &mut []));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll always reuse the start of the buffer even of the current block is
|
// We'll always reuse the start of the buffer even of the current block is
|
||||||
// shorter for cache locality reasons
|
// shorter for cache locality reasons
|
||||||
let block_len = block_end - block_start;
|
|
||||||
for (channel_idx, channel_storage) in storage.iter_mut().enumerate() {
|
for (channel_idx, channel_storage) in storage.iter_mut().enumerate() {
|
||||||
// The `set_len()` avoids having to unnecessarily fill the buffer with
|
// The `set_len()` avoids having to unnecessarily fill the buffer with
|
||||||
// zeroes when sizing up
|
// zeroes when sizing up
|
||||||
|
@ -2113,7 +2113,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.set_slices(|slices| {
|
buffer.set_slices(block_len, |slices| {
|
||||||
for (channel_slice, channel_storage) in
|
for (channel_slice, channel_storage) in
|
||||||
slices.iter_mut().zip(storage.iter_mut())
|
slices.iter_mut().zip(storage.iter_mut())
|
||||||
{
|
{
|
||||||
|
@ -2148,12 +2148,11 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
|
|
||||||
// If the host passes weird data then we need to be very sure that there are
|
// If the host passes weird data then we need to be very sure that there are
|
||||||
// no dangling references to previous data
|
// no dangling references to previous data
|
||||||
buffer.set_slices(|slices| slices.fill_with(|| &mut []));
|
buffer.set_slices(0, |slices| slices.fill_with(|| &mut []));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let block_len = block_end - block_start;
|
buffer.set_slices(block_len, |slices| {
|
||||||
buffer.set_slices(|slices| {
|
|
||||||
for (channel_idx, channel_slice) in slices.iter_mut().enumerate() {
|
for (channel_idx, channel_slice) in slices.iter_mut().enumerate() {
|
||||||
*channel_slice = std::slice::from_raw_parts_mut(
|
*channel_slice = std::slice::from_raw_parts_mut(
|
||||||
(*(*host_output).data32.add(channel_idx)).add(block_start)
|
(*(*host_output).data32.add(channel_idx)).add(block_start)
|
||||||
|
|
|
@ -325,7 +325,7 @@ impl Cpal {
|
||||||
];
|
];
|
||||||
let mut buffer = Buffer::default();
|
let mut buffer = Buffer::default();
|
||||||
unsafe {
|
unsafe {
|
||||||
buffer.set_slices(|output_slices| {
|
buffer.set_slices(0, |output_slices| {
|
||||||
// Pre-allocate enough storage, the pointers are set in the data callback because
|
// Pre-allocate enough storage, the pointers are set in the data callback because
|
||||||
// `channels` will have been moved between now and the next callback
|
// `channels` will have been moved between now and the next callback
|
||||||
output_slices.resize_with(channels.len(), || &mut []);
|
output_slices.resize_with(channels.len(), || &mut []);
|
||||||
|
@ -339,12 +339,11 @@ impl Cpal {
|
||||||
// Can't borrow from `self` in the callback
|
// Can't borrow from `self` in the callback
|
||||||
let config = self.config.clone();
|
let config = self.config.clone();
|
||||||
let mut num_processed_samples = 0;
|
let mut num_processed_samples = 0;
|
||||||
|
|
||||||
move |data, _info| {
|
move |data, _info| {
|
||||||
// Things may have been moved in between callbacks, so these pointers need to be set up
|
// Things may have been moved in between callbacks, so these pointers need to be set up
|
||||||
// again on each invocation
|
// again on each invocation
|
||||||
unsafe {
|
unsafe {
|
||||||
buffer.set_slices(|output_slices| {
|
buffer.set_slices(config.period_size as usize, |output_slices| {
|
||||||
for (output_slice, channel) in output_slices.iter_mut().zip(channels.iter_mut())
|
for (output_slice, channel) in output_slices.iter_mut().zip(channels.iter_mut())
|
||||||
{
|
{
|
||||||
// SAFETY: `channels` is no longer used directly after this, and it outlives
|
// SAFETY: `channels` is no longer used directly after this, and it outlives
|
||||||
|
|
|
@ -33,7 +33,7 @@ impl Backend for Dummy {
|
||||||
];
|
];
|
||||||
let mut buffer = Buffer::default();
|
let mut buffer = Buffer::default();
|
||||||
unsafe {
|
unsafe {
|
||||||
buffer.set_slices(|output_slices| {
|
buffer.set_slices(self.config.period_size as usize, |output_slices| {
|
||||||
// SAFETY: `channels` is no longer used directly after this
|
// SAFETY: `channels` is no longer used directly after this
|
||||||
*output_slices = channels
|
*output_slices = channels
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
|
|
|
@ -40,7 +40,7 @@ impl Backend for Jack {
|
||||||
|
|
||||||
let mut buffer = Buffer::default();
|
let mut buffer = Buffer::default();
|
||||||
unsafe {
|
unsafe {
|
||||||
buffer.set_slices(|output_slices| {
|
buffer.set_slices(0, |output_slices| {
|
||||||
output_slices.resize_with(self.outputs.lock().len(), || &mut []);
|
output_slices.resize_with(self.outputs.lock().len(), || &mut []);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ impl Backend for Jack {
|
||||||
|
|
||||||
// And the buffer's slices need to point to the JACK output ports
|
// And the buffer's slices need to point to the JACK output ports
|
||||||
unsafe {
|
unsafe {
|
||||||
buffer.set_slices(|output_slices| {
|
buffer.set_slices(num_frames as usize, |output_slices| {
|
||||||
for (output_slice, output) in output_slices.iter_mut().zip(outputs.iter_mut()) {
|
for (output_slice, output) in output_slices.iter_mut().zip(outputs.iter_mut()) {
|
||||||
// SAFETY: This buffer is only read from after in this callback, and the
|
// SAFETY: This buffer is only read from after in this callback, and the
|
||||||
// reference passed to `cb` cannot outlive that function call
|
// reference passed to `cb` cannot outlive that function call
|
||||||
|
|
|
@ -402,7 +402,7 @@ impl<P: Vst3Plugin> IComponent for Wrapper<P> {
|
||||||
self.inner
|
self.inner
|
||||||
.output_buffer
|
.output_buffer
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.set_slices(|output_slices| {
|
.set_slices(0, |output_slices| {
|
||||||
output_slices
|
output_slices
|
||||||
.resize_with(bus_config.num_output_channels as usize, || &mut [])
|
.resize_with(bus_config.num_output_channels as usize, || &mut [])
|
||||||
});
|
});
|
||||||
|
@ -429,7 +429,7 @@ impl<P: Vst3Plugin> IComponent for Wrapper<P> {
|
||||||
Buffer::default,
|
Buffer::default,
|
||||||
);
|
);
|
||||||
for buffer in aux_input_buffers.iter_mut() {
|
for buffer in aux_input_buffers.iter_mut() {
|
||||||
buffer.set_slices(|channel_slices| {
|
buffer.set_slices(0, |channel_slices| {
|
||||||
channel_slices.resize_with(
|
channel_slices.resize_with(
|
||||||
bus_config.aux_input_busses.num_channels as usize,
|
bus_config.aux_input_busses.num_channels as usize,
|
||||||
|| &mut [],
|
|| &mut [],
|
||||||
|
@ -444,7 +444,7 @@ impl<P: Vst3Plugin> IComponent for Wrapper<P> {
|
||||||
Buffer::default,
|
Buffer::default,
|
||||||
);
|
);
|
||||||
for buffer in aux_output_buffers.iter_mut() {
|
for buffer in aux_output_buffers.iter_mut() {
|
||||||
buffer.set_slices(|channel_slices| {
|
buffer.set_slices(0, |channel_slices| {
|
||||||
channel_slices.resize_with(
|
channel_slices.resize_with(
|
||||||
bus_config.aux_output_busses.num_channels as usize,
|
bus_config.aux_output_busses.num_channels as usize,
|
||||||
|| &mut [],
|
|| &mut [],
|
||||||
|
@ -1322,9 +1322,10 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
||||||
// channels. In case the does does not provide an output or if they don't provide
|
// channels. In case the does does not provide an output or if they don't provide
|
||||||
// all of the channels (this should not happen, but Ableton Live might do it) then
|
// all of the channels (this should not happen, but Ableton Live might do it) then
|
||||||
// we'll skip the process function.
|
// we'll skip the process function.
|
||||||
|
let block_len = block_end - block_start;
|
||||||
let mut output_buffer = self.inner.output_buffer.borrow_mut();
|
let mut output_buffer = self.inner.output_buffer.borrow_mut();
|
||||||
let mut buffer_is_valid = false;
|
let mut buffer_is_valid = false;
|
||||||
output_buffer.set_slices(|output_slices| {
|
output_buffer.set_slices(block_len, |output_slices| {
|
||||||
// Buffers for zero-channel plugins like note effects should always be allowed
|
// Buffers for zero-channel plugins like note effects should always be allowed
|
||||||
buffer_is_valid = output_slices.is_empty();
|
buffer_is_valid = output_slices.is_empty();
|
||||||
|
|
||||||
|
@ -1352,7 +1353,7 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
||||||
*((*data.outputs).buffers as *mut *mut f32).add(output_channel_idx);
|
*((*data.outputs).buffers as *mut *mut f32).add(output_channel_idx);
|
||||||
*output_channel_slice = std::slice::from_raw_parts_mut(
|
*output_channel_slice = std::slice::from_raw_parts_mut(
|
||||||
channel_ptr.add(block_start),
|
channel_ptr.add(block_start),
|
||||||
block_end - block_start,
|
block_len,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1377,7 +1378,7 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
||||||
ptr::copy_nonoverlapping(
|
ptr::copy_nonoverlapping(
|
||||||
input_channel_ptr.add(block_start),
|
input_channel_ptr.add(block_start),
|
||||||
output_channel_ptr.add(block_start),
|
output_channel_ptr.add(block_start),
|
||||||
block_end - block_start,
|
block_len,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1416,13 +1417,12 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
||||||
|
|
||||||
// If the host passes weird data then we need to be very sure that there are
|
// If the host passes weird data then we need to be very sure that there are
|
||||||
// no dangling references to previous data
|
// no dangling references to previous data
|
||||||
buffer.set_slices(|slices| slices.fill_with(|| &mut []));
|
buffer.set_slices(0, |slices| slices.fill_with(|| &mut []));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll always reuse the start of the buffer even of the current block is
|
// We'll always reuse the start of the buffer even of the current block is
|
||||||
// shorter for cache locality reasons
|
// shorter for cache locality reasons
|
||||||
let block_len = block_end - block_start;
|
|
||||||
for (channel_idx, channel_storage) in storage.iter_mut().enumerate() {
|
for (channel_idx, channel_storage) in storage.iter_mut().enumerate() {
|
||||||
// The `set_len()` avoids having to unnecessarily fill the buffer with
|
// The `set_len()` avoids having to unnecessarily fill the buffer with
|
||||||
// zeroes when sizing up
|
// zeroes when sizing up
|
||||||
|
@ -1435,7 +1435,7 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.set_slices(|slices| {
|
buffer.set_slices(block_len, |slices| {
|
||||||
for (channel_slice, channel_storage) in
|
for (channel_slice, channel_storage) in
|
||||||
slices.iter_mut().zip(storage.iter_mut())
|
slices.iter_mut().zip(storage.iter_mut())
|
||||||
{
|
{
|
||||||
|
@ -1470,12 +1470,11 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
|
||||||
|
|
||||||
// If the host passes weird data then we need to be very sure that there are
|
// If the host passes weird data then we need to be very sure that there are
|
||||||
// no dangling references to previous data
|
// no dangling references to previous data
|
||||||
buffer.set_slices(|slices| slices.fill_with(|| &mut []));
|
buffer.set_slices(0, |slices| slices.fill_with(|| &mut []));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let block_len = block_end - block_start;
|
buffer.set_slices(block_len, |slices| {
|
||||||
buffer.set_slices(|slices| {
|
|
||||||
for (channel_idx, channel_slice) in slices.iter_mut().enumerate() {
|
for (channel_idx, channel_slice) in slices.iter_mut().enumerate() {
|
||||||
*channel_slice = std::slice::from_raw_parts_mut(
|
*channel_slice = std::slice::from_raw_parts_mut(
|
||||||
(*(*host_output).buffers.add(channel_idx)).add(block_start)
|
(*(*host_output).buffers.add(channel_idx)).add(block_start)
|
||||||
|
|
Loading…
Reference in a new issue