-
Notifications
You must be signed in to change notification settings - Fork 476
Description
I'm facing a very upsetting bug. I want to play some data coming from some audio file. In my PC, it works fine, but in Raspberry Pi the data callback is fired only once, and never again. No error is shout at any point.
I've assembled a minimal reproducible example:
Example
use std::{io, sync::{Arc, Mutex}};
use cpal::{Device, OutputCallbackInfo, Stream, StreamConfig};
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use hound::WavReader;
fn main() {
let host = cpal::default_host();
let device = host.default_input_device().unwrap();
let mut stream = None;
let stdin = io::stdin();
let mut buffer = String::new();
while let Ok(_) = stdin.read_line(&mut buffer) {
if stream.is_some() {
stream = None;
} else {
stream = Some(create_stream(&device));
}
}
}
fn create_stream(device: &Device) -> Stream {
println!("loading audio data");
let reader = WavReader::open("fur-elise.wav").unwrap();
let sample_rate = reader.spec().sample_rate;
let audio_data: Vec<i16> = reader.into_samples::<i16>()
.map(|sample| sample.unwrap())
.collect();
println!("loaded audio data");
let stream_config = StreamConfig {
channels: 1,
sample_rate,
buffer_size: cpal::BufferSize::Default
};
// I know I don't need a mutex right here, but in my app I allow users to control the playback
// time, so I use a mutex to grant access to the playback stage to other parts of the module.
let current_frame_mutex1 = Arc::new(Mutex::new(0));
let current_frame_mutex2 = Arc::clone(¤t_frame_mutex1);
let data_callback = move |out_data: &mut [i16], _info: &OutputCallbackInfo| {
println!("Callback start");
let mut current_frame = current_frame_mutex2.lock().unwrap();
let end_of_slice = *current_frame + out_data.len();
if end_of_slice < audio_data.len() - 1 {
out_data.copy_from_slice(&audio_data[*current_frame..end_of_slice]);
*current_frame = end_of_slice;
}
// There's not enough audio data to fill the entire buffer, so we fill what we have and fill
// the rest with zeros.
else {
println!("No more data to play");
let remaining_data_len = audio_data.len() - *current_frame;
out_data[..remaining_data_len].copy_from_slice(&audio_data[*current_frame..]);
out_data[remaining_data_len..].fill(0);
}
println!("Callback finished");
};
let error_callback = |error: cpal::StreamError| eprintln!("stream error: {error:?}");
let stream = device.build_output_stream(&stream_config, data_callback, error_callback, None)
.unwrap();
stream.play().unwrap();
stream
}You just need to add cpal and hound packages and place the following file in the same path as the binary. (Alternatively, provide any WAV with i16 samples and mono channel).
My Raspberry is a Raspberry OS bookworm with ALSA 1.2.4. The OS actually came with ALSA 1.2.8 originally, but I've already installed another version to check if that was the problem.
Weirdly, in my app, some times the stream actually starts and keeps playing until it ends. But after like 3 our 4 attempts, it bugs like that. About the mutex that I commented above, I've tried to set a local variable to be captured by the callback as well, and nothing changed.