/// Macros pour simplifier la manipulation des AudioChunk et AudioSegment /// Extrait les données typées d'un AudioChunk /// /// # Exemples /// ``` /// use pmoaudio::{AudioChunk, AudioChunkData, extract_chunk_data}; /// /// fn process_i32(chunk: &AudioChunk) { /// if let Some(data) = extract_chunk_data!(chunk, I32) { /// println!("I32 chunk with {} frames", data.len()); /// } /// } /// ``` #[macro_export] macro_rules! extract_chunk_data { ($chunk:expr, I16) => { match $chunk { $crate::AudioChunk::I16(data) => Some(data), _ => None, } }; ($chunk:expr, I24) => { match $chunk { $crate::AudioChunk::I24(data) => Some(data), _ => None, } }; ($chunk:expr, I32) => { match $chunk { $crate::AudioChunk::I32(data) => Some(data), _ => None, } }; ($chunk:expr, F32) => { match $chunk { $crate::AudioChunk::F32(data) => Some(data), _ => None, } }; ($chunk:expr, F64) => { match $chunk { $crate::AudioChunk::F64(data) => Some(data), _ => None, } }; } /// Match sur le type d'un AudioChunk avec exécution de code pour chaque cas /// /// # Exemples /// ``` /// use pmoaudio::{AudioChunk, match_chunk}; /// /// fn print_chunk_info(chunk: &AudioChunk) { /// match_chunk!(chunk, data => { /// println!("Chunk type: {}, frames: {}", chunk.type_name(), data.len()); /// }); /// } /// ``` #[macro_export] macro_rules! match_chunk { ($chunk:expr, $data:ident => $body:expr) => { match $chunk { $crate::AudioChunk::I16($data) => $body, $crate::AudioChunk::I24($data) => $body, $crate::AudioChunk::I32($data) => $body, $crate::AudioChunk::F32($data) => $body, $crate::AudioChunk::F64($data) => $body, } }; } /// Map sur un AudioChunk - transforme les données et retourne un nouveau AudioChunk du même type /// /// # Exemples /// ``` /// use pmoaudio::{AudioChunk, map_chunk}; /// /// fn add_gain_db(chunk: &AudioChunk, gain_db: f64) -> AudioChunk { /// map_chunk!(chunk, data => { /// data.set_gain_db(data.gain_db() + gain_db) /// }) /// } /// ``` #[macro_export] macro_rules! map_chunk { ($chunk:expr, $data:ident => $transform:expr) => { match $chunk { $crate::AudioChunk::I16($data) => $crate::AudioChunk::I16($transform), $crate::AudioChunk::I24($data) => $crate::AudioChunk::I24($transform), $crate::AudioChunk::I32($data) => $crate::AudioChunk::I32($transform), $crate::AudioChunk::F32($data) => $crate::AudioChunk::F32($transform), $crate::AudioChunk::F64($data) => $crate::AudioChunk::F64($transform), } }; } /// Prédicat sur le type d'un AudioChunk /// /// # Exemples /// ``` /// use pmoaudio::{AudioChunk, is_chunk_type}; /// /// fn process_only_i32(chunk: &AudioChunk) { /// if is_chunk_type!(chunk, I32) { /// println!("Processing I32 chunk"); /// } /// } /// ``` #[macro_export] macro_rules! is_chunk_type { ($chunk:expr, I16) => { matches!($chunk, $crate::AudioChunk::I16(_)) }; ($chunk:expr, I24) => { matches!($chunk, $crate::AudioChunk::I24(_)) }; ($chunk:expr, I32) => { matches!($chunk, $crate::AudioChunk::I32(_)) }; ($chunk:expr, F32) => { matches!($chunk, $crate::AudioChunk::F32(_)) }; ($chunk:expr, F64) => { matches!($chunk, $crate::AudioChunk::F64(_)) }; } /// Extrait un AudioChunk d'un AudioSegment /// /// # Exemples /// ``` /// use pmoaudio::{AudioSegment, extract_audio_chunk}; /// /// fn get_chunk(segment: &AudioSegment) -> Option<&Arc> { /// extract_audio_chunk!(segment) /// } /// ``` #[macro_export] macro_rules! extract_audio_chunk { ($segment:expr) => { match &$segment.segment { $crate::_AudioSegment::Chunk(chunk) => Some(chunk), _ => None, } }; } /// Extrait un SyncMarker d'un AudioSegment /// /// # Exemples /// ``` /// use pmoaudio::{AudioSegment, extract_sync_marker}; /// /// fn get_marker(segment: &AudioSegment) -> Option<&Arc> { /// extract_sync_marker!(segment) /// } /// ``` #[macro_export] macro_rules! extract_sync_marker { ($segment:expr) => { match &$segment.segment { $crate::_AudioSegment::Sync(marker) => Some(marker), _ => None, } }; } /// Match sur le contenu d'un AudioSegment /// /// # Exemples /// ``` /// use pmoaudio::{AudioSegment, match_segment}; /// /// fn process_segment(segment: &AudioSegment) { /// match_segment!(segment, /// chunk => println!("Audio chunk: {}", chunk.type_name()), /// marker => println!("Sync marker") /// ); /// } /// ``` #[macro_export] macro_rules! match_segment { ($segment:expr, $chunk_name:ident => $chunk_body:expr, $marker_name:ident => $marker_body:expr) => { match &$segment.segment { $crate::_AudioSegment::Chunk($chunk_name) => $chunk_body, $crate::_AudioSegment::Sync($marker_name) => $marker_body, } }; } #[cfg(test)] mod tests { use crate::{AudioChunk, AudioChunkData, AudioSegment, BitDepth}; #[test] fn test_extract_chunk_data() { let data = AudioChunkData::new(vec![[100i32, 200i32]], 44100, 0.0); let chunk = AudioChunk::I32(data.clone()); // Test extraction réussie assert!(extract_chunk_data!(&chunk, I32).is_some()); assert!(extract_chunk_data!(&chunk, F32).is_none()); // Test avec F32 let f32_chunk = AudioChunk::F32(AudioChunkData::new(vec![[0.5f32, -0.5f32]], 44100, 0.0)); assert!(extract_chunk_data!(&f32_chunk, F32).is_some()); assert!(extract_chunk_data!(&f32_chunk, I32).is_none()); } #[test] fn test_match_chunk() { let chunk = AudioChunk::I32(AudioChunkData::new(vec![[100i32, 200i32]], 44100, 0.0)); let len = match_chunk!(&chunk, data => data.len()); assert_eq!(len, 1); let sample_rate = match_chunk!(&chunk, data => data.get_sample_rate()); assert_eq!(sample_rate, 44100); } #[test] fn test_map_chunk() { let chunk = AudioChunk::I32(AudioChunkData::new(vec![[100i32, 200i32]], 44100, 0.0)); let modified = map_chunk!(&chunk, data => data.set_gain_db(6.0)); match_chunk!(&modified, data => { assert_eq!(data.get_gain_db(), 6.0); }); } #[test] fn test_is_chunk_type() { let i32_chunk = AudioChunk::I32(AudioChunkData::new(vec![[100i32, 200i32]], 44100, 0.0)); let f32_chunk = AudioChunk::F32(AudioChunkData::new(vec![[0.5f32, -0.5f32]], 44100, 0.0)); assert!(is_chunk_type!(&i32_chunk, I32)); assert!(!is_chunk_type!(&i32_chunk, F32)); assert!(is_chunk_type!(&f32_chunk, F32)); assert!(!is_chunk_type!(&f32_chunk, I32)); } #[test] fn test_extract_audio_chunk() { let segment = AudioSegment::new_chunk(0, 0.0, vec![[100i32, 200i32]], 44100, BitDepth::B32); assert!(extract_audio_chunk!(&*segment).is_some()); let sync_segment = AudioSegment::new_hearbeat(1, 1.0); assert!(extract_audio_chunk!(&*sync_segment).is_none()); } #[test] fn test_extract_sync_marker() { let segment = AudioSegment::new_hearbeat(1, 1.0); assert!(extract_sync_marker!(&*segment).is_some()); let audio_segment = AudioSegment::new_chunk(0, 0.0, vec![[100i32, 200i32]], 44100, BitDepth::B32); assert!(extract_sync_marker!(&*audio_segment).is_none()); } #[test] fn test_match_segment() { let audio_segment = AudioSegment::new_chunk(0, 0.0, vec![[100i32, 200i32]], 44100, BitDepth::B32); let result = match_segment!(&*audio_segment, chunk => format!("audio: {}", chunk.type_name()), _marker => "sync".to_string() ); assert_eq!(result, "audio: i32"); let sync_segment = AudioSegment::new_hearbeat(1, 1.0); let result = match_segment!(&*sync_segment, _chunk => "audio".to_string(), _marker => "sync".to_string() ); assert_eq!(result, "sync"); } }