/
magnusroot
/
nm
Обзор
Документация
Войти
/
magnusroot
/
nm
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
crates/player/src/resample.rs
85 строк
4 KB
Magnus Root
First version
13 июл 2026, 09:24
13 июл 2026, 09:24
50f8b8c
Код
Авторство
О чём код?
//! Изменение скорости воспроизведения через ресемплинг: чтобы играть быстрее, //! мы "читаем" сэмплы как будто исходная частота дискретизации выше (и наоборот). //! Это простой подход "как на магнитофоне" — питч меняется вместе со скоростью. //! Более сложный time-stretch с сохранением питча можно добавить позже отдельным режимом. use rubato::{SincFixedIn, SincInterpolationParameters, SincInterpolationType, VecResampler, WindowFunction}; pub struct SpeedResampler { channels: usize, resampler: SincFixedIn<f32>, chunk_size: usize, input_buf: Vec<Vec<f32>>, } impl SpeedResampler { /// device_rate / track_rate — базовое соотношение без учёта скорости. pub fn new(channels: usize, base_ratio: f64, speed: f32) -> anyhow::Result<Self> { let chunk_size = 1024; let params = SincInterpolationParameters { sinc_len: 128, f_cutoff: 0.95, interpolation: SincInterpolationType::Linear, oversampling_factor: 128, window: WindowFunction::BlackmanHarris2, }; let ratio = base_ratio / speed as f64; let resampler = SincFixedIn::<f32>::new(ratio, 2.0, params, chunk_size, channels)?; Ok(SpeedResampler { channels, resampler, chunk_size, input_buf: vec![Vec::new(); channels], }) } /// Принимает interleaved f32, копит до chunk_size сэмплов на канал и прогоняет через /// ресемплер столько полных чанков, сколько накопилось. Возвращает готовый interleaved /// блок (может быть пустым, если данных пока не хватает на целый чанк). pub fn push_interleaved(&mut self, interleaved: &[f32]) -> anyhow::Result<Vec<f32>> { for frame in interleaved.chunks(self.channels) { for (ch, &s) in frame.iter().enumerate() { self.input_buf[ch].push(s); } } let mut interleaved_out = Vec::new(); while self.input_buf[0].len() >= self.chunk_size { let chunk: Vec<Vec<f32>> = self .input_buf .iter_mut() .map(|ch| ch.drain(0..self.chunk_size).collect()) .collect(); let out = self.resampler.process(&chunk, None)?; let frames = out[0].len(); for i in 0..frames { for ch in 0..self.channels { interleaved_out.push(out[ch][i]); } } } Ok(interleaved_out) } /// Дренирует остаток входного буфера (например, при завершении трека), дополняя нулями /// до полного чанка, чтобы не потерять хвост. pub fn flush(&mut self) -> anyhow::Result<Vec<f32>> { if self.input_buf[0].is_empty() { return Ok(Vec::new()); } let pad = self.chunk_size - self.input_buf[0].len(); for ch in self.input_buf.iter_mut() { ch.extend(std::iter::repeat(0.0f32).take(pad)); } let chunk: Vec<Vec<f32>> = self.input_buf.iter_mut().map(std::mem::take).collect(); let out = self.resampler.process(&chunk, None)?; let mut interleaved_out = Vec::new(); let frames = out[0].len(); for i in 0..frames { for ch in 0..self.channels { interleaved_out.push(out[ch][i]); } } Ok(interleaved_out) } }