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