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