use backend::cork_state::CorkState; use backend::*; use cubeb_backend::{
ffi, log_enabled, ChannelLayout, DeviceId, DeviceRef, Error, InputProcessingParams, Result,
SampleFormat, StreamOps, StreamParamsRef, StreamPrefs,
}; use pulse::{self, CVolumeExt, ChannelMapExt, SampleSpecExt, StreamLatency, USecExt}; use pulse_ffi::*; use ringbuf::RingBuffer; use std::ffi::{CStr, CString}; use std::os::raw::{c_long, c_void}; use std::slice; use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; use std::{mem, ptr};
/// Iterator interface to `ChannelLayout`. /// /// Iterates each channel in the set represented by `ChannelLayout`. struct ChannelLayoutIter { /// The layout set being iterated
layout: ChannelLayout, /// The next flag to test
index: u8,
}
fn channel_layout_iter(layout: ChannelLayout) -> ChannelLayoutIter { let index = 0;
ChannelLayoutIter { layout, index }
}
impl Iterator for ChannelLayoutIter { type Item = ChannelLayout;
fn next(&mutself) -> Option<Self::Item> { while !self.layout.is_empty() { let test = Self::Item::from_bits_truncate(1 << self.index); self.index += 1; ifself.layout.contains(test) { self.layout.remove(test); return Some(test);
}
}
None
}
}
letmut cm = pulse::ChannelMap::init(); for (i, channel) in channel_layout_iter(layout).enumerate() {
cm.map[i] = cubeb_channel_to_pa_channel(channel.into());
}
cm.channels = layout.num_channels() as _;
// Special case single channel center mapping as mono. if cm.channels == 1 && cm.map[0] == PA_CHANNEL_POSITION_FRONT_CENTER {
cm.map[0] = PA_CHANNEL_POSITION_MONO;
}
cm
}
fn default_layout_for_channels(ch: u32) -> ChannelLayout { match ch { 1 => ChannelLayout::MONO, 2 => ChannelLayout::STEREO, 3 => ChannelLayout::_3F, 4 => ChannelLayout::QUAD, 5 => ChannelLayout::_3F2, 6 => ChannelLayout::_3F_LFE | ChannelLayout::SIDE_LEFT | ChannelLayout::SIDE_RIGHT, 7 => ChannelLayout::_3F3R_LFE, 8 => ChannelLayout::_3F4_LFE,
_ => panic!("channel must be between 1 to 8."),
}
}
pubstruct Device(ffi::cubeb_device);
impl Drop for Device { fn drop(&mutself) { unsafe { if !self.0.input_name.is_null() { let _ = CString::from_raw(self.0.input_name as *mut _);
} if !self.0.output_name.is_null() { let _ = CString::from_raw(self.0.output_name as *mut _);
}
}
}
}
impl BufferManager { // When opening a duplex stream, the sample-spec are guaranteed to match. It's ok to have // either the input or output sample-spec here. fn new(input_buffer_size: usize, sample_spec: &pulse::SampleSpec) -> BufferManager { if sample_spec.format == PA_SAMPLE_S16BE || sample_spec.format == PA_SAMPLE_S16LE { let ring = RingBuffer::<i16>::new(input_buffer_size); let (prod, cons) = ring.split();
BufferManager {
producer: IntegerRingBufferProducer(prod),
consumer: IntegerRingBufferConsumer(cons),
linear_input_buffer: IntegerLinearInputBuffer(Vec::<i16>::with_capacity(
input_buffer_size,
)),
}
} else { let ring = RingBuffer::<f32>::new(input_buffer_size); let (prod, cons) = ring.split();
BufferManager {
producer: FloatRingBufferProducer(prod),
consumer: FloatRingBufferConsumer(cons),
linear_input_buffer: FloatLinearInputBuffer(Vec::<f32>::with_capacity(
input_buffer_size,
)),
}
}
}
fn push_input_data(&mutself, input_data: *const c_void, read_samples: usize) { match &mutself.producer {
RingBufferProducer::FloatRingBufferProducer(p) => { let input_data = unsafe { slice::from_raw_parts::<f32>(input_data as *const f32, read_samples) }; // we don't do anything in particular if we can't push everything
p.push_slice(input_data);
}
RingBufferProducer::IntegerRingBufferProducer(p) => { let input_data = unsafe { slice::from_raw_parts::<i16>(input_data as *const i16, read_samples) };
p.push_slice(input_data);
}
}
}
fn pull_input_data(&mutself, input_data: *mut c_void, needed_samples: usize) { match &mutself.consumer {
IntegerRingBufferConsumer(p) => { let input: &mut [i16] = unsafe {
slice::from_raw_parts_mut::<i16>(input_data as *mut i16, needed_samples)
}; let read = p.pop_slice(input); if read < needed_samples { for i in0..(needed_samples - read) {
input[read + i] = 0;
}
}
}
FloatRingBufferConsumer(p) => { let input: &mut [f32] = unsafe {
slice::from_raw_parts_mut::<f32>(input_data as *mut f32, needed_samples)
}; let read = p.pop_slice(input); if read < needed_samples { for i in0..(needed_samples - read) {
input[read + i] = 0.;
}
}
}
}
}
fn get_linear_input_data(&mutself, nsamples: usize) -> *const c_void { let p = match &mutself.linear_input_buffer {
LinearInputBuffer::IntegerLinearInputBuffer(b) => {
b.resize(nsamples, 0);
b.as_mut_ptr() as *mut c_void
}
LinearInputBuffer::FloatLinearInputBuffer(b) => {
b.resize(nsamples, 0.);
b.as_mut_ptr() as *mut c_void
}
}; self.pull_input_data(p, nsamples);
fn read_data(s: &pulse::Stream, nbytes: usize, u: *mut c_void) { fn read_from_input(
s: &pulse::Stream,
buffer: *mut *const c_void,
size: *mut usize,
) -> i32 { let readable_size = s.readable_size().map(|s| s as i32).unwrap_or(-1); if readable_size > 0 && unsafe { s.peek(buffer, size).is_err() } {
cubeb_logv!("Error while peeking the input stream"); return -1;
}
readable_size
}
cubeb_alogv!("Input callback buffer size {}", nbytes); let stm = unsafe { &mut *(u as *mut PulseStream) }; if stm.shutdown { return;
}
letmut read_data: *const c_void = ptr::null(); letmut read_size: usize = 0; while read_from_input(s, &mut read_data, &mut read_size) > 0 { /* read_data can be NULL in case of a hole. */ if !read_data.is_null() { let in_frame_size = stm.input_sample_spec.frame_size(); let read_frames = read_size / in_frame_size; let read_samples = read_size / stm.input_sample_spec.sample_size();
if stm.output_stream.is_some() { // duplex stream: push the input data to the ring buffer.
stm.input_buffer_manager
.as_mut()
.unwrap()
.push_input_data(read_data, read_samples);
} else { // input/capture only operation. Call callback directly let got = unsafe {
stm.data_callback.unwrap()(
stm as *mut _ as *mut _,
stm.user_ptr,
read_data,
ptr::null_mut(),
read_frames as c_long,
)
};
if got < 0 || got as usize != read_frames { let _ = s.cancel_write();
stm.shutdown = true; if got < 0 { unsafe {
stm.state_callback.unwrap()(
stm as *mut _ as *mut _,
stm.user_ptr,
ffi::CUBEB_STATE_ERROR,
);
}
} break;
}
}
}
if read_size > 0 { let _ = s.drop();
}
if stm.shutdown { return;
}
}
}
fn write_data(_: &pulse::Stream, nbytes: usize, u: *mut c_void) {
cubeb_alogv!("Output callback to be written buffer size {}", nbytes); let stm = unsafe { &mut *(u as *mut PulseStream) }; if stm.shutdown || stm.state != ffi::CUBEB_STATE_STARTED { return;
}
let nframes = nbytes / stm.output_sample_spec.frame_size(); let first_callback = stm.output_frame_count.fetch_add(nframes, Ordering::SeqCst) == 0; if stm.input_stream.is_some() { let nsamples_input = nframes * stm.input_sample_spec.channels as usize; let input_buffer_manager = stm.input_buffer_manager.as_mut().unwrap();
if first_callback { let buffered_input_frames = input_buffer_manager.available_samples()
/ stm.input_sample_spec.channels as usize; if buffered_input_frames > nframes { // Trim the buffer to ensure minimal roundtrip latency let popped_frames = buffered_input_frames - nframes;
input_buffer_manager
.trim(nframes * stm.input_sample_spec.channels as usize);
cubeb_alog!("Dropping {} frames in input buffer.", popped_frames);
}
}
let p = input_buffer_manager.get_linear_input_data(nsamples_input);
stm.trigger_user_callback(p, nbytes);
} else { // Output/playback only operation. // Write directly to output
debug_assert!(stm.output_stream.is_some());
stm.trigger_user_callback(ptr::null(), nbytes);
}
}
// Duplex, set up the ringbuffer if input_stream_params.is_some() && output_stream_params.is_some() { // A bit more room in case of output underrun. let buffer_size_bytes = 2 * latency_frames * stm.input_sample_spec.frame_size() as u32;
stm.input_buffer_manager = Some(BufferManager::new(
buffer_size_bytes as usize,
&stm.input_sample_spec,
))
}
let r = if stm.wait_until_ready() { /* force a timing update now, otherwise timing info does not become valid
until some point after initialization has completed. */
stm.update_timing_info()
} else { false
};
stm.context.mainloop.unlock();
if !r {
stm.destroy();
cubeb_log!("Error while waiting for the stream to be ready"); return Err(Error::error());
}
self.context.mainloop.lock();
{ iflet Some(stm) = self.output_stream.take() { let drain_timer = self.drain_timer.load(Ordering::Acquire); if !drain_timer.is_null() { /* there's no pa_rttime_free, so use this instead. */ self.context.mainloop.get_api().time_free(drain_timer);
}
stm.clear_state_callback();
stm.clear_write_callback(); let _ = stm.disconnect();
stm.unref();
}
impl<'ctx> Drop for PulseStream<'ctx> { fn drop(&mutself) { self.destroy();
}
}
impl<'ctx> StreamOps for PulseStream<'ctx> { fn start(&mutself) -> Result<()> { fn output_preroll(_: &pulse::MainloopApi, u: *mut c_void) { let stm = unsafe { &mut *(u as *mut PulseStream) }; if !stm.shutdown { let size = stm
.output_stream
.as_ref()
.map_or(0, |s| s.writable_size().unwrap_or(0));
stm.trigger_user_callback(std::ptr::null(), size);
}
} self.shutdown = false; self.cork(CorkState::uncork() | CorkState::notify());
ifself.output_stream.is_some() { /* When doing output-only or duplex, we need to manually call user cb once in order to *makethingsroll.ThisisdoneviaadefereventinordertoexecuteitfromPA
* server thread. */ self.context.mainloop.lock(); self.context
.mainloop
.get_api()
.once(output_preroll, selfas *const _ as *mut _); self.context.mainloop.unlock();
}
Ok(())
}
fn stop(&mutself) -> Result<()> {
{ self.context.mainloop.lock(); self.shutdown = true; // If draining is taking place wait to finish
cubeb_log!("Stream stop: waiting for drain"); while !self.drain_timer.load(Ordering::Acquire).is_null() { self.context.mainloop.wait();
}
cubeb_log!("Stream stop: waited for drain"); self.context.mainloop.unlock();
} self.cork(CorkState::cork() | CorkState::notify());
Ok(())
}
fn position(&mutself) -> Result<u64> { let in_thread = self.context.mainloop.in_thread();
if !in_thread { self.context.mainloop.lock();
}
ifself.output_stream.is_none() {
cubeb_log!("Calling position() on an input-only stream"); return Err(Error::error());
}
let stm = self.output_stream.as_ref().unwrap(); let r = match stm.get_time() {
Ok(r_usec) => { let bytes = USecExt::to_bytes(r_usec, &self.output_sample_spec);
Ok((bytes / self.output_sample_spec.frame_size()) as u64)
}
Err(_) => {
cubeb_log!("Error: stm.get_time failed");
Err(Error::error())
}
};
if !in_thread { self.context.mainloop.unlock();
}
r
}
fn latency(&mutself) -> Result<u32> { matchself.output_stream {
None => {
cubeb_log!("Error: calling latency() on an input-only stream");
Err(Error::error())
}
Some(ref stm) => match stm.get_latency() {
Ok(StreamLatency::Positive(r_usec)) => { let latency = (r_usec * pa_usec_t::from(self.output_sample_spec.rate)
/ PA_USEC_PER_SEC) as u32;
Ok(latency)
}
Ok(_) => {
panic!("Can not handle negative latency values.");
}
Err(_) => {
cubeb_log!("Error: get_latency() failed for an output stream");
Err(Error::error())
}
},
}
}
fn input_latency(&mutself) -> Result<u32> { matchself.input_stream {
None => {
cubeb_log!("Error: calling input_latency() on an output-only stream");
Err(Error::error())
}
Some(ref stm) => match stm.get_latency() {
Ok(StreamLatency::Positive(w_usec)) => { let latency = (w_usec * pa_usec_t::from(self.input_sample_spec.rate)
/ PA_USEC_PER_SEC) as u32;
Ok(latency)
} // Input stream can be negative only if it is attached to a // monitor source device
Ok(StreamLatency::Negative(_)) => Ok(0),
Err(_) => {
cubeb_log!("Error: stm.get_latency() failed for an input stream");
Err(Error::error())
}
},
}
}
/* if the pulse daemon is configured to use flat *volumes,applyourowngaininsteadofchanging
* the input volume on the sink. */ let flags = { matchself.context.default_sink_info {
Some(ref info) => info.flags,
_ => pulse::SinkFlags::empty(),
}
};
if flags.contains(pulse::SinkFlags::FLAT_VOLUME) { self.volume = volume;
} else { let channels = stm.get_sample_spec().channels; let vol = pulse::sw_volume_from_linear(f64::from(volume));
cvol.set(u32::from(channels), vol);
let index = stm.get_index();
let context_ptr = self.context as *const _ as *mut _; iflet Ok(o) = context.set_sink_input_volume(
index,
&cvol,
context_success,
context_ptr,
) { self.context.operation_wait(stm, &o);
}
}
cubeb_logv!( "Trigger user callback with output buffer size={}, read_offset={}",
size,
read_offset
); let read_ptr = unsafe { (input_data as *const u8).add(read_offset) }; #[allow(clippy::unnecessary_cast)] letmut got = unsafe { self.data_callback.unwrap()( selfas *const _ as *mut _, self.user_ptr,
read_ptr as *const _ as *mut _,
buffer,
(size / frame_size) as c_long,
) as i64
}; if got < 0 { let _ = stm.cancel_write(); self.shutdown = true; unsafe { self.state_callback.unwrap()( selfas *const _ as *mut _, self.user_ptr,
ffi::CUBEB_STATE_ERROR,
);
} return;
}
// If more iterations move offset of read buffer if !input_data.is_null() { let in_frame_size = self.input_sample_spec.frame_size();
read_offset += (size / frame_size) * in_frame_size;
}
ifself.volume != PULSE_NO_GAIN { let samples = (self.output_sample_spec.channels as usize * size
/ frame_size) as isize;
ifself.output_sample_spec.format == PA_SAMPLE_S16BE
|| self.output_sample_spec.format == PA_SAMPLE_S16LE
{ let b = buffer as *mut i16; for i in0..samples { unsafe { *b.offset(i) *= self.volume as i16 };
}
} else { let b = buffer as *mut f32; for i in0..samples { unsafe { *b.offset(i) *= self.volume };
}
}
}
let should_drain = (got as usize) < size / frame_size;
if should_drain && self.output_frame_count.load(Ordering::SeqCst) == 0 { // Draining during preroll, ensure `prebuf` frames are written so // the stream starts. If not, pad with a bit of silence. let prebuf_size_bytes = stm.get_buffer_attr().prebuf as usize; let got_bytes = got as usize * frame_size; if prebuf_size_bytes > got_bytes { let padding_bytes = prebuf_size_bytes - got_bytes; if padding_bytes + got_bytes <= size { // A slice that starts after the data provided by the callback, // with just enough room to provide a final buffer big enough. let padding_buf: &mut [u8] = unsafe {
slice::from_raw_parts_mut::<u8>(
buffer.add(got_bytes) as *mut u8,
padding_bytes,
)
};
padding_buf.fill(0);
got += (padding_bytes / frame_size) as i64;
}
} else {
cubeb_logv!( "Not enough room to pad up to prebuf when prebuffering."
)
}
}
let r = stm.write(
buffer,
got as usize * frame_size, 0,
pulse::SeekMode::Relative,
);
if should_drain {
cubeb_logv!("Draining {} < {}", got, size / frame_size); let latency = match stm.get_latency() {
Ok(StreamLatency::Positive(l)) => l,
Ok(_) => {
panic!("Can not handle negative latency values.");
}
Err(e) => {
debug_assert_eq!(
e,
pulse::ErrorCode::from_error_code(PA_ERR_NODATA)
); /* this needs a better guess. */ 100 * PA_USEC_PER_MSEC
}
};
/* pa_stream_drain is useless, see PA bug# 866. this is a workaround. */ /* arbitrary safety margin: double the current latency. */
debug_assert!(self.drain_timer.load(Ordering::Acquire).is_null()); let stream_ptr = selfas *const _ as *mut _; iflet Some(ref context) = self.context.context { self.drain_timer.store(
context.rttime_new(
pulse::rtclock_now() + 2 * latency,
drained_cb,
stream_ptr,
),
Ordering::Release,
);
} self.shutdown = true; return;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.