core/soundcvt: fix convertsion of certain sample types
[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 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 { (val * 31.0f32.exp2()) as i32 }
143 }
144 impl FromFmt<f32> for f32 {
145 fn cvt_from(val: f32) -> f32 { val }
146 }
147
148 trait IntoFmt<T:Copy> {
149 fn cvt_into(self) -> T;
150 }
151
152 impl<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
157 trait 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
162 struct GenericSampleReader<'a, T:Copy> {
163 data: &'a [T],
164 stride: usize,
165 }
166
167 impl<'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 }
181 }
182 }
183
184 struct PackedSampleReader<'a> {
185 data: &'a [u8],
186 fmt: NASoniton,
187 bpp: usize,
188 }
189
190 impl<'a> PackedSampleReader<'a> {
191 fn new(data: &'a [u8], fmt: NASoniton) -> Self {
192 if (fmt.bits & 7) != 0 { unimplemented!(); }
193 let bpp = (fmt.bits >> 3) as usize;
194 Self { data, fmt, bpp }
195 }
196 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> {
197 let mut offset = pos * self.bpp * dst.len();
198
199 for el in dst.iter_mut() {
200 let src = &self.data[offset..];
201 *el = if !self.fmt.float {
202 match (self.bpp, self.fmt.be) {
203 (1, _) => src[0].cvt_into(),
204 (2, true) => (read_u16be(src).unwrap() as i16).cvt_into(),
205 (2, false) => (read_u16le(src).unwrap() as i16).cvt_into(),
206 (3, true) => ((read_u24be(src).unwrap() << 8) as i32).cvt_into(),
207 (3, false) => ((read_u24be(src).unwrap() << 8) as i32).cvt_into(),
208 (4, true) => (read_u32be(src).unwrap() as i32).cvt_into(),
209 (4, false) => (read_u32be(src).unwrap() as i32).cvt_into(),
210 _ => unreachable!(),
211 }
212 } else {
213 match (self.bpp, self.fmt.be) {
214 (4, true) => read_f32be(src).unwrap().cvt_into(),
215 (4, false) => read_f32le(src).unwrap().cvt_into(),
216 (8, true) => (read_f64be(src).unwrap() as f32).cvt_into(),
217 (8, false) => (read_f64le(src).unwrap() as f32).cvt_into(),
218 (_, _) => unreachable!(),
219 }
220 };
221 offset += self.bpp;
222 }
223 }
224 }
225
226 impl SampleReader for PackedSampleReader<'_> {
227 fn get_samples_i32(&self, pos: usize, dst: &mut Vec<i32>) {
228 self.get_samples(pos, dst);
229 }
230 fn get_samples_f32(&self, pos: usize, dst: &mut Vec<f32>) {
231 self.get_samples(pos, dst);
232 }
233 }
234
235 trait SampleWriter {
236 fn store_samples_i32(&mut self, pos: usize, src: &[i32]);
237 fn store_samples_f32(&mut self, pos: usize, src: &[f32]);
238 }
239
240 struct GenericSampleWriter<'a, T:Copy> {
241 data: &'a mut [T],
242 stride: usize,
243 }
244
245 impl<'a, T:Copy+FromFmt<i32>+FromFmt<f32>> SampleWriter for GenericSampleWriter<'a, T> {
246 fn store_samples_i32(&mut self, pos: usize, src: &[i32]) {
247 let mut off = pos;
248 for el in src.iter() {
249 self.data[off] = (*el).cvt_into();
250 off += self.stride;
251 }
252 }
253 fn store_samples_f32(&mut self, pos: usize, src: &[f32]) {
254 let mut off = pos;
255 for el in src.iter() {
256 self.data[off] = (*el).cvt_into();
257 off += self.stride;
258 }
259 }
260 }
261
262 struct PackedSampleWriter<'a> {
263 data: &'a mut [u8],
264 fmt: NASoniton,
265 bpp: usize,
266 }
267
268 impl<'a> PackedSampleWriter<'a> {
269 fn new(data: &'a mut [u8], fmt: NASoniton) -> Self {
270 if (fmt.bits & 7) != 0 { unimplemented!(); }
271 let bpp = (fmt.bits >> 3) as usize;
272 Self { data, fmt, bpp }
273 }
274
275 fn store_samples<T:Copy>(&mut self, pos: usize, src: &[T]) where u8: FromFmt<T>, i16: FromFmt<T>, i32: FromFmt<T>, f32: FromFmt<T> {
276 let mut offset = pos * self.bpp * src.len();
277 for el in src.iter() {
278 let dst = &mut self.data[offset..];
279 if !self.fmt.float {
280 match (self.bpp, self.fmt.be) {
281 (1, _) => {
282 dst[0] = u8::cvt_from(*el);
283 },
284 (2, true) => write_u16be(dst, i16::cvt_from(*el) as u16).unwrap(),
285 (2, false) => write_u16le(dst, i16::cvt_from(*el) as u16).unwrap(),
286 (3, true) => write_u24be(dst, (i32::cvt_from(*el) >> 8) as u32).unwrap(),
287 (3, false) => write_u24le(dst, (i32::cvt_from(*el) >> 8) as u32).unwrap(),
288 (4, true) => write_u32be(dst, i32::cvt_from(*el) as u32).unwrap(),
289 (4, false) => write_u32le(dst, i32::cvt_from(*el) as u32).unwrap(),
290 _ => unreachable!(),
291 };
292 } else {
293 match (self.bpp, self.fmt.be) {
294 (4, true) => write_f32be(dst, f32::cvt_from(*el)).unwrap(),
295 (4, false) => write_f32le(dst, f32::cvt_from(*el)).unwrap(),
296 (8, true) => write_f64be(dst, f64::from(f32::cvt_from(*el))).unwrap(),
297 (8, false) => write_f64le(dst, f64::from(f32::cvt_from(*el))).unwrap(),
298 (_, _) => unreachable!(),
299 };
300 }
301 offset += self.bpp;
302 }
303 }
304 }
305
306 impl SampleWriter for PackedSampleWriter<'_> {
307 fn store_samples_i32(&mut self, pos: usize, src: &[i32]) {
308 self.store_samples(pos, src);
309 }
310 fn store_samples_f32(&mut self, pos: usize, src: &[f32]) {
311 self.store_samples(pos, src);
312 }
313 }
314
315 /// Converts input audio buffer into desired format and returns a newly allocated buffer.
316 pub fn convert_audio_frame(src: &NABufferType, dst_info: &NAAudioInfo, dst_chmap: &NAChannelMap) ->
317 Result<NABufferType, SoundConvertError> {
318 let mut nsamples = src.get_audio_length();
319 if nsamples == 0 {
320 return Err(SoundConvertError::InvalidInput);
321 }
322 let src_chmap = src.get_chmap().unwrap();
323 let src_info = src.get_audio_info().unwrap();
324 if (src_chmap.num_channels() == 0) || (dst_chmap.num_channels() == 0) {
325 return Err(SoundConvertError::InvalidInput);
326 }
327
328 if let NABufferType::AudioPacked(_) = src {
329 nsamples = nsamples * 8 / (src_info.get_format().get_bits() as usize) / src_chmap.num_channels();
330 }
331
332 let needs_remix = src_chmap.num_channels() != dst_chmap.num_channels();
333 let no_channel_needs = !needs_remix && channel_maps_equal(src_chmap, dst_chmap);
334 let needs_reorder = !needs_remix && !no_channel_needs && channel_maps_reordered(src_chmap, dst_chmap);
335
336 let channel_op = if no_channel_needs {
337 ChannelOp::Passthrough
338 } else if needs_reorder {
339 let reorder_mat = calculate_reorder_matrix(src_chmap, dst_chmap);
340 ChannelOp::Reorder(reorder_mat)
341 } else if src_chmap.num_channels() > 1 {
342 let remix_mat = calculate_remix_matrix(src_chmap, dst_chmap);
343 ChannelOp::Remix(remix_mat)
344 } else {
345 let mut dup_mat: Vec<bool> = Vec::with_capacity(dst_chmap.num_channels());
346 for i in 0..dst_chmap.num_channels() {
347 let ch = dst_chmap.get_channel(i);
348 if ch.is_left() || ch.is_right() || ch == NAChannelType::C {
349 dup_mat.push(true);
350 } else {
351 dup_mat.push(false);
352 }
353 }
354 ChannelOp::DupMono(dup_mat)
355 };
356
357 let src_fmt = src_info.get_format();
358 let dst_fmt = dst_info.get_format();
359 let no_conversion = src_fmt == dst_fmt;
360
361 if no_conversion && no_channel_needs {
362 return Ok(src.clone());
363 }
364
365 let ret = alloc_audio_buffer(*dst_info, nsamples, dst_chmap.clone());
366 if ret.is_err() {
367 return Err(SoundConvertError::AllocError);
368 }
369 let mut dst_buf = ret.unwrap();
370
371 let sstep = src.get_audio_step().max(1);
372 let dstep = dst_buf.get_audio_step().max(1);
373 let sr: Box<dyn SampleReader> = match src {
374 NABufferType::AudioU8(ref ab) => {
375 let stride = ab.get_stride();
376 let data = ab.get_data();
377 Box::new(GenericSampleReader { data, stride })
378 },
379 NABufferType::AudioI16(ref ab) => {
380 let data = ab.get_data();
381 let stride = ab.get_stride();
382 Box::new(GenericSampleReader { data, stride })
383 },
384 NABufferType::AudioI32(ref ab) => {
385 let data = ab.get_data();
386 let stride = ab.get_stride();
387 Box::new(GenericSampleReader { data, stride })
388 },
389 NABufferType::AudioF32(ref ab) => {
390 let data = ab.get_data();
391 let stride = ab.get_stride();
392 Box::new(GenericSampleReader { data, stride })
393 },
394 NABufferType::AudioPacked(ref ab) => {
395 let data = ab.get_data();
396 Box::new(PackedSampleReader::new(data, src_fmt))
397 },
398 _ => unimplemented!(),
399 };
400 let mut sw: Box<dyn SampleWriter> = match dst_buf {
401 NABufferType::AudioU8(ref mut ab) => {
402 let stride = ab.get_stride();
403 let data = ab.get_data_mut().unwrap();
404 Box::new(GenericSampleWriter { data, stride })
405 },
406 NABufferType::AudioI16(ref mut ab) => {
407 let stride = ab.get_stride();
408 let data = ab.get_data_mut().unwrap();
409 Box::new(GenericSampleWriter { data, stride })
410 },
411 NABufferType::AudioI32(ref mut ab) => {
412 let stride = ab.get_stride();
413 let data = ab.get_data_mut().unwrap();
414 Box::new(GenericSampleWriter { data, stride })
415 },
416 NABufferType::AudioF32(ref mut ab) => {
417 let stride = ab.get_stride();
418 let data = ab.get_data_mut().unwrap();
419 Box::new(GenericSampleWriter { data, stride })
420 },
421 NABufferType::AudioPacked(ref mut ab) => {
422 let data = ab.get_data_mut().unwrap();
423 Box::new(PackedSampleWriter::new(data, dst_fmt))
424 },
425 _ => unimplemented!(),
426 };
427
428 let into_float = dst_fmt.float;
429 if !into_float {
430 let mut svec = vec![0; src_chmap.num_channels()];
431 let mut dvec = vec![0; dst_chmap.num_channels()];
432 let mut spos = 0;
433 let mut dpos = 0;
434 for _ in 0..nsamples {
435 sr.get_samples_i32(spos, &mut svec);
436 if !channel_op.is_remix() {
437 apply_channel_op(&channel_op, &svec, &mut dvec);
438 } else {
439 remix_i32(&channel_op, &svec, &mut dvec);
440 }
441 sw.store_samples_i32(dpos, &dvec);
442 spos += sstep;
443 dpos += dstep;
444 }
445 } else {
446 let mut svec = vec![0.0; src_chmap.num_channels()];
447 let mut dvec = vec![0.0; dst_chmap.num_channels()];
448 let mut spos = 0;
449 let mut dpos = 0;
450 for _ in 0..nsamples {
451 sr.get_samples_f32(spos, &mut svec);
452 if !channel_op.is_remix() {
453 apply_channel_op(&channel_op, &svec, &mut dvec);
454 } else {
455 remix_f32(&channel_op, &svec, &mut dvec);
456 }
457 sw.store_samples_f32(dpos, &dvec);
458 spos += sstep;
459 dpos += dstep;
460 }
461 }
462 drop(sw);
463
464 Ok(dst_buf)
465 }
466
467 /// Checks whether two channel maps are identical.
468 pub fn channel_maps_equal(a: &NAChannelMap, b: &NAChannelMap) -> bool {
469 if a.num_channels() != b.num_channels() { return false; }
470 for i in 0..a.num_channels() {
471 if a.get_channel(i) != b.get_channel(i) {
472 return false;
473 }
474 }
475 true
476 }
477
478 /// Checks whether two channel maps have identical channels (but maybe in different order).
479 pub fn channel_maps_reordered(a: &NAChannelMap, b: &NAChannelMap) -> bool {
480 if a.num_channels() != b.num_channels() { return false; }
481 let mut count_a = [0u8; 32];
482 let mut count_b = [0u8; 32];
483 for i in 0..a.num_channels() {
484 count_a[a.get_channel(i) as usize] += 1;
485 count_b[b.get_channel(i) as usize] += 1;
486 }
487 for (c0, c1) in count_a.iter().zip(count_b.iter()) {
488 if *c0 != *c1 {
489 return false;
490 }
491 }
492 true
493 }
494
495 /// Calculates permutation matrix for reordering channels from source channel map into destination one.
496 pub fn calculate_reorder_matrix(src: &NAChannelMap, dst: &NAChannelMap) -> Vec<usize> {
497 if src.num_channels() != dst.num_channels() { return Vec::new(); }
498 let num_channels = src.num_channels();
499 let mut reorder: Vec<usize> = Vec::with_capacity(num_channels);
500 for i in 0..num_channels {
501 let dst_ch = dst.get_channel(i);
502 for j in 0..num_channels {
503 if src.get_channel(j) == dst_ch {
504 reorder.push(j);
505 break;
506 }
507 }
508 }
509 if reorder.len() != num_channels { reorder.clear(); }
510 reorder
511 }
512
513 fn is_stereo(chmap: &NAChannelMap) -> bool {
514 (chmap.num_channels() == 2) &&
515 (chmap.get_channel(0) == NAChannelType::L) &&
516 (chmap.get_channel(1) == NAChannelType::R)
517 }
518
519 /// Calculates matrix of remixing coefficients for converting input channel layout into destination one.
520 pub fn calculate_remix_matrix(src: &NAChannelMap, dst: &NAChannelMap) -> Vec<f32> {
521 if is_stereo(src) && dst.num_channels() == 1 &&
522 (dst.get_channel(0) == NAChannelType::L || dst.get_channel(0) == NAChannelType::C) {
523 return vec![0.5, 0.5];
524 }
525 if src.num_channels() >= 5 && is_stereo(dst) {
526 let src_nch = src.num_channels();
527 let mut mat = vec![0.0f32; src_nch * 2];
528 let (l_mat, r_mat) = mat.split_at_mut(src_nch);
529 for ch in 0..src_nch {
530 match src.get_channel(ch) {
531 NAChannelType::L => l_mat[ch] = 1.0,
532 NAChannelType::R => r_mat[ch] = 1.0,
533 NAChannelType::C => { l_mat[ch] = SQRT_2 / 2.0; r_mat[ch] = SQRT_2 / 2.0; },
534 NAChannelType::Ls => l_mat[ch] = SQRT_2 / 2.0,
535 NAChannelType::Rs => r_mat[ch] = SQRT_2 / 2.0,
536 _ => {},
537 };
538 }
539 return mat;
540 }
541 unimplemented!();
542 }
543
544 #[cfg(test)]
545 mod test {
546 use super::*;
547 use std::str::FromStr;
548 use crate::formats::*;
549
550 #[test]
551 fn test_matrices() {
552 let chcfg51 = NAChannelMap::from_str("L,R,C,LFE,Ls,Rs").unwrap();
553 let chcfg52 = NAChannelMap::from_str("C,L,R,Ls,Rs,LFE").unwrap();
554 let stereo = NAChannelMap::from_str("L,R").unwrap();
555 let reorder = calculate_reorder_matrix(&chcfg51, &chcfg52);
556 assert_eq!(reorder.as_slice(), [ 2, 0, 1, 4, 5, 3]);
557 let remix = calculate_remix_matrix(&chcfg51, &stereo);
558 assert_eq!(remix.as_slice(), [ 1.0, 0.0, SQRT_2 / 2.0, 0.0, SQRT_2 / 2.0, 0.0,
559 0.0, 1.0, SQRT_2 / 2.0, 0.0, 0.0, SQRT_2 / 2.0 ]);
560 }
561 #[test]
562 fn test_conversion() {
563 const CHANNEL_VALUES: [u8; 6] = [ 140, 90, 130, 128, 150, 70 ];
564 let chcfg51 = NAChannelMap::from_str("L,R,C,LFE,Ls,Rs").unwrap();
565 let stereo = NAChannelMap::from_str("L,R").unwrap();
566 let src_ainfo = NAAudioInfo {
567 sample_rate: 44100,
568 channels: chcfg51.num_channels() as u8,
569 format: SND_U8_FORMAT,
570 block_len: 512,
571 };
572 let mut dst_ainfo = NAAudioInfo {
573 sample_rate: 44100,
574 channels: stereo.num_channels() as u8,
575 format: SND_S16P_FORMAT,
576 block_len: 512,
577 };
578 let mut src_frm = alloc_audio_buffer(src_ainfo, 42, chcfg51.clone()).unwrap();
579 if let NABufferType::AudioU8(ref mut abuf) = src_frm {
580 let data = abuf.get_data_mut().unwrap();
581 let mut idx = 0;
582 for _ in 0..42 {
583 for ch in 0..chcfg51.num_channels() {
584 data[idx] = CHANNEL_VALUES[ch];
585 idx += 1;
586 }
587 }
588 } else {
589 panic!("wrong buffer type");
590 }
591
592 let out_frm = convert_audio_frame(&src_frm, &dst_ainfo, &stereo).unwrap();
593 if let NABufferType::AudioI16(ref abuf) = out_frm {
594 let off0 = abuf.get_offset(0);
595 let off1 = abuf.get_offset(1);
596 let data = abuf.get_data();
597 let l = data[off0];
598 let r = data[off1];
599 assert_eq!(l, 7445);
600 assert_eq!(r, -19943);
601 } else {
602 panic!("wrong buffer type");
603 }
604
605 dst_ainfo.format = SND_F32P_FORMAT;
606 let out_frm = convert_audio_frame(&src_frm, &dst_ainfo, &stereo).unwrap();
607 if let NABufferType::AudioF32(ref abuf) = out_frm {
608 let off0 = abuf.get_offset(0);
609 let off1 = abuf.get_offset(1);
610 let data = abuf.get_data();
611 let l = data[off0];
612 let r = data[off1];
613 assert_eq!(l, 0.22633252);
614 assert_eq!(r, -0.6062342);
615 } else {
616 panic!("wrong buffer type");
617 }
618 }
619 }