diff options
Diffstat (limited to 'third_party/rust/cubeb-coreaudio/src/backend/buffer_manager.rs')
-rw-r--r-- | third_party/rust/cubeb-coreaudio/src/backend/buffer_manager.rs | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/third_party/rust/cubeb-coreaudio/src/backend/buffer_manager.rs b/third_party/rust/cubeb-coreaudio/src/backend/buffer_manager.rs new file mode 100644 index 0000000000..6f1c299bfb --- /dev/null +++ b/third_party/rust/cubeb-coreaudio/src/backend/buffer_manager.rs @@ -0,0 +1,355 @@ +use std::cmp::Ordering; +use std::fmt; +use std::os::raw::c_void; +use std::slice; + +use cubeb_backend::SampleFormat; + +use super::ringbuf::RingBuffer; + +use self::LinearBuffer::*; +use self::RingBufferConsumer::*; +use self::RingBufferProducer::*; + +// Shuffles the data so that the first n channels of the interleaved buffer are overwritten by +// the remaining channels. +fn drop_first_n_channels_in_place<T: Copy>( + n: usize, + data: &mut [T], + frame_count: usize, + channel_count: usize, +) { + // This function works if the numbers are equal but it's not particularly useful, so we hope to + // catch issues by checking using > and not >= here. + assert!(channel_count > n); + let mut read_idx: usize = 0; + let mut write_idx: usize = 0; + + let channel_to_keep = channel_count - n; + for _ in 0..frame_count { + read_idx += n; + for _ in 0..channel_to_keep { + data[write_idx] = data[read_idx]; + read_idx += 1; + write_idx += 1; + } + } +} + +// It can be that the a stereo microphone is in use, but the user asked for mono input. In this +// particular case, downmix the stereo pair into a mono channel. In all other cases, simply drop +// the remaining channels before appending to the ringbuffer, becauses there is no right or wrong +// way to do this, unlike with the output side, where proper channel matrixing can be done. +// Return the number of valid samples in the buffer. +fn remix_or_drop_channels<T: Copy + std::ops::Add<Output = T>>( + input_channels: usize, + output_channels: usize, + data: &mut [T], + frame_count: usize, +) -> usize { + assert!(input_channels >= output_channels); + // Nothing to do, just return + if input_channels == output_channels { + return output_channels * frame_count; + } + // Simple stereo downmix + if input_channels == 2 && output_channels == 1 { + let mut read_idx = 0; + for (write_idx, _) in (0..frame_count).enumerate() { + data[write_idx] = data[read_idx] + data[read_idx + 1]; + read_idx += 2; + } + return output_channels * frame_count; + } + // Drop excess channels + let mut read_idx = 0; + let mut write_idx = 0; + let channel_dropped_count = input_channels - output_channels; + for _ in 0..frame_count { + for _ in 0..output_channels { + data[write_idx] = data[read_idx]; + write_idx += 1; + read_idx += 1; + } + read_idx += channel_dropped_count; + } + output_channels * frame_count +} + +fn process_data<T: Copy + std::ops::Add<Output = T>>( + data: *mut c_void, + frame_count: usize, + input_channel_count: usize, + input_channels_to_ignore: usize, + output_channel_count: usize, +) -> &'static [T] { + assert!( + input_channels_to_ignore == 0 + || input_channel_count >= input_channels_to_ignore + output_channel_count + ); + let input_slice = unsafe { + slice::from_raw_parts_mut::<T>(data as *mut T, frame_count * input_channel_count) + }; + match input_channel_count.cmp(&output_channel_count) { + Ordering::Equal => { + assert_eq!(input_channels_to_ignore, 0); + input_slice + } + Ordering::Greater => { + if input_channels_to_ignore > 0 { + drop_first_n_channels_in_place( + input_channels_to_ignore, + input_slice, + frame_count, + input_channel_count, + ); + } + let new_count_remixed = remix_or_drop_channels( + input_channel_count - input_channels_to_ignore, + output_channel_count, + input_slice, + frame_count, + ); + unsafe { slice::from_raw_parts_mut::<T>(data as *mut T, new_count_remixed) } + } + Ordering::Less => { + assert!(input_channel_count < output_channel_count); + // Upmix happens on pull. + input_slice + } + } +} + +pub enum RingBufferConsumer { + IntegerRingBufferConsumer(ringbuf::Consumer<i16>), + FloatRingBufferConsumer(ringbuf::Consumer<f32>), +} + +pub enum RingBufferProducer { + IntegerRingBufferProducer(ringbuf::Producer<i16>), + FloatRingBufferProducer(ringbuf::Producer<f32>), +} + +pub enum LinearBuffer { + IntegerLinearBuffer(Vec<i16>), + FloatLinearBuffer(Vec<f32>), +} + +pub struct BufferManager { + consumer: RingBufferConsumer, + producer: RingBufferProducer, + linear_buffer: LinearBuffer, + // The number of channels in the interleaved data given to push_data + input_channel_count: usize, + // The number of channels that needs to be skipped in the beginning of input_channel_count + input_channels_to_ignore: usize, + // The number of channels we actually needs, which is also the channel count of the + // processed data stored in the internal ring buffer. + output_channel_count: usize, +} + +impl BufferManager { + pub fn new( + format: SampleFormat, + buffer_size_frames: usize, + input_channel_count: usize, + input_channels_to_ignore: usize, + output_channel_count: usize, + ) -> Self { + assert!( + (input_channels_to_ignore == 0 && input_channel_count == 1) + || input_channel_count >= input_channels_to_ignore + output_channel_count + ); + // 8 times the expected callback size, to handle the input callback being caled multiple + // times in a row correctly. + let buffer_element_count = output_channel_count * buffer_size_frames * 8; + match format { + SampleFormat::S16LE | SampleFormat::S16BE | SampleFormat::S16NE => { + let ring = RingBuffer::<i16>::new(buffer_element_count); + let (prod, cons) = ring.split(); + Self { + producer: IntegerRingBufferProducer(prod), + consumer: IntegerRingBufferConsumer(cons), + linear_buffer: IntegerLinearBuffer(Vec::<i16>::with_capacity( + buffer_element_count, + )), + input_channel_count, + input_channels_to_ignore, + output_channel_count, + } + } + SampleFormat::Float32LE | SampleFormat::Float32BE | SampleFormat::Float32NE => { + let ring = RingBuffer::<f32>::new(buffer_element_count); + let (prod, cons) = ring.split(); + Self { + producer: FloatRingBufferProducer(prod), + consumer: FloatRingBufferConsumer(cons), + linear_buffer: FloatLinearBuffer(Vec::<f32>::with_capacity( + buffer_element_count, + )), + input_channel_count, + input_channels_to_ignore, + output_channel_count, + } + } + } + } + fn stored_channel_count(&self) -> usize { + if self.output_channel_count > self.input_channel_count { + // This case allows upmix from mono on pull. + self.input_channel_count + } else { + // Other cases only downmix on push. + self.output_channel_count + } + } + fn input_channel_count(&self) -> usize { + self.input_channel_count + } + fn input_channels_to_ignore(&self) -> usize { + self.input_channels_to_ignore + } + fn output_channel_count(&self) -> usize { + self.output_channel_count + } + pub fn push_data(&mut self, data: *mut c_void, frame_count: usize) { + let to_push = frame_count * self.stored_channel_count(); + let input_channel_count = self.input_channel_count(); + let input_channels_to_ignore = self.input_channels_to_ignore(); + let output_channel_count = self.output_channel_count(); + let pushed = match &mut self.producer { + RingBufferProducer::FloatRingBufferProducer(p) => { + let processed_input = process_data( + data, + frame_count, + input_channel_count, + input_channels_to_ignore, + output_channel_count, + ); + p.push_slice(processed_input) + } + RingBufferProducer::IntegerRingBufferProducer(p) => { + let processed_input = process_data( + data, + frame_count, + input_channel_count, + input_channels_to_ignore, + output_channel_count, + ); + p.push_slice(processed_input) + } + }; + assert!(pushed <= to_push); + if pushed != to_push { + cubeb_alog!( + "Input ringbuffer full, could only push {} instead of {}", + pushed, + to_push + ); + } + } + fn pull_data(&mut self, data: *mut c_void, needed_samples: usize) { + assert_eq!(needed_samples % self.output_channel_count(), 0); + let needed_frames = needed_samples / self.output_channel_count(); + let to_pull = needed_frames * self.stored_channel_count(); + match &mut self.consumer { + IntegerRingBufferConsumer(p) => { + let input: &mut [i16] = + unsafe { slice::from_raw_parts_mut::<i16>(data as *mut i16, needed_samples) }; + let pulled = p.pop_slice(input); + if pulled < to_pull { + cubeb_alog!( + "Underrun during input data pull: (needed: {}, available: {})", + to_pull, + pulled + ); + for i in 0..(to_pull - pulled) { + input[pulled + i] = 0; + } + } + if needed_samples > to_pull { + // Mono upmix. This can happen with voice processing. + let mut write_idx = needed_samples; + for read_idx in (0..to_pull).rev() { + write_idx -= self.output_channel_count(); + for offset in 0..self.output_channel_count() { + input[write_idx + offset] = input[read_idx]; + } + } + } + } + FloatRingBufferConsumer(p) => { + let input: &mut [f32] = + unsafe { slice::from_raw_parts_mut::<f32>(data as *mut f32, needed_samples) }; + let pulled = p.pop_slice(input); + if pulled < to_pull { + cubeb_alog!( + "Underrun during input data pull: (needed: {}, available: {})", + to_pull, + pulled + ); + for i in 0..(to_pull - pulled) { + input[pulled + i] = 0.0; + } + } + if needed_samples > to_pull { + // Mono upmix. This can happen with voice processing. + let mut write_idx = needed_samples; + for read_idx in (0..to_pull).rev() { + write_idx -= self.output_channel_count(); + for offset in 0..self.output_channel_count() { + input[write_idx + offset] = input[read_idx]; + } + } + } + } + } + } + pub fn get_linear_data(&mut self, frame_count: usize) -> *mut c_void { + let output_sample_count = frame_count * self.output_channel_count(); + let p = match &mut self.linear_buffer { + LinearBuffer::IntegerLinearBuffer(b) => { + b.resize(output_sample_count, 0); + b.as_mut_ptr() as *mut c_void + } + LinearBuffer::FloatLinearBuffer(b) => { + b.resize(output_sample_count, 0.); + b.as_mut_ptr() as *mut c_void + } + }; + self.pull_data(p, output_sample_count); + + p + } + pub fn available_frames(&self) -> usize { + assert_ne!(self.stored_channel_count(), 0); + let stored_samples = match &self.consumer { + IntegerRingBufferConsumer(p) => p.len(), + FloatRingBufferConsumer(p) => p.len(), + }; + stored_samples / self.stored_channel_count() + } + pub fn trim(&mut self, final_frame_count: usize) { + let final_sample_count = final_frame_count * self.stored_channel_count(); + match &mut self.consumer { + IntegerRingBufferConsumer(c) => { + let available = c.len(); + assert!(available >= final_sample_count); + let to_pop = available - final_sample_count; + c.discard(to_pop); + } + FloatRingBufferConsumer(c) => { + let available = c.len(); + assert!(available >= final_sample_count); + let to_pop = available - final_sample_count; + c.discard(to_pop); + } + } + } +} + +impl fmt::Debug for BufferManager { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + Ok(()) + } +} |