From 6f8a80a58ab92fbfb206f75dd0fc276f014ea617 Mon Sep 17 00:00:00 2001 From: Eric Coissac Date: Thu, 26 Feb 2026 20:18:14 +0100 Subject: [PATCH] =?UTF-8?q?Refactorisation=20des=20n=C5=93uds=20audio=20po?= =?UTF-8?q?ur=20utiliser=20boxed()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cette mise à jour refactorise les nœuds audio pour utiliser la méthode `boxed()` lors de l'enregistrement des enfants, améliorant ainsi la cohérence et la lisibilité du code. Les méthodes `make()` sont ajoutées pour faciliter la création d'instances boxées des nœuds, et les exemples sont mis à jour en conséquence. --- pmoaudio/examples/convert_to_flac24.rs | 4 +- pmoaudio/examples/file_nodes_test.rs | 2 +- pmoaudio/examples/play_audio.rs | 2 +- pmoaudio/examples/play_with_resampling.rs | 24 ++--- pmoaudio/src/nodes/audio_sink.rs | 12 ++- pmoaudio/src/nodes/converter_nodes.rs | 121 ++++++++++++++++------ pmoaudio/src/nodes/file_source.rs | 13 ++- pmoaudio/src/nodes/flac_file_sink.rs | 16 ++- pmoaudio/src/nodes/http_source.rs | 13 ++- pmoaudio/src/nodes/resampling_node.rs | 13 ++- pmoaudio/src/nodes/timer_buffer_node.rs | 13 ++- pmoaudio/src/nodes/timer_node.rs | 13 ++- pmoaudio/src/pipeline.rs | 8 ++ pmoparadise/examples/download_block.rs | 2 +- pmoparadise/examples/play_and_cache.rs | 6 +- pmowebrenderer/src/pipeline.rs | 10 +- 16 files changed, 180 insertions(+), 92 deletions(-) diff --git a/pmoaudio/examples/convert_to_flac24.rs b/pmoaudio/examples/convert_to_flac24.rs index b27b7bb0..acb174cc 100755 --- a/pmoaudio/examples/convert_to_flac24.rs +++ b/pmoaudio/examples/convert_to_flac24.rs @@ -70,8 +70,8 @@ async fn main() -> Result<(), Box> { let sink = FlacFileSink::new(output_path); // Construire la chaîne: source → converter → sink - converter.register(Box::new(sink)); - source.register(converter); + converter.register(sink.boxed()); + source.register(converter.boxed()); // Créer un token d'arrêt pour contrôle manuel si besoin let stop_token = CancellationToken::new(); diff --git a/pmoaudio/examples/file_nodes_test.rs b/pmoaudio/examples/file_nodes_test.rs index e40f1d5e..758d2b6b 100755 --- a/pmoaudio/examples/file_nodes_test.rs +++ b/pmoaudio/examples/file_nodes_test.rs @@ -38,7 +38,7 @@ async fn main() -> Result<(), Box> { let sink = FlacFileSink::new(output_path); // Enregistrer le sink comme enfant de la source - source.register(Box::new(sink)); + source.register(sink.boxed()); // Créer un token d'arrêt pour contrôle manuel si besoin let stop_token = CancellationToken::new(); diff --git a/pmoaudio/examples/play_audio.rs b/pmoaudio/examples/play_audio.rs index 07f2bfbf..c3ed80e0 100644 --- a/pmoaudio/examples/play_audio.rs +++ b/pmoaudio/examples/play_audio.rs @@ -34,7 +34,7 @@ async fn main() -> Result<(), Box> { let sink = AudioSink::new(); // Connecter la source au sink - source.register(Box::new(sink)); + source.register(sink.boxed()); println!("Démarrage de la lecture..."); println!("Appuyez sur Ctrl+C pour arrêter"); diff --git a/pmoaudio/examples/play_with_resampling.rs b/pmoaudio/examples/play_with_resampling.rs index 2b856672..3bac8c0b 100644 --- a/pmoaudio/examples/play_with_resampling.rs +++ b/pmoaudio/examples/play_with_resampling.rs @@ -35,22 +35,16 @@ async fn main() -> Result<(), Box> { println!("Lecture de: {}", file_path); println!("Sample rate cible: {} Hz", target_sample_rate); - // Créer la source audio - let mut source = FileSource::new(file_path).await?; - - // Créer le nœud de resampling - let mut resampler = ResamplingNode::new(target_sample_rate); - - // Créer le nœud de conversion vers I24 - let mut converter = ToI24Node::new(); - - // Créer le sink audio avec volume à 80% - let sink = AudioSink::with_volume(0.8); - // Construire le pipeline: Source → Resampler → Converter → Sink - source.register(Box::new(resampler)); - resampler.register(Box::new(converter)); - converter.register(Box::new(sink)); + let sink = AudioSink::new(); + let mut converter = ToI24Node::new(); + converter.register(sink.boxed()); + + let mut resampler = ResamplingNode::new(target_sample_rate); + resampler.register(converter.boxed()); + + let mut source = FileSource::new(file_path); + source.register(resampler.boxed()); println!( "Pipeline créé: FileSource → Resampling({} Hz) → ToI24 → AudioSink", diff --git a/pmoaudio/src/nodes/audio_sink.rs b/pmoaudio/src/nodes/audio_sink.rs index 4ba91f19..f995800d 100644 --- a/pmoaudio/src/nodes/audio_sink.rs +++ b/pmoaudio/src/nodes/audio_sink.rs @@ -547,19 +547,23 @@ impl AudioSink { } } + pub fn make() -> Box { + Self::new().boxed() + } + /// Crée un nouveau AudioSink avec une taille de channel personnalisée - pub fn with_channel_size(channel_size: usize) -> Self { + pub fn with_channel_size(channel_size: usize) -> Box { Self { inner: Node::new_with_input(AudioSinkLogic::new(), channel_size), - } + }.boxed() } /// Crée un AudioSink avec null output (pour tests sans carte audio) /// Consomme les segments audio sans les jouer - pub fn with_null_output() -> Self { + pub fn with_null_output() -> Box { Self { inner: Node::new_with_input(AudioSinkLogic::with_null_output(), DEFAULT_CHANNEL_SIZE), - } + }.boxed() } } diff --git a/pmoaudio/src/nodes/converter_nodes.rs b/pmoaudio/src/nodes/converter_nodes.rs index 952cf3ae..925ef3c0 100755 --- a/pmoaudio/src/nodes/converter_nodes.rs +++ b/pmoaudio/src/nodes/converter_nodes.rs @@ -22,6 +22,7 @@ use std::sync::Arc; use tokio::sync::mpsc; use tokio_util::sync::CancellationToken; + /// Logique de conversion générique /// /// Cette struct contient la logique pure de conversion d'un type vers un autre. @@ -112,102 +113,162 @@ where // ═══════════════════════════════════════════════════════════════════════════ /// Node de conversion vers I16 (16-bit signed integer) -pub struct ToI16Node; +pub struct ToI16Node(Node AudioChunk>>); impl ToI16Node { - pub fn new() -> Box { - Self::with_channel_size(16) + pub fn new() -> Self { + Self(Node::new_with_input(ConverterLogic::new(|chunk: &AudioChunk| chunk.to_i16()), 16)) + } + + pub fn make() -> Box { + Self::new().boxed() } pub fn with_channel_size(channel_size: usize) -> Box { - let logic = ConverterLogic::new(|chunk: &AudioChunk| chunk.to_i16()); - Box::new(Node::new_with_input(logic, channel_size)) + Self(Node::new_with_input(ConverterLogic::new(|chunk: &AudioChunk| chunk.to_i16()), channel_size)).boxed() } } impl Default for ToI16Node { fn default() -> Self { - Self + Self::new() + } +} + +#[async_trait::async_trait] +impl AudioPipelineNode for ToI16Node { + fn get_tx(&self) -> Option>> { self.0.get_tx() } + fn register(&mut self, child: Box) { self.0.register(child) } + async fn run(self: Box, stop_token: CancellationToken) -> Result<(), AudioError> { + Box::new(self.0).run(stop_token).await } } /// Node de conversion vers I24 (24-bit signed integer) -pub struct ToI24Node; +pub struct ToI24Node(Node AudioChunk>>); impl ToI24Node { - pub fn new() -> Box { - Self::with_channel_size(16) + pub fn new() -> Self { + Self(Node::new_with_input(ConverterLogic::new(|chunk: &AudioChunk| chunk.to_i24()), 16)) + } + + pub fn make() -> Box { + Self::new().boxed() } pub fn with_channel_size(channel_size: usize) -> Box { - let logic = ConverterLogic::new(|chunk: &AudioChunk| chunk.to_i24()); - Box::new(Node::new_with_input(logic, channel_size)) + Self(Node::new_with_input(ConverterLogic::new(|chunk: &AudioChunk| chunk.to_i24()), channel_size)).boxed() } } impl Default for ToI24Node { fn default() -> Self { - Self + Self::new() + } +} + +#[async_trait::async_trait] +impl AudioPipelineNode for ToI24Node { + fn get_tx(&self) -> Option>> { self.0.get_tx() } + fn register(&mut self, child: Box) { self.0.register(child) } + async fn run(self: Box, stop_token: CancellationToken) -> Result<(), AudioError> { + Box::new(self.0).run(stop_token).await } } /// Node de conversion vers I32 (32-bit signed integer) -pub struct ToI32Node; +pub struct ToI32Node(Node AudioChunk>>); impl ToI32Node { - pub fn new() -> Box { - Self::with_channel_size(16) + pub fn new() -> Self { + Self(Node::new_with_input(ConverterLogic::new(|chunk: &AudioChunk| chunk.to_i32()), 16)) + } + + pub fn make() -> Box { + Self::new().boxed() } pub fn with_channel_size(channel_size: usize) -> Box { - let logic = ConverterLogic::new(|chunk: &AudioChunk| chunk.to_i32()); - Box::new(Node::new_with_input(logic, channel_size)) + Self(Node::new_with_input(ConverterLogic::new(|chunk: &AudioChunk| chunk.to_i32()), channel_size)).boxed() } } impl Default for ToI32Node { fn default() -> Self { - Self + Self::new() + } +} + +#[async_trait::async_trait] +impl AudioPipelineNode for ToI32Node { + fn get_tx(&self) -> Option>> { self.0.get_tx() } + fn register(&mut self, child: Box) { self.0.register(child) } + async fn run(self: Box, stop_token: CancellationToken) -> Result<(), AudioError> { + Box::new(self.0).run(stop_token).await } } /// Node de conversion vers F32 (32-bit floating point) -pub struct ToF32Node; +pub struct ToF32Node(Node AudioChunk>>); impl ToF32Node { - pub fn new() -> Box { - Self::with_channel_size(16) + pub fn new() -> Self { + Self(Node::new_with_input(ConverterLogic::new(|chunk: &AudioChunk| chunk.to_f32()), 16)) + } + + pub fn make() -> Box { + Self::new().boxed() } pub fn with_channel_size(channel_size: usize) -> Box { - let logic = ConverterLogic::new(|chunk: &AudioChunk| chunk.to_f32()); - Box::new(Node::new_with_input(logic, channel_size)) + Self(Node::new_with_input(ConverterLogic::new(|chunk: &AudioChunk| chunk.to_f32()), channel_size)).boxed() } } impl Default for ToF32Node { fn default() -> Self { - Self + Self::new() + } +} + +#[async_trait::async_trait] +impl AudioPipelineNode for ToF32Node { + fn get_tx(&self) -> Option>> { self.0.get_tx() } + fn register(&mut self, child: Box) { self.0.register(child) } + async fn run(self: Box, stop_token: CancellationToken) -> Result<(), AudioError> { + Box::new(self.0).run(stop_token).await } } /// Node de conversion vers F64 (64-bit floating point) -pub struct ToF64Node; +pub struct ToF64Node(Node AudioChunk>>); impl ToF64Node { - pub fn new() -> Box { - Self::with_channel_size(16) + pub fn new() -> Self { + Self(Node::new_with_input(ConverterLogic::new(|chunk: &AudioChunk| chunk.to_f64()), 16)) + } + + pub fn make() -> Box { + Self::new().boxed() } pub fn with_channel_size(channel_size: usize) -> Box { - let logic = ConverterLogic::new(|chunk: &AudioChunk| chunk.to_f64()); - Box::new(Node::new_with_input(logic, channel_size)) + Self(Node::new_with_input(ConverterLogic::new(|chunk: &AudioChunk| chunk.to_f64()), channel_size)).boxed() } } impl Default for ToF64Node { fn default() -> Self { - Self + Self::new() + } +} + +#[async_trait::async_trait] +impl AudioPipelineNode for ToF64Node { + fn get_tx(&self) -> Option>> { self.0.get_tx() } + fn register(&mut self, child: Box) { self.0.register(child) } + async fn run(self: Box, stop_token: CancellationToken) -> Result<(), AudioError> { + Box::new(self.0).run(stop_token).await } } diff --git a/pmoaudio/src/nodes/file_source.rs b/pmoaudio/src/nodes/file_source.rs index ccd7bf07..ff5de77c 100755 --- a/pmoaudio/src/nodes/file_source.rs +++ b/pmoaudio/src/nodes/file_source.rs @@ -224,18 +224,21 @@ impl FileSource { /// /// * `path` - chemin du fichier audio à lire pub fn new>(path: P) -> Self { - Self::with_chunk_size(path, 0) // 0 = auto-calculer + let logic = FileSourceLogic::new(path, 0); + Self { inner: Node::new_source(logic) } + } + + pub fn make>(path: P) -> Box { + Self::new(path).boxed() } /// Crée une nouvelle source de fichier avec une taille de chunk spécifique. /// /// * `path` - chemin du fichier audio à lire /// * `chunk_frames` - nombre d'échantillons par canal par chunk (0 = auto) - pub fn with_chunk_size>(path: P, chunk_frames: usize) -> Self { + pub fn with_chunk_size>(path: P, chunk_frames: usize) -> Box { let logic = FileSourceLogic::new(path, chunk_frames); - Self { - inner: Node::new_source(logic), - } + Self { inner: Node::new_source(logic) }.boxed() } } diff --git a/pmoaudio/src/nodes/flac_file_sink.rs b/pmoaudio/src/nodes/flac_file_sink.rs index 8b22d8d3..124b6ad5 100755 --- a/pmoaudio/src/nodes/flac_file_sink.rs +++ b/pmoaudio/src/nodes/flac_file_sink.rs @@ -305,7 +305,12 @@ impl FlacFileSink { /// * `base_path` - Chemin de base pour les fichiers FLAC. Si des TrackBoundary sont reçus, /// des fichiers seront créés avec des suffixes (_01, _02, etc.) pub fn new>(base_path: P) -> Self { - Self::with_channel_size(base_path, DEFAULT_CHANNEL_SIZE) + let logic = FlacFileSinkLogic::new(base_path, EncoderOptions::default(), 8); + Self { inner: Node::new_with_input(logic, DEFAULT_CHANNEL_SIZE) } + } + + pub fn make>(base_path: P) -> Box { + Self::new(base_path).boxed() } /// Crée un sink FLAC avec une taille de buffer MPSC personnalisée. @@ -314,8 +319,9 @@ impl FlacFileSink { /// /// * `base_path` - Chemin de base pour les fichiers FLAC /// * `channel_size` - Taille du buffer MPSC (nombre de segments en attente avant backpressure) - pub fn with_channel_size>(base_path: P, channel_size: usize) -> Self { - Self::with_config(base_path, channel_size, EncoderOptions::default()) + pub fn with_channel_size>(base_path: P, channel_size: usize) -> Box { + let logic = FlacFileSinkLogic::new(base_path, EncoderOptions::default(), 8); + Self { inner: Node::new_with_input(logic, channel_size) }.boxed() } /// Crée un sink FLAC avec une configuration complète. @@ -329,11 +335,11 @@ impl FlacFileSink { base_path: P, channel_size: usize, encoder_options: EncoderOptions, - ) -> Self { + ) -> Box { let logic = FlacFileSinkLogic::new(base_path, encoder_options, 8); Self { inner: Node::new_with_input(logic, channel_size), - } + }.boxed() } } diff --git a/pmoaudio/src/nodes/http_source.rs b/pmoaudio/src/nodes/http_source.rs index b6718336..3ca51ae3 100755 --- a/pmoaudio/src/nodes/http_source.rs +++ b/pmoaudio/src/nodes/http_source.rs @@ -297,7 +297,12 @@ impl HttpSource { /// let source = HttpSource::new("http://example.com/music.flac"); /// ``` pub fn new>(url: S) -> Self { - Self::with_chunk_size(url, 0) + let logic = HttpSourceLogic::new(url.into(), 0); + Self { inner: Node::new_source(logic) } + } + + pub fn make>(url: S) -> Box { + Self::new(url).boxed() } /// Crée une nouvelle source HTTP avec une taille de chunk spécifique. @@ -315,11 +320,9 @@ impl HttpSource { /// // Utiliser des chunks de 2048 frames /// let source = HttpSource::with_chunk_size("http://example.com/music.mp3", 2048); /// ``` - pub fn with_chunk_size>(url: S, chunk_frames: usize) -> Self { + pub fn with_chunk_size>(url: S, chunk_frames: usize) -> Box { let logic = HttpSourceLogic::new(url.into(), chunk_frames); - Self { - inner: Node::new_source(logic), - } + Self { inner: Node::new_source(logic) }.boxed() } pub fn get_url(&self) -> String { diff --git a/pmoaudio/src/nodes/resampling_node.rs b/pmoaudio/src/nodes/resampling_node.rs index 7cbac24b..de17e5bb 100644 --- a/pmoaudio/src/nodes/resampling_node.rs +++ b/pmoaudio/src/nodes/resampling_node.rs @@ -333,8 +333,13 @@ impl ResamplingNode { /// Crée un nouveau node de resampling /// /// * `target_sample_rate` - Sample rate de sortie en Hz (ex: 48000) - pub fn new(target_sample_rate: u32) -> Box { - Self::with_channel_size(target_sample_rate, 16) + pub fn new(target_sample_rate: u32) -> Self { + let logic = ResamplingLogic::new(target_sample_rate); + Self { inner: Node::new_with_input(logic, 16) } + } + + pub fn make(target_sample_rate: u32) -> Box { + Self::new(target_sample_rate).boxed() } /// Crée un nouveau node de resampling avec taille de canal personnalisée @@ -346,9 +351,7 @@ impl ResamplingNode { channel_size: usize, ) -> Box { let logic = ResamplingLogic::new(target_sample_rate); - Box::new(Self { - inner: Node::new_with_input(logic, channel_size), - }) + Self { inner: Node::new_with_input(logic, channel_size) }.boxed() } } diff --git a/pmoaudio/src/nodes/timer_buffer_node.rs b/pmoaudio/src/nodes/timer_buffer_node.rs index f35b4c1b..dae81935 100644 --- a/pmoaudio/src/nodes/timer_buffer_node.rs +++ b/pmoaudio/src/nodes/timer_buffer_node.rs @@ -305,7 +305,12 @@ impl TimerBufferNode { /// let buffer = TimerBufferNode::new(3.0); /// ``` pub fn new(capacity_sec: f64) -> Self { - Self::with_channel_size(capacity_sec, DEFAULT_CHANNEL_SIZE) + let logic = TimerBufferNodeLogic::new(capacity_sec); + Self { inner: Node::new_with_input(logic, DEFAULT_CHANNEL_SIZE) } + } + + pub fn make(capacity_sec: f64) -> Box { + Self::new(capacity_sec).boxed() } /// Crée un TimerBufferNode avec une taille de buffer MPSC personnalisée @@ -314,11 +319,9 @@ impl TimerBufferNode { /// /// * `capacity_sec` - Capacité du buffer en secondes /// * `channel_size` - Taille du buffer MPSC (nombre de segments en attente) - pub fn with_channel_size(capacity_sec: f64, channel_size: usize) -> Self { + pub fn with_channel_size(capacity_sec: f64, channel_size: usize) -> Box { let logic = TimerBufferNodeLogic::new(capacity_sec); - Self { - inner: Node::new_with_input(logic, channel_size), - } + Self { inner: Node::new_with_input(logic, channel_size) }.boxed() } } diff --git a/pmoaudio/src/nodes/timer_node.rs b/pmoaudio/src/nodes/timer_node.rs index bc005153..92ceda9c 100644 --- a/pmoaudio/src/nodes/timer_node.rs +++ b/pmoaudio/src/nodes/timer_node.rs @@ -308,7 +308,12 @@ impl TimerNode { /// let timer = TimerNode::new(3.0); /// ``` pub fn new(max_lead_time_sec: f64) -> Self { - Self::with_channel_size(max_lead_time_sec, DEFAULT_CHANNEL_SIZE) + let logic = TimerNodeLogic::new(max_lead_time_sec); + Self { inner: Node::new_with_input(logic, DEFAULT_CHANNEL_SIZE) } + } + + pub fn make(max_lead_time_sec: f64) -> Box { + Self::new(max_lead_time_sec).boxed() } /// Crée un TimerNode avec une taille de buffer MPSC personnalisée @@ -317,11 +322,9 @@ impl TimerNode { /// /// * `max_lead_time_sec` - Avance maximale en secondes /// * `channel_size` - Taille du buffer MPSC (nombre de segments en attente) - pub fn with_channel_size(max_lead_time_sec: f64, channel_size: usize) -> Self { + pub fn with_channel_size(max_lead_time_sec: f64, channel_size: usize) -> Box { let logic = TimerNodeLogic::new(max_lead_time_sec); - Self { - inner: Node::new_with_input(logic, channel_size), - } + Self { inner: Node::new_with_input(logic, channel_size) }.boxed() } } diff --git a/pmoaudio/src/pipeline.rs b/pmoaudio/src/pipeline.rs index eac59ebf..164fc481 100755 --- a/pmoaudio/src/pipeline.rs +++ b/pmoaudio/src/pipeline.rs @@ -82,6 +82,14 @@ pub trait AudioPipelineNode: Send + 'static { /// Le parent extrait le tx via `child.get_tx()` avant de stocker le child. fn register(&mut self, child: Box); + /// Encapsule ce nœud dans un `Box` pour l'utiliser dans un pipeline. + fn boxed(self) -> Box + where + Self: Sized + 'static, + { + Box::new(self) + } + /// Lance le nœud et tous ses enfants /// /// # Arguments diff --git a/pmoparadise/examples/download_block.rs b/pmoparadise/examples/download_block.rs index a5d5f3bb..d5273cea 100644 --- a/pmoparadise/examples/download_block.rs +++ b/pmoparadise/examples/download_block.rs @@ -117,7 +117,7 @@ async fn main() -> Result<(), Box> { let sink = FlacFileSink::new(&base_path); // Construire la chaîne: source → sink - source.register(Box::new(sink)); + source.register(sink.boxed()); // Créer un token d'arrêt let stop_token = CancellationToken::new(); diff --git a/pmoparadise/examples/play_and_cache.rs b/pmoparadise/examples/play_and_cache.rs index b0ce7f77..a55c782e 100644 --- a/pmoparadise/examples/play_and_cache.rs +++ b/pmoparadise/examples/play_and_cache.rs @@ -217,15 +217,15 @@ async fn main() -> Result<(), Box> { let audio_sink = if use_null_audio { AudioSink::with_null_output() } else { - AudioSink::new() + AudioSink::make() }; tracing::debug!("AudioSink created"); // Connecter timer → audio (AVANT de mettre timer dans une Box) - timer.register(Box::new(audio_sink)); + timer.register(audio_sink); // Connecter playlist → timer - playlist_source.register(Box::new(timer)); + playlist_source.register(timer.boxed()); tracing::info!("Playback pipeline connected: PlaylistSource → TimerNode → AudioSink"); // ═══════════════════════════════════════════════════════════════════════════ diff --git a/pmowebrenderer/src/pipeline.rs b/pmowebrenderer/src/pipeline.rs index 321c33bd..36528f89 100644 --- a/pmowebrenderer/src/pipeline.rs +++ b/pmowebrenderer/src/pipeline.rs @@ -83,21 +83,21 @@ impl InstancePipeline { // Nœud de suivi de position : lit le timestamp des chunks sortant du buffer let (mut position_tracker, position_handle) = PositionTrackerNode::new(); - position_tracker.register(Box::new(sink)); + position_tracker.register(sink.boxed()); // Nœud de pacing : régule le débit pour éviter les rafales et pertes de segments // 2s de buffer absorbe les irrégularités de la source réseau let mut timer_buffer = TimerBufferNode::new(2.0); - timer_buffer.register(Box::new(position_tracker)); + timer_buffer.register(position_tracker.boxed()); // Nœud de conversion de profondeur : tout type entier → I24 // Placé avant le buffer pour réduire la mémoire utilisée let mut to_i24 = ToI24Node::new(); - to_i24.register(Box::new(timer_buffer)); + to_i24.register(timer_buffer.boxed()); // Nœud de rééchantillonnage : n'importe quel sample rate → 96 kHz let mut resampler = ResamplingNode::new(DIRECT_OGG_FLAC_SAMPLE_RATE); - resampler.register(to_i24); + resampler.register(to_i24.boxed()); // Le tx d'entrée du resampler est le point d'entrée du pipeline let segment_tx = resampler.get_tx().expect("ResamplingNode doit avoir un sender"); @@ -110,7 +110,7 @@ impl InstancePipeline { // Lancer la chaîne resampler → to_i24 → sink en background let sink_stop = stop_token.clone(); tokio::spawn(async move { - if let Err(e) = resampler.run(sink_stop).await { + if let Err(e) = resampler.boxed().run(sink_stop).await { warn!("Audio pipeline error: {:?}", e); } debug!("Sink task terminated");