use std::sync::{atomic, Arc}; use std::sync::atomic::{AtomicBool, AtomicUsize}; use bytes::Bytes; use tokio::sync::{mpsc, Notify}; use crate::utils::ringbuf::{RingBufReader, RingBufWriter, RingBuffer}; pub struct Mixer { // writer: Arc>, // reader: Arc>, pre_buffer: Arc, worker_sender: Option>>, buffer: Bytes // 1920 frames } impl Mixer { pub fn new() -> Self { let (writer, reader) = RingBuffer::::new(1024).split(); Self { // writer: Arc::new(writer), // reader: Arc::new(reader), pre_buffer: Arc::new(PreBuffer::new()), worker_sender: None, } } // Démarrer le worker de pré-traitement pub async fn start(&mut self){ let (sender, mut receiver) = mpsc::unbounded_channel::>(); // let (writer, reader) = RingBuffer::::new(1024).split(); self.worker_sender = Some(sender); let prebuffer = self.pre_buffer.clone(); // worker de pré-traitement tokio::spawn(async move { while let Some(data) = receiver.recv().await { // data doit exactement faire 960 (mono) ou 1920 (stéréo), 20ms // si il fait 960, on converti en stéréo // on écrit dans un buffer de pré-traitement // si data rempli pas les condition, on ignore // on vérifie la taille des données match data.len() { 960 => { // Mono 20ms @ 48kHz - convertir en stéréo let stereo_data = Self::mono_to_stereo(data); // push dans un buffer de pré-traitement prebuffer.push(stereo_data).await; }, 1920 => { // push dans un buffer de pré-traitement prebuffer.push(data).await; } _ => { println!("⚠️ Données audio ignorées - taille incorrecte: {} bytes", data.len()); } } } }); } // Envoyer des données au pré-traitement pub async fn write(&self, data: Vec){ if let Some(sender) = self.worker_sender.as_ref() { let _ = sender.send(data); } } // S'occupe de générer la trame qui sera lu dans 20ms pub async fn mix(&self){ // récupérer le buffer de pré-traitement, qui sera tableau de Vec (le lock ? pour éviter que le worker de pré-traitement continue d'alimenter) // le mixer (sum de chaque trame ?) // le mettre dans un buffer, qui sera accessible par AudioPlayback // écraser le buffer de pré-traitement // (libérer le lock) let frames = self.pre_buffer.read_all().await; let mixed_frame = if frames.is_empty() { [0i16; 1920] } else { Self::mix_frames(&frames) }; } // Récupérer la trame présente qui est déjà pré-généré par mix pub async fn read(&self) -> [i16; 1920]{ let buffer = self.buffer; // vider le buffer self.buffer = buffer } } impl Mixer { // Functions helpers fn mono_to_stereo(mono_samples: Vec) -> Vec { 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 } // Mixer plusieurs trames fn mix_frames(frames: &[Vec]) -> [i16; 1920] { let mut mixed = [0i32; 1920]; for frame in frames { for (i, &sample) in frame.iter().enumerate() { if i < 1920 { mixed[i] += sample as i32; } } } let mut result = [0i16; 1920]; let count = frames.len() as i32; for (i, &sample) in mixed.iter().enumerate() { result[i] = (sample / count).clamp(i16::MIN as i32, i16::MAX as i32) as i16; } result } } /// Pre buffer #[derive(Clone)] struct PreBuffer { sender: Arc>>, receiver: Arc>>, is_being_read: Arc, read_done_notify: Arc, } impl PreBuffer { fn new() -> Self { let (sender, reader) = kanal::unbounded_async::>(); Self { sender: Arc::new(sender), receiver: Arc::new(reader), is_being_read: Arc::new(AtomicBool::new(false)), read_done_notify: Arc::new(Notify::new()), } } async fn push(&self, frame: Vec) { if self.is_being_read.load(atomic::Ordering::Acquire) { self.read_done_notify.notified().await; } let _ = self.sender.send(frame); } async fn read_all(&self) -> Vec> { self.is_being_read.store(true, atomic::Ordering::Release); let mut frames = Vec::new(); while let Ok(frame) = self.receiver.recv().await { frames.push(frame); } // Libérer et notifier les writers en attente self.is_being_read.store(false, atomic::Ordering::Release); self.read_done_notify.notify_waiters(); frames } } // struct PreBuffer { // // Vec dynamique pour stocker les trames // frames: Vec>, // // Compteur atomique pour le nombre de trames disponibles // frame_count: AtomicUsize, // // Flag atomique pour indiquer si le buffer est en cours de lecture // is_being_read: AtomicBool, // read_done_notify: Arc, // } // // impl PreBuffer { // fn new() -> Self { // Self { // frames: Vec::new(), // frame_count: AtomicUsize::new(0), // is_being_read: AtomicBool::new(false), // read_done_notify: Arc::new(Notify::new()), // } // } // // async fn push(&mut self, frame: Vec) { // if self.is_being_read.load(atomic::Ordering::Acquire) { // self.read_done_notify.notified().await; // } // // self.frames.push(frame); // self.frame_count.fetch_add(1, atomic::Ordering::Release); // } // // fn reset(&mut self) { // self.frame_count.store(0, atomic::Ordering::Release); // } // // fn len(&self) -> usize { // self.frame_count.load(atomic::Ordering::Acquire) // } // // fn read_all(&mut self) -> Vec> { // self.is_being_read.store(true, atomic::Ordering::Release); // // let frames = std::mem::take(&mut self.frames); // self.frame_count.store(0, atomic::Ordering::Release); // // // Libérer et notifier les writers en attente // self.is_being_read.store(false, atomic::Ordering::Release); // self.read_done_notify.notify_waiters(); // // frames // } // } //