| 1 | //! Sound format conversion. |
| 2 | //! |
| 3 | //! This module implements the functionality for conversion between different sound formats: packed or planar audio, 8-/16-/24-/32-bit, integer or floating point, different number of channels. |
| 4 | //! Eventually this might support resampling as well. |
| 5 | pub use crate::formats::{NASoniton,NAChannelMap}; |
| 6 | pub use crate::frame::{NAAudioBuffer,NAAudioInfo,NABufferType}; |
| 7 | use crate::formats::NAChannelType; |
| 8 | use crate::frame::alloc_audio_buffer; |
| 9 | use crate::io::byteio::*; |
| 10 | use std::f32::consts::SQRT_2; |
| 11 | |
| 12 | /// A list specifying general sound conversion errors. |
| 13 | #[derive(Clone,Copy,Debug,PartialEq)] |
| 14 | pub enum SoundConvertError { |
| 15 | /// Invalid input arguments. |
| 16 | InvalidInput, |
| 17 | /// Allocation failed. |
| 18 | AllocError, |
| 19 | /// Requested feature is not supported. |
| 20 | Unsupported, |
| 21 | } |
| 22 | |
| 23 | enum ChannelOp { |
| 24 | Passthrough, |
| 25 | Reorder(Vec<usize>), |
| 26 | Remix(Vec<f32>), |
| 27 | DupMono(Vec<bool>), |
| 28 | } |
| 29 | |
| 30 | impl ChannelOp { |
| 31 | fn is_remix(&self) -> bool { |
| 32 | match *self { |
| 33 | ChannelOp::Remix(_) => true, |
| 34 | ChannelOp::DupMono(_) => true, |
| 35 | _ => false, |
| 36 | } |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | fn apply_channel_op<T:Copy>(ch_op: &ChannelOp, src: &[T], dst: &mut Vec<T>) { |
| 41 | match *ch_op { |
| 42 | ChannelOp::Passthrough => { |
| 43 | dst.copy_from_slice(src); |
| 44 | }, |
| 45 | ChannelOp::Reorder(ref reorder) => { |
| 46 | for (out, idx) in dst.iter_mut().zip(reorder.iter()) { |
| 47 | *out = src[*idx]; |
| 48 | } |
| 49 | }, |
| 50 | _ => {}, |
| 51 | }; |
| 52 | } |
| 53 | |
| 54 | fn remix_i32(ch_op: &ChannelOp, src: &[i32], dst: &mut Vec<i32>) { |
| 55 | if let ChannelOp::Remix(ref remix_mat) = ch_op { |
| 56 | let sch = src.len(); |
| 57 | for (out, coeffs) in dst.iter_mut().zip(remix_mat.chunks(sch)) { |
| 58 | let mut sum = 0.0; |
| 59 | for (inval, coef) in src.iter().zip(coeffs.iter()) { |
| 60 | sum += (*inval as f32) * *coef; |
| 61 | } |
| 62 | *out = sum as i32; |
| 63 | } |
| 64 | } |
| 65 | if let ChannelOp::DupMono(ref dup_mat) = ch_op { |
| 66 | let src = src[0]; |
| 67 | for (out, copy) in dst.iter_mut().zip(dup_mat.iter()) { |
| 68 | *out = if *copy { src } else { 0 }; |
| 69 | } |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | fn remix_f32(ch_op: &ChannelOp, src: &[f32], dst: &mut Vec<f32>) { |
| 74 | if let ChannelOp::Remix(ref remix_mat) = ch_op { |
| 75 | let sch = src.len(); |
| 76 | for (out, coeffs) in dst.iter_mut().zip(remix_mat.chunks(sch)) { |
| 77 | let mut sum = 0.0; |
| 78 | for (inval, coef) in src.iter().zip(coeffs.iter()) { |
| 79 | sum += *inval * *coef; |
| 80 | } |
| 81 | *out = sum; |
| 82 | } |
| 83 | } |
| 84 | if let ChannelOp::DupMono(ref dup_mat) = ch_op { |
| 85 | let src = src[0]; |
| 86 | for (out, copy) in dst.iter_mut().zip(dup_mat.iter()) { |
| 87 | *out = if *copy { src } else { 0.0 }; |
| 88 | } |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | trait FromFmt<T:Copy> { |
| 93 | fn cvt_from(val: T) -> Self; |
| 94 | } |
| 95 | |
| 96 | impl FromFmt<u8> for u8 { |
| 97 | fn cvt_from(val: u8) -> u8 { val } |
| 98 | } |
| 99 | impl FromFmt<u8> for i16 { |
| 100 | fn cvt_from(val: u8) -> i16 { u16::from(val ^ 0x80).wrapping_mul(0x101) as i16} |
| 101 | } |
| 102 | impl FromFmt<u8> for i32 { |
| 103 | fn cvt_from(val: u8) -> i32 { u32::from(val ^ 0x80).wrapping_mul(0x01010101) as i32 } |
| 104 | } |
| 105 | impl FromFmt<u8> for f32 { |
| 106 | fn cvt_from(val: u8) -> f32 { (f32::from(val) - 128.0) / 128.0 } |
| 107 | } |
| 108 | |
| 109 | impl FromFmt<i16> for u8 { |
| 110 | fn cvt_from(val: i16) -> u8 { ((val >> 8) + 128).min(255).max(0) as u8 } |
| 111 | } |
| 112 | impl FromFmt<i16> for i16 { |
| 113 | fn cvt_from(val: i16) -> i16 { val } |
| 114 | } |
| 115 | impl FromFmt<i16> for i32 { |
| 116 | fn cvt_from(val: i16) -> i32 { (i32::from(val) & 0xFFFF) | (i32::from(val) << 16) } |
| 117 | } |
| 118 | impl FromFmt<i16> for f32 { |
| 119 | fn cvt_from(val: i16) -> f32 { f32::from(val) / 32768.0 } |
| 120 | } |
| 121 | |
| 122 | impl FromFmt<i32> for u8 { |
| 123 | fn cvt_from(val: i32) -> u8 { ((val >> 24) + 128).min(255).max(0) as u8 } |
| 124 | } |
| 125 | impl FromFmt<i32> for i16 { |
| 126 | fn cvt_from(val: i32) -> i16 { (val >> 16) as i16 } |
| 127 | } |
| 128 | impl FromFmt<i32> for i32 { |
| 129 | fn cvt_from(val: i32) -> i32 { val } |
| 130 | } |
| 131 | impl FromFmt<i32> for f32 { |
| 132 | fn cvt_from(val: i32) -> f32 { (val as f32) / 31.0f32.exp2() } |
| 133 | } |
| 134 | |
| 135 | impl FromFmt<f32> for u8 { |
| 136 | fn cvt_from(val: f32) -> u8 { ((val * 128.0) + 128.0).min(255.0).max(0.0) as u8 } |
| 137 | } |
| 138 | impl FromFmt<f32> for i16 { |
| 139 | fn cvt_from(val: f32) -> i16 { (val * 32768.0).min(32767.0).max(-32768.0) as i16 } |
| 140 | } |
| 141 | impl FromFmt<f32> for i32 { |
| 142 | fn cvt_from(val: f32) -> i32 { |
| 143 | if val >= 1.0 { |
| 144 | std::i32::MAX |
| 145 | } else if val <= -1.0 { |
| 146 | std::i32::MIN |
| 147 | } else { |
| 148 | let scale = (1u32 << 31) as f32; |
| 149 | (val * scale) as i32 |
| 150 | } |
| 151 | } |
| 152 | } |
| 153 | impl FromFmt<f32> for f32 { |
| 154 | fn cvt_from(val: f32) -> f32 { val } |
| 155 | } |
| 156 | |
| 157 | trait IntoFmt<T:Copy> { |
| 158 | fn cvt_into(self) -> T; |
| 159 | } |
| 160 | |
| 161 | impl<T:Copy, U:Copy> IntoFmt<U> for T where U: FromFmt<T> { |
| 162 | fn cvt_into(self) -> U { U::cvt_from(self) } |
| 163 | } |
| 164 | |
| 165 | |
| 166 | trait SampleReader { |
| 167 | fn get_samples_i32(&self, pos: usize, dst: &mut Vec<i32>); |
| 168 | fn get_samples_f32(&self, pos: usize, dst: &mut Vec<f32>); |
| 169 | } |
| 170 | |
| 171 | struct GenericSampleReader<'a, T:Copy> { |
| 172 | data: &'a [T], |
| 173 | stride: usize, |
| 174 | } |
| 175 | |
| 176 | impl<'a, T:Copy+IntoFmt<i32>+IntoFmt<f32>> SampleReader for GenericSampleReader<'a, T> { |
| 177 | fn get_samples_i32(&self, pos: usize, dst: &mut Vec<i32>) { |
| 178 | let mut off = pos; |
| 179 | for el in dst.iter_mut() { |
| 180 | *el = self.data[off].cvt_into(); |
| 181 | off += self.stride; |
| 182 | } |
| 183 | } |
| 184 | fn get_samples_f32(&self, pos: usize, dst: &mut Vec<f32>) { |
| 185 | let mut off = pos; |
| 186 | for el in dst.iter_mut() { |
| 187 | *el = self.data[off].cvt_into(); |
| 188 | off += self.stride; |
| 189 | } |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | struct S8SampleReader<'a> { |
| 194 | data: &'a [u8], |
| 195 | stride: usize, |
| 196 | } |
| 197 | |
| 198 | impl<'a> SampleReader for S8SampleReader<'a> { |
| 199 | fn get_samples_i32(&self, pos: usize, dst: &mut Vec<i32>) { |
| 200 | let mut off = pos; |
| 201 | for el in dst.iter_mut() { |
| 202 | *el = (self.data[off] ^ 0x80).cvt_into(); |
| 203 | off += self.stride; |
| 204 | } |
| 205 | } |
| 206 | fn get_samples_f32(&self, pos: usize, dst: &mut Vec<f32>) { |
| 207 | let mut off = pos; |
| 208 | for el in dst.iter_mut() { |
| 209 | *el = (self.data[off] ^ 0x80).cvt_into(); |
| 210 | off += self.stride; |
| 211 | } |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | struct PackedSampleReader<'a> { |
| 216 | data: &'a [u8], |
| 217 | fmt: NASoniton, |
| 218 | bpp: usize, |
| 219 | } |
| 220 | |
| 221 | impl<'a> PackedSampleReader<'a> { |
| 222 | fn new(data: &'a [u8], fmt: NASoniton) -> Self { |
| 223 | if (fmt.bits & 7) != 0 { unimplemented!(); } |
| 224 | let bpp = (fmt.bits >> 3) as usize; |
| 225 | Self { data, fmt, bpp } |
| 226 | } |
| 227 | fn get_samples<T:Copy>(&self, pos: usize, dst: &mut Vec<T>) where u8: IntoFmt<T>, i16: IntoFmt<T>, i32: IntoFmt<T>, f32: IntoFmt<T> { |
| 228 | let mut offset = pos * self.bpp * dst.len(); |
| 229 | |
| 230 | for el in dst.iter_mut() { |
| 231 | let src = &self.data[offset..]; |
| 232 | *el = if !self.fmt.float { |
| 233 | match (self.bpp, self.fmt.be) { |
| 234 | (1, _) => if !self.fmt.signed { src[0].cvt_into() } else { (src[0] ^ 0x80).cvt_into() }, |
| 235 | (2, true) => (read_u16be(src).unwrap() as i16).cvt_into(), |
| 236 | (2, false) => (read_u16le(src).unwrap() as i16).cvt_into(), |
| 237 | (3, true) => ((read_u24be(src).unwrap() << 8) as i32).cvt_into(), |
| 238 | (3, false) => ((read_u24be(src).unwrap() << 8) as i32).cvt_into(), |
| 239 | (4, true) => (read_u32be(src).unwrap() as i32).cvt_into(), |
| 240 | (4, false) => (read_u32be(src).unwrap() as i32).cvt_into(), |
| 241 | _ => unreachable!(), |
| 242 | } |
| 243 | } else { |
| 244 | match (self.bpp, self.fmt.be) { |
| 245 | (4, true) => read_f32be(src).unwrap().cvt_into(), |
| 246 | (4, false) => read_f32le(src).unwrap().cvt_into(), |
| 247 | (8, true) => (read_f64be(src).unwrap() as f32).cvt_into(), |
| 248 | (8, false) => (read_f64le(src).unwrap() as f32).cvt_into(), |
| 249 | (_, _) => unreachable!(), |
| 250 | } |
| 251 | }; |
| 252 | offset += self.bpp; |
| 253 | } |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | impl SampleReader for PackedSampleReader<'_> { |
| 258 | fn get_samples_i32(&self, pos: usize, dst: &mut Vec<i32>) { |
| 259 | self.get_samples(pos, dst); |
| 260 | } |
| 261 | fn get_samples_f32(&self, pos: usize, dst: &mut Vec<f32>) { |
| 262 | self.get_samples(pos, dst); |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | trait SampleWriter { |
| 267 | fn store_samples_i32(&mut self, pos: usize, src: &[i32]); |
| 268 | fn store_samples_f32(&mut self, pos: usize, src: &[f32]); |
| 269 | } |
| 270 | |
| 271 | struct GenericSampleWriter<'a, T:Copy> { |
| 272 | data: &'a mut [T], |
| 273 | stride: usize, |
| 274 | } |
| 275 | |
| 276 | impl<'a, T:Copy+FromFmt<i32>+FromFmt<f32>> SampleWriter for GenericSampleWriter<'a, T> { |
| 277 | fn store_samples_i32(&mut self, pos: usize, src: &[i32]) { |
| 278 | let mut off = pos; |
| 279 | for el in src.iter() { |
| 280 | self.data[off] = (*el).cvt_into(); |
| 281 | off += self.stride; |
| 282 | } |
| 283 | } |
| 284 | fn store_samples_f32(&mut self, pos: usize, src: &[f32]) { |
| 285 | let mut off = pos; |
| 286 | for el in src.iter() { |
| 287 | self.data[off] = (*el).cvt_into(); |
| 288 | off += self.stride; |
| 289 | } |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | struct PackedSampleWriter<'a> { |
| 294 | data: &'a mut [u8], |
| 295 | fmt: NASoniton, |
| 296 | bpp: usize, |
| 297 | } |
| 298 | |
| 299 | impl<'a> PackedSampleWriter<'a> { |
| 300 | fn new(data: &'a mut [u8], fmt: NASoniton) -> Self { |
| 301 | if (fmt.bits & 7) != 0 { unimplemented!(); } |
| 302 | let bpp = (fmt.bits >> 3) as usize; |
| 303 | Self { data, fmt, bpp } |
| 304 | } |
| 305 | |
| 306 | fn store_samples<T:Copy>(&mut self, pos: usize, src: &[T]) where u8: FromFmt<T>, i16: FromFmt<T>, i32: FromFmt<T>, f32: FromFmt<T> { |
| 307 | let mut offset = pos * self.bpp * src.len(); |
| 308 | for el in src.iter() { |
| 309 | let dst = &mut self.data[offset..]; |
| 310 | if !self.fmt.float { |
| 311 | match (self.bpp, self.fmt.be) { |
| 312 | (1, _) => { |
| 313 | dst[0] = u8::cvt_from(*el); |
| 314 | if self.fmt.signed { |
| 315 | dst[0] ^= 0x80; |
| 316 | } |
| 317 | }, |
| 318 | (2, true) => write_u16be(dst, i16::cvt_from(*el) as u16).unwrap(), |
| 319 | (2, false) => write_u16le(dst, i16::cvt_from(*el) as u16).unwrap(), |
| 320 | (3, true) => write_u24be(dst, (i32::cvt_from(*el) >> 8) as u32).unwrap(), |
| 321 | (3, false) => write_u24le(dst, (i32::cvt_from(*el) >> 8) as u32).unwrap(), |
| 322 | (4, true) => write_u32be(dst, i32::cvt_from(*el) as u32).unwrap(), |
| 323 | (4, false) => write_u32le(dst, i32::cvt_from(*el) as u32).unwrap(), |
| 324 | _ => unreachable!(), |
| 325 | }; |
| 326 | } else { |
| 327 | match (self.bpp, self.fmt.be) { |
| 328 | (4, true) => write_f32be(dst, f32::cvt_from(*el)).unwrap(), |
| 329 | (4, false) => write_f32le(dst, f32::cvt_from(*el)).unwrap(), |
| 330 | (8, true) => write_f64be(dst, f64::from(f32::cvt_from(*el))).unwrap(), |
| 331 | (8, false) => write_f64le(dst, f64::from(f32::cvt_from(*el))).unwrap(), |
| 332 | (_, _) => unreachable!(), |
| 333 | }; |
| 334 | } |
| 335 | offset += self.bpp; |
| 336 | } |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | impl SampleWriter for PackedSampleWriter<'_> { |
| 341 | fn store_samples_i32(&mut self, pos: usize, src: &[i32]) { |
| 342 | self.store_samples(pos, src); |
| 343 | } |
| 344 | fn store_samples_f32(&mut self, pos: usize, src: &[f32]) { |
| 345 | self.store_samples(pos, src); |
| 346 | } |
| 347 | } |
| 348 | |
| 349 | /// Converts input audio buffer into desired format and returns a newly allocated buffer. |
| 350 | pub fn convert_audio_frame(src: &NABufferType, dst_info: &NAAudioInfo, dst_chmap: &NAChannelMap) -> |
| 351 | Result<NABufferType, SoundConvertError> { |
| 352 | let mut nsamples = src.get_audio_length(); |
| 353 | if nsamples == 0 { |
| 354 | return Err(SoundConvertError::InvalidInput); |
| 355 | } |
| 356 | let src_chmap = src.get_chmap().unwrap(); |
| 357 | let src_info = src.get_audio_info().unwrap(); |
| 358 | if (src_chmap.num_channels() == 0) || (dst_chmap.num_channels() == 0) { |
| 359 | return Err(SoundConvertError::InvalidInput); |
| 360 | } |
| 361 | |
| 362 | if let NABufferType::AudioPacked(_) = src { |
| 363 | nsamples = nsamples * 8 / (src_info.get_format().get_bits() as usize) / src_chmap.num_channels(); |
| 364 | } |
| 365 | |
| 366 | let needs_remix = src_chmap.num_channels() != dst_chmap.num_channels(); |
| 367 | let no_channel_needs = !needs_remix && channel_maps_equal(src_chmap, dst_chmap); |
| 368 | let needs_reorder = !needs_remix && !no_channel_needs && channel_maps_reordered(src_chmap, dst_chmap); |
| 369 | |
| 370 | let channel_op = if no_channel_needs { |
| 371 | ChannelOp::Passthrough |
| 372 | } else if needs_reorder { |
| 373 | let reorder_mat = calculate_reorder_matrix(src_chmap, dst_chmap); |
| 374 | ChannelOp::Reorder(reorder_mat) |
| 375 | } else if src_chmap.num_channels() > 1 { |
| 376 | let remix_mat = calculate_remix_matrix(src_chmap, dst_chmap); |
| 377 | ChannelOp::Remix(remix_mat) |
| 378 | } else { |
| 379 | let mut dup_mat: Vec<bool> = Vec::with_capacity(dst_chmap.num_channels()); |
| 380 | for i in 0..dst_chmap.num_channels() { |
| 381 | let ch = dst_chmap.get_channel(i); |
| 382 | if ch.is_left() || ch.is_right() || ch == NAChannelType::C { |
| 383 | dup_mat.push(true); |
| 384 | } else { |
| 385 | dup_mat.push(false); |
| 386 | } |
| 387 | } |
| 388 | ChannelOp::DupMono(dup_mat) |
| 389 | }; |
| 390 | |
| 391 | let src_fmt = src_info.get_format(); |
| 392 | let dst_fmt = dst_info.get_format(); |
| 393 | let no_conversion = src_fmt == dst_fmt; |
| 394 | |
| 395 | if no_conversion && no_channel_needs { |
| 396 | return Ok(src.clone()); |
| 397 | } |
| 398 | |
| 399 | let ret = alloc_audio_buffer(*dst_info, nsamples, dst_chmap.clone()); |
| 400 | if ret.is_err() { |
| 401 | return Err(SoundConvertError::AllocError); |
| 402 | } |
| 403 | let mut dst_buf = ret.unwrap(); |
| 404 | |
| 405 | let sstep = src.get_audio_step().max(1); |
| 406 | let dstep = dst_buf.get_audio_step().max(1); |
| 407 | let sr: Box<dyn SampleReader> = match src { |
| 408 | NABufferType::AudioU8(ref ab) => { |
| 409 | let stride = ab.get_stride(); |
| 410 | let data = ab.get_data(); |
| 411 | if !src_fmt.signed { |
| 412 | Box::new(GenericSampleReader { data, stride }) |
| 413 | } else { |
| 414 | Box::new(S8SampleReader { data, stride }) |
| 415 | } |
| 416 | }, |
| 417 | NABufferType::AudioI16(ref ab) => { |
| 418 | let data = ab.get_data(); |
| 419 | let stride = ab.get_stride(); |
| 420 | Box::new(GenericSampleReader { data, stride }) |
| 421 | }, |
| 422 | NABufferType::AudioI32(ref ab) => { |
| 423 | let data = ab.get_data(); |
| 424 | let stride = ab.get_stride(); |
| 425 | Box::new(GenericSampleReader { data, stride }) |
| 426 | }, |
| 427 | NABufferType::AudioF32(ref ab) => { |
| 428 | let data = ab.get_data(); |
| 429 | let stride = ab.get_stride(); |
| 430 | Box::new(GenericSampleReader { data, stride }) |
| 431 | }, |
| 432 | NABufferType::AudioPacked(ref ab) => { |
| 433 | let data = ab.get_data(); |
| 434 | Box::new(PackedSampleReader::new(data, src_fmt)) |
| 435 | }, |
| 436 | _ => unimplemented!(), |
| 437 | }; |
| 438 | let mut sw: Box<dyn SampleWriter> = match dst_buf { |
| 439 | NABufferType::AudioU8(ref mut ab) => { |
| 440 | let stride = ab.get_stride(); |
| 441 | let data = ab.get_data_mut().unwrap(); |
| 442 | Box::new(GenericSampleWriter { data, stride }) |
| 443 | }, |
| 444 | NABufferType::AudioI16(ref mut ab) => { |
| 445 | let stride = ab.get_stride(); |
| 446 | let data = ab.get_data_mut().unwrap(); |
| 447 | Box::new(GenericSampleWriter { data, stride }) |
| 448 | }, |
| 449 | NABufferType::AudioI32(ref mut ab) => { |
| 450 | let stride = ab.get_stride(); |
| 451 | let data = ab.get_data_mut().unwrap(); |
| 452 | Box::new(GenericSampleWriter { data, stride }) |
| 453 | }, |
| 454 | NABufferType::AudioF32(ref mut ab) => { |
| 455 | let stride = ab.get_stride(); |
| 456 | let data = ab.get_data_mut().unwrap(); |
| 457 | Box::new(GenericSampleWriter { data, stride }) |
| 458 | }, |
| 459 | NABufferType::AudioPacked(ref mut ab) => { |
| 460 | let data = ab.get_data_mut().unwrap(); |
| 461 | Box::new(PackedSampleWriter::new(data, dst_fmt)) |
| 462 | }, |
| 463 | _ => unimplemented!(), |
| 464 | }; |
| 465 | |
| 466 | let into_float = dst_fmt.float; |
| 467 | if !into_float { |
| 468 | let mut svec = vec![0; src_chmap.num_channels()]; |
| 469 | let mut dvec = vec![0; dst_chmap.num_channels()]; |
| 470 | let mut spos = 0; |
| 471 | let mut dpos = 0; |
| 472 | for _ in 0..nsamples { |
| 473 | sr.get_samples_i32(spos, &mut svec); |
| 474 | if !channel_op.is_remix() { |
| 475 | apply_channel_op(&channel_op, &svec, &mut dvec); |
| 476 | } else { |
| 477 | remix_i32(&channel_op, &svec, &mut dvec); |
| 478 | } |
| 479 | sw.store_samples_i32(dpos, &dvec); |
| 480 | spos += sstep; |
| 481 | dpos += dstep; |
| 482 | } |
| 483 | } else { |
| 484 | let mut svec = vec![0.0; src_chmap.num_channels()]; |
| 485 | let mut dvec = vec![0.0; dst_chmap.num_channels()]; |
| 486 | let mut spos = 0; |
| 487 | let mut dpos = 0; |
| 488 | for _ in 0..nsamples { |
| 489 | sr.get_samples_f32(spos, &mut svec); |
| 490 | if !channel_op.is_remix() { |
| 491 | apply_channel_op(&channel_op, &svec, &mut dvec); |
| 492 | } else { |
| 493 | remix_f32(&channel_op, &svec, &mut dvec); |
| 494 | } |
| 495 | sw.store_samples_f32(dpos, &dvec); |
| 496 | spos += sstep; |
| 497 | dpos += dstep; |
| 498 | } |
| 499 | } |
| 500 | drop(sw); |
| 501 | |
| 502 | Ok(dst_buf) |
| 503 | } |
| 504 | |
| 505 | /// Checks whether two channel maps are identical. |
| 506 | pub fn channel_maps_equal(a: &NAChannelMap, b: &NAChannelMap) -> bool { |
| 507 | if a.num_channels() != b.num_channels() { return false; } |
| 508 | for i in 0..a.num_channels() { |
| 509 | if a.get_channel(i) != b.get_channel(i) { |
| 510 | return false; |
| 511 | } |
| 512 | } |
| 513 | true |
| 514 | } |
| 515 | |
| 516 | /// Checks whether two channel maps have identical channels (but maybe in different order). |
| 517 | pub fn channel_maps_reordered(a: &NAChannelMap, b: &NAChannelMap) -> bool { |
| 518 | if a.num_channels() != b.num_channels() { return false; } |
| 519 | let mut count_a = [0u8; 32]; |
| 520 | let mut count_b = [0u8; 32]; |
| 521 | for i in 0..a.num_channels() { |
| 522 | count_a[a.get_channel(i) as usize] += 1; |
| 523 | count_b[b.get_channel(i) as usize] += 1; |
| 524 | } |
| 525 | for (c0, c1) in count_a.iter().zip(count_b.iter()) { |
| 526 | if *c0 != *c1 { |
| 527 | return false; |
| 528 | } |
| 529 | } |
| 530 | true |
| 531 | } |
| 532 | |
| 533 | /// Calculates permutation matrix for reordering channels from source channel map into destination one. |
| 534 | pub fn calculate_reorder_matrix(src: &NAChannelMap, dst: &NAChannelMap) -> Vec<usize> { |
| 535 | if src.num_channels() != dst.num_channels() { return Vec::new(); } |
| 536 | let num_channels = src.num_channels(); |
| 537 | let mut reorder: Vec<usize> = Vec::with_capacity(num_channels); |
| 538 | for i in 0..num_channels { |
| 539 | let dst_ch = dst.get_channel(i); |
| 540 | for j in 0..num_channels { |
| 541 | if src.get_channel(j) == dst_ch { |
| 542 | reorder.push(j); |
| 543 | break; |
| 544 | } |
| 545 | } |
| 546 | } |
| 547 | if reorder.len() != num_channels { reorder.clear(); } |
| 548 | reorder |
| 549 | } |
| 550 | |
| 551 | fn is_stereo(chmap: &NAChannelMap) -> bool { |
| 552 | (chmap.num_channels() == 2) && |
| 553 | (chmap.get_channel(0) == NAChannelType::L) && |
| 554 | (chmap.get_channel(1) == NAChannelType::R) |
| 555 | } |
| 556 | |
| 557 | /// Calculates matrix of remixing coefficients for converting input channel layout into destination one. |
| 558 | pub fn calculate_remix_matrix(src: &NAChannelMap, dst: &NAChannelMap) -> Vec<f32> { |
| 559 | if is_stereo(src) && dst.num_channels() == 1 && |
| 560 | (dst.get_channel(0) == NAChannelType::L || dst.get_channel(0) == NAChannelType::C) { |
| 561 | return vec![0.5, 0.5]; |
| 562 | } |
| 563 | if src.num_channels() >= 5 && is_stereo(dst) { |
| 564 | let src_nch = src.num_channels(); |
| 565 | let mut mat = vec![0.0f32; src_nch * 2]; |
| 566 | let (l_mat, r_mat) = mat.split_at_mut(src_nch); |
| 567 | for ch in 0..src_nch { |
| 568 | match src.get_channel(ch) { |
| 569 | NAChannelType::L => l_mat[ch] = 1.0, |
| 570 | NAChannelType::R => r_mat[ch] = 1.0, |
| 571 | NAChannelType::C => { l_mat[ch] = SQRT_2 / 2.0; r_mat[ch] = SQRT_2 / 2.0; }, |
| 572 | NAChannelType::Ls => l_mat[ch] = SQRT_2 / 2.0, |
| 573 | NAChannelType::Rs => r_mat[ch] = SQRT_2 / 2.0, |
| 574 | _ => {}, |
| 575 | }; |
| 576 | } |
| 577 | return mat; |
| 578 | } |
| 579 | unimplemented!(); |
| 580 | } |
| 581 | |
| 582 | #[cfg(test)] |
| 583 | mod test { |
| 584 | use super::*; |
| 585 | use std::str::FromStr; |
| 586 | use crate::formats::*; |
| 587 | |
| 588 | #[test] |
| 589 | fn test_matrices() { |
| 590 | let chcfg51 = NAChannelMap::from_str("L,R,C,LFE,Ls,Rs").unwrap(); |
| 591 | let chcfg52 = NAChannelMap::from_str("C,L,R,Ls,Rs,LFE").unwrap(); |
| 592 | let stereo = NAChannelMap::from_str("L,R").unwrap(); |
| 593 | let reorder = calculate_reorder_matrix(&chcfg51, &chcfg52); |
| 594 | assert_eq!(reorder.as_slice(), [ 2, 0, 1, 4, 5, 3]); |
| 595 | let remix = calculate_remix_matrix(&chcfg51, &stereo); |
| 596 | assert_eq!(remix.as_slice(), [ 1.0, 0.0, SQRT_2 / 2.0, 0.0, SQRT_2 / 2.0, 0.0, |
| 597 | 0.0, 1.0, SQRT_2 / 2.0, 0.0, 0.0, SQRT_2 / 2.0 ]); |
| 598 | } |
| 599 | #[test] |
| 600 | fn test_conversion() { |
| 601 | const CHANNEL_VALUES: [u8; 6] = [ 140, 90, 130, 128, 150, 70 ]; |
| 602 | let chcfg51 = NAChannelMap::from_str("L,R,C,LFE,Ls,Rs").unwrap(); |
| 603 | let stereo = NAChannelMap::from_str("L,R").unwrap(); |
| 604 | let src_ainfo = NAAudioInfo { |
| 605 | sample_rate: 44100, |
| 606 | channels: chcfg51.num_channels() as u8, |
| 607 | format: SND_U8_FORMAT, |
| 608 | block_len: 512, |
| 609 | }; |
| 610 | let mut dst_ainfo = NAAudioInfo { |
| 611 | sample_rate: 44100, |
| 612 | channels: stereo.num_channels() as u8, |
| 613 | format: SND_S16P_FORMAT, |
| 614 | block_len: 512, |
| 615 | }; |
| 616 | let mut src_frm = alloc_audio_buffer(src_ainfo, 42, chcfg51.clone()).unwrap(); |
| 617 | if let NABufferType::AudioU8(ref mut abuf) = src_frm { |
| 618 | let data = abuf.get_data_mut().unwrap(); |
| 619 | let mut idx = 0; |
| 620 | for _ in 0..42 { |
| 621 | for ch in 0..chcfg51.num_channels() { |
| 622 | data[idx] = CHANNEL_VALUES[ch]; |
| 623 | idx += 1; |
| 624 | } |
| 625 | } |
| 626 | } else { |
| 627 | panic!("wrong buffer type"); |
| 628 | } |
| 629 | |
| 630 | let out_frm = convert_audio_frame(&src_frm, &dst_ainfo, &stereo).unwrap(); |
| 631 | if let NABufferType::AudioI16(ref abuf) = out_frm { |
| 632 | let off0 = abuf.get_offset(0); |
| 633 | let off1 = abuf.get_offset(1); |
| 634 | let data = abuf.get_data(); |
| 635 | let l = data[off0]; |
| 636 | let r = data[off1]; |
| 637 | assert_eq!(l, 7445); |
| 638 | assert_eq!(r, -19943); |
| 639 | } else { |
| 640 | panic!("wrong buffer type"); |
| 641 | } |
| 642 | |
| 643 | dst_ainfo.format = SND_F32P_FORMAT; |
| 644 | let out_frm = convert_audio_frame(&src_frm, &dst_ainfo, &stereo).unwrap(); |
| 645 | if let NABufferType::AudioF32(ref abuf) = out_frm { |
| 646 | let off0 = abuf.get_offset(0); |
| 647 | let off1 = abuf.get_offset(1); |
| 648 | let data = abuf.get_data(); |
| 649 | let l = data[off0]; |
| 650 | let r = data[off1]; |
| 651 | assert_eq!(l, 0.22633252); |
| 652 | assert_eq!(r, -0.6062342); |
| 653 | } else { |
| 654 | panic!("wrong buffer type"); |
| 655 | } |
| 656 | } |
| 657 | } |