I’m trying to mix two streams with cpal and write them to a single wav file
The first one is the default Mic, and the second is the Speakers output (It works in cpal on Windows we can use it as input)
The final wav file seems to be invalid, as the length of it isn’t 5 seconds.
How can I mix these two streams and write them in realtime?
use std::time::Duration;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use ringbuf::HeapRb;
fn main() -> anyhow::Result<()> {
let host = cpal::default_host();
// Find devices.
let input_device = host.default_input_device().unwrap();
let input_device1 = host.default_output_device().unwrap();
println!("Using input device: "{}"", input_device.name()?);
println!("Using input1 device: "{}"", input_device1.name()?);
// We'll try and use the same configuration between streams to keep it simple.
let config: cpal::StreamConfig = input_device.default_input_config()?.into();
let config1: cpal::StreamConfig = input_device1.default_output_config()?.into();
// Create a delay in case the input and output devices aren't synced.
let latency = 150.0;
let latency_frames = (latency / 1_000.0) * config.sample_rate.0 as f32;
let latency_samples = latency_frames as usize * config.channels as usize;
// The buffer to share samples
let ring = HeapRb::<f32>::new(latency_samples * 2);
let ring1 = HeapRb::<f32>::new(latency_samples * 2);
let (mut producer, mut consumer) = ring.split();
let (mut producer1, mut consumer1) = ring1.split();
// Fill the samples with 0.0 equal to the length of the delay.
for _ in 0..latency_samples {
// The ring buffer has twice as much space as necessary to add latency here,
// so this should never fail
producer.push(0.0).unwrap();
}
let input_data_fn = move |data: &[f32], _: &cpal::InputCallbackInfo| {
let mut output_fell_behind = false;
for &sample in data {
if producer.push(sample).is_err() {
output_fell_behind = true;
}
}
if output_fell_behind {
eprintln!("output stream fell behind: try increasing latency");
}
};
let input_data_fn1 = move |data: &[f32], _: &cpal::InputCallbackInfo| {
let mut output_fell_behind1 = false;
for &sample in data {
if producer1.push(sample).is_err() {
output_fell_behind1 = true;
}
}
if output_fell_behind1 {
eprintln!("output stream fell behind: try increasing latency");
}
};
println!(
"Attempting to build both streams with f32 samples and `{:?}`.",
config
);
let input_stream = input_device.build_input_stream(&config, input_data_fn, err_fn, None)?;
let input_stream1 = input_device1.build_input_stream(&config, input_data_fn1, err_fn, None)?;
// let output_stream = output_device.build_output_stream(&config, output_data_fn, err_fn, None)?;
println!("Successfully built streams.");
// Play the streams.
println!(
"Starting the input and output streams with `{}` milliseconds of latency.",
latency
);
input_stream.play()?;
input_stream1.play()?;
// Create WAV writer
let spec = hound::WavSpec {
channels: 1,
sample_rate: config.sample_rate.0,
bits_per_sample: 32,
sample_format: hound::SampleFormat::Float,
};
let mut writer = hound::WavWriter::create("output.wav", spec)?;
let duration = Duration::from_secs(5);
let start_time = std::time::Instant::now();
while start_time.elapsed() < duration {
// Consume samples from both streams
let sample = consumer.pop();
let sample1 = consumer1.pop();
// Combine samples from both buffers
let combined_sample = match (sample, sample1) {
(Some(s), Some(s1)) => (s + s1) / 2.0,
(Some(s), None) => s,
(None, Some(s1)) => s1,
(None, None) => continue,
};
// Write combined sample to WAV file
println!("writing sample {:?}", combined_sample);
writer.write_sample(combined_sample)?;
}
// Close the WAV writer
writer.finalize()?;
println!("WAV file finalized successfully.");
println!("Done!");
drop(input_stream);
drop(input_stream1);
println!("Done!");
Ok(())
}
fn err_fn(err: cpal::StreamError) {
eprintln!("an error occurred on stream: {}", err);
}