media_core/audio/
convert.rs

1use bytemuck::Pod;
2use strum::EnumCount;
3
4use super::{
5    audio::{AudioFrameDescriptor, SampleFormat},
6    frame::AudioFrame,
7};
8use crate::{
9    error::Error,
10    frame::{DataMappable, Frame, FrameData, MappedPlanes},
11    FrameDescriptor, Result,
12};
13
14macro_rules! impl_convert {
15    ($func_name:ident, $src_type:ty, $dst_type:ty, $convert_expr:expr) => {
16        #[allow(clippy::too_many_arguments)]
17        fn $func_name(
18            src_planes: &MappedPlanes,
19            dst_planes: &mut MappedPlanes,
20            src_plane_index_step: usize,
21            dst_plane_index_step: usize,
22            src_data_step: usize,
23            dst_data_step: usize,
24            channels: u8,
25            samples: u32,
26        ) -> Result<()> {
27            convert_samples::<$src_type, $dst_type>(
28                src_planes,
29                dst_planes,
30                src_plane_index_step,
31                dst_plane_index_step,
32                src_data_step,
33                dst_data_step,
34                channels,
35                samples,
36                |src_val| $convert_expr(src_val),
37            )
38        }
39    };
40}
41
42impl_convert!(u8_to_u8, u8, u8, |x: u8| x);
43impl_convert!(u8_to_s16, u8, i16, |x: u8| (x as i16 - 0x80i16) << 8);
44impl_convert!(u8_to_s32, u8, i32, |x: u8| (x as i32 - 0x80i32) << 24);
45impl_convert!(u8_to_s64, u8, i64, |x: u8| (x as i64 - 0x80i64) << 56);
46impl_convert!(u8_to_f32, u8, f32, |x: u8| (x as i32 - 0x80i32) as f32 * (1.0f32 / (1 << 7) as f32));
47impl_convert!(u8_to_f64, u8, f64, |x: u8| (x as i32 - 0x80i32) as f64 * (1.0f64 / (1 << 7) as f64));
48impl_convert!(s16_to_u8, i16, u8, |x: i16| ((x >> 8) + 0x80) as u8);
49impl_convert!(s16_to_s16, i16, i16, |x: i16| x);
50impl_convert!(s16_to_s32, i16, i32, |x: i16| (x as i32) << 16);
51impl_convert!(s16_to_s64, i16, i64, |x: i16| (x as i64) << 48);
52impl_convert!(s16_to_f32, i16, f32, |x: i16| (x as f32) * (1.0f32 / (1u16 << 15) as f32));
53impl_convert!(s16_to_f64, i16, f64, |x: i16| (x as f64) * (1.0f64 / (1u16 << 15) as f64));
54impl_convert!(s32_to_u8, i32, u8, |x: i32| ((x >> 24) + 0x80) as u8);
55impl_convert!(s32_to_s16, i32, i16, |x: i32| (x >> 16) as i16);
56impl_convert!(s32_to_s32, i32, i32, |x: i32| x);
57impl_convert!(s32_to_s64, i32, i64, |x: i32| (x as i64) << 32);
58impl_convert!(s32_to_f32, i32, f32, |x: i32| (x as f32) * (1.0f32 / (1u32 << 31) as f32));
59impl_convert!(s32_to_f64, i32, f64, |x: i32| (x as f64) * (1.0f64 / (1u32 << 31) as f64));
60impl_convert!(s64_to_u8, i64, u8, |x: i64| ((x >> 56) + 0x80) as u8);
61impl_convert!(s64_to_s16, i64, i16, |x: i64| (x >> 48) as i16);
62impl_convert!(s64_to_s32, i64, i32, |x: i64| (x >> 32) as i32);
63impl_convert!(s64_to_s64, i64, i64, |x: i64| x);
64impl_convert!(s64_to_f32, i64, f32, |x: i64| (x as f32) * (1.0f32 / (1u64 << 63) as f32));
65impl_convert!(s64_to_f64, i64, f64, |x: i64| (x as f64) * (1.0f64 / (1u64 << 63) as f64));
66impl_convert!(f32_to_u8, f32, u8, |x: f32| ((x * (1u8 << 7) as f32).round() as i32 + 0x80).clamp(0, 255) as u8);
67impl_convert!(f32_to_s16, f32, i16, |x: f32| ((x * (1u16 << 15) as f32).round() as i32).clamp(i16::MIN as i32, i16::MAX as i32) as i16);
68impl_convert!(f32_to_s32, f32, i32, |x: f32| ((x * (1u32 << 31) as f32).round() as i64).clamp(i32::MIN as i64, i32::MAX as i64) as i32);
69impl_convert!(f32_to_s64, f32, i64, |x: f32| (x * (1u64 << 63) as f32).round() as i64);
70impl_convert!(f32_to_f32, f32, f32, |x: f32| x);
71impl_convert!(f32_to_f64, f32, f64, |x: f32| x as f64);
72impl_convert!(f64_to_u8, f64, u8, |x: f64| ((x * (1u8 << 7) as f64).round() as i32 + 0x80).clamp(0, 255) as u8);
73impl_convert!(f64_to_s16, f64, i16, |x: f64| ((x * (1u16 << 15) as f64).round() as i32).clamp(i16::MIN as i32, i16::MAX as i32) as i16);
74impl_convert!(f64_to_s32, f64, i32, |x: f64| ((x * (1u32 << 31) as f64).round() as i64).clamp(i32::MIN as i64, i32::MAX as i64) as i32);
75impl_convert!(f64_to_s64, f64, i64, |x: f64| (x * (1u64 << 63) as f64).round() as i64);
76impl_convert!(f64_to_f32, f64, f32, |x: f64| x as f32);
77impl_convert!(f64_to_f64, f64, f64, |x: f64| x);
78
79type SampleFormatConvertFunc = fn(&MappedPlanes, &mut MappedPlanes, usize, usize, usize, usize, u8, u32) -> Result<()>;
80
81const AUDIO_SAMPLE_FORMAT_MAX: usize = SampleFormat::COUNT / 2; // Only handle packed formats
82
83static AUDIO_SAMPLE_CONVERT_FUNCS: [[SampleFormatConvertFunc; AUDIO_SAMPLE_FORMAT_MAX]; AUDIO_SAMPLE_FORMAT_MAX] = [
84    [u8_to_u8, u8_to_s16, u8_to_s32, u8_to_s64, u8_to_f32, u8_to_f64],
85    [s16_to_u8, s16_to_s16, s16_to_s32, s16_to_s64, s16_to_f32, s16_to_f64],
86    [s32_to_u8, s32_to_s16, s32_to_s32, s32_to_s64, s32_to_f32, s32_to_f64],
87    [s64_to_u8, s64_to_s16, s64_to_s32, s64_to_s64, s64_to_f32, s64_to_f64],
88    [f32_to_u8, f32_to_s16, f32_to_s32, f32_to_s64, f32_to_f32, f32_to_f64],
89    [f64_to_u8, f64_to_s16, f64_to_s32, f64_to_s64, f64_to_f32, f64_to_f64],
90];
91
92#[allow(clippy::too_many_arguments)]
93fn convert_samples<S: Pod, D: Pod>(
94    src_planes: &MappedPlanes,
95    dst_planes: &mut MappedPlanes,
96    src_plane_index_step: usize,
97    dst_plane_index_step: usize,
98    src_data_step: usize,
99    dst_data_step: usize,
100    channels: u8,
101    samples: u32,
102    convert: impl Fn(S) -> D,
103) -> Result<()> {
104    for ch in 0..channels as usize {
105        let src_i = ch * src_plane_index_step;
106        let dst_i = ch * dst_plane_index_step;
107        let src_data = src_planes.plane_data(src_i).ok_or_else(|| Error::Invalid(format!("out of range: src index {}", src_i)))?;
108        let dst_data = dst_planes.plane_data_mut(dst_i).ok_or_else(|| Error::Invalid(format!("out of range: dst index {}", dst_i)))?;
109
110        let src_data: &[S] = bytemuck::cast_slice(src_data);
111        let dst_data: &mut [D] = bytemuck::cast_slice_mut(dst_data);
112
113        for i in 0..samples as usize {
114            dst_data[i * dst_data_step] = convert(src_data[i * src_data_step]);
115        }
116    }
117
118    Ok(())
119}
120
121fn data_copy(src_planes: &MappedPlanes, dst_planes: &mut MappedPlanes) -> Result<()> {
122    for (src_plane, dst_plane) in src_planes.iter().zip(dst_planes.iter_mut()) {
123        if let (Some(src), Some(dst)) = (src_plane.data(), dst_plane.data_mut()) {
124            if src.len() != dst.len() {
125                return Err(Error::Invalid("plane size mismatch".to_string()));
126            }
127            dst.copy_from_slice(src);
128        }
129    }
130
131    Ok(())
132}
133
134fn data_convert(
135    src_planes: &MappedPlanes,
136    dst_planes: &mut MappedPlanes,
137    src_format: SampleFormat,
138    dst_format: SampleFormat,
139    channels: u8,
140    samples: u32,
141) -> Result<()> {
142    // Get conversion function from table
143    let convert = AUDIO_SAMPLE_CONVERT_FUNCS[src_format.packed_sample_format() as usize][dst_format.packed_sample_format() as usize];
144
145    let (src_plane_index_step, src_data_step) = if src_format.is_planar() {
146        (1, 1)
147    } else {
148        (0, channels as usize)
149    };
150
151    let (dst_plane_index_step, dst_data_step) = if dst_format.is_planar() {
152        (1, 1)
153    } else {
154        (0, channels as usize)
155    };
156
157    convert(src_planes, dst_planes, src_plane_index_step, dst_plane_index_step, src_data_step, dst_data_step, channels, samples)
158}
159
160impl Frame<'_> {
161    pub fn convert_audio_to(&self, dst: &mut Frame) -> Result<()> {
162        let (FrameDescriptor::Audio(src_desc), FrameDescriptor::Audio(dst_desc)) = (&self.desc, &dst.desc) else {
163            return Err(Error::Invalid("not audio frame".to_string()));
164        };
165
166        AudioFrame::convert_audio_to_internal(src_desc, &self.data, dst_desc, &mut dst.data)
167    }
168}
169
170impl AudioFrame<'_> {
171    fn convert_audio_to_internal(
172        src_desc: &AudioFrameDescriptor,
173        src_data: &FrameData,
174        dst_desc: &AudioFrameDescriptor,
175        dst_data: &mut FrameData,
176    ) -> Result<()> {
177        if src_desc.samples != dst_desc.samples {
178            return Err(Error::Unsupported("samples mismatch".to_string()));
179        }
180
181        let src_channels = src_desc.channels().get();
182        let dst_channels = dst_desc.channels().get();
183
184        if src_channels != dst_channels {
185            return Err(Error::Unsupported("channels mismatch".to_string()));
186        }
187
188        let guard = src_data.map().map_err(|_| Error::Invalid("not readable".into()))?;
189        let mut dst_guard = dst_data.map_mut().map_err(|_| Error::Invalid("not writable".into()))?;
190        let src_planes = guard.planes().unwrap();
191        let mut dst_planes = dst_guard.planes_mut().unwrap();
192
193        let (src_format, dst_format) = if src_channels == 1 {
194            (src_desc.format.planar_sample_format(), dst_desc.format.planar_sample_format())
195        } else {
196            (src_desc.format, dst_desc.format)
197        };
198
199        if src_format == dst_format {
200            data_copy(&src_planes, &mut dst_planes)
201        } else {
202            data_convert(&src_planes, &mut dst_planes, src_format, dst_format, src_channels, src_desc.samples.get())
203        }
204    }
205
206    pub fn convert_to(&self, dst: &mut AudioFrame) -> Result<()> {
207        Self::convert_audio_to_internal(&self.desc, &self.data, &dst.desc, &mut dst.data)
208    }
209}