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; static 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 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}