use firewheel::{
StreamInfo,
backend::{AudioBackend, DeviceInfoSimple},
node::StreamStatus,
processor::FirewheelProcessor,
};
use std::{
num::NonZeroU32,
sync::mpsc::{self, TryRecvError},
};
#[allow(dead_code)]
pub struct ProfilingBackend {
processor: mpsc::Sender<FirewheelProcessor<Self>>,
}
impl core::fmt::Debug for ProfilingBackend {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ProfilingBackend")
.field("processor", &())
.finish()
}
}
#[derive(Debug)]
#[allow(missing_docs)]
pub struct ProfilingError;
impl core::fmt::Display for ProfilingError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
<_ as core::fmt::Debug>::fmt(self, f)
}
}
impl std::error::Error for ProfilingError {}
impl AudioBackend for ProfilingBackend {
type Enumerator = ();
type Config = ();
type Instant = std::time::Instant;
type StartStreamError = ProfilingError;
type StreamError = ProfilingError;
fn enumerator() -> Self::Enumerator {}
fn input_devices_simple(&mut self) -> Vec<DeviceInfoSimple> {
vec![]
}
fn output_devices_simple(&mut self) -> Vec<DeviceInfoSimple> {
vec![DeviceInfoSimple {
id: "default output".into(),
name: "default output".into(),
}]
}
fn convert_simple_config(
&mut self,
_config: &firewheel::backend::SimpleStreamConfig,
) -> Self::Config {
unimplemented!()
}
fn delay_from_last_process(
&self,
process_timestamp: Self::Instant,
) -> Option<std::time::Duration> {
Some(std::time::Instant::now() - process_timestamp)
}
fn start_stream(_: Self::Config) -> Result<(Self, StreamInfo), Self::StartStreamError> {
let sample_rate = NonZeroU32::new(48000).unwrap();
let (sender, receiver) = mpsc::channel();
const BLOCK_SIZE: usize = 128;
const CHANNELS: usize = 2;
std::thread::spawn(move || {
let mut processor = None;
let block_duration = BLOCK_SIZE as f64 / sample_rate.get() as f64;
let input = [0f32; BLOCK_SIZE * CHANNELS];
let mut output = [0f32; BLOCK_SIZE * CHANNELS];
let start = std::time::Instant::now();
loop {
match &mut processor {
None => {
let new_processor: FirewheelProcessor<Self> = receiver.recv().unwrap();
processor = Some(new_processor);
}
Some(processor) => {
let now = std::time::Instant::now();
processor.process_interleaved(
&input,
&mut output,
firewheel::backend::BackendProcessInfo {
num_in_channels: CHANNELS,
num_out_channels: CHANNELS,
frames: BLOCK_SIZE,
process_timestamp: now,
duration_since_stream_start: start - now,
input_stream_status: StreamStatus::empty(),
output_stream_status: StreamStatus::empty(),
dropped_frames: 0,
},
);
std::thread::sleep(std::time::Duration::from_secs_f64(block_duration));
match receiver.try_recv() {
Ok(new_processor) => *processor = new_processor,
Err(TryRecvError::Empty) => {}
Err(TryRecvError::Disconnected) => break,
}
}
}
}
});
Ok((
Self { processor: sender },
StreamInfo {
prev_sample_rate: sample_rate,
sample_rate,
sample_rate_recip: 1.0 / sample_rate.get() as f64,
max_block_frames: NonZeroU32::new(BLOCK_SIZE as u32).unwrap(),
num_stream_in_channels: 0,
num_stream_out_channels: 2,
declick_frames: NonZeroU32::new(16).unwrap(),
input_device_id: None,
output_device_id: "default output".into(),
input_to_output_latency_seconds: 0.0,
},
))
}
fn set_processor(&mut self, processor: FirewheelProcessor<Self>) {
self.processor.send(processor).unwrap();
}
fn poll_status(&mut self) -> Result<(), Self::StreamError> {
Ok(())
}
}