public ALACWriter(string path, Stream IO, AudioPCMConfig pcm) { _pcm = pcm; if (_pcm.BitsPerSample != 16) throw new Exception("Bits per sample must be 16."); if (_pcm.ChannelCount != 2) throw new Exception("ChannelCount must be 2."); _path = path; _IO = IO; _pathGiven = _IO == null; if (_IO != null && !_IO.CanSeek) throw new NotSupportedException("stream doesn't support seeking"); samplesBuffer = new int[Alac.MAX_BLOCKSIZE * (_pcm.ChannelCount == 2 ? 5 : _pcm.ChannelCount)]; residualBuffer = new int[Alac.MAX_BLOCKSIZE * (_pcm.ChannelCount == 2 ? 6 : _pcm.ChannelCount + 1)]; windowBuffer = new float[Alac.MAX_BLOCKSIZE * 2 * Alac.MAX_LPC_WINDOWS]; eparams.set_defaults(_compressionLevel); eparams.padding_size = 4096; crc8 = new Crc8(); crc16 = new Crc16(); frame = new ALACFrame(_pcm.ChannelCount == 2 ? 5 : _pcm.ChannelCount); chunk_pos = new List<int>(); }
unsafe void encode_residual_pass2(ALACFrame frame, int ch) { encode_residual(frame, ch, 2, estimate_best_window(frame, ch)); }
unsafe int estimate_best_window(ALACFrame frame, int ch) { if (_windowcount == 1) return 0; switch (eparams.window_method) { case WindowMethod.Estimate: { int best_window = -1; double best_error = 0; int order = 2; for (int i = 0; i < _windowcount; i++) { frame.subframes[ch].lpc_ctx[i].GetReflection(order, frame.subframes[ch].samples, frame.blocksize, frame.window_buffer + i * Alac.MAX_BLOCKSIZE * 2); double err = frame.subframes[ch].lpc_ctx[i].prediction_error[order - 1] / frame.subframes[ch].lpc_ctx[i].autocorr_values[0]; if (best_window == -1 || best_error > err) { best_window = i; best_error = err; } } return best_window; } case WindowMethod.Evaluate: encode_residual_pass1(frame, ch, -1); return frame.subframes[ch].best.window; case WindowMethod.Search: return -1; } return -1; }
unsafe void output_frame_header(ALACFrame frame, BitWriter bitwriter) { bitwriter.writebits(3, _pcm.ChannelCount - 1); bitwriter.writebits(16, 0); bitwriter.writebits(1, frame.blocksize != eparams.block_size ? 1 : 0); // sample count is in the header bitwriter.writebits(2, 0); // wasted bytes bitwriter.writebits(1, frame.type == FrameType.Verbatim ? 1 : 0); // is verbatim if (frame.blocksize != eparams.block_size) bitwriter.writebits(32, frame.blocksize); if (frame.type != FrameType.Verbatim) { bitwriter.writebits(8, frame.interlacing_shift); bitwriter.writebits(8, frame.interlacing_leftweight); for (int ch = 0; ch < _pcm.ChannelCount; ch++) { bitwriter.writebits(4, 0); // prediction type bitwriter.writebits(4, frame.subframes[ch].best.shift); bitwriter.writebits(3, frame.subframes[ch].best.ricemodifier); bitwriter.writebits(5, frame.subframes[ch].best.order); if (frame.subframes[ch].best.order != 31) for (int c = 0; c < frame.subframes[ch].best.order; c++) bitwriter.writebits_signed(16, frame.subframes[ch].best.coefs[c]); } } }
unsafe void encode_residual_pass1(ALACFrame frame, int ch, int best_window) { int max_prediction_order = eparams.max_prediction_order; int estimation_depth = eparams.estimation_depth; int min_modifier = eparams.min_modifier; int adaptive_passes = eparams.adaptive_passes; eparams.max_prediction_order = Math.Min(8,eparams.max_prediction_order); eparams.estimation_depth = 1; eparams.min_modifier = eparams.max_modifier; eparams.adaptive_passes = 0; encode_residual(frame, ch, 1, best_window); eparams.max_prediction_order = max_prediction_order; eparams.estimation_depth = estimation_depth; eparams.min_modifier = min_modifier; eparams.adaptive_passes = adaptive_passes; }
unsafe void encode_residual(ALACFrame frame, int ch, int pass, int best_window) { int* smp = frame.subframes[ch].samples; int i, n = frame.blocksize; int bps = _pcm.BitsPerSample + _pcm.ChannelCount - 1; // FIXED //if (0 == (2 & frame.subframes[ch].done_fixed) && (pass != 1 || n < eparams.max_prediction_order)) //{ // frame.subframes[ch].done_fixed |= 2; // frame.current.order = 31; // frame.current.window = -1; // alac_encode_residual_31(frame.current.residual, frame.subframes[ch].samples, frame.blocksize); // frame.current.size = (uint)(alac_entropy_coder(frame.current.residual, frame.blocksize, bps, out frame.current.ricemodifier) + 16); // frame.ChooseBestSubframe(ch); //} //if (0 == (1 & frame.subframes[ch].done_fixed) && (pass != 1 || n < eparams.max_prediction_order)) //{ // frame.subframes[ch].done_fixed |= 1; // frame.current.order = 0; // frame.current.window = -1; // alac_encode_residual_0(frame.current.residual, frame.subframes[ch].samples, frame.blocksize); // frame.current.size = (uint)(alac_entropy_coder(frame.current.residual, frame.blocksize, bps, out frame.current.ricemodifier) + 16); // frame.ChooseBestSubframe(ch); //} // LPC if (n < eparams.max_prediction_order) return; float* lpcs = stackalloc float[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER]; int min_order = eparams.min_prediction_order; int max_order = eparams.max_prediction_order; for (int iWindow = 0; iWindow < _windowcount; iWindow++) { if (best_window != -1 && iWindow != best_window) continue; LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow]; lpc_ctx.GetReflection(max_order, smp, n, frame.window_buffer + iWindow * Alac.MAX_BLOCKSIZE * 2); lpc_ctx.ComputeLPC(lpcs); lpc_ctx.SortOrdersAkaike(frame.blocksize, eparams.estimation_depth, max_order, 5.0, 1.0/18); for (i = 0; i < eparams.estimation_depth && i < max_order; i++) encode_residual_lpc_sub(frame, lpcs, iWindow, lpc_ctx.best_orders[i], ch); } }
unsafe void encode_residual_lpc_sub(ALACFrame frame, float* lpcs, int iWindow, int order, int ch) { // check if we already calculated with this order, window and precision if ((frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[eparams.adaptive_passes] & (1U << (order - 1))) == 0) { frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[eparams.adaptive_passes] |= (1U << (order - 1)); uint cbits = 15U; frame.current.order = order; frame.current.window = iWindow; int bps = _pcm.BitsPerSample + _pcm.ChannelCount - 1; int* coefs = stackalloc int[lpc.MAX_LPC_ORDER]; //if (frame.subframes[ch].best.order == order && frame.subframes[ch].best.window == iWindow) //{ // frame.current.shift = frame.subframes[ch].best.shift; // for (int i = 0; i < frame.current.order; i++) // frame.current.coefs[i] = frame.subframes[ch].best.coefs_adapted[i]; //} //else { lpc.quantize_lpc_coefs(lpcs + (frame.current.order - 1) * lpc.MAX_LPC_ORDER, frame.current.order, cbits, coefs, out frame.current.shift, 15, 1); if (frame.current.shift < 0 || frame.current.shift > 15) throw new Exception("negative shift"); for (int i = 0; i < frame.current.order; i++) frame.current.coefs[i] = coefs[i]; } for (int i = 0; i < frame.current.order; i++) coefs[i] = frame.current.coefs[frame.current.order - 1 - i]; for (int i = frame.current.order; i < lpc.MAX_LPC_ORDER; i++) coefs[i] = 0; alac_encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, bps); for (int i = 0; i < frame.current.order; i++) frame.current.coefs_adapted[i] = coefs[frame.current.order - 1 - i]; for (int adaptive_pass = 0; adaptive_pass < eparams.adaptive_passes; adaptive_pass++) { for (int i = 0; i < frame.current.order; i++) frame.current.coefs[i] = frame.current.coefs_adapted[i]; alac_encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, bps); for (int i = 0; i < frame.current.order; i++) frame.current.coefs_adapted[i] = coefs[frame.current.order - 1 - i]; } frame.current.size = (uint)(alac_entropy_estimate(frame.current.residual, frame.blocksize, bps, eparams.max_modifier) + 16 + 16 * order); frame.ChooseBestSubframe(ch); } }
unsafe void encode_estimated_frame(ALACFrame frame) { switch (eparams.stereo_method) { case StereoMethod.Estimate: for (int ch = 0; ch < _pcm.ChannelCount; ch++) { frame.subframes[ch].best.size = AudioSamples.UINT32_MAX; encode_residual_pass2(frame, ch); } break; case StereoMethod.Evaluate: for (int ch = 0; ch < _pcm.ChannelCount; ch++) encode_residual_pass2(frame, ch); break; case StereoMethod.Search: break; } }
unsafe uint measure_frame_size(ALACFrame frame, bool do_midside) { // crude estimation of header/footer size uint total = 16 + 3; if (do_midside) { uint bitsBest = frame.subframes[0].best.size + frame.subframes[1].best.size; frame.interlacing_leftweight = 0; frame.interlacing_shift = 0; if (bitsBest > frame.subframes[3].best.size + frame.subframes[0].best.size) // leftside { bitsBest = frame.subframes[3].best.size + frame.subframes[0].best.size; frame.interlacing_leftweight = 1; frame.interlacing_shift = 0; } if (bitsBest > frame.subframes[3].best.size + frame.subframes[2].best.size) // midside { bitsBest = frame.subframes[3].best.size + frame.subframes[2].best.size; frame.interlacing_leftweight = 1; frame.interlacing_shift = 1; } if (bitsBest > frame.subframes[3].best.size + frame.subframes[4].best.size) // rightside { bitsBest = frame.subframes[3].best.size + frame.subframes[4].best.size; frame.interlacing_leftweight = 1; frame.interlacing_shift = 31; } return total + bitsBest; } for (int ch = 0; ch < _pcm.ChannelCount; ch++) total += frame.subframes[ch].best.size; return total; }
unsafe void estimate_frame(ALACFrame frame, bool do_midside) { int subframes = do_midside ? 5 : _pcm.ChannelCount; switch (eparams.stereo_method) { case StereoMethod.Estimate: for (int ch = 0; ch < subframes; ch++) { LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[0]; int stereo_order = Math.Min(8, eparams.max_prediction_order); double alpha = 1.5; // 4.5 + eparams.max_prediction_order / 10.0; lpc_ctx.GetReflection(stereo_order, frame.subframes[ch].samples, frame.blocksize, frame.window_buffer); lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, stereo_order, alpha, 0); frame.subframes[ch].best.size = (uint)Math.Max(0, lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], alpha, 0)); } break; case StereoMethod.Evaluate: for (int ch = 0; ch < subframes; ch++) encode_residual_pass1(frame, ch, 0); break; case StereoMethod.Search: for (int ch = 0; ch < subframes; ch++) encode_residual_pass2(frame, ch); break; } }