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