Cook encoder
[nihav.git] / nihav-realmedia / src / codecs / cookenc.rs
CommitLineData
f5c7ce01
KS
1use nihav_core::codecs::*;
2use nihav_core::io::byteio::*;
3use nihav_core::io::bitwriter::*;
4use nihav_codec_support::dsp::fft::*;
5use super::cookdata::*;
6
7/*
8 TODO:
9 - calculate gains?
10*/
11const MAX_FRAME_SIZE: usize = 1024;
12
13const EPS: f32 = 1.0e-6;
14
15fn get_block_size(srate: u32) -> usize {
16 match srate {
17 8000 | 11025 => 256,
18 22050 => 512,
19 _ => 1024,
20 }
21}
22
23trait ClipCat {
24 fn clip_cat(&self) -> usize;
25}
26
27impl ClipCat for i32 {
28 fn clip_cat(&self) -> usize { ((*self).max(0) as usize).min(NUM_CATEGORIES - 1) }
29}
30
31fn bitalloc(samples: usize, bits: usize, vector_bits: u8, total_subbands: usize, qindex: &[i8], category: &mut [u8], cat_index: &mut [u8]) {
32 let avail_bits = (if bits > samples { samples + ((bits - samples) * 5) / 8 } else { bits }) as i32;
33
34 let mut bias: i32 = -32;
35 for i in 0..6 {
36 let mut sum = 0;
37 for j in 0..total_subbands {
38 let idx = ((32 >> i) + bias - (qindex[j] as i32)) / 2;
39 sum += COOK_EXP_BITS[idx.clip_cat()];
40 }
41 if sum >= (avail_bits - 32) {
42 bias += 32 >> i;
43 }
44 }
45
46 let mut exp_index1: [usize; MAX_SUBBANDS * 2] = [0; MAX_SUBBANDS * 2];
47 let mut exp_index2: [usize; MAX_SUBBANDS * 2] = [0; MAX_SUBBANDS * 2];
48 let mut sum = 0;
49 for i in 0..total_subbands {
50 let idx = ((bias - (qindex[i] as i32)) / 2).clip_cat();
51 sum += COOK_EXP_BITS[idx];
52 exp_index1[i] = idx;
53 exp_index2[i] = idx;
54 }
55
56 let mut tbias1 = sum;
57 let mut tbias2 = sum;
58 let mut tcat: [usize; 128*2] = [0; 128*2];
59 let mut tcat_idx1 = 128;
60 let mut tcat_idx2 = 128;
61 for _ in 1..(1 << vector_bits) {
62 if tbias1 + tbias2 > avail_bits * 2 {
63 let mut max = -999999;
64 let mut idx = total_subbands + 1;
65 for j in 0..total_subbands {
66 if exp_index1[j] >= (NUM_CATEGORIES - 1) { continue; }
67 let t = -2 * (exp_index1[j] as i32) - (qindex[j] as i32) + bias;
68 if t >= max {
69 max = t;
70 idx = j;
71 }
72 }
73 if idx >= total_subbands { break; }
74 tcat[tcat_idx1] = idx;
75 tcat_idx1 += 1;
76 tbias1 -= COOK_EXP_BITS[exp_index1[idx]] - COOK_EXP_BITS[exp_index1[idx] + 1];
77 exp_index1[idx] += 1;
78 } else {
79 let mut min = 999999;
80 let mut idx = total_subbands + 1;
81 for j in 0..total_subbands {
82 if exp_index2[j] == 0 { continue; }
83 let t = -2 * (exp_index2[j] as i32) - (qindex[j] as i32) + bias;
84 if t < min {
85 min = t;
86 idx = j;
87 }
88 }
89 if idx >= total_subbands { break; }
90 tcat_idx2 -= 1;
91 tcat[tcat_idx2] = idx;
92 tbias2 -= COOK_EXP_BITS[exp_index2[idx]] - COOK_EXP_BITS[exp_index2[idx] - 1];
93 exp_index2[idx] -= 1;
94 }
95 }
96 for i in 0..total_subbands {
97 category[i] = exp_index2[i] as u8;
98 }
99 for el in cat_index.iter_mut() {
100 *el = 255;
101 }
102 for (dst, &src) in cat_index.iter_mut().zip(tcat[tcat_idx2..tcat_idx1].iter()) {
103 *dst = src as u8;
104 }
105}
106
107fn map_coef(val: f32, centroids: &[f32]) -> usize {
108 if val < centroids[1] * 0.5 {
109 0
110 } else {
111 let len = centroids.len();
112 if val < centroids[len - 1] {
113 for (i, pair) in centroids.windows(2).enumerate().skip(1) {
114 if val <= (pair[0] + pair[1]) * 0.5 {
115 return i;
116 }
117 }
118 }
119 len - 1
120 }
121}
122
123fn couple_bands(left: &[f32], right: &[f32], dst: &mut [f32], cpl_scales: &[f32]) -> u8 {
124 let nrg0 = left.iter().fold(0.0f32, |acc, &v| acc + v * v);
125 let nrg1 = right.iter().fold(0.0f32, |acc, &v| acc + v * v);
126 let last_idx = cpl_scales.len() - 3;
127 match (nrg0 > EPS, nrg1 > EPS) {
128 (true, true) => {
129 let tgt_scale0 = (nrg0 / (nrg0 + nrg1)).sqrt();
130 let tgt_scale1 = (nrg1 / (nrg0 + nrg1)).sqrt();
131
132 let mut best_dist = 42.0;
133 let mut best_idx = 0;
134 for i in 0..=last_idx {
135 let scale0 = cpl_scales[i];
136 let scale1 = cpl_scales[cpl_scales.len() - 1 - i];
137 let dist = (scale0 - tgt_scale0).abs() + (scale1 - tgt_scale1).abs();
138 if dist < best_dist {
139 best_dist = dist;
140 best_idx = i;
141 }
142 }
143 let scale_l = cpl_scales[best_idx];
144 let scale_r = cpl_scales[cpl_scales.len() - 1 - best_idx];
145
146 if best_idx == 0 {
147 dst.copy_from_slice(left);
148 } else {
149 for (dst, (&ll, &rr)) in dst.iter_mut().zip(left.iter().zip(right.iter())) {
150 *dst = (ll / scale_l + rr / scale_r) * 0.5;
151 }
152 }
153 best_idx as u8
154 },
155 (false, true) => {
156 dst.copy_from_slice(right);
157 last_idx as u8
158 },
159 (true, false) => {
160 dst.copy_from_slice(left);
161 0
162 },
163 _ => {
164 for (dst, (&ll, &rr)) in dst.iter_mut().zip(left.iter().zip(right.iter())) {
165 *dst = (ll + rr) * std::f32::consts::FRAC_1_SQRT_2;
166 }
167 (cpl_scales.len() / 2) as u8
168 }
169 }
170}
171
172#[derive(Clone,Copy,Default)]
173struct PackedCoeffs {
174 cw: [u16; 10],
175 bits: [u8; 10],
176 signs: [u16; 10],
177 nnz: [u8; 10],
178 num: usize,
179 cat: usize,
180 nbits: u16,
181}
182
183impl PackedCoeffs {
184 fn pack(&mut self, coeffs: &[f32], cat: usize) -> u16 {
185 self.cat = cat;
186 self.nbits = 0;
187
188 if cat >= COOK_NUM_VQ_GROUPS.len() {
189 self.num = 0;
190 return 0;
191 }
192
193 let group_size = COOK_VQ_GROUP_SIZE[cat];
194 let multiplier = COOK_VQ_MULT[cat] as usize + 1;
195 let (code_words, code_bits) = match cat {
196 0 => (&COOK_VQ0_CODES[..], &COOK_VQ0_BITS[..]),
197 1 => (&COOK_VQ1_CODES[..], &COOK_VQ1_BITS[..]),
198 2 => (&COOK_VQ2_CODES[..], &COOK_VQ2_BITS[..]),
199 3 => (&COOK_VQ3_CODES[..], &COOK_VQ3_BITS[..]),
200 4 => (&COOK_VQ4_CODES[..], &COOK_VQ4_BITS[..]),
201 5 => (&COOK_VQ5_CODES[..], &COOK_VQ5_BITS[..]),
202 6 => (&COOK_VQ6_CODES[..], &COOK_VQ6_BITS[..]),
203 _ => unreachable!(),
204 };
205 let centroids = &COOK_QUANT_CENTROID[cat][..multiplier];
206 self.num = COOK_NUM_VQ_GROUPS[cat];
207
208 for (group_no, group) in coeffs.chunks(group_size).enumerate() {
209 let mut cw = 0;
210 let mut cvals = [0; 5];
211 let mut sarr = [0; 5];
212 for ((dval, sign), &el) in cvals.iter_mut().zip(sarr.iter_mut()).zip(group.iter()) {
213 let cur_val = map_coef(el.abs(), centroids);
214 *dval = cur_val;
215 *sign = (el < 0.0) as u16;
216 cw = cw * multiplier + cur_val;
217 }
218 while cw >= code_bits.len() || code_bits[cw] == 0 {
219 let mut max_pos = 0;
220 let mut max_val = cvals[0];
221 for (i, &val) in cvals.iter().enumerate().skip(1) {
222 if val > max_val {
223 max_val = val;
224 max_pos = i;
225 }
226 }
227 cvals[max_pos] -= 1;
228 cw = 0;
229 for &dval in cvals.iter().take(group_size) {
230 cw = cw * multiplier + dval;
231 }
232 }
233 let mut signs = 0;
234 let mut nnz = 0u8;
235 for (&sign, &val) in sarr.iter().zip(cvals.iter()) {
236 if val != 0 {
237 signs = (signs << 1) | sign;
238 nnz += 1;
239 }
240 }
241
242 self.cw [group_no] = code_words[cw];
243 self.bits [group_no] = code_bits[cw];
244 self.signs[group_no] = signs;
245 self.nnz [group_no] = nnz;
246 self.nbits += u16::from(code_bits[cw]) + u16::from(nnz);
247 }
248 self.nbits
249 }
250 fn write(&self, bw: &mut BitWriter, mut bits_left: u16) {
251 for ((&cw, &bits), (&signs, &nnz)) in
252 self.cw.iter().zip(self.bits.iter()).zip(
253 self.signs.iter().zip(self.nnz.iter())).take(self.num) {
254 let cur_bits = u16::from(bits + nnz);
255 if cur_bits > bits_left {
256 break;
257 }
258 bits_left -= cur_bits;
259 bw.write(cw.into(), bits);
260 if nnz > 0 {
261 bw.write(signs.into(), nnz);
262 }
263 }
264 }
265}
266
267struct TempData {
268 bands: [PackedCoeffs; MAX_SUBBANDS * 2],
269}
270
271impl Default for TempData {
272 fn default() -> Self {
273 Self {
274 bands: [PackedCoeffs::default(); MAX_SUBBANDS * 2],
275 }
276 }
277}
278
279struct ChannelDataParams<'a> {
280 size: usize,
281 frame_size: usize,
282 hpow_tab: &'a [f32; 128],
283 coupling: &'a [u8],
284 js_bits: u8,
285 js_start: usize,
286 vector_bits: u8,
287}
288
289struct CookChannelPair {
290 br_info: &'static BitrateParams,
291 delay: [[f32; MAX_FRAME_SIZE]; 2],
292}
293
294fn calc_qindex(nrg: f32) -> i8 {
295 let mut nrg0 = nrg * 0.05;
296 if nrg0 <= 1.0 {
297 nrg0 *= std::f32::consts::FRAC_1_SQRT_2;
298 } else {
299 nrg0 *= std::f32::consts::SQRT_2;
300 }
301 nrg0.log2().max(-31.0).min(47.0) as i8
302}
303
304impl CookChannelPair {
305 fn new(br_info: &'static BitrateParams) -> Self {
306 Self {
307 br_info,
308 delay: [[0.0; MAX_FRAME_SIZE]; 2],
309 }
310 }
311 fn encode_bands(params: ChannelDataParams, dbuf: Vec<u8>, coeffs: &mut [f32], total_bands: usize, tmp: &mut TempData) -> EncoderResult<Vec<u8>> {
312 let output_start = dbuf.len();
313 let mut bw = BitWriter::new(dbuf, BitWriterMode::BE);
314 let data_end = bw.tell() + params.frame_size;
315
316 let mut qindex = [0i8; MAX_SUBBANDS * 2];
317 for (qscale, band) in qindex.iter_mut().zip(coeffs.chunks(BAND_SIZE)).take(total_bands) {
318 let nrg = band.iter().fold(0.0f32, |acc, &v| acc + v * v);
319 *qscale = calc_qindex(nrg);
320 }
321 qindex[0] = qindex[0].max(-6);
322
323 bw.write0(); // no gains
324 //todo gains
325
326 if params.js_bits > 0 {
327 let (cpl_cb_codes, cpl_cb_bits) = match params.js_bits {
328 2 => (&COOK_CPL_2BITS_CODES[..], &COOK_CPL_2BITS_BITS[..]),
329 3 => (&COOK_CPL_3BITS_CODES[..], &COOK_CPL_3BITS_BITS[..]),
330 4 => (&COOK_CPL_4BITS_CODES[..], &COOK_CPL_4BITS_BITS[..]),
331 5 => (&COOK_CPL_5BITS_CODES[..], &COOK_CPL_5BITS_BITS[..]),
332 6 => (&COOK_CPL_6BITS_CODES[..], &COOK_CPL_6BITS_BITS[..]),
333 _ => unreachable!(),
334 };
335 let mut bit_size = 0;
336 let mut raw_bit_size = 0;
337 for &el in params.coupling.iter() {
338 bit_size += cpl_cb_bits[usize::from(el)];
339 raw_bit_size += params.js_bits;
340 }
341 if bit_size < raw_bit_size {
342 bw.write1();
343 for &el in params.coupling.iter() {
344 let idx = usize::from(el);
345 bw.write(cpl_cb_codes[idx].into(), cpl_cb_bits[idx]);
346 }
347 } else {
348 bw.write0();
349 for &el in params.coupling.iter() {
350 bw.write(u32::from(el), params.js_bits);
351 }
352 }
353 }
354
355 let mut last_q = qindex[0];
356 bw.write((qindex[0] + 6) as u32, 6);
357 if params.js_bits == 0 {
358 for (i, qscale) in qindex[..total_bands].iter_mut().enumerate().skip(1) {
359 let cb_idx = (i - 1).min(12);
360 let diff = (*qscale - last_q).max(-12).min(11);
361 *qscale = last_q + diff;
362 last_q = *qscale;
363
364 let idx2 = (diff + 12) as usize;
365 bw.write(COOK_QUANT_CODES[cb_idx][idx2].into(), COOK_QUANT_BITS[cb_idx][idx2]);
366 }
367 } else {
368 for (i, qscale) in qindex[..total_bands].iter_mut().enumerate().skip(1) {
369 let band_no = if i < params.js_start * 2 { i >> 1 } else { i - params.js_start };
370 let cb_idx = band_no.saturating_sub(1).min(12);
371 let diff = (*qscale - last_q).max(-12).min(11);
372 *qscale = last_q + diff;
373 last_q = *qscale;
374
375 let idx2 = (diff + 12) as usize;
376 bw.write(COOK_QUANT_CODES[cb_idx][idx2].into(), COOK_QUANT_BITS[cb_idx][idx2]);
377 }
378 }
379
380 let mut category = [0; MAX_SUBBANDS * 2];
381 let mut cat_index = [0; 127];
382 let bits_avail = data_end - bw.tell() - usize::from(params.vector_bits);
383
384 bitalloc(params.size, bits_avail, params.vector_bits, total_bands, &qindex, &mut category, &mut cat_index);
385
386 let mut tot_bits = 0;
387 for ((band, packed), (&qindex, &cat)) in
388 coeffs.chunks_exact_mut(BAND_SIZE).zip(tmp.bands.iter_mut())
389 .zip(qindex.iter().zip(category.iter())).take(total_bands) {
390 for coef in band.iter_mut() {
391 *coef *= params.hpow_tab[(64 - qindex) as usize];
392 }
393 tot_bits += packed.pack(band, cat.into());
394 }
395
396 let mut bits_left = bits_avail as u16;
397 let mut bands_corrected = 0;
398 let max_corr_bands = (1 << params.vector_bits) - 1;
399 for &index in cat_index.iter() {
400 if bands_corrected >= max_corr_bands || tot_bits <= bits_left || index == 255 {
401 break;
402 }
403 let index = usize::from(index);
404 let pband = &mut tmp.bands[index];
405 let band_coeffs = &coeffs[index * BAND_SIZE..][..BAND_SIZE];
406 let new_cat = (pband.cat + 1).min(NUM_CATEGORIES - 1);
407 tot_bits -= pband.nbits;
408 pband.pack(band_coeffs, new_cat);
409 tot_bits += pband.nbits;
410 bands_corrected += 1;
411 }
412
413 bw.write(bands_corrected, params.vector_bits);
414 for packed in tmp.bands.iter().take(total_bands) {
415 packed.write(&mut bw, bits_left);
416 bits_left = bits_left.saturating_sub(packed.nbits);
417 if bits_left == 0 {
418 break;
419 }
420 }
421
422 pad(&mut bw, data_end);
423
424 let mut dbuf = bw.end();
425 for (i, el) in dbuf[output_start..].iter_mut().enumerate() {
426 *el ^= COOK_XOR_KEY[i & 3];
427 }
428
429 Ok(dbuf)
430 }
431 fn encode_mono(&mut self, dbuf: Vec<u8>, dsp: &mut CookDSP, src: &[f32], ch_no: usize, tmp: &mut TempData) -> EncoderResult<Vec<u8>> {
432 dsp.mdct(&mut self.delay[ch_no], src, true);
433 let coeffs = &mut dsp.coeffs;
434
435 let frame_size = (self.br_info.frame_bits / u32::from(self.br_info.channels)) as usize;
436 let total_bands: usize = self.br_info.max_subbands.into();
437
438 let params = ChannelDataParams {
439 size: dsp.size,
440 hpow_tab: &dsp.hpow_tab,
441 coupling: &[],
442 js_bits: 0,
443 js_start: 0,
444 vector_bits: 5,
445 frame_size,
446 };
447 Self::encode_bands(params, dbuf, coeffs, total_bands, tmp)
448 }
449 fn encode_jstereo(&mut self, dbuf: Vec<u8>, dsp: &mut CookDSP, l_ch: &[f32], r_ch: &[f32], tmp: &mut TempData) -> EncoderResult<Vec<u8>> {
450 let frame_size = self.br_info.frame_bits as usize;
451 let low_bands = usize::from(self.br_info.js_start);
452 let total_bands = usize::from(self.br_info.max_subbands) + low_bands;
453
454 dsp.mdct(&mut self.delay[0], l_ch, true);
455 dsp.mdct(&mut self.delay[1], r_ch, false);
456
457 let mut decouple = [0; 20];
458 let cpl_scales = COOK_CPL_SCALES[usize::from(self.br_info.js_bits - 2)];
459 let cpl_start_band = COOK_CPL_BAND[usize::from(self.br_info.js_start)] as usize;
460 let cpl_end_band = COOK_CPL_BAND[usize::from(self.br_info.max_subbands) - 1] as usize;
461
462 let coeffs = &mut dsp.coeffs;
463 let coeffs2 = &mut dsp.coeffs2;
464 let coupled = &mut dsp.coupled;
465 for (dst, (src0, src1)) in coupled.chunks_exact_mut(BAND_SIZE * 2).zip(coeffs.chunks(BAND_SIZE).zip(coeffs2.chunks(BAND_SIZE))).take(low_bands) {
466 let (dst0, dst1) = dst.split_at_mut(BAND_SIZE);
467 dst0.copy_from_slice(src0);
468 dst1.copy_from_slice(src1);
469 }
470 for el in coupled[total_bands * BAND_SIZE..].iter_mut() {
471 *el = 0.0;
472 }
473
474 let mut band = low_bands;
475 let mut start_band = band;
476 let mut cpl_band = cpl_start_band;
477 let mut last_cpl_band = cpl_band;
478 let end_band = usize::from(self.br_info.max_subbands);
479 while band < end_band {
480 cpl_band = usize::from(COOK_CPL_BAND[band]);
481 if cpl_band != last_cpl_band {
482 let length = (band - start_band) * BAND_SIZE;
483 decouple[last_cpl_band] = couple_bands(&coeffs[start_band * BAND_SIZE..][..length],
484 &coeffs2[start_band * BAND_SIZE..][..length],
485 &mut coupled[(start_band + low_bands) * BAND_SIZE..][..length],
486 cpl_scales);
487 last_cpl_band = cpl_band;
488 start_band = band;
489 }
490 band += 1;
491 }
492 if band != start_band {
493 let length = (band - start_band) * BAND_SIZE;
494 decouple[last_cpl_band] = couple_bands(&coeffs[start_band * BAND_SIZE..][..length],
495 &coeffs2[start_band * BAND_SIZE..][..length],
496 &mut coupled[(start_band + low_bands) * BAND_SIZE..][..length],
497 cpl_scales);
498 }
499
500 let vector_bits = match dsp.size {
501 1024 => 7,
502 512 => 6,
503 _ => 5,
504 };
505 let params = ChannelDataParams {
506 size: dsp.size,
507 hpow_tab: &dsp.hpow_tab,
508 coupling: &decouple[cpl_start_band..=cpl_end_band],
509 js_bits: self.br_info.js_bits,
510 js_start: self.br_info.js_start.into(),
511 frame_size,
512 vector_bits,
513 };
514
515 Self::encode_bands(params, dbuf, coupled, total_bands, tmp)
516 }
517}
518
519fn pad(bw: &mut BitWriter, data_end: usize) {
520 while (bw.tell() & 7) != 0 {
521 bw.write0();
522 }
523 while bw.tell() + 32 <= data_end {
524 bw.write(0, 32);
525 }
526 while bw.tell() + 8 <= data_end {
527 bw.write(0, 8);
528 }
529}
530pub struct MDCT {
531 table: Vec<FFTComplex>,
532 tmp: Vec<FFTComplex>,
533 fft: FFT,
534 size: usize,
535}
536
537impl MDCT {
538 pub fn new(size: usize) -> Self {
539 let fft = FFTBuilder::new_fft(size / 4, false);
540 let mut table = Vec::with_capacity(size / 4);
541 let factor = std::f32::consts::PI * 2.0 / (size as f32);
542 for i in 0..(size / 4) {
543 let val = factor * ((i as f32) + 1.0 / 8.0);
544 table.push(FFTComplex::exp(val));
545 }
546 Self {
547 fft,
548 tmp: vec![FFTC_ZERO; size / 4],
549 table,
550 size,
551 }
552 }
553 pub fn mdct(&mut self, src: &[f32], dst: &mut [f32]) {
554 let size1_8 = self.size / 8;
555 let size1_4 = self.size / 4;
556 let size1_2 = self.size / 2;
557 let size3_4 = size1_4 * 3;
558
559 for i in 0..size1_8 {
560 let a0 = FFTComplex{re: -src[2 * i + size3_4] - src[size3_4 - 1 - 2 * i],
561 im: -src[size1_4 + 2 * i] + src[size1_4 - 1 - 2 * i]};
562 let t0 = !self.table[i];
563 let a1 = FFTComplex{re: src[2 * i] - src[size1_2 - 1 - 2 * i],
564 im: -src[size1_2 + 2 * i] - src[self.size - 1 - 2 * i]};
565 let t1 = !self.table[size1_8 + i];
566 self.tmp[i] = a0 * t0;
567 self.tmp[i + size1_8] = a1 * t1;
568 }
569 self.fft.do_fft_inplace(&mut self.tmp);
570
571 for i in 0..size1_8 {
572 let a0 = self.tmp[size1_8 - 1 - i] * !self.table[size1_8 - 1 - i].rotate();
573 let a1 = self.tmp[size1_8 + i] * !self.table[size1_8 + i].rotate();
574 dst[size1_4 - 2 - i * 2] = a0.im;
575 dst[size1_4 - 1 - i * 2] = a1.re;
576 dst[size1_4 + i * 2] = a1.im;
577 dst[size1_4 + 1 + i * 2] = a0.re;
578 }
579 }
580}
581
582
583struct CookDSP {
584 size: usize,
585 mdct: MDCT,
586 tmp: [f32; MAX_FRAME_SIZE * 2],
587 window: [f32; MAX_FRAME_SIZE * 2],
588 pow_tab: [f32; 128],
589 hpow_tab: [f32; 128],
590 gain_tab: [f32; 23],
591
592 coeffs: [f32; MAX_FRAME_SIZE],
593 coeffs2: [f32; MAX_FRAME_SIZE],
594 coupled: [f32; MAX_FRAME_SIZE * 2],
595}
596
597impl CookDSP {
598 fn init(&mut self, frame_size: usize) {
599 if self.size == frame_size {
600 return;
601 }
602 self.size = frame_size;
603 self.mdct = MDCT::new(self.size * 2);
604
605 let fsamples = self.size as f32;
606 let factor = std::f32::consts::PI / (2.0 * fsamples);
607 let scale = fsamples;
608 for (k, dst) in self.window[..self.size * 2].iter_mut().enumerate() {
609 *dst = (factor * ((k as f32) + 0.5)).sin() * scale;
610 }
611
612 for (dst, &pow_val) in self.gain_tab.iter_mut().zip(self.pow_tab[53..].iter()) {
613 *dst = pow_val.powf(8.0 / fsamples);
614 }
615 }
616 fn mdct(&mut self, delay: &mut [f32], src: &[f32], mono: bool) {
617 let (w0, w1) = self.window[..self.size * 2].split_at(self.size);
618 let (d0, d1) = self.tmp[..self.size * 2].split_at_mut(self.size);
619
620 for (dst, (&src, &win)) in d1.iter_mut().zip(delay.iter().zip(w0.iter()).rev()) {
621 *dst = src * win;
622 }
623 for (dst, (&src, &win)) in d0.iter_mut().zip(src.iter().zip(w1.iter()).rev()) {
624 *dst = src * win;
625 }
626 self.mdct.mdct(&self.tmp, if mono { &mut self.coeffs } else { &mut self.coeffs2 });
627
628 delay[..self.size].copy_from_slice(&src[..self.size]);
629 }
630}
631
632impl Default for CookDSP {
633 fn default() -> Self {
634 let mut pow_tab: [f32; 128] = [0.0; 128];
635 let mut hpow_tab: [f32; 128] = [0.0; 128];
636 for i in 0..128 {
637 pow_tab[i] = 2.0f32.powf((i as f32) - 64.0);
638 hpow_tab[i] = 2.0f32.powf(((i as f32) - 64.0) * 0.5);
639 }
640 Self {
641 size: 0,
642 mdct: MDCT::new(64),
643 tmp: [0.0; MAX_FRAME_SIZE * 2],
644 window: [0.0; MAX_FRAME_SIZE * 2],
645 gain_tab: [0.0; 23],
646 pow_tab, hpow_tab,
647
648 coeffs: [0.0; MAX_FRAME_SIZE],
649 coeffs2: [0.0; MAX_FRAME_SIZE],
650 coupled: [0.0; MAX_FRAME_SIZE * 2],
651 }
652 }
653}
654
655#[derive(Default)]
656struct CookEncoder {
657 stream: Option<NAStreamRef>,
658 flavour: usize,
659 force_flv: bool,
660 g8_only: bool,
661
662 samples: Vec<Vec<f32>>,
663 tgt_size: usize,
664 nframes: usize,
665 frm_per_blk: usize,
666 frm_size: usize,
667
668 rd_pos: usize,
669 wr_pos: usize,
670 apts: u64,
671
672 dsp: CookDSP,
673 pairs: Vec<CookChannelPair>,
674 tmp: TempData,
675}
676
677impl CookEncoder {
678 fn new() -> Self { Self::default() }
679 fn encode_packet(&mut self) -> EncoderResult<NAPacket> {
680 if self.rd_pos == self.nframes {
681 let mut start = self.wr_pos * self.frm_size;
682 let mut dbuf = Vec::with_capacity(self.tgt_size * self.frm_per_blk);
683 for _ in 0..self.frm_per_blk {
684 let mut channels = self.samples.iter();
685
686 for ch_pair in self.pairs.iter_mut() {
687 if ch_pair.br_info.channels == 1 {
688 let ch = channels.next().unwrap();
689 dbuf = ch_pair.encode_mono(dbuf, &mut self.dsp, &ch[start..][..self.frm_size], 0, &mut self.tmp)?;
690 } else {
691 let l_ch = channels.next().unwrap();
692 let r_ch = channels.next().unwrap();
693 if ch_pair.br_info.js_bits == 0 {
694 dbuf = ch_pair.encode_mono(dbuf, &mut self.dsp, &l_ch[start..][..self.frm_size], 0, &mut self.tmp)?;
695 dbuf = ch_pair.encode_mono(dbuf, &mut self.dsp, &r_ch[start..][..self.frm_size], 1, &mut self.tmp)?;
696 } else {
697 dbuf = ch_pair.encode_jstereo(dbuf, &mut self.dsp, &l_ch[start..][..self.frm_size], &r_ch[start..][..self.frm_size], &mut self.tmp)?;
698 }
699 }
700 }
701
702 start += self.frm_size;
703 }
704
705 let first = self.wr_pos == 0;
706
707 self.wr_pos += self.frm_per_blk;
708 if self.wr_pos == self.nframes {
709 self.wr_pos = 0;
710 self.rd_pos = 0;
711 }
712
713 let stream = self.stream.clone().unwrap();
714 let (tb_num, tb_den) = stream.get_timebase();
715 let ts = NATimeInfo::new(Some(self.apts), None, Some(1), tb_num, tb_den);
716 self.apts += self.frm_per_blk as u64;
717 Ok(NAPacket::new(self.stream.clone().unwrap(), ts, first, dbuf))
718 } else {
719 Err(EncoderError::TryAgain)
720 }
721 }
722}
723
724impl NAEncoder for CookEncoder {
725 fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> {
726 match encinfo.format {
727 NACodecTypeInfo::None => {
728 Ok(EncodeParameters {
729 format: NACodecTypeInfo::Audio(NAAudioInfo::new(0, 1, SND_F32P_FORMAT, 512)),
730 ..Default::default() })
731 },
732 NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError),
733 NACodecTypeInfo::Audio(ainfo) => {
734 if !self.force_flv {
735 let mut outinfo = ainfo;
736 outinfo.format = SND_F32P_FORMAT;
737 outinfo.sample_rate = match ainfo.sample_rate {
738 0..= 8000 => 8000,
739 8001..=11025 => 11025,
740 11026..=22050 => 22050,
741 _ => 44100,
742 };
743 if !matches!(outinfo.channels, 1 | 2) && (outinfo.sample_rate != 44100 || !matches!(outinfo.channels, 4 | 5)) {
744 outinfo.channels = 2;
745 }
746 outinfo.block_len = get_block_size(outinfo.sample_rate);
747 let mut ofmt = *encinfo;
748 ofmt.format = NACodecTypeInfo::Audio(outinfo);
749 Ok(ofmt)
750 } else {
751 let flavour = &COOK_FLAVOURS[self.flavour];
752 let blk_len = match flavour.sample_rate {
753 44100 => 1024,
754 22050 => 512,
755 _ => 256,
756 };
757 let newinfo = NAAudioInfo::new(flavour.sample_rate, flavour.channels, SND_F32P_FORMAT, blk_len);
758 let ofmt = EncodeParameters {
759 format: NACodecTypeInfo::Audio(newinfo),
760 tb_num: blk_len as u32,
761 tb_den: flavour.sample_rate,
762 bitrate: u32::from(flavour.bitrate) * 1000,
763 flags: ENC_MODE_CBR,
764 quality: 0,
765 };
766 Ok(ofmt)
767 }
768 }
769 }
770 }
771 fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> {
772 match encinfo.format {
773 NACodecTypeInfo::None => Err(EncoderError::FormatError),
774 NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError),
775 NACodecTypeInfo::Audio(ainfo) => {
776 if ainfo.format != SND_F32P_FORMAT || !matches!(ainfo.sample_rate, 8000 | 11025 | 22050 | 44100) {
777 return Err(EncoderError::FormatError);
778 }
779 let blk_size = get_block_size(ainfo.sample_rate);
780 if ainfo.block_len != blk_size {
781 return Err(EncoderError::FormatError);
782 }
783
784 let bitrate = if encinfo.bitrate > 0 { Some(encinfo.bitrate / 1000) } else { None };
785
786 let flavours = if self.g8_only { &COOK_FLAVOURS[..RM8_AUDIO] } else { COOK_FLAVOURS };
787 let mut cand_id = None;
788 for (i, flavour) in flavours.iter().enumerate().rev() {
789 if flavour.sample_rate == ainfo.sample_rate && flavour.channels == ainfo.channels {
790 if let Some(target) = bitrate {
791 if target <= flavour.bitrate.into() {
792 cand_id = Some(i);
793 if target == flavour.bitrate.into() {
794 break;
795 }
796 }
797 } else {
798 cand_id = Some(i);
799 break;
800 }
801 }
802 }
803
804 if let Some(id) = cand_id {
805 self.flavour = id;
806 } else {
807 return Err(EncoderError::FormatError);
808 }
809
810 let flavour = &COOK_FLAVOURS[self.flavour];
811 self.nframes = flavour.frames_per_blk * flavour.factor;
812 self.frm_per_blk = flavour.frames_per_blk;
813 self.samples.clear();
814 self.frm_size = blk_size;
815 for _ in 0..flavour.channels {
816 self.samples.push(vec![0.0; self.nframes * self.frm_size]);
817 }
818 self.dsp.init(self.frm_size);
819
820 self.pairs.clear();
821 for br_info in flavour.br_infos() {
822 self.pairs.push(CookChannelPair::new(br_info));
823 }
824
825 self.tgt_size = 0;
826 for br_info in flavour.br_infos() {
827 self.tgt_size += br_info.frame_bits as usize / 8;
828 }
829
830 let mut edata = Vec::new();
831 let mut gw = GrowableMemoryWriter::new_write(&mut edata);
832 let mut bw = ByteWriter::new(&mut gw);
833 if flavour.channels <= 2 {
834 let br_info = &BITRATE_PARAMS[flavour.br_ids[0]];
835 let ch_mode = if br_info.channels == 1 {
836 1
837 } else if br_info.js_bits == 0 {
838 2
839 } else {
840 3
841 };
842 bw.write_u32be((1 << 24) | ch_mode)?;
843 bw.write_u16be((self.frm_size as u16) * u16::from(br_info.channels))?;
844 bw.write_u16be(br_info.max_subbands.into())?;
845 if ch_mode == 3 {
846 bw.write_u32be(0)?; // delay
847 bw.write_u16be(br_info.js_start.into())?;
848 bw.write_u16be(br_info.js_bits.into())?;
849 }
850 } else {
851 let chmap: &[u32] = match flavour.channels {
852 1 => &[ 0x04 ], // C
853 2 => &[ 0x03 ], // L,R
854 5 => &[ 0x03, 0x04, 0x30 ], // L,R C Ls,Rs
855 6 => &[ 0x03, 0x04, 0x08, 0x30 ], // L,R C LFE Ls,Rs
856 _ => unreachable!(),
857 };
858 for (br_info, &channel_map) in flavour.br_infos().zip(chmap.iter()) {
859 bw.write_u32be(2 << 24)?;
860 bw.write_u16be((self.frm_size as u16) * u16::from(br_info.channels))?;
861 bw.write_u16be(br_info.max_subbands.into())?;
862 bw.write_u32be(0)?; // delay
863 bw.write_u16be(br_info.js_start.into())?;
864 bw.write_u16be(br_info.js_bits.into())?;
865 bw.write_u32be(channel_map)?;
866 }
867 }
868
869 let soniton = NASoniton::new(16, 0);
870 let out_ainfo = NAAudioInfo::new(ainfo.sample_rate, ainfo.channels, soniton, self.tgt_size);
871 let info = NACodecInfo::new("cook", NACodecTypeInfo::Audio(out_ainfo), Some(edata));
872 let mut stream = NAStream::new(StreamType::Audio, stream_id, info, blk_size as u32, ainfo.sample_rate, 0);
873 stream.set_num(stream_id as usize);
874 let stream = stream.into_ref();
875
876 self.stream = Some(stream.clone());
877 self.wr_pos = 0;
878 self.rd_pos = 0;
879
880 Ok(stream)
881 },
882 }
883 }
884 fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> {
885 if let Some(ref abuf) = frm.get_buffer().get_abuf_f32() {
886 if self.rd_pos == self.nframes {
887 return Err(EncoderError::TryAgain);
888 }
889 let stride = abuf.get_stride();
890 let src = abuf.get_data();
891 let start = self.rd_pos * self.frm_size;
892
893 for (dst, src) in self.samples.iter_mut().zip(src.chunks(stride)) {
894 dst[start..][..self.frm_size].copy_from_slice(&src[..self.frm_size]);
895 }
896 self.rd_pos += 1;
897
898 Ok(())
899 } else {
900 Err(EncoderError::InvalidParameters)
901 }
902 }
903 fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> {
904 if let Ok(pkt) = self.encode_packet() {
905 Ok(Some(pkt))
906 } else {
907 Ok(None)
908 }
909 }
910 fn flush(&mut self) -> EncoderResult<()> {
911 if self.wr_pos != 0 {
912 let start = self.wr_pos * self.frm_size;
913 for channel in self.samples.iter_mut() {
914 for el in channel[start..].iter_mut() {
915 *el = 0.0;
916 }
917 }
918 self.wr_pos = self.nframes;
919 }
920 Ok(())
921 }
922}
923
924const FLAVOUR_OPTION: &str = "flavor";
925const G8_ONLY_OPTION: &str = "g8_only";
926
927const ENCODER_OPTS: &[NAOptionDefinition] = &[
928 NAOptionDefinition {
929 name: FLAVOUR_OPTION, description: "Codec-specific profile (0..32, -1 = auto)",
930 opt_type: NAOptionDefinitionType::Int(Some(-1), Some((COOK_FLAVOURS.len() - 1) as i64)) },
931 NAOptionDefinition {
932 name: G8_ONLY_OPTION, description: "Force formats present in RealMedia G8",
933 opt_type: NAOptionDefinitionType::Bool },
934];
935
936impl NAOptionHandler for CookEncoder {
937 fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS }
938 fn set_options(&mut self, options: &[NAOption]) {
939 for option in options.iter() {
940 for opt_def in ENCODER_OPTS.iter() {
941 if opt_def.check(option).is_ok() {
942 match option.name {
943 FLAVOUR_OPTION => {
944 if let NAValue::Int(val) = option.value {
945 if val >= 0 {
946 self.flavour = val as usize;
947 self.force_flv = true;
948 } else {
949 self.force_flv = false;
950 }
951 }
952 },
953 G8_ONLY_OPTION => {
954 if let NAValue::Bool(val) = option.value {
955 self.g8_only = val;
956 }
957 },
958 _ => {},
959 };
960 }
961 }
962 }
963 }
964 fn query_option_value(&self, name: &str) -> Option<NAValue> {
965 match name {
966 FLAVOUR_OPTION => Some(NAValue::Int(self.flavour as i64)),
967 G8_ONLY_OPTION => Some(NAValue::Bool(self.g8_only)),
968 _ => None,
969 }
970 }
971}
972
973pub fn get_encoder() -> Box<dyn NAEncoder + Send> { Box::new(CookEncoder::new()) }
974
975struct FlavourInfo {
976 sample_rate: u32,
977 channels: u8,
978 bitrate: u16,
979 frames_per_blk: usize,
980 factor: usize,
981 br_ids: [usize; 4],
982}
983
984impl FlavourInfo {
985 fn br_infos(&self) -> BitrateParamsIterator {
986 BitrateParamsIterator::new(self.br_ids, self.channels)
987 }
988}
989
990struct BitrateParamsIterator {
991 ids: [usize; 4],
992 cur: usize,
993 len: usize,
994}
995
996impl BitrateParamsIterator {
997 fn new(ids: [usize; 4], chans: u8) -> Self {
998 Self {
999 ids,
1000 cur: 0,
1001 len: usize::from((chans + 1) / 2)
1002 }
1003 }
1004}
1005
1006impl Iterator for BitrateParamsIterator {
1007 type Item = &'static BitrateParams;
1008 fn next(&mut self) -> Option<Self::Item> {
1009 if self.cur < self.len {
1010 let ret = &BITRATE_PARAMS[self.ids[self.cur]];
1011 self.cur += 1;
1012 Some(ret)
1013 } else {
1014 None
1015 }
1016 }
1017}
1018
1019macro_rules! flavour_desc {
1020 ($srate:expr, $ch:expr, $br:expr, $fpb:expr, $factor:expr, $br_ids:expr) => {
1021 FlavourInfo {
1022 sample_rate: $srate,
1023 channels: $ch,
1024 bitrate: $br,
1025 frames_per_blk: $fpb,
1026 factor: $factor,
1027 br_ids: $br_ids,
1028 }
1029 }
1030}
1031
1032const RM8_AUDIO: usize = 17; // newer variants that use joint-stereo coding and support multichannel
1033const COOK_FLAVOURS: &[FlavourInfo] = &[
1034 flavour_desc!( 8000, 1, 8, 9, 8, [ 0, 0, 0, 0]),
1035 flavour_desc!(11025, 1, 11, 11, 8, [ 1, 0, 0, 0]),
1036 flavour_desc!(22050, 1, 16, 12, 8, [ 2, 0, 0, 0]),
1037 flavour_desc!(22050, 1, 20, 10, 9, [ 3, 0, 0, 0]),
1038 flavour_desc!(44100, 1, 32, 7, 14, [ 4, 0, 0, 0]),
1039 flavour_desc!(44100, 1, 44, 5, 16, [ 5, 0, 0, 0]),
1040 flavour_desc!(44100, 1, 64, 4, 20, [ 6, 0, 0, 0]),
1041 flavour_desc!(22050, 1, 32, 6, 16, [ 7, 0, 0, 0]),
1042 flavour_desc!( 8000, 1, 6, 12, 6, [ 8, 0, 0, 0]),
1043 flavour_desc!(11025, 2, 19, 10, 10, [ 9, 0, 0, 0]),
1044 flavour_desc!(22050, 2, 32, 6, 14, [10, 0, 0, 0]),
1045 flavour_desc!(22050, 2, 44, 5, 16, [11, 0, 0, 0]),
1046 flavour_desc!(44100, 2, 64, 4, 20, [12, 0, 0, 0]),
1047 flavour_desc!(44100, 2, 95, 3, 30, [13, 0, 0, 0]),
1048 flavour_desc!(44100, 1, 64, 4, 20, [14, 0, 0, 0]),
1049 flavour_desc!(44100, 1, 20, 10, 9, [15, 0, 0, 0]),
1050 flavour_desc!(44100, 1, 32, 7, 14, [16, 0, 0, 0]),
1051 flavour_desc!(22050, 2, 16, 10, 10, [17, 0, 0, 0]),
1052 flavour_desc!(22050, 2, 20, 10, 10, [18, 0, 0, 0]),
1053 flavour_desc!(22050, 2, 20, 10, 10, [19, 0, 0, 0]),
1054 flavour_desc!(22050, 2, 32, 5, 16, [20, 0, 0, 0]),
1055 flavour_desc!(44100, 2, 32, 5, 16, [21, 0, 0, 0]),
1056 flavour_desc!(44100, 2, 44, 5, 16, [22, 0, 0, 0]),
1057 flavour_desc!(44100, 2, 44, 5, 16, [23, 0, 0, 0]),
1058 flavour_desc!(44100, 2, 64, 5, 16, [24, 0, 0, 0]),
1059 flavour_desc!(44100, 2, 96, 5, 16, [25, 0, 0, 0]),
1060 flavour_desc!(11025, 2, 12, 11, 8, [26, 0, 0, 0]),
1061 flavour_desc!(44100, 2, 64, 5, 16, [27, 0, 0, 0]),
1062 flavour_desc!(44100, 2, 96, 5, 16, [28, 0, 0, 0]),
1063 flavour_desc!(22050, 2, 44, 5, 16, [29, 0, 0, 0]),
1064 flavour_desc!(44100, 5, 96, 5, 16, [21, 16, 21, 0]),
1065 flavour_desc!(44100, 6, 131, 3, 10, [23, 5, 30, 21]),
1066 flavour_desc!(44100, 6, 183, 2, 10, [24, 6, 30, 23]),
1067 flavour_desc!(44100, 6, 268, 1, 1, [25, 6, 30, 25])
1068];
1069
1070struct BitrateParams {
1071 channels: u8,
1072 max_subbands: u8,
1073 frame_bits: u32,
1074 js_start: u8,
1075 js_bits: u8,
1076}
1077
1078macro_rules! br_desc {
1079 ($ch:expr, $bits:expr, $subbands:expr, $js_start:expr, $js_bits:expr) => {
1080 BitrateParams {
1081 channels: $ch,
1082 max_subbands: $subbands,
1083 frame_bits: $bits,
1084 js_start: $js_start,
1085 js_bits: $js_bits,
1086 }
1087 }
1088}
1089
1090const BITRATE_PARAMS: &[BitrateParams] = &[
1091 br_desc!(1, 256, 12, 0, 0),
1092 br_desc!(1, 256, 12, 0, 0),
1093 br_desc!(1, 376, 18, 0, 0),
1094 br_desc!(1, 480, 23, 0, 0),
1095 br_desc!(1, 744, 37, 0, 0),
1096 br_desc!(1, 1024, 47, 0, 0),
1097 br_desc!(1, 1488, 47, 0, 0),
1098 br_desc!(1, 744, 24, 0, 0),
1099
1100 br_desc!(1, 192, 9, 0, 0),
1101 br_desc!(2, 464, 11, 0, 0),
1102 br_desc!(2, 752, 18, 0, 0),
1103 br_desc!(2, 1024, 24, 0, 0),
1104 br_desc!(2, 1488, 37, 0, 0),
1105 br_desc!(2, 2224, 47, 0, 0),
1106 br_desc!(1, 1488, 47, 0, 0),
1107 br_desc!(1, 480, 47, 0, 0),
1108
1109 br_desc!(1, 744, 47, 0, 0),
1110 br_desc!(2, 384, 16, 1, 3),
1111 br_desc!(2, 480, 20, 1, 3),
1112 br_desc!(2, 480, 23, 1, 3),
1113 br_desc!(2, 744, 24, 2, 4),
1114 br_desc!(2, 744, 32, 2, 4),
1115 br_desc!(2, 1024, 32, 5, 5),
1116 br_desc!(2, 1024, 37, 2, 4),
1117
1118 br_desc!(2, 1488, 37, 6, 5),
1119 br_desc!(2, 2240, 37, 8, 5),
1120 br_desc!(2, 288, 9, 1, 2),
1121 br_desc!(2, 1488, 30, 17, 5),
1122 br_desc!(2, 2240, 34, 19, 5),
1123 br_desc!(2, 1024, 23, 17, 5),
1124 br_desc!(1, 256, 1, 0, 0)
1125];
1126
1127#[cfg(test)]
1128mod test {
1129 use nihav_core::codecs::*;
1130 use nihav_core::demuxers::*;
1131 use nihav_core::muxers::*;
1132 use nihav_codec_support::test::enc_video::*;
1133 use crate::*;
1134
1135 fn test_encoder(name: &'static str, enc_options: &[NAOption], bitrate: u32, channels: u8, hash: &[u32; 4]) {
1136 let mut dmx_reg = RegisteredDemuxers::new();
1137 realmedia_register_all_demuxers(&mut dmx_reg);
1138 let mut dec_reg = RegisteredDecoders::new();
1139 realmedia_register_all_decoders(&mut dec_reg);
1140 let mut mux_reg = RegisteredMuxers::new();
1141 realmedia_register_all_muxers(&mut mux_reg);
1142 let mut enc_reg = RegisteredEncoders::new();
1143 realmedia_register_all_encoders(&mut enc_reg);
1144
1145 // sample from a private collection
1146 let dec_config = DecoderTestParams {
1147 demuxer: "realmedia",
1148 in_name: "assets/RV/rv30_weighted_mc.rm",
1149 stream_type: StreamType::Audio,
1150 limit: None,
1151 dmx_reg, dec_reg,
1152 };
1153 let enc_config = EncoderTestParams {
1154 muxer: "realmedia",
1155 enc_name: "cook",
1156 out_name: name,
1157 mux_reg, enc_reg,
1158 };
1159 let dst_ainfo = NAAudioInfo {
1160 sample_rate: 0,
1161 channels,
1162 format: SND_F32P_FORMAT,
1163 block_len: 1024,
1164 };
1165 let enc_params = EncodeParameters {
1166 format: NACodecTypeInfo::Audio(dst_ainfo),
1167 quality: 0,
1168 bitrate,
1169 tb_num: 0,
1170 tb_den: 0,
1171 flags: 0,
1172 };
1173 //test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options);
1174
1175 test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options, hash);
1176 }
1177 #[test]
1178 fn test_cook_encoder_mono() {
1179 let enc_options = &[
1180 ];
1181 test_encoder("cook-mono.rma", enc_options, 0, 1,
1182 &[0xa07cffaa, 0x257e8ca1, 0x27f58960, 0xb48f64f4]);
1183 }
1184 #[test]
1185 fn test_cook_encoder_stereo() {
1186 let enc_options = &[
1187 NAOption { name: super::G8_ONLY_OPTION, value: NAValue::Bool(true) },
1188 ];
1189 test_encoder("cook-stereo.rma", enc_options, 0, 2,
1190 &[0xbc407879, 0x18c7b334, 0xc0299482, 0x89c6b953]);
1191 }
1192 #[test]
1193 fn test_cook_encoder_jstereo_32kbps() {
1194 let enc_options = &[
1195 ];
1196 test_encoder("cook-jstereo32.rma", enc_options, 32000, 2,
1197 &[0xe7526c4e, 0xda095156, 0x840a74f7, 0x0e9a1603]);
1198 }
1199 #[test]
1200 fn test_cook_encoder_jstereo_64kbps() {
1201 let enc_options = &[
1202 ];
1203 test_encoder("cook-jstereo64.rma", enc_options, 64000, 2,
1204 &[0x09688175, 0x9abe1aac, 0x3c3f15fb, 0x066b99f0]);
1205 }
1206 #[test]
1207 fn test_cook_encoder_jstereo_96kbps() {
1208 let enc_options = &[
1209 ];
1210 test_encoder("cook-jstereo96.rma", enc_options, 96000, 2,
1211 &[0x51829ff8, 0x00017191, 0x5bc95490, 0xd1d03faf]);
1212 }
1213}