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