f32 instead of i16
This commit is contained in:
@@ -4,48 +4,82 @@ use std::sync::Arc;
|
|||||||
use arc_swap::ArcSwap;
|
use arc_swap::ArcSwap;
|
||||||
use cpal::SampleRate;
|
use cpal::SampleRate;
|
||||||
use crate::domain::audio_client::AudioClientManager;
|
use crate::domain::audio_client::AudioClientManager;
|
||||||
|
use crate::utils::audio_utils::{AudioResampler, AudioTools};
|
||||||
use crate::utils::ringbuf::{RingBufReader, RingBufWriter, RingBuffer};
|
use crate::utils::ringbuf::{RingBufReader, RingBufWriter, RingBuffer};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AudioMixer {
|
pub struct AudioMixer {
|
||||||
audio_client_manager: AudioClientManager,
|
audio_client_manager: AudioClientManager,
|
||||||
buffer_writer: Arc<RingBufWriter<i16>>,
|
buffer_writer: Arc<RingBufWriter<f32>>,
|
||||||
buffer_reader: Arc<RingBufReader<i16>>,
|
buffer_reader: Arc<RingBufReader<f32>>,
|
||||||
sample_rate: SampleRate,
|
|
||||||
channels: usize,
|
internal_sample_rate: usize,
|
||||||
|
output_channels: usize,
|
||||||
|
output_sample_rate: usize,
|
||||||
|
resampler: AudioResampler,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AudioMixer {
|
impl AudioMixer {
|
||||||
pub fn new(sample_rate: usize, channels: usize, audio_client_manager: AudioClientManager) -> Self {
|
pub fn new(output_sample_rate: usize, output_channels: usize, audio_client_manager: AudioClientManager) -> Self {
|
||||||
let (buffer_writer, buffer_reader) = RingBuffer::new(2048).split();
|
let (buffer_writer, buffer_reader) = RingBuffer::new(2048).split();
|
||||||
Self {
|
Self {
|
||||||
audio_client_manager,
|
audio_client_manager,
|
||||||
buffer_writer: Arc::new(buffer_writer),
|
buffer_writer: Arc::new(buffer_writer),
|
||||||
buffer_reader: Arc::new(buffer_reader)
|
buffer_reader: Arc::new(buffer_reader),
|
||||||
|
internal_sample_rate: 48000,
|
||||||
|
output_sample_rate,
|
||||||
|
output_channels,
|
||||||
|
resampler: AudioResampler::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn mix_next_frame(&self, size: usize) {
|
pub fn mix_next_frame(&self, output_size: usize) {
|
||||||
let mut frames = Vec::<Vec<f32>>::new();
|
// 1. Calcule combien de samples 48kHz on doit récupérer
|
||||||
let users_audio = self.audio_client_manager.take_audio_collection(size/2).into_iter()
|
let ratio = self.internal_sample_rate as f32 / self.output_sample_rate as f32;
|
||||||
.map(|audio| AudioMixer::mono_to_stereo(audio))
|
let internal_frames_needed = ((output_size / self.output_channels) as f32 * ratio).ceil() as usize;
|
||||||
.collect::<Vec<Vec<i16>>>();
|
|
||||||
|
|
||||||
|
// 2. Récupère les données utilisateurs (48kHz mono)
|
||||||
|
let users_audio = self.audio_client_manager.take_audio_collection(internal_frames_needed);
|
||||||
|
|
||||||
frames.extend_from_slice(&users_audio);
|
// 3. Mix en 48kHz mono
|
||||||
|
let mixed_internal = if users_audio.is_empty() {
|
||||||
// Récupérer tous les sons des notifications (pas encore dev)
|
vec![0f32; internal_frames_needed]
|
||||||
|
} else {
|
||||||
let mixed_frame = if frames.is_empty() {
|
AudioTools::mix_frames(&users_audio, internal_frames_needed)
|
||||||
vec![0i16; size]
|
|
||||||
}else{
|
|
||||||
Self::mix_frames(&frames, size)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.buffer_writer.push_slice_overwrite(&mixed_frame);
|
// 3. Mix en 48kHz mono (résultat en f32)
|
||||||
|
let mixed_internal = if users_audio.is_empty() {
|
||||||
|
vec![0.0f32; internal_frames_needed]
|
||||||
|
} else {
|
||||||
|
AudioTools::mix_frames(&users_audio, internal_frames_needed)
|
||||||
|
};
|
||||||
|
|
||||||
|
// 4. Resample 48kHz -> output_rate si nécessaire
|
||||||
|
let resampled = if self.internal_sample_rate != self.output_sample_rate {
|
||||||
|
self.resampler.resample(
|
||||||
|
&mixed_internal,
|
||||||
|
self.internal_sample_rate,
|
||||||
|
self.output_sample_rate,
|
||||||
|
1 // mono
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
mixed_internal
|
||||||
|
};
|
||||||
|
|
||||||
|
// 5. Convert mono -> output_channels
|
||||||
|
let final_frame = AudioTools::change_channel_count(
|
||||||
|
&resampled,
|
||||||
|
1,
|
||||||
|
self.output_channels
|
||||||
|
);
|
||||||
|
|
||||||
|
// 6. Écrit dans le ringbuffer (pas besoin de resize exacte)
|
||||||
|
self.buffer_writer.push_slice_overwrite(&final_frame);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&self, size: usize) -> Vec<i16> {
|
pub fn read(&self, size: usize) -> Vec<f32> {
|
||||||
let mut data = vec![0i16; size];
|
let mut data = vec![0f32; size];
|
||||||
// Essaie de pop autant d'échantillons que possible
|
// Essaie de pop autant d'échantillons que possible
|
||||||
let read = self.buffer_reader.pop_slice(&mut data);
|
let read = self.buffer_reader.pop_slice(&mut data);
|
||||||
// Si on n'a pas tout eu, les éléments restants sont déjà à 0
|
// Si on n'a pas tout eu, les éléments restants sont déjà à 0
|
||||||
@@ -54,36 +88,3 @@ impl AudioMixer {
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AudioMixer {
|
|
||||||
fn mono_to_stereo(mono_samples: Vec<i16>) -> Vec<i16> {
|
|
||||||
let mut stereo_data = Vec::with_capacity(mono_samples.len() * 2);
|
|
||||||
|
|
||||||
// Chaque échantillon mono devient deux échantillons stéréo identiques
|
|
||||||
for sample in mono_samples {
|
|
||||||
stereo_data.push(sample); // Canal gauche
|
|
||||||
stereo_data.push(sample); // Canal droit
|
|
||||||
}
|
|
||||||
|
|
||||||
stereo_data
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mix_frames(frames: &[Vec<i16>], size: usize) -> Vec<i16> {
|
|
||||||
let mut mixed = vec![0i32; size];
|
|
||||||
|
|
||||||
for frame in frames {
|
|
||||||
for (i, &sample) in frame.iter().enumerate() {
|
|
||||||
if i < size {
|
|
||||||
mixed[i] += sample as i32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let count = frames.len().max(1) as i32; // éviter la division par zéro
|
|
||||||
mixed
|
|
||||||
.into_iter()
|
|
||||||
.map(|sample| (sample / count).clamp(i16::MIN as i32, i16::MAX as i32) as i16)
|
|
||||||
.collect()
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -127,7 +127,7 @@ impl Speaker {
|
|||||||
|
|
||||||
pub fn build_stream<F>(&self, callback: F) -> Stream
|
pub fn build_stream<F>(&self, callback: F) -> Stream
|
||||||
where
|
where
|
||||||
F: FnMut(&mut [i16], &cpal::OutputCallbackInfo) + Send + 'static,
|
F: FnMut(&mut [f32], &cpal::OutputCallbackInfo) + Send + 'static,
|
||||||
{
|
{
|
||||||
let config = self.get_stream_config();
|
let config = self.get_stream_config();
|
||||||
|
|
||||||
|
|||||||
@@ -77,15 +77,15 @@ pub struct AudioResampler {
|
|||||||
|
|
||||||
struct ResamplerState {
|
struct ResamplerState {
|
||||||
resampler: Option<SincFixedIn<f32>>,
|
resampler: Option<SincFixedIn<f32>>,
|
||||||
current_from_rate: u32,
|
current_from_rate: usize,
|
||||||
current_to_rate: u32,
|
current_to_rate: usize,
|
||||||
current_channels: usize,
|
current_channels: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConversionBuffers {
|
struct ConversionBuffers {
|
||||||
f32_buffer: Vec<f32>,
|
f32_buffer: Vec<f32>,
|
||||||
planar_input: Vec<Vec<f32>>,
|
planar_input: Vec<Vec<f32>>,
|
||||||
output_i16: Vec<i16>,
|
planar_output_buffer: Vec<Vec<f32>>, // Ajouté pour éviter des allocs
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AudioResampler {
|
impl AudioResampler {
|
||||||
@@ -100,19 +100,19 @@ impl AudioResampler {
|
|||||||
conversion_buffers: Arc::new(Mutex::new(ConversionBuffers {
|
conversion_buffers: Arc::new(Mutex::new(ConversionBuffers {
|
||||||
f32_buffer: Vec::with_capacity(8192),
|
f32_buffer: Vec::with_capacity(8192),
|
||||||
planar_input: Vec::new(),
|
planar_input: Vec::new(),
|
||||||
output_i16: Vec::with_capacity(8192),
|
planar_output_buffer: Vec::new(),
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resample audio en gardant la continuité entre les chunks
|
/// Resample audio générique - fonctionne avec i16 et f32
|
||||||
pub fn resample(
|
pub fn resample<T: AudioSample>(
|
||||||
&self,
|
&self,
|
||||||
input: &[i16],
|
input: &[T],
|
||||||
from_sample_rate: u32,
|
from_sample_rate: usize,
|
||||||
to_sample_rate: u32,
|
to_sample_rate: usize,
|
||||||
channels: usize,
|
channels: usize,
|
||||||
) -> Vec<i16> {
|
) -> Vec<T> {
|
||||||
// ✅ Pas de conversion si même sample rate
|
// ✅ Pas de conversion si même sample rate
|
||||||
if from_sample_rate == to_sample_rate || input.is_empty() {
|
if from_sample_rate == to_sample_rate || input.is_empty() {
|
||||||
return input.to_vec();
|
return input.to_vec();
|
||||||
@@ -122,11 +122,6 @@ impl AudioResampler {
|
|||||||
let mut buffers = self.conversion_buffers.lock();
|
let mut buffers = self.conversion_buffers.lock();
|
||||||
|
|
||||||
// 🔄 Recrée le resampler si configuration changée
|
// 🔄 Recrée le resampler si configuration changée
|
||||||
let need_new_resampler = state.resampler.is_none()
|
|
||||||
|| state.current_from_rate != from_sample_rate
|
|
||||||
|| state.current_to_rate != to_sample_rate
|
|
||||||
|| state.current_channels != channels;
|
|
||||||
|
|
||||||
if state.resampler.is_none()
|
if state.resampler.is_none()
|
||||||
|| state.current_from_rate != from_sample_rate
|
|| state.current_from_rate != from_sample_rate
|
||||||
|| state.current_to_rate != to_sample_rate
|
|| state.current_to_rate != to_sample_rate
|
||||||
@@ -149,7 +144,7 @@ impl AudioResampler {
|
|||||||
|
|
||||||
// 🚀 Processing avec le resampler
|
// 🚀 Processing avec le resampler
|
||||||
if let Some(ref mut resampler) = state.resampler {
|
if let Some(ref mut resampler) = state.resampler {
|
||||||
match Self::process_with_resampler(resampler, input, channels, &mut buffers) {
|
match Self::process_with_resampler_generic(resampler, input, channels, &mut buffers) {
|
||||||
Ok(output) => output,
|
Ok(output) => output,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("❌ Erreur resampling: {}", e);
|
eprintln!("❌ Erreur resampling: {}", e);
|
||||||
@@ -163,8 +158,8 @@ impl AudioResampler {
|
|||||||
|
|
||||||
/// Crée un resampler optimisé pour votre cas d'usage
|
/// Crée un resampler optimisé pour votre cas d'usage
|
||||||
fn create_resampler(
|
fn create_resampler(
|
||||||
from_rate: u32,
|
from_rate: usize,
|
||||||
to_rate: u32,
|
to_rate: usize,
|
||||||
channels: usize,
|
channels: usize,
|
||||||
) -> Result<SincFixedIn<f32>, Box<dyn std::error::Error>> {
|
) -> Result<SincFixedIn<f32>, Box<dyn std::error::Error>> {
|
||||||
let ratio = to_rate as f64 / from_rate as f64;
|
let ratio = to_rate as f64 / from_rate as f64;
|
||||||
@@ -190,20 +185,20 @@ impl AudioResampler {
|
|||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process audio avec buffers réutilisables
|
/// Process audio générique avec buffers réutilisables
|
||||||
fn process_with_resampler(
|
fn process_with_resampler_generic<T: AudioSample>(
|
||||||
resampler: &mut SincFixedIn<f32>,
|
resampler: &mut SincFixedIn<f32>,
|
||||||
input: &[i16],
|
input: &[T],
|
||||||
channels: usize,
|
channels: usize,
|
||||||
buffers: &mut ConversionBuffers,
|
buffers: &mut ConversionBuffers,
|
||||||
) -> Result<Vec<i16>, Box<dyn std::error::Error>> {
|
) -> Result<Vec<T>, Box<dyn std::error::Error>> {
|
||||||
let frames = input.len() / channels;
|
let frames = input.len() / channels;
|
||||||
|
|
||||||
// 🔄 1. Conversion i16 → f32 (réutilise buffer)
|
// 🔄 1. Conversion vers f32 (utilise AudioSample trait)
|
||||||
buffers.f32_buffer.clear();
|
buffers.f32_buffer.clear();
|
||||||
buffers.f32_buffer.extend(input.iter().map(|&s| s as f32 / 32768.0));
|
buffers.f32_buffer.extend(input.iter().map(|sample| sample.to_f32()));
|
||||||
|
|
||||||
// 🔄 2. Conversion interleaved → planar (réutilise buffers)
|
// 🔄 2. Conversion interleaved → planar
|
||||||
buffers.planar_input.clear();
|
buffers.planar_input.clear();
|
||||||
buffers.planar_input.resize(channels, Vec::with_capacity(frames));
|
buffers.planar_input.resize(channels, Vec::with_capacity(frames));
|
||||||
|
|
||||||
@@ -211,30 +206,117 @@ impl AudioResampler {
|
|||||||
buffers.planar_input[ch].clear();
|
buffers.planar_input[ch].clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (frame_idx, frame) in buffers.f32_buffer.chunks_exact(channels).enumerate() {
|
for frame in buffers.f32_buffer.chunks_exact(channels) {
|
||||||
for (ch, &sample) in frame.iter().enumerate() {
|
for (ch, &sample) in frame.iter().enumerate() {
|
||||||
buffers.planar_input[ch].push(sample);
|
buffers.planar_input[ch].push(sample);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🎯 3. Resampling magique !
|
// 🎯 3. Resampling magique ! (Rubato travaille directement en f32)
|
||||||
let output_planar = resampler.process(&buffers.planar_input, None)?;
|
let output_planar = resampler.process(&buffers.planar_input, None)?;
|
||||||
|
|
||||||
// 🔄 4. Conversion planar → interleaved i16 (réutilise buffer)
|
// 🔄 4. Conversion planar → interleaved avec type générique
|
||||||
let output_frames = output_planar[0].len();
|
let output_frames = output_planar[0].len();
|
||||||
buffers.output_i16.clear();
|
let mut output = Vec::with_capacity(output_frames * channels);
|
||||||
buffers.output_i16.reserve(output_frames * channels);
|
|
||||||
|
|
||||||
for frame_idx in 0..output_frames {
|
for frame_idx in 0..output_frames {
|
||||||
for ch in 0..channels {
|
for ch in 0..channels {
|
||||||
let sample = (output_planar[ch][frame_idx] * 32767.0)
|
let f32_sample = output_planar[ch][frame_idx];
|
||||||
.round()
|
// Utilise AudioSample pour reconvertir au format d'origine
|
||||||
.clamp(-32768.0, 32767.0) as i16;
|
let converted_sample = T::from_f32(f32_sample).clamp_audio();
|
||||||
buffers.output_i16.push(sample);
|
output.push(converted_sample);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(buffers.output_i16.clone())
|
Ok(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Version spécialisée pour f32 (plus efficace, évite conversions inutiles)
|
||||||
|
pub fn resample_f32(
|
||||||
|
&self,
|
||||||
|
input: &[f32],
|
||||||
|
from_sample_rate: usize,
|
||||||
|
to_sample_rate: usize,
|
||||||
|
channels: usize,
|
||||||
|
) -> Vec<f32> {
|
||||||
|
// ✅ Pas de conversion si même sample rate
|
||||||
|
if from_sample_rate == to_sample_rate || input.is_empty() {
|
||||||
|
return input.to_vec();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut state = self.state.lock();
|
||||||
|
let mut buffers = self.conversion_buffers.lock();
|
||||||
|
|
||||||
|
// 🔄 Recrée le resampler si configuration changée
|
||||||
|
if state.resampler.is_none()
|
||||||
|
|| state.current_from_rate != from_sample_rate
|
||||||
|
|| state.current_to_rate != to_sample_rate
|
||||||
|
|| state.current_channels != channels {
|
||||||
|
match Self::create_resampler(from_sample_rate, to_sample_rate, channels) {
|
||||||
|
Ok(new_resampler) => {
|
||||||
|
state.resampler = Some(new_resampler);
|
||||||
|
state.current_from_rate = from_sample_rate;
|
||||||
|
state.current_to_rate = to_sample_rate;
|
||||||
|
state.current_channels = channels;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("❌ Erreur création resampler: {}", e);
|
||||||
|
return input.to_vec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🚀 Processing optimisé pour f32 (pas de conversion)
|
||||||
|
if let Some(ref mut resampler) = state.resampler {
|
||||||
|
match Self::process_f32_direct(resampler, input, channels, &mut buffers) {
|
||||||
|
Ok(output) => output,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("❌ Erreur resampling f32: {}", e);
|
||||||
|
input.to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
input.to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Processing optimisé pour f32 natif (pas de conversions)
|
||||||
|
fn process_f32_direct(
|
||||||
|
resampler: &mut SincFixedIn<f32>,
|
||||||
|
input: &[f32],
|
||||||
|
channels: usize,
|
||||||
|
buffers: &mut ConversionBuffers,
|
||||||
|
) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
|
||||||
|
let frames = input.len() / channels;
|
||||||
|
|
||||||
|
// 🔄 1. Conversion interleaved → planar (pas de conversion de format!)
|
||||||
|
buffers.planar_input.clear();
|
||||||
|
buffers.planar_input.resize(channels, Vec::with_capacity(frames));
|
||||||
|
|
||||||
|
for ch in 0..channels {
|
||||||
|
buffers.planar_input[ch].clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for frame in input.chunks_exact(channels) {
|
||||||
|
for (ch, &sample) in frame.iter().enumerate() {
|
||||||
|
buffers.planar_input[ch].push(sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🎯 2. Resampling direct !
|
||||||
|
let output_planar = resampler.process(&buffers.planar_input, None)?;
|
||||||
|
|
||||||
|
// 🔄 3. Conversion planar → interleaved (pas de conversion de format!)
|
||||||
|
let output_frames = output_planar[0].len();
|
||||||
|
let mut output = Vec::with_capacity(output_frames * channels);
|
||||||
|
|
||||||
|
for frame_idx in 0..output_frames {
|
||||||
|
for ch in 0..channels {
|
||||||
|
output.push(output_planar[ch][frame_idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Réinitialise l'état interne (pour éviter glitches lors de changements)
|
/// Réinitialise l'état interne (pour éviter glitches lors de changements)
|
||||||
@@ -246,13 +328,13 @@ impl AudioResampler {
|
|||||||
println!("🔄 Resampler reset");
|
println!("🔄 Resampler reset");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Version oneshot sans état (pour tests)
|
/// Version oneshot générique sans état (pour tests)
|
||||||
pub fn resample_oneshot(
|
pub fn resample_oneshot<T: AudioSample>(
|
||||||
input: &[i16],
|
input: &[T],
|
||||||
from_rate: u32,
|
from_rate: usize,
|
||||||
to_rate: u32,
|
to_rate: usize,
|
||||||
channels: usize,
|
channels: usize,
|
||||||
) -> Result<Vec<i16>, Box<dyn std::error::Error>> {
|
) -> Result<Vec<T>, Box<dyn std::error::Error>> {
|
||||||
let resampler = AudioResampler::new();
|
let resampler = AudioResampler::new();
|
||||||
Ok(resampler.resample(input, from_rate, to_rate, channels))
|
Ok(resampler.resample(input, from_rate, to_rate, channels))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user