Skip to content

Commit

Permalink
feat: input capture example
Browse files Browse the repository at this point in the history
  • Loading branch information
SolarLiner committed Jul 27, 2024
1 parent 1db34be commit ac0121b
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 13 deletions.
145 changes: 145 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ thiserror = "1.0.63"

[dev-dependencies]
anyhow = "1.0.86"
indicatif = "0.17.8"

[build-dependencies]
cfg_aliases = "0.2.1"
Expand Down
82 changes: 82 additions & 0 deletions examples/input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use anyhow::Result;
use indicatif::{ProgressBar, ProgressStyle};

use interflow::prelude::*;
use interflow::timestamp::Timestamp;

fn main() -> Result<()> {
let device = default_input_device();
let config = StreamConfig {
samplerate: 48000.,
channels: 0b1,
buffer_size_range: (Some(16384), None),
};
assert!(device.is_config_supported(&config));
let stream = device
.create_input_stream(config, RmsMeter::default())
.unwrap();
println!("Press Enter to stop");
std::io::stdin().read_line(&mut String::new()).unwrap();
let meter = stream.eject().unwrap();
meter.progress.finish();
Ok(())
}

struct RmsMeter {
progress: ProgressBar,
last_out: f32,
last_show: f64,
}

impl Default for RmsMeter {
fn default() -> Self {
let progress = ProgressBar::new(100);
progress.set_style(
ProgressStyle::default_bar()
.template("{bar:40.green} {msg}")
.unwrap(),
);
Self {
progress,
last_out: 0.,
last_show: f64::NEG_INFINITY,
}
}
}

impl AudioInputCallback for RmsMeter {
fn on_input_data(&mut self, context: AudioCallbackContext, input: AudioInput<f32>) {
let buffer_duration =
Timestamp::from_count(input.timestamp.samplerate, input.buffer.num_samples() as _);
let peak_lin = input
.buffer
.channels()
.flat_map(|ch| ch.iter().copied().max_by(f32::total_cmp))
.max_by(f32::total_cmp)
.unwrap_or(0.);
let rms_lin =
peak_lin.max(self.last_out * f32::exp(-15. * buffer_duration.as_seconds() as f32));
self.last_out = rms_lin;

let time = context.timestamp.as_seconds();
if time > self.last_show + 50e-3 {
let peak_db = 20. * rms_lin.log10();
let pc = normalize(-60., 6., peak_db);
let pos = if let Some(len) = self.progress.length() {
pc * len as f32
} else {
self.progress.set_length(100);
100. * pc
};
self.progress.set_position(pos as _);
self.progress
.set_message(format!("Peak: {peak_db:2.1} dB | Runtime: {time:2.3} s"));
self.last_show = time;
}
}
}

fn normalize(min: f32, max: f32, value: f32) -> f32 {
let range = max - min;
(value - min) / range
}
12 changes: 1 addition & 11 deletions examples/sine_wave.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
use std::f32::consts::TAU;
use std::io::Read;

use anyhow::Result;

use interflow::{
AudioCallbackContext,
AudioDevice,
AudioOutput,
AudioOutputCallback,
AudioStreamHandle,
StreamConfig,
AudioOutputDevice,
backends::default_output_device
};
use interflow::prelude::*;

fn main() -> Result<()> {
let device = default_output_device();
Expand Down
22 changes: 21 additions & 1 deletion src/backends/alsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ impl<Callback: 'static + Send + AudioInputCallback> AlsaStream<Callback> {
let (hwp, _, io) = device.apply_config(&stream_config)?;
let (_, period_size) = device.pcm.get_params()?;
let period_size = period_size as usize;
eprintln!("Period size: {period_size}");
let num_channels = hwp.get_channels()? as usize;
let samplerate = hwp.get_rate()? as f64;
let stream_config = StreamConfig {
Expand All @@ -213,13 +214,20 @@ impl<Callback: 'static + Send + AudioInputCallback> AlsaStream<Callback> {
};
let mut timestamp = Timestamp::new(samplerate);
let mut buffer = vec![0f32; period_size * num_channels];
device.pcm.prepare()?;
if device.pcm.state() != pcm::State::Running {
device.pcm.start()?;
}
let _try = || loop {
if eject_signal.load(Ordering::Relaxed) {
break Ok(callback);
}
let frames = device.pcm.avail_update()? as usize;
let len = frames * num_channels;
io.readi(&mut buffer[..len])?;
match io.readi(&mut buffer[..len]) {
Err(err) => device.pcm.try_recover(err, true)?,
_ => {}
}
let buffer = AudioRef::from_interleaved(&buffer[..len], num_channels).unwrap();
let context = AudioCallbackContext {
stream_config,
Expand All @@ -228,6 +236,18 @@ impl<Callback: 'static + Send + AudioInputCallback> AlsaStream<Callback> {
let input = AudioInput { buffer, timestamp };
callback.on_input_data(context, input);
timestamp += frames as u64;

match device.pcm.state() {
pcm::State::Suspended => {
if hwp.can_resume() {
device.pcm.resume()?;
} else {
device.pcm.prepare()?;
}
}
pcm::State::Paused => std::thread::sleep(Duration::from_secs(1)),
_ => {}
}
};
_try()
}
Expand Down
7 changes: 6 additions & 1 deletion src/backends/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ where
.clone()
}

pub fn default_input_device() -> impl AudioInputDevice {
#[cfg(os_alsa)]
default_input_device_from(&alsa::AlsaDriver)
}

pub fn default_output_device_from<Driver: AudioDriver>(driver: &Driver) -> Driver::Device
where
Driver::Device: Clone + AudioOutputDevice,
Expand All @@ -36,4 +41,4 @@ where
pub fn default_output_device() -> impl AudioOutputDevice {
#[cfg(os_alsa)]
default_output_device_from(&alsa::AlsaDriver)
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod audio_buffer;
pub mod backends;
pub mod channel_map;
pub mod timestamp;
pub mod prelude;

/// Audio drivers provide access to the inputs and outputs of physical devices.
/// Several drivers might provide the same accesses, some sharing it with other applications,
Expand Down
2 changes: 2 additions & 0 deletions src/prelude.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub use crate::*;
pub use crate::backends::*;

0 comments on commit ac0121b

Please sign in to comment.