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