Skip to content

Commit fd5280b

Browse files
committed
fix: support playback at other sample rates
1 parent 16c455f commit fd5280b

1 file changed

Lines changed: 53 additions & 12 deletions

File tree

‎player/src/audio.rs‎

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use cpal::Stream;
77
use opus::Decoder;
88
use ringbuf::ring_buffer::RbBase;
99
use ringbuf::{LocalRb, Rb};
10+
use rubato::{InterpolationParameters, Resampler, SincFixedIn};
1011
use tokio::sync::mpsc;
1112
use tokio::time::{Instant, Interval};
1213

@@ -21,6 +22,7 @@ type Ringbuf<T> = LocalRb<T, Vec<MaybeUninit<T>>>;
2122

2223
pub struct Player {
2324
decoder: Decoder,
25+
resampler: Option<SincFixedIn<f32>>,
2426
buffer: Arc<Mutex<Ringbuf<f32>>>,
2527
stream: Option<Stream>,
2628
volume: Arc<Mutex<f32>>,
@@ -30,6 +32,7 @@ impl Player {
3032
pub fn new() -> Self {
3133
Self {
3234
decoder: Decoder::new(48000, opus::Channels::Stereo).unwrap(),
35+
resampler: None,
3336
buffer: Arc::new(Mutex::new(Ringbuf::<f32>::new(PLAYER_BUFFER_SIZE))),
3437
stream: None,
3538
last_frame: None,
@@ -49,8 +52,24 @@ impl Player {
4952

5053
let mut buffer = self.buffer.lock().unwrap();
5154
if buffer.len() < buffer.free_len() {
52-
// copy to ringbuffer
53-
buffer.push_slice(&pcm);
55+
if let Some(resampler) = &mut self.resampler {
56+
// resample first
57+
let mut chunk_samples: Vec<Vec<f32>> = vec![Vec::with_capacity(480); 2];
58+
for (sample_idx, sample) in pcm.into_iter().enumerate() {
59+
chunk_samples[sample_idx % 2].push(sample);
60+
}
61+
let resampled = resampler.process(&chunk_samples, None).unwrap();
62+
let flat = resampled[0]
63+
.iter()
64+
.copied()
65+
.zip(resampled[1].iter().copied())
66+
.flat_map(|(a, b)| [a, b])
67+
.collect::<Vec<_>>();
68+
buffer.push_slice(&flat);
69+
} else {
70+
// copy decoded packet directly to ringbuffer
71+
buffer.push_slice(&pcm);
72+
}
5473
} else {
5574
// otherwise just discard i guess
5675
println!("player buffer full... discarding :/")
@@ -66,19 +85,41 @@ impl Player {
6685
.default_output_device()
6786
.expect("no output device available");
6887

69-
let mut supported_configs_range = device
70-
.supported_output_configs()
71-
.expect("error while querying configs");
72-
let supported_config = supported_configs_range
73-
.next()
74-
.expect("no supported config?!")
75-
.with_sample_rate(cpal::SampleRate(48000));
76-
// .with_max_sample_rate(); //???
88+
let stream_config = {
89+
let mut configs = device
90+
.supported_output_configs()
91+
.expect("error while querying configs");
92+
93+
let range = configs.next().expect("no supported config?!");
94+
95+
if range.min_sample_rate().0 <= 48000 && range.max_sample_rate().0 >= 48000 {
96+
range.with_sample_rate(cpal::SampleRate(48000))
97+
} else {
98+
range.with_max_sample_rate()
99+
}
100+
};
101+
102+
let sample_rate = stream_config.sample_rate().0;
103+
104+
if stream_config.sample_rate().0 != 48000 {
105+
let params = InterpolationParameters {
106+
sinc_len: 256,
107+
f_cutoff: 0.95,
108+
interpolation: rubato::InterpolationType::Linear,
109+
oversampling_factor: 256,
110+
window: rubato::WindowFunction::BlackmanHarris2,
111+
};
112+
self.resampler = Some(
113+
SincFixedIn::new(sample_rate as f64 / 48000_f64, 1.0, params, 480, 2).unwrap(),
114+
);
115+
} else {
116+
self.resampler = None;
117+
}
77118

78119
let err_fn = |err| eprintln!("an error occurred on the output audio stream: {}", err);
79-
let sample_format = supported_config.sample_format();
120+
let sample_format = stream_config.sample_format();
80121
println!("sample format is {:?}", sample_format); //?? do we care about other sample formats
81-
let config = supported_config.into();
122+
let config = stream_config.into();
82123

83124
let buffer_handle = self.buffer.clone();
84125
let volume_handle = self.volume.clone();

0 commit comments

Comments
 (0)