use crate::traits::StreamTrait; use crate::ChannelCount; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use crate::{ BackendSpecificError, Data, InputCallbackInfo, OutputCallbackInfo, PauseStreamError, PlayStreamError, SampleRate, StreamError, }; use super::JACK_SAMPLE_FORMAT; type ErrorCallbackPtr = Arc>; pub struct Stream { // TODO: It might be faster to send a message when playing/pausing than to check this every iteration playing: Arc, async_client: jack::AsyncClient, // Port names are stored in order to connect them to other ports in jack automatically input_port_names: Vec, output_port_names: Vec, } // Compile-time assertion that Stream is Send and Sync crate::assert_stream_send!(Stream); crate::assert_stream_sync!(Stream); impl Stream { // TODO: Return error messages pub fn new_input( client: jack::Client, channels: ChannelCount, data_callback: D, mut error_callback: E, ) -> Stream where D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let mut ports = vec![]; let mut port_names: Vec = vec![]; // Create ports for i in 0..channels { let port_try = client.register_port(&format!("in_{}", i), jack::AudioIn::default()); match port_try { Ok(port) => { // Get the port name in order to later connect it automatically if let Ok(port_name) = port.name() { port_names.push(port_name); } // Store the port into a Vec to move to the ProcessHandler ports.push(port); } Err(e) => { // If port creation failed, send the error back via the error_callback error_callback( BackendSpecificError { description: e.to_string(), } .into(), ); } } } let playing = Arc::new(AtomicBool::new(true)); let error_callback_ptr = Arc::new(Mutex::new(error_callback)) as ErrorCallbackPtr; let input_process_handler = LocalProcessHandler::new( vec![], ports, client.sample_rate(), client.buffer_size() as usize, Some(Box::new(data_callback)), None, playing.clone(), Arc::clone(&error_callback_ptr), ); let notification_handler = JackNotificationHandler::new(error_callback_ptr); let async_client = client .activate_async(notification_handler, input_process_handler) .unwrap(); Stream { playing, async_client, input_port_names: port_names, output_port_names: vec![], } } pub fn new_output( client: jack::Client, channels: ChannelCount, data_callback: D, mut error_callback: E, ) -> Stream where D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let mut ports = vec![]; let mut port_names: Vec = vec![]; // Create ports for i in 0..channels { let port_try = client.register_port(&format!("out_{}", i), jack::AudioOut::default()); match port_try { Ok(port) => { // Get the port name in order to later connect it automatically if let Ok(port_name) = port.name() { port_names.push(port_name); } // Store the port into a Vec to move to the ProcessHandler ports.push(port); } Err(e) => { // If port creation failed, send the error back via the error_callback error_callback( BackendSpecificError { description: e.to_string(), } .into(), ); } } } let playing = Arc::new(AtomicBool::new(true)); let error_callback_ptr = Arc::new(Mutex::new(error_callback)) as ErrorCallbackPtr; let output_process_handler = LocalProcessHandler::new( ports, vec![], client.sample_rate(), client.buffer_size() as usize, None, Some(Box::new(data_callback)), playing.clone(), Arc::clone(&error_callback_ptr), ); let notification_handler = JackNotificationHandler::new(error_callback_ptr); let async_client = client .activate_async(notification_handler, output_process_handler) .unwrap(); Stream { playing, async_client, input_port_names: vec![], output_port_names: port_names, } } /// Connect to the standard system outputs in jack, system:playback_1 and system:playback_2 /// This has to be done after the client is activated, doing it just after creating the ports doesn't work. pub fn connect_to_system_outputs(&mut self) { // Get the system ports let system_ports = self.async_client.as_client().ports( Some("system:playback_.*"), None, jack::PortFlags::empty(), ); // Connect outputs from this client to the system playback inputs for i in 0..self.output_port_names.len() { if i >= system_ports.len() { break; } match self .async_client .as_client() .connect_ports_by_name(&self.output_port_names[i], &system_ports[i]) { Ok(_) => (), Err(e) => println!("Unable to connect to port with error {}", e), } } } /// Connect to the standard system outputs in jack, system:capture_1 and system:capture_2 /// This has to be done after the client is activated, doing it just after creating the ports doesn't work. pub fn connect_to_system_inputs(&mut self) { // Get the system ports let system_ports = self.async_client.as_client().ports( Some("system:capture_.*"), None, jack::PortFlags::empty(), ); // Connect outputs from this client to the system playback inputs for i in 0..self.input_port_names.len() { if i >= system_ports.len() { break; } match self .async_client .as_client() .connect_ports_by_name(&system_ports[i], &self.input_port_names[i]) { Ok(_) => (), Err(e) => println!("Unable to connect to port with error {}", e), } } } } impl StreamTrait for Stream { fn play(&self) -> Result<(), PlayStreamError> { self.playing.store(true, Ordering::SeqCst); Ok(()) } fn pause(&self) -> Result<(), PauseStreamError> { self.playing.store(false, Ordering::SeqCst); Ok(()) } fn buffer_size(&self) -> Option { Some(self.async_client.as_client().buffer_size() as crate::FrameCount) } } type InputDataCallback = Box; type OutputDataCallback = Box; struct LocalProcessHandler { /// No new ports are allowed to be created after the creation of the LocalProcessHandler as that would invalidate the buffer sizes out_ports: Vec>, in_ports: Vec>, sample_rate: SampleRate, buffer_size: usize, input_data_callback: Option, output_data_callback: Option, // JACK audio samples are 32-bit float (unless you do some custom dark magic) temp_input_buffer: Vec, temp_output_buffer: Vec, playing: Arc, creation_timestamp: std::time::Instant, /// This should not be called on `process`, only on `buffer_size` because it can block. error_callback_ptr: ErrorCallbackPtr, } impl LocalProcessHandler { #[allow(clippy::too_many_arguments)] fn new( out_ports: Vec>, in_ports: Vec>, sample_rate: SampleRate, buffer_size: usize, input_data_callback: Option, output_data_callback: Option, playing: Arc, error_callback_ptr: ErrorCallbackPtr, ) -> Self { // These may be reallocated in the `buffer_size` callback. let temp_input_buffer = vec![0.0; in_ports.len() * buffer_size]; let temp_output_buffer = vec![0.0; out_ports.len() * buffer_size]; LocalProcessHandler { out_ports, in_ports, sample_rate, buffer_size, input_data_callback, output_data_callback, temp_input_buffer, temp_output_buffer, playing, creation_timestamp: std::time::Instant::now(), error_callback_ptr, } } } fn temp_buffer_to_data(temp_input_buffer: &mut [f32], total_buffer_size: usize) -> Data { let slice = &mut temp_input_buffer[0..total_buffer_size]; let data: *mut () = slice.as_mut_ptr().cast(); let len = total_buffer_size; unsafe { Data::from_parts(data, len, JACK_SAMPLE_FORMAT) } } impl jack::ProcessHandler for LocalProcessHandler { fn process(&mut self, _: &jack::Client, process_scope: &jack::ProcessScope) -> jack::Control { if !self.playing.load(Ordering::SeqCst) { return jack::Control::Continue; } // This should be equal to self.buffer_size, but the implementation will // work even if it is less. Will panic in `temp_buffer_to_data` if greater. let current_frame_count = process_scope.n_frames() as usize; // Get timestamp data let (current_start_usecs, next_usecs_opt) = match process_scope.cycle_times() { Ok(times) => (times.current_usecs, Some(times.next_usecs)), Err(_) => { // jack was unable to get the current time information // Fall back to using Instants let now = std::time::Instant::now(); let duration = now.duration_since(self.creation_timestamp); (duration.as_micros() as u64, None) } }; let start_cycle_instant = micros_to_stream_instant(current_start_usecs); let start_callback_instant = start_cycle_instant .add(frames_to_duration( process_scope.frames_since_cycle_start() as usize, self.sample_rate, )) .expect("`playback` occurs beyond representation supported by `StreamInstant`"); if let Some(input_callback) = &mut self.input_data_callback { // Let's get the data from the input ports and run the callback let num_in_channels = self.in_ports.len(); // Read the data from the input ports into the temporary buffer // Go through every channel and store its data in the temporary input buffer for ch_ix in 0..num_in_channels { let input_channel = &self.in_ports[ch_ix].as_slice(process_scope); for i in 0..current_frame_count { self.temp_input_buffer[ch_ix + i * num_in_channels] = input_channel[i]; } } // Create a slice of exactly current_frame_count frames let data = temp_buffer_to_data( &mut self.temp_input_buffer, current_frame_count * num_in_channels, ); // Create timestamp let callback = start_callback_instant; // Input data was made available at the start of the cycle (current_usecs). let capture = start_cycle_instant; let timestamp = crate::InputStreamTimestamp { callback, capture }; let info = crate::InputCallbackInfo { timestamp }; input_callback(&data, &info); } if let Some(output_callback) = &mut self.output_data_callback { let num_out_channels = self.out_ports.len(); // Create a slice of exactly current_frame_count frames let mut data = temp_buffer_to_data( &mut self.temp_output_buffer, current_frame_count * num_out_channels, ); // Create timestamp let callback = start_callback_instant; // Use next_usecs (the hardware deadline for this cycle) when available; it is the // exact instant at which the last sample written here will be consumed by the device. let playback = match next_usecs_opt { Some(next_usecs) => micros_to_stream_instant(next_usecs), None => start_cycle_instant .add(frames_to_duration(current_frame_count, self.sample_rate)) .expect("`playback` occurs beyond representation supported by `StreamInstant`"), }; let timestamp = crate::OutputStreamTimestamp { callback, playback }; let info = crate::OutputCallbackInfo { timestamp }; output_callback(&mut data, &info); // Deinterlace for ch_ix in 0..num_out_channels { let output_channel = &mut self.out_ports[ch_ix].as_mut_slice(process_scope); for i in 0..current_frame_count { output_channel[i] = self.temp_output_buffer[ch_ix + i * num_out_channels]; } } } // Continue as normal jack::Control::Continue } fn buffer_size(&mut self, _: &jack::Client, size: jack::Frames) -> jack::Control { // The `buffer_size` callback is actually called on the process thread, but // it does not need to be suitable for real-time use. Thus we can simply allocate // new buffers here. It is also fine to call the error callback. // Details: https://github.com/RustAudio/rust-jack/issues/137 let new_size = size as usize; if new_size != self.buffer_size { self.buffer_size = new_size; self.temp_input_buffer = vec![0.0; self.in_ports.len() * new_size]; self.temp_output_buffer = vec![0.0; self.out_ports.len() * new_size]; let description = format!("buffer size changed to: {}", new_size); if let Ok(mut mutex_guard) = self.error_callback_ptr.lock() { let err = &mut *mutex_guard; err(BackendSpecificError { description }.into()); } } jack::Control::Continue } } fn micros_to_stream_instant(micros: u64) -> crate::StreamInstant { crate::StreamInstant::from_nanos_i128(micros as i128 * 1_000) .expect("`micros` out of range of `StreamInstant` representation") } // Convert the given duration in frames at the given sample rate to a `std::time::Duration`. fn frames_to_duration(frames: usize, rate: crate::SampleRate) -> std::time::Duration { let secsf = frames as f64 / rate as f64; let secs = secsf as u64; let nanos = ((secsf - secs as f64) * 1_000_000_000.0) as u32; std::time::Duration::new(secs, nanos) } /// Receives notifications from the JACK server. It is unclear if this may be run concurrent with itself under JACK2 specs /// so it needs to be Sync. struct JackNotificationHandler { error_callback_ptr: ErrorCallbackPtr, init_sample_rate_flag: Arc, } impl JackNotificationHandler { pub fn new(error_callback_ptr: ErrorCallbackPtr) -> Self { JackNotificationHandler { error_callback_ptr, init_sample_rate_flag: Arc::new(AtomicBool::new(false)), } } fn send_error(&mut self, description: String) { // This thread isn't the audio thread, it's fine to block if let Ok(mut mutex_guard) = self.error_callback_ptr.lock() { let err = &mut *mutex_guard; err(BackendSpecificError { description }.into()); } } } impl jack::NotificationHandler for JackNotificationHandler { unsafe fn shutdown(&mut self, _status: jack::ClientStatus, reason: &str) { self.send_error(format!("JACK was shut down for reason: {}", reason)); } fn sample_rate(&mut self, _: &jack::Client, _srate: jack::Frames) -> jack::Control { match self.init_sample_rate_flag.load(Ordering::SeqCst) { false => { // One of these notifications is sent every time a client is started. self.init_sample_rate_flag.store(true, Ordering::SeqCst); jack::Control::Continue } true => { // The JACK server has changed the sample rate, invalidating this stream. // The stream configuration must be rebuilt with the new sample rate. if let Ok(mut cb) = self.error_callback_ptr.lock() { cb(StreamError::StreamInvalidated); } jack::Control::Quit } } } fn xrun(&mut self, _: &jack::Client) -> jack::Control { if let Ok(mut cb) = self.error_callback_ptr.lock() { cb(StreamError::BufferUnderrun); } jack::Control::Continue } }