//! Conversions entre différents types de AudioChunk //! //! Ce module fournit des conversions optimisées (SIMD où possible) entre //! tous les types de samples audio supportés. use std::sync::Arc; use crate::{dsp, AudioChunk, AudioChunkData, BitDepth, I24}; // ============================================================================ // Conversions int → int (changement de bit depth) // ============================================================================ // // Ces fonctions utilisent la fonction DSP optimisée SIMD `bitdepth_change_stereo` // pour les conversions i32 ↔ i32 avec différents bit depths. /// Convertit i32 vers i16 (downsampling via bit depth change) pub fn convert_i32_to_i16(chunk: &AudioChunkData) -> Arc> { let mut stereo = chunk.clone_frames(); // Utiliser la fonction DSP optimisée pour passer de B32 → B16 dsp::bitdepth_change_stereo(&mut stereo, BitDepth::B32, BitDepth::B16); // Convertir i32 → i16 (les valeurs sont maintenant dans la plage i16) let stereo_i16: Vec<[i16; 2]> = stereo .into_iter() .map(|[l, r]| [l as i16, r as i16]) .collect(); AudioChunkData::new(stereo_i16, chunk.get_sample_rate(), chunk.get_gain_db()) } /// Convertit i32 vers I24 (downsampling via bit depth change) pub fn convert_i32_to_i24(chunk: &AudioChunkData) -> Arc> { let mut stereo = chunk.clone_frames(); // Utiliser la fonction DSP optimisée pour passer de B32 → B24 dsp::bitdepth_change_stereo(&mut stereo, BitDepth::B32, BitDepth::B24); // Convertir i32 → I24 (les valeurs sont maintenant dans la plage I24) let stereo_i24: Vec<[I24; 2]> = stereo .into_iter() .map(|[l, r]| [I24::new_clamped(l), I24::new_clamped(r)]) .collect(); AudioChunkData::new(stereo_i24, chunk.get_sample_rate(), chunk.get_gain_db()) } /// Convertit i16 vers i32 (upsampling via bit depth change) pub fn convert_i16_to_i32(chunk: &AudioChunkData) -> Arc> { // Convertir i16 → i32 d'abord let mut stereo: Vec<[i32; 2]> = chunk .get_frames() .iter() .map(|[l, r]| [*l as i32, *r as i32]) .collect(); // Utiliser la fonction DSP optimisée pour passer de B16 → B32 dsp::bitdepth_change_stereo(&mut stereo, BitDepth::B16, BitDepth::B32); AudioChunkData::new(stereo, chunk.get_sample_rate(), chunk.get_gain_db()) } /// Convertit I24 vers i32 (upsampling via bit depth change) pub fn convert_i24_to_i32(chunk: &AudioChunkData) -> Arc> { // Convertir I24 → i32 d'abord let mut stereo: Vec<[i32; 2]> = chunk .get_frames() .iter() .map(|[l, r]| [l.as_i32(), r.as_i32()]) .collect(); // Utiliser la fonction DSP optimisée pour passer de B24 → B32 dsp::bitdepth_change_stereo(&mut stereo, BitDepth::B24, BitDepth::B32); AudioChunkData::new(stereo, chunk.get_sample_rate(), chunk.get_gain_db()) } // ============================================================================ // Conversions int → float (normalisation) // ============================================================================ /// Convertit i32 vers f32 via les fonctions DSP optimisées SIMD /// /// I32 = 32 bits complets, donc normalisation par 2^31 pub fn convert_i32_to_f32(chunk: &AudioChunkData) -> Arc> { let frames = chunk.get_frames(); let len = frames.len(); // Séparer les canaux pour utiliser les fonctions DSP SIMD let mut left = Vec::with_capacity(len); let mut right = Vec::with_capacity(len); for [l, r] in frames { left.push(*l); right.push(*r); } // Utiliser la fonction SIMD optimisée du module DSP avec BitDepth::B32 let mut out_pairs = vec![[0.0f32; 2]; len]; dsp::i32_stereo_to_pairs_f32(&left, &right, &mut out_pairs, BitDepth::B32); AudioChunkData::new(out_pairs, chunk.get_sample_rate(), chunk.get_gain_db()) } /// Convertit i32 vers f64 /// /// I32 = 32 bits complets, donc normalisation par 2^31 pub fn convert_i32_to_f64(chunk: &AudioChunkData) -> Arc> { // Via f32 puis upcast let f32_chunk = convert_i32_to_f32(chunk); convert_f32_to_f64(&f32_chunk) } /// Convertit I24 vers f32 via les fonctions DSP optimisées SIMD pub fn convert_i24_to_f32(chunk: &AudioChunkData) -> Arc> { let frames = chunk.get_frames(); let len = frames.len(); // Séparer les canaux I24 en i32 let mut left = Vec::with_capacity(len); let mut right = Vec::with_capacity(len); for [l, r] in frames { left.push(l.as_i32()); right.push(r.as_i32()); } // Utiliser la fonction SIMD optimisée du module DSP pour I24 let mut out_pairs = vec![[0.0f32; 2]; len]; dsp::i24_as_i32_stereo_to_pairs_f32(&left, &right, &mut out_pairs); AudioChunkData::new(out_pairs, chunk.get_sample_rate(), chunk.get_gain_db()) } /// Convertit I24 vers f64 pub fn convert_i24_to_f64(chunk: &AudioChunkData) -> Arc> { let frames = chunk.get_frames(); let max_value = 8_388_608.0f64; // 2^23 let stereo: Vec<[f64; 2]> = frames .iter() .map(|[l, r]| { let lf = l.as_i32() as f64 / max_value; let rf = r.as_i32() as f64 / max_value; [lf, rf] }) .collect(); AudioChunkData::new(stereo, chunk.get_sample_rate(), chunk.get_gain_db()) } /// Convertit i16 vers f32 via les fonctions DSP optimisées SIMD pub fn convert_i16_to_f32(chunk: &AudioChunkData) -> Arc> { let frames = chunk.get_frames(); let len = frames.len(); // Séparer les canaux let mut left = Vec::with_capacity(len); let mut right = Vec::with_capacity(len); for [l, r] in frames { left.push(*l); right.push(*r); } // Utiliser la fonction SIMD optimisée du module DSP let mut out_pairs = vec![[0.0f32; 2]; len]; dsp::i16_stereo_to_pairs_f32(&left, &right, &mut out_pairs); AudioChunkData::new(out_pairs, chunk.get_sample_rate(), chunk.get_gain_db()) } /// Convertit i16 vers f64 pub fn convert_i16_to_f64(chunk: &AudioChunkData) -> Arc> { let frames = chunk.get_frames(); let max_value = 32_768.0f64; // 2^15 let stereo: Vec<[f64; 2]> = frames .iter() .map(|[l, r]| { let lf = *l as f64 / max_value; let rf = *r as f64 / max_value; [lf, rf] }) .collect(); AudioChunkData::new(stereo, chunk.get_sample_rate(), chunk.get_gain_db()) } // ============================================================================ // Conversions float → int (quantization) // ============================================================================ /// Convertit f32 vers i32 via les fonctions DSP optimisées SIMD /// /// I32 = 32 bits complets, donc quantization vers ±2^31 pub fn convert_f32_to_i32(chunk: &AudioChunkData) -> Arc> { let frames = chunk.get_frames(); let len = frames.len(); // Utiliser la fonction SIMD optimisée du module DSP avec BitDepth::B32 let mut left = vec![0i32; len]; let mut right = vec![0i32; len]; dsp::pairs_f32_to_i32_stereo(frames, &mut left, &mut right, BitDepth::B32); // Recombiner en frames let stereo: Vec<[i32; 2]> = left .into_iter() .zip(right.into_iter()) .map(|(l, r)| [l, r]) .collect(); AudioChunkData::new(stereo, chunk.get_sample_rate(), chunk.get_gain_db()) } /// Convertit f64 vers i32 (via f32) /// /// I32 = 32 bits complets, donc quantization vers ±2^31 pub fn convert_f64_to_i32(chunk: &AudioChunkData) -> Arc> { // Downcast f64 → f32 puis quantize let f32_chunk = convert_f64_to_f32(chunk); convert_f32_to_i32(&f32_chunk) } /// Convertit f32 vers I24 via les fonctions DSP optimisées SIMD pub fn convert_f32_to_i24(chunk: &AudioChunkData) -> Arc> { let frames = chunk.get_frames(); let len = frames.len(); // Utiliser la fonction SIMD optimisée du module DSP let mut left = vec![0i32; len]; let mut right = vec![0i32; len]; dsp::pairs_f32_to_i24_as_i32_stereo(frames, &mut left, &mut right); // Recombiner en frames I24 let stereo: Vec<[I24; 2]> = left .into_iter() .zip(right.into_iter()) .map(|(l, r)| [I24::new_clamped(l), I24::new_clamped(r)]) .collect(); AudioChunkData::new(stereo, chunk.get_sample_rate(), chunk.get_gain_db()) } /// Convertit f64 vers I24 pub fn convert_f64_to_i24(chunk: &AudioChunkData) -> Arc> { let frames = chunk.get_frames(); let max_value = 8_388_607.0f64; // 2^23 - 1 let min_value = -8_388_608.0f64; // -2^23 let stereo: Vec<[I24; 2]> = frames .iter() .map(|[l, r]| { let l_scaled = (l * max_value).clamp(min_value, max_value).round() as i32; let r_scaled = (r * max_value).clamp(min_value, max_value).round() as i32; [I24::new_clamped(l_scaled), I24::new_clamped(r_scaled)] }) .collect(); AudioChunkData::new(stereo, chunk.get_sample_rate(), chunk.get_gain_db()) } /// Convertit f32 vers i16 via les fonctions DSP optimisées SIMD pub fn convert_f32_to_i16(chunk: &AudioChunkData) -> Arc> { let frames = chunk.get_frames(); let len = frames.len(); // Utiliser la fonction SIMD optimisée du module DSP let mut left = vec![0i16; len]; let mut right = vec![0i16; len]; dsp::pairs_f32_to_i16_stereo(frames, &mut left, &mut right); // Recombiner en frames let stereo: Vec<[i16; 2]> = left .into_iter() .zip(right.into_iter()) .map(|(l, r)| [l, r]) .collect(); AudioChunkData::new(stereo, chunk.get_sample_rate(), chunk.get_gain_db()) } /// Convertit f64 vers i16 pub fn convert_f64_to_i16(chunk: &AudioChunkData) -> Arc> { let frames = chunk.get_frames(); let max_value = 32_767.0f64; // 2^15 - 1 let min_value = -32_768.0f64; // -2^15 let stereo: Vec<[i16; 2]> = frames .iter() .map(|[l, r]| { let l16 = (l * max_value).clamp(min_value, max_value).round() as i16; let r16 = (r * max_value).clamp(min_value, max_value).round() as i16; [l16, r16] }) .collect(); AudioChunkData::new(stereo, chunk.get_sample_rate(), chunk.get_gain_db()) } // ============================================================================ // Conversions F32 ↔ F64 // ============================================================================ /// Convertit f32 vers f64 (upcast simple) pub fn convert_f32_to_f64(chunk: &AudioChunkData) -> Arc> { let frames = chunk.get_frames(); let stereo: Vec<[f64; 2]> = frames.iter().map(|[l, r]| [*l as f64, *r as f64]).collect(); AudioChunkData::new(stereo, chunk.get_sample_rate(), chunk.get_gain_db()) } /// Convertit f64 vers f32 (downcast simple) pub fn convert_f64_to_f32(chunk: &AudioChunkData) -> Arc> { let frames = chunk.get_frames(); let stereo: Vec<[f32; 2]> = frames.iter().map(|[l, r]| [*l as f32, *r as f32]).collect(); AudioChunkData::new(stereo, chunk.get_sample_rate(), chunk.get_gain_db()) } // ============================================================================ // Méthodes de conversion sur AudioChunk enum // ============================================================================ impl AudioChunk { /// Convertit ce chunk vers f32 /// /// Chaque type utilise sa plage native (I16=±2^15, I24=±2^23, I32=±2^31) pub fn to_f32(&self) -> AudioChunk { match self { AudioChunk::I16(d) => AudioChunk::F32(convert_i16_to_f32(d)), AudioChunk::I24(d) => AudioChunk::F32(convert_i24_to_f32(d)), AudioChunk::I32(d) => AudioChunk::F32(convert_i32_to_f32(d)), AudioChunk::F32(d) => AudioChunk::F32(d.clone()), AudioChunk::F64(d) => AudioChunk::F32(convert_f64_to_f32(d)), } } /// Convertit ce chunk vers f64 /// /// Chaque type utilise sa plage native (I16=±2^15, I24=±2^23, I32=±2^31) pub fn to_f64(&self) -> AudioChunk { match self { AudioChunk::I16(d) => AudioChunk::F64(convert_i16_to_f64(d)), AudioChunk::I24(d) => AudioChunk::F64(convert_i24_to_f64(d)), AudioChunk::I32(d) => AudioChunk::F64(convert_i32_to_f64(d)), AudioChunk::F32(d) => AudioChunk::F64(convert_f32_to_f64(d)), AudioChunk::F64(d) => AudioChunk::F64(d.clone()), } } /// Convertit ce chunk vers i32 /// /// I32 = 32 bits complets (±2^31) pub fn to_i32(&self) -> AudioChunk { match self { AudioChunk::I16(d) => AudioChunk::I32(convert_i16_to_i32(d)), AudioChunk::I24(d) => AudioChunk::I32(convert_i24_to_i32(d)), AudioChunk::I32(d) => AudioChunk::I32(d.clone()), AudioChunk::F32(d) => AudioChunk::I32(convert_f32_to_i32(d)), AudioChunk::F64(d) => AudioChunk::I32(convert_f64_to_i32(d)), } } /// Convertit ce chunk vers I24 pub fn to_i24(&self) -> AudioChunk { match self { AudioChunk::I16(d) => { // I16 → I32 → I24 let i32_chunk = convert_i16_to_i32(d); AudioChunk::I24(convert_i32_to_i24(&i32_chunk)) } AudioChunk::I24(d) => AudioChunk::I24(d.clone()), AudioChunk::I32(d) => AudioChunk::I24(convert_i32_to_i24(d)), AudioChunk::F32(d) => AudioChunk::I24(convert_f32_to_i24(d)), AudioChunk::F64(d) => AudioChunk::I24(convert_f64_to_i24(d)), } } /// Convertit ce chunk vers i16 pub fn to_i16(&self) -> AudioChunk { match self { AudioChunk::I16(d) => AudioChunk::I16(d.clone()), AudioChunk::I24(d) => { // I24 → I32 → I16 let i32_chunk = convert_i24_to_i32(d); AudioChunk::I16(convert_i32_to_i16(&i32_chunk)) } AudioChunk::I32(d) => AudioChunk::I16(convert_i32_to_i16(d)), AudioChunk::F32(d) => AudioChunk::I16(convert_f32_to_i16(d)), AudioChunk::F64(d) => AudioChunk::I16(convert_f64_to_i16(d)), } } } // ============================================================================ // Implémentations des traits From/Into // ============================================================================ // ---------- From>> pour AudioChunk ---------- impl From>> for AudioChunk { fn from(data: Arc>) -> Self { AudioChunk::I16(data) } } impl From>> for AudioChunk { fn from(data: Arc>) -> Self { AudioChunk::I24(data) } } impl From>> for AudioChunk { fn from(data: Arc>) -> Self { AudioChunk::I32(data) } } impl From>> for AudioChunk { fn from(data: Arc>) -> Self { AudioChunk::F32(data) } } impl From>> for AudioChunk { fn from(data: Arc>) -> Self { AudioChunk::F64(data) } } // ---------- From entre AudioChunkData types (sans BitDepth requis) ---------- // I16 conversions impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_i16_to_i32(chunk) } } impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_i16_to_f32(chunk) } } impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_i16_to_f64(chunk) } } // I24 conversions impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_i24_to_i32(chunk) } } impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_i24_to_f32(chunk) } } impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_i24_to_f64(chunk) } } // I32 conversions vers types int (downsampling) impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_i32_to_i16(chunk) } } impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_i32_to_i24(chunk) } } // I32 conversions vers float (normalisation par 2^31) impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_i32_to_f32(chunk) } } impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_i32_to_f64(chunk) } } // F32 conversions impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_f32_to_f64(chunk) } } impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_f32_to_i16(chunk) } } impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_f32_to_i24(chunk) } } impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_f32_to_i32(chunk) } } // F64 conversions impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_f64_to_f32(chunk) } } impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_f64_to_i16(chunk) } } impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_f64_to_i24(chunk) } } impl From<&AudioChunkData> for Arc> { fn from(chunk: &AudioChunkData) -> Self { convert_f64_to_i32(chunk) } } // ============================================================================ // Tests // ============================================================================ #[cfg(test)] mod tests { use super::*; #[test] fn test_i32_to_f32_roundtrip() { let stereo = vec![[1_000_000_000i32, 2_000_000_000i32]; 100]; let chunk_i32 = AudioChunkData::new(stereo.clone(), 48_000, 0.0); let chunk_f32 = convert_i32_to_f32(&chunk_i32); let chunk_back = convert_f32_to_i32(&chunk_f32); // Vérifier que les valeurs sont proches (tolérance d'arrondi) // Note: Pour I32 on utilise toute la plage ±2^31 for (orig, back) in stereo.iter().zip(chunk_back.get_frames().iter()) { assert!((orig[0] - back[0]).abs() <= 100); // Tolérance plus élevée pour 32-bit assert!((orig[1] - back[1]).abs() <= 100); } } #[test] fn test_f32_to_f64_roundtrip() { let stereo = vec![[0.5f32, -0.25f32]; 100]; let chunk_f32 = AudioChunkData::new(stereo.clone(), 48_000, 0.0); let chunk_f64 = convert_f32_to_f64(&chunk_f32); let chunk_back = convert_f64_to_f32(&chunk_f64); // Vérifier égalité exacte (pas de perte de précision significative) for (orig, back) in stereo.iter().zip(chunk_back.get_frames().iter()) { assert!((orig[0] - back[0]).abs() < 1e-6); assert!((orig[1] - back[1]).abs() < 1e-6); } } #[test] fn test_i16_to_i32_upsampling() { let stereo = vec![[16_000i16, -8_000i16]; 10]; let chunk_i16 = AudioChunkData::new(stereo.clone(), 48_000, 0.0); let chunk_i32 = convert_i16_to_i32(&chunk_i16); // Vérifier que les valeurs sont correctement upsamplées (shift de 16 bits) for (orig, result) in stereo.iter().zip(chunk_i32.get_frames().iter()) { assert_eq!(result[0], (orig[0] as i32) << 16); assert_eq!(result[1], (orig[1] as i32) << 16); } } #[test] fn test_i32_to_i16_downsampling() { let stereo = vec![[1_000_000i32 << 16, -500_000i32 << 16]; 10]; let chunk_i32 = AudioChunkData::new(stereo.clone(), 48_000, 0.0); let chunk_i16 = convert_i32_to_i16(&chunk_i32); // Vérifier que les valeurs sont correctement downsamplées for (orig, result) in stereo.iter().zip(chunk_i16.get_frames().iter()) { assert_eq!(result[0], (orig[0] >> 16) as i16); assert_eq!(result[1], (orig[1] >> 16) as i16); } } #[test] fn test_i24_conversions() { let stereo = vec![[I24::new(1_000_000).unwrap(), I24::new(-500_000).unwrap()]; 10]; let chunk_i24 = AudioChunkData::new(stereo.clone(), 48_000, 0.0); // I24 → F32 → I24 let chunk_f32 = convert_i24_to_f32(&chunk_i24); let chunk_back = convert_f32_to_i24(&chunk_f32); for (orig, back) in stereo.iter().zip(chunk_back.get_frames().iter()) { assert!((orig[0].as_i32() - back[0].as_i32()).abs() <= 1); assert!((orig[1].as_i32() - back[1].as_i32()).abs() <= 1); } } #[test] fn test_audio_chunk_enum_conversions() { // Créer un chunk I32 let stereo = vec![[1_000_000_000i32, -500_000_000i32]; 100]; let chunk_data = AudioChunkData::new(stereo, 48_000, 0.0); let chunk = AudioChunk::I32(chunk_data); // Convertir vers F32 (I32 utilise plage complète ±2^31) let chunk_f32 = chunk.to_f32(); assert_eq!(chunk_f32.type_name(), "f32"); // Convertir vers I24 let chunk_i24 = chunk.to_i24(); assert_eq!(chunk_i24.type_name(), "I24"); // Convertir vers I16 let chunk_i16 = chunk.to_i16(); assert_eq!(chunk_i16.type_name(), "i16"); } #[test] fn test_from_trait_audio_chunk() { // Test From>> pour AudioChunk let stereo_f32 = vec![[0.5f32, -0.25f32]; 100]; let chunk_data = AudioChunkData::new(stereo_f32, 48_000, 0.0); // Utiliser From/Into let chunk: AudioChunk = chunk_data.into(); assert_eq!(chunk.type_name(), "f32"); assert_eq!(chunk.len(), 100); } #[test] fn test_from_trait_conversions() { // Test From entre AudioChunkData types let stereo_i16 = vec![[16_000i16, -8_000i16]; 50]; let chunk_i16 = AudioChunkData::new(stereo_i16, 48_000, 0.0); // I16 → I32 via From let chunk_i32: Arc> = (&*chunk_i16).into(); assert_eq!(chunk_i32.len(), 50); // I16 → F32 via From let chunk_f32: Arc> = (&*chunk_i16).into(); assert_eq!(chunk_f32.len(), 50); // I16 → F64 via From let chunk_f64: Arc> = (&*chunk_i16).into(); assert_eq!(chunk_f64.len(), 50); } #[test] fn test_from_trait_i24() { // Test conversions I24 via From let stereo_i24 = vec![[I24::new(1_000_000).unwrap(), I24::new(-500_000).unwrap()]; 50]; let chunk_i24 = AudioChunkData::new(stereo_i24, 48_000, 0.0); // I24 → I32 via From let chunk_i32: Arc> = (&*chunk_i24).into(); assert_eq!(chunk_i32.len(), 50); // I24 → F32 via From let chunk_f32: Arc> = (&*chunk_i24).into(); assert_eq!(chunk_f32.len(), 50); } #[test] fn test_from_trait_float_conversions() { // Test conversions float via From let stereo_f32 = vec![[0.5f32, -0.25f32]; 50]; let chunk_f32 = AudioChunkData::new(stereo_f32, 48_000, 0.0); // F32 → F64 via From let chunk_f64: Arc> = (&*chunk_f32).into(); assert_eq!(chunk_f64.len(), 50); // F32 → I16 via From let chunk_i16: Arc> = (&*chunk_f32).into(); assert_eq!(chunk_i16.len(), 50); // F32 → I24 via From let chunk_i24: Arc> = (&*chunk_f32).into(); assert_eq!(chunk_i24.len(), 50); } #[test] fn test_from_trait_roundtrip() { // Test round-trip I24 → F32 → I24 via From let original = vec![[I24::new(1_000_000).unwrap(), I24::new(-500_000).unwrap()]; 10]; let chunk_i24 = AudioChunkData::new(original.clone(), 48_000, 0.0); // I24 → F32 via From let chunk_f32: Arc> = (&*chunk_i24).into(); // F32 → I24 via From let chunk_back: Arc> = (&*chunk_f32).into(); // Vérifier la précision for (orig, back) in original.iter().zip(chunk_back.get_frames().iter()) { assert!((orig[0].as_i32() - back[0].as_i32()).abs() <= 1); assert!((orig[1].as_i32() - back[1].as_i32()).abs() <= 1); } } #[test] fn test_from_trait_i32_conversions() { // Test conversions I32 via From (maintenant disponibles!) let stereo_i32 = vec![[1_000_000_000i32, -500_000_000i32]; 50]; let chunk_i32 = AudioChunkData::new(stereo_i32, 48_000, 0.0); // I32 → F32 via From (normalisation par 2^31) let chunk_f32: Arc> = (&*chunk_i32).into(); assert_eq!(chunk_f32.len(), 50); // I32 → F64 via From let chunk_f64: Arc> = (&*chunk_i32).into(); assert_eq!(chunk_f64.len(), 50); // F32 → I32 via From (quantization vers 2^31) let chunk_back_i32: Arc> = (&*chunk_f32).into(); assert_eq!(chunk_back_i32.len(), 50); } }