diff --git a/CUETools.Codecs.ALAC/ALAC.cs b/CUETools.Codecs.ALAC/ALAC.cs new file mode 100644 index 0000000..e22feeb --- /dev/null +++ b/CUETools.Codecs.ALAC/ALAC.cs @@ -0,0 +1,229 @@ +/** + * CUETools.Codecs.ALAC: pure managed ALAC audio encoder + * Copyright (c) 2009 Gregory S. Chudov + * Based on ffdshow ALAC audio encoder + * Copyright (c) 2008 Jaikrishnan Menon, realityman@gmx.net + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace CUETools.Codecs.ALAC +{ + public class Alac + { + public const int MAX_BLOCKSIZE = 65535; + public const int MAX_RICE_PARAM = 14; + public const int MAX_PARTITION_ORDER = 8; + public const int MAX_PARTITIONS = 1 << MAX_PARTITION_ORDER; + + public const uint UINT32_MAX = 0xffffffff; + + public static StereoMethod LookupStereoMethod(string name) + { + return (StereoMethod)(Enum.Parse(typeof(StereoMethod), name, true)); + } + + public static OrderMethod LookupOrderMethod(string name) + { + return (OrderMethod)(Enum.Parse(typeof(OrderMethod), name, true)); + } + + public static WindowFunction LookupWindowFunction(string name) + { + return (WindowFunction)(Enum.Parse(typeof(WindowFunction), name, true)); + } + + } + + unsafe class RiceContext + { + public RiceContext() + { + rparams = new int[Alac.MAX_PARTITIONS]; + esc_bps = new int[Alac.MAX_PARTITIONS]; + } + /// + /// partition order + /// + public int porder; + + /// + /// Rice parameters + /// + public int[] rparams; + + /// + /// bps if using escape code + /// + public int[] esc_bps; + }; + + unsafe class ALACSubframe + { + public ALACSubframe() + { + rc = new RiceContext(); + coefs = new int[lpc.MAX_LPC_ORDER]; + } + public int order; + public int* residual; + public RiceContext rc; + public uint size; + + public int ricemodifier; + public int cbits; + public int shift; + public int[] coefs; + public int window; + }; + + unsafe class ALACSubframeInfo + { + public ALACSubframeInfo() + { + best = new ALACSubframe(); + lpc_ctx = new LpcContext[lpc.MAX_LPC_WINDOWS]; + for (int i = 0; i < lpc.MAX_LPC_WINDOWS; i++) + lpc_ctx[i] = new LpcContext(); + } + + public void Init(int* s, int* r) + { + samples = s; + best.residual = r; + best.size = AudioSamples.UINT32_MAX; + for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++) + lpc_ctx[iWindow].Reset(); + done_fixed = 0; + } + + public ALACSubframe best; + public int* samples; + public uint done_fixed; + public LpcContext[] lpc_ctx; + }; + + unsafe class ALACFrame + { + public ALACFrame(int subframes_count) + { + subframes = new ALACSubframeInfo[subframes_count]; + for (int ch = 0; ch < subframes_count; ch++) + subframes[ch] = new ALACSubframeInfo(); + current = new ALACSubframe(); + } + + public void InitSize(int bs) + { + blocksize = bs; + type = FrameType.Verbatim; + interlacing_shift = interlacing_leftweight = 0; + } + + public void ChooseBestSubframe(int ch) + { + if (current.size >= subframes[ch].best.size) + return; + ALACSubframe tmp = subframes[ch].best; + subframes[ch].best = current; + current = tmp; + } + + public void SwapSubframes(int ch1, int ch2) + { + ALACSubframeInfo tmp = subframes[ch1]; + subframes[ch1] = subframes[ch2]; + subframes[ch2] = tmp; + } + + /// + /// Swap subframes according to channel mode. + /// It is assumed that we have 4 subframes, + /// 0 is right, 1 is left, 2 is middle, 3 is difference + /// + public void ChooseSubframes() + { + if (interlacing_leftweight != 0) + { + SwapSubframes(1, 3); + switch (interlacing_shift) + { + case 0: // leftside + break; + case 1: // midside + SwapSubframes(0, 2); + break; + case 31: // rightside + SwapSubframes(0, 4); + break; + } + } + } + + public FrameType type; + public int blocksize; + public int interlacing_shift, interlacing_leftweight; + public ALACSubframeInfo[] subframes; + public ALACSubframe current; + public double* window_buffer; + } + + public enum OrderMethod + { + Max = 0, + Estimate = 1, + LogFast = 2, + LogSearch = 3, + EstSearch2 = 4, + Search = 5 + } + + public enum StereoMethod + { + Independent = 0, + Estimate = 1, + Estimate2 = 2, + Evaluate = 3, + Search = 4 + } + + public enum FrameType + { + Verbatim = 0, + Compressed = 1 + }; + + public enum ChannelMode + { + NotStereo = 0, + LeftRight = 1, + LeftSide = 8, + RightSide = 9, + MidSide = 10 + } + + public enum WindowFunction + { + Welch = 1, + Tukey = 2, + Hann = 4, + Flattop = 8, + TukFlat = 10 + } +} diff --git a/CUETools.Codecs.ALAC/ALACDotNet.cs b/CUETools.Codecs.ALAC/ALACDotNet.cs index 1dcb828..8e84a3c 100644 --- a/CUETools.Codecs.ALAC/ALACDotNet.cs +++ b/CUETools.Codecs.ALAC/ALACDotNet.cs @@ -42,6 +42,24 @@ namespace CUETools.Codecs.ALAC calculate_length(); } + public ALACReader(int channels, int bps, int rice_historymult, int rice_initialhistory, int rice_kmodifier, int blocksize) + { + _channelCount = channels; + _bitsPerSample = bps; + _sampleRate = 44100; + + setinfo_max_samples_per_frame = (uint)blocksize; + setinfo_rice_historymult = (byte)rice_historymult; + setinfo_rice_initialhistory = (byte)rice_initialhistory; + setinfo_rice_kmodifier = (byte)rice_kmodifier; + + _predicterror_buffer_a = new int[setinfo_max_samples_per_frame]; + _predicterror_buffer_b = new int[setinfo_max_samples_per_frame]; + _outputsamples_buffer_a = new int[setinfo_max_samples_per_frame]; + _outputsamples_buffer_b = new int[setinfo_max_samples_per_frame]; + _framesBuffer = new byte[65536]; + } + public int[,] Read(int[,] buff) { return AudioSamples.Read(this, buff); @@ -96,7 +114,7 @@ namespace CUETools.Codecs.ALAC return (uint)offset; get_sample_info(_iSample, out sampleDuration, out sampleSize); _IO.Read(_framesBuffer, 0, (int) sampleSize); - decodeFrame(sampleDuration, sampleSize); + decodeFrame(sampleSize); if (sampleDuration != _samplesInBuffer) throw new Exception("sample count mismatch"); _samplesInBuffer -= _samplesBufferOffset; @@ -157,6 +175,8 @@ namespace CUETools.Codecs.ALAC _IO.Position = _saved_mdat_pos + fileOffs; return; } + if ((int)_iSample >= _sample_byte_size.Length) + throw new Exception("seeking past end of stream"); get_sample_info(_iSample, out sampleDuration, out sampleSize); durOffs += sampleDuration; fileOffs += sampleSize; @@ -225,6 +245,8 @@ namespace CUETools.Codecs.ALAC while (_time_to_sample_count[duration_cur_index] + duration_index_accum <= iSample) { duration_index_accum += _time_to_sample_count[duration_cur_index]; + if (duration_cur_index == _time_to_sample_count.Length - 1) + throw new Exception("seeking past end of stream"); duration_cur_index ++; } sampleDuration = _time_to_sample_duration[duration_cur_index]; @@ -250,7 +272,7 @@ namespace CUETools.Codecs.ALAC get_sample_info(_iSample, out sampleDuration, out sampleSize); InitTables(); _IO.Read(_framesBuffer, 0, (int)sampleSize); - decodeFrame(sampleDuration, sampleSize); + decodeFrame(sampleSize); if (_samplesInBuffer < sampleDuration) { _time_to_sample_duration = new uint[2] { sample_duration_0, _samplesInBuffer }; @@ -395,11 +417,6 @@ namespace CUETools.Codecs.ALAC return result; } - private static uint SIGN_EXTENDED32(uint val, int bits) - { - return ((val << (32 - bits)) >> (32 - bits)); - } - private void unreadbits(ref int pos, int bits) { int new_accumulator = (_bitaccumulator - bits); @@ -412,7 +429,7 @@ namespace CUETools.Codecs.ALAC private static int count_leading_zeroes(uint input) { - int zeroes = 1; + int zeroes = 0; uint shifted_input = input >> 16; if (shifted_input == 0) zeroes += 16; @@ -423,22 +440,7 @@ namespace CUETools.Codecs.ALAC zeroes += 8; else input = shifted_input; - shifted_input = input >> 4; - if (shifted_input == 0) - zeroes += 4; - else - input = shifted_input; - shifted_input = input >> 2; - if (shifted_input == 0) - zeroes += 2; - else - input = shifted_input; - shifted_input = input >> 1; - if (shifted_input == 0) - zeroes ++; - else - input = shifted_input; - return zeroes - (int)input; + return zeroes + BitReader.byte_to_unary_table[input]; } private unsafe void readPredictor(ref int pos, ref predictor_t predictor_info) @@ -462,30 +464,14 @@ namespace CUETools.Codecs.ALAC private unsafe int decode_scalar(byte * buff, ref int pos, int k, int limit, int readsamplesize) { - int x = 0; uint next = peekbits_9(buff, pos); - if (next == 0x1ff) /* RICE THRESHOLD 9 bits */ + int x = (next >> 8 == 0) ? 0 : + 1 + BitReader.byte_to_unary_table[(~next) & 0xff]; + if (x == 9) /* RICE THRESHOLD 9 bits */ { skipbits(ref pos, 9); return (int)readbits(buff, ref pos, readsamplesize); } - if ((next & 0x1e0) == 0x1e0) - { - x += 4; - next <<= 4; - } - if ((next & 0x180) == 0x180) - { - x += 2; - next <<= 2; - } - if ((next & 0x100) == 0x100) - { - x += 1; - next <<= 1; - } - x += (int) (next & 0x100) >> 8; - //x = count_leading_zeroes((~next) & 0x1ff) - 23; skipbits(ref pos, x + 1); if (k >= limit) k = limit; @@ -637,7 +623,7 @@ namespace CUETools.Codecs.ALAC } } - private unsafe void deinterlace(int[,] samplesBuffer, uint offset, uint sampleCount) + internal unsafe void deinterlace(int[,] samplesBuffer, uint offset, uint sampleCount) { if (sampleCount <= 0 || sampleCount > _samplesInBuffer) return; @@ -670,17 +656,18 @@ namespace CUETools.Codecs.ALAC } /* otherwise basic interlacing took place */ - for (i = 0; i < sampleCount; i++) - { - int a = buf_a[i]; - int b = buf_b[i]; - buf_s[i * 2] = a; - buf_s[i * 2 + 1] = b; - } + AudioSamples.Interlace(buf_s, buf_a, buf_b, (int)sampleCount); } } - private void decodeFrame(ulong sampleDuration, uint sampleSize) + internal int DecodeFrame(byte[] buffer, int pos, int len) + { + Array.Copy(buffer, pos, _framesBuffer, 0, len); + decodeFrame((uint)len); + return len; // pos + } + + private void decodeFrame(uint sampleSize) { _bitaccumulator = 0; int pos = 0; @@ -723,8 +710,6 @@ namespace CUETools.Codecs.ALAC else { /* not compressed, easy case */ - if (_bitsPerSample != 16) - throw new Exception("Not 16 bit"); for (int i = 0; i < outputSamples; i++) { _outputsamples_buffer_a[i] = extend_sign32((int)readbits(_framesBuffer, ref pos, _bitsPerSample), _bitsPerSample); @@ -735,8 +720,8 @@ namespace CUETools.Codecs.ALAC _interlacing_leftweight = 0; } - if (_bitsPerSample != 16) - throw new Exception("Not 16 bit"); + if (readbits(_framesBuffer, ref pos, 3) != 7) + throw new Exception("Invalid frame."); _samplesInBuffer = outputSamples; } diff --git a/CUETools.Codecs.ALAC/ALACWriter.cs b/CUETools.Codecs.ALAC/ALACWriter.cs new file mode 100644 index 0000000..0db3e55 --- /dev/null +++ b/CUETools.Codecs.ALAC/ALACWriter.cs @@ -0,0 +1,1725 @@ +/** + * CUETools.Codecs.ALAC: pure managed ALAC audio encoder + * Copyright (c) 2009 Gregory S. Chudov + * Based on ffdshow ALAC audio encoder + * Copyright (c) 2008 Jaikrishnan Menon, realityman@gmx.net + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Text; +using System.IO; +using System.Collections.Generic; +using System.Collections.Specialized; +//using System.Runtime.InteropServices; +using CUETools.Codecs; + +namespace CUETools.Codecs.ALAC +{ + public class ALACWriter : IAudioDest + { + Stream _IO = null; + string _path; + long _position; + + // number of audio channels + // valid values are 1 to 8 + int channels, ch_code; + + // audio sample rate in Hz + int sample_rate; + + // sample size in bits + // only 16-bit is currently supported + uint bits_per_sample; + + // total stream samples + // if 0, stream length is unknown + int sample_count; + + ALACEncodeParams eparams; + + // maximum frame size in bytes + // this can be used to allocate memory for output + int max_frame_size; + + int initial_history, history_mult, k_modifier; + + byte[] frame_buffer = null; + + int frame_count = 0; + + long first_frame_offset = 0; + + TimeSpan _userProcessorTime; + + // header bytes + byte[] header; + + uint[] _sample_byte_size; + int[] samplesBuffer; + int[] verifyBuffer; + int[] residualBuffer; + double[] windowBuffer; + int samplesInBuffer = 0; + + int _compressionLevel = 4; + int _blocksize = 0; + int _totalSize = 0; + int _windowsize = 0, _windowcount = 0; + + Crc8 crc8; + Crc16 crc16; + ALACFrame frame; + ALACReader verify; + + int mdat_pos; + + bool inited = false; + + List chunk_pos; + + public ALACWriter(string path, int bitsPerSample, int channelCount, int sampleRate, Stream IO) + { + if (bitsPerSample != 16) + throw new Exception("Bits per sample must be 16."); + if (channelCount != 2) + throw new Exception("ChannelCount must be 2."); + + channels = channelCount; + sample_rate = sampleRate; + bits_per_sample = (uint) bitsPerSample; + + _path = path; + _IO = IO; + + samplesBuffer = new int[Alac.MAX_BLOCKSIZE * (channels == 2 ? 5 : channels)]; + residualBuffer = new int[Alac.MAX_BLOCKSIZE * (channels == 2 ? 6 : channels + 1)]; + windowBuffer = new double[Alac.MAX_BLOCKSIZE * 2 * lpc.MAX_LPC_WINDOWS]; + + eparams.set_defaults(_compressionLevel); + eparams.padding_size = 8192; + + crc8 = new Crc8(); + crc16 = new Crc16(); + frame = new ALACFrame(channels == 2 ? 5 : channels); + chunk_pos = new List(); + } + + public int TotalSize + { + get + { + return _totalSize; + } + } + + public int PaddingLength + { + get + { + return eparams.padding_size; + } + set + { + eparams.padding_size = value; + } + } + + public int CompressionLevel + { + get + { + return _compressionLevel; + } + set + { + if (value < 0 || value > 11) + throw new Exception("unsupported compression level"); + _compressionLevel = value; + eparams.set_defaults(_compressionLevel); + } + } + + //[DllImport("kernel32.dll")] + //static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime); + //[DllImport("kernel32.dll")] + //static extern IntPtr GetCurrentThread(); + + void chunk_start(BitWriter bitwriter) + { + bitwriter.flush(); + chunk_pos.Add(bitwriter.Length); + bitwriter.writebits(32, 0); // length placeholder + } + + void chunk_end(BitWriter bitwriter) + { + bitwriter.flush(); + int pos = chunk_pos[chunk_pos.Count - 1]; + chunk_pos.RemoveAt(chunk_pos.Count - 1); + int chunk_end = bitwriter.Length; + bitwriter.Length = pos; + bitwriter.writebits(32, chunk_end - pos); + bitwriter.Length = chunk_end; + } + + void DoClose() + { + if (inited) + { + while (samplesInBuffer > 0) + output_frame(samplesInBuffer); + + if (_IO.CanSeek) + { + int mdat_len = (int)_IO.Position - mdat_pos; + _IO.Position = mdat_pos; + BitWriter bitwriter = new BitWriter(header, 0, 4); + bitwriter.writebits(32, mdat_len); + bitwriter.flush(); + _IO.Write(header, 0, 4); + + _IO.Position = _IO.Length; + int trailer_len = write_trailers(); + _IO.Write(header, 0, trailer_len); + } + _IO.Close(); + inited = false; + } + + //long fake, KernelStart, UserStart; + //GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart); + //_userProcessorTime = new TimeSpan(UserStart); + } + + public void Close() + { + DoClose(); + if (sample_count != 0 && _position != sample_count) + throw new Exception("Samples written differs from the expected sample count."); + } + + public void Delete() + { + if (inited) + { + _IO.Close(); + inited = false; + } + + if (_path != "") + File.Delete(_path); + } + + public long Position + { + get + { + return _position; + } + } + + public long FinalSampleCount + { + set { sample_count = (int)value; } + } + + public long BlockSize + { + set { _blocksize = (int)value; } + get { return _blocksize == 0 ? eparams.block_size : _blocksize; } + } + + public OrderMethod OrderMethod + { + get { return eparams.order_method; } + set { eparams.order_method = value; } + } + + public StereoMethod StereoMethod + { + get { return eparams.stereo_method; } + set { eparams.stereo_method = value; } + } + + public WindowFunction WindowFunction + { + get { return eparams.window_function; } + set { eparams.window_function = value; } + } + + public bool DoVerify + { + get { return eparams.do_verify; } + set { eparams.do_verify = value; } + } + + public bool DoSeekTable + { + get { return eparams.do_seektable; } + set { eparams.do_seektable = value; } + } + + public int MinLPCOrder + { + get + { + return eparams.min_prediction_order; + } + set + { + if (value < 1) + throw new Exception("invalid MinLPCOrder " + value.ToString()); + eparams.min_prediction_order = value; + if (eparams.max_prediction_order < value) + eparams.max_prediction_order = value; + } + } + + public int MaxLPCOrder + { + get + { + return eparams.max_prediction_order; + } + set + { + if (value > 30 || value < eparams.min_prediction_order) + throw new Exception("invalid MaxLPCOrder " + value.ToString()); + eparams.max_prediction_order = value; + if (eparams.min_prediction_order > value) + eparams.min_prediction_order = value; + } + } + + public int MinHistoryModifier + { + get + { + return eparams.min_modifier; + } + set + { + if (value < 1) + throw new Exception("invalid MinHistoryModifier " + value.ToString()); + eparams.min_modifier = value; + if (eparams.max_modifier < value) + eparams.max_modifier = value; + } + } + + public int MaxHistoryModifier + { + get + { + return eparams.max_modifier; + } + set + { + if (value > 100) + throw new Exception("invalid MaxHistoryModifier " + value.ToString()); + eparams.max_modifier = value; + if (eparams.min_modifier > value) + eparams.min_modifier = value; + } + } + + public int EstimationDepth + { + get + { + return eparams.estimation_depth; + } + set + { + if (value > 32 || value < 1) + throw new Exception("invalid estimation_depth " + value.ToString()); + eparams.estimation_depth = value; + } + } + + public TimeSpan UserProcessorTime + { + get { return _userProcessorTime; } + } + + public int BitsPerSample + { + get { return 16; } + } + + /// + /// Copy channel-interleaved input samples into separate subframes + /// + /// + /// + /// + unsafe void copy_samples(int[,] samples, int pos, int block) + { + fixed (int* fsamples = samplesBuffer, src = &samples[pos, 0]) + { + if (channels == 2) + AudioSamples.Deinterlace(fsamples + samplesInBuffer, fsamples + Alac.MAX_BLOCKSIZE + samplesInBuffer, src, block); + else + for (int ch = 0; ch < channels; ch++) + { + int* psamples = fsamples + ch * Alac.MAX_BLOCKSIZE + samplesInBuffer; + for (int i = 0; i < block; i++) + psamples[i] = src[i * channels + ch]; + } + } + samplesInBuffer += block; + } + + unsafe static void channel_decorrelation(int* leftS, int* rightS, int *leftM, int *rightM, int blocksize) + { + for (int i = 0; i < blocksize; i++) + { + leftM[i] = (leftS[i] + rightS[i]) >> 1; + rightM[i] = leftS[i] - rightS[i]; + } + } + + unsafe static void channel_decorrelation(int* leftS, int* rightS, int* leftM, int* rightM, int blocksize, int leftweight, int shift) + { + for (int i = 0; i < blocksize; i++) + { + leftM[i] = rightS[i] + ((leftS[i] - rightS[i]) * leftweight >> shift); + rightM[i] = leftS[i] - rightS[i]; + } + } + + private static int extend_sign32(int val, int bits) + { + return (val << (32 - bits)) >> (32 - bits); + } + + private static short sign_only(int val) + { + return (short)((val >> 31) + ((val - 1) >> 31) + 1); + } + + unsafe static void alac_encode_residual_31(int* res, int* smp, int n) + { + res[0] = smp[0]; + for (int i = 1; i < n; i++) + res[i] = smp[i] - smp[i - 1]; + } + + unsafe static void alac_encode_residual_0(int* res, int* smp, int n) + { + AudioSamples.MemCpy(res, smp, n); + } + + unsafe static void alac_encode_residual(int* res, int* smp, int n, int order, int* coefs, int shift, int bps) + { + int csum = 0; + + for (int i = order - 1; i >= 0; i--) + csum += coefs[i]; + + if (n <= order || order <= 0 || order > 30) + throw new Exception("invalid output"); + + /* generate warm-up samples */ + res[0] = smp[0]; + for (int i = 1; i <= order; i++) + res[i] = smp[i] - smp[i - 1]; + + /* general case */ + for (int i = order + 1; i < n; i++) + { + int sample = *(smp++); + int/*long*/ sum = (1 << (shift - 1)) -csum * sample; + int sum2 = 0; + for (int j = 0; j < order; j+= 2) + { + sum += smp[j] * coefs[j]; + sum2 += smp[j+1] * coefs[j+1]; + } + int resval = extend_sign32(smp[order] - (int)((sum + sum2) >> shift) - sample, bps); + res[i] = resval; + int error_sign = sign_only(resval); + for (int j = 0; j < order && resval * error_sign > 0; j++) + { + int val = sample - smp[j]; + int sign = error_sign * sign_only(val); + coefs[j] -= sign; + csum -= sign; + resval -= ((val * sign) >> shift) * (j + 1); + //error_sign = (error_sign + sign_only(resval)) / 2; + } + } + res[n] = 1; // Stop byte to help alac_entropy_coder; + } + + unsafe static int encode_scalar(int x, int k, int bps) + { + int divisor = (1 << k) - 1; + int q = x / divisor; + int r = x % divisor; + return q > 8 ? 9 + bps : q + k + (r - 1 >> 31) + 1;//== 0 ? 0 : 1); + } + + unsafe void encode_scalar(BitWriter bitwriter, int x, int k, int bps) + { + k = Math.Min(k, k_modifier); + int divisor = (1 << k) - 1; + int q = x / divisor; + int r = x % divisor; + + if (q > 8) + { + // write escape code and sample value directly + bitwriter.writebits(9, 0x1ff); + bitwriter.writebits(bps, x); + return; + } + + // q times one, then 1 zero, e.g. q == 3 is written as 1110 + int unary = ((1 << (q + 1)) - 2); + if (r == 0) + { + bitwriter.writebits(q + k, unary << (k - 1)); + return; + } + + bitwriter.writebits(q + 1 + k, (unary << k) + r + 1); + } + + unsafe int alac_entropy_coder(int* res, int n, int bps, out int modifier) + { + int size = 1 << 30; + modifier = eparams.min_modifier; + for (int i = eparams.min_modifier; i <= eparams.max_modifier; i++) + { + int newsize = alac_entropy_coder(res, n, bps, i); + if (size > newsize) + { + size = newsize; + modifier = i; + } + } + return size; + } + + unsafe int alac_entropy_coder(int* res, int n, int bps, int modifier) + { + int history = initial_history; + int sign_modifier = 0; + int rice_historymult = modifier * history_mult / 4; + int size = 0; + + for (int i = 0; i < n; ) + { + int k = BitReader.log2i((history >> 9) + 3); + int x = -2 * (*res) - 1; + x ^= (x >> 31); + + res++; + i++; + + size += encode_scalar(x - sign_modifier, Math.Min(k, k_modifier), bps); + + history += x * rice_historymult - ((history * rice_historymult) >> 9); + + sign_modifier = 0; + if (x > 0xFFFF) + history = 0xFFFF; + + if (history < 128 && i < n) + { + k = 7 - BitReader.log2i(history) + ((history + 16) >> 6); + int block_size = 0; + while (res[block_size] == 0) // we have a stop byte, so need not check if i + blocksize < n + block_size++; + res += block_size; + i += block_size; + size += encode_scalar(block_size, Math.Min(k, k_modifier), 16); + //sign_modifier = (block_size <= 0xFFFF) ? 1 : 0; //never happens + sign_modifier = 1; + history = 0; + } + } + return size; + } + + unsafe void alac_entropy_coder(BitWriter bitwriter, int* res, int n, int bps, int modifier) + { + int history = initial_history; + int sign_modifier = 0; + int rice_historymult = modifier * history_mult / 4; + + for (int i = 0; i < n; ) + { + int k = BitReader.log2i((history >> 9) + 3); + int x = -2 * (*res) - 1; + x ^= (x >> 31); + + res++; + i++; + + encode_scalar(bitwriter, x - sign_modifier, k, bps); + + history += x * rice_historymult - ((history * rice_historymult) >> 9); + + sign_modifier = 0; + if (x > 0xFFFF) + history = 0xFFFF; + + if (history < 128 && i < n) + { + k = 7 - BitReader.log2i(history) + ((history + 16) >> 6); + int block_size = 0; + while (res[block_size] == 0) // we have a stop byte, so need not check if i + blocksize < n + block_size++; + res += block_size; + i += block_size; + encode_scalar(bitwriter, block_size, k, 16); + sign_modifier = (block_size <= 0xFFFF) ? 1 : 0; + history = 0; + } + } + } + + unsafe void encode_residual_lpc_sub(ALACFrame frame, double * lpcs, int iWindow, int order, int ch) + { + // select LPC precision based on block size + uint lpc_precision = 15; + int i_precision = 0; + //if (frame.blocksize <= 192) lpc_precision = 7U; + //else if (frame.blocksize <= 384) lpc_precision = 8U; + //else if (frame.blocksize <= 576) lpc_precision = 9U; + //else if (frame.blocksize <= 1152) lpc_precision = 10U; + //else if (frame.blocksize <= 2304) lpc_precision = 11U; + //else if (frame.blocksize <= 4608) lpc_precision = 12U; + //else if (frame.blocksize <= 8192) lpc_precision = 13U; + //else if (frame.blocksize <= 16384) lpc_precision = 14U; + + //for (int i_precision = eparams.lpc_min_precision_search; i_precision <= eparams.lpc_max_precision_search && lpc_precision + i_precision < 16; i_precision++) + // check if we already calculated with this order, window and precision + if ((frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[i_precision] & (1U << (order - 1))) == 0) + { + frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[i_precision] |= (1U << (order - 1)); + + uint cbits = lpc_precision + (uint)i_precision; + + frame.current.order = order; + frame.current.window = iWindow; + + int bps = (int)bits_per_sample + channels - 1; + + int* coefs = stackalloc int[lpc.MAX_LPC_ORDER]; + + 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]; + coefs[frame.current.order] = 0; + + alac_encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, + frame.current.order, coefs, frame.current.shift, bps); + + frame.current.size = (uint)(alac_entropy_coder(frame.current.residual, frame.blocksize, bps, out frame.current.ricemodifier) + 16 + 16 * order); + + frame.ChooseBestSubframe(ch); + } + } + + unsafe void encode_residual(ALACFrame frame, int ch, OrderMethod omethod, int pass) + { + int* smp = frame.subframes[ch].samples; + int i, n = frame.blocksize; + int best_window = frame.subframes[ch].best.window; + int bps = (int)bits_per_sample + channels - 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; + + double* lpcs = stackalloc double[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 (pass == 2 && 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); + + switch (omethod) + { + case OrderMethod.Max: + // always use maximum order + encode_residual_lpc_sub(frame, lpcs, iWindow, max_order, ch); + break; + case OrderMethod.Estimate: + // estimated orders + // Search at reflection coeff thresholds (where they cross 0.10) + { + int found = 0; + for (i = max_order; i >= min_order && found < eparams.estimation_depth; i--) + if (lpc_ctx.IsInterestingOrder(i)) + { + encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch); + found++; + } + if (0 == found) + encode_residual_lpc_sub(frame, lpcs, iWindow, min_order, ch); + } + break; + case OrderMethod.EstSearch2: + // Search at reflection coeff thresholds (where they cross 0.10) + { + int found = 0; + for (i = min_order; i <= max_order && found < eparams.estimation_depth; i++) + if (lpc_ctx.IsInterestingOrder(i)) + { + encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch); + found++; + } + if (0 == found) + encode_residual_lpc_sub(frame, lpcs, iWindow, min_order, ch); + } + break; + case OrderMethod.Search: + // brute-force optimal order search + for (i = max_order; i >= min_order; i--) + encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch); + break; + case OrderMethod.LogFast: + // Try max, est, 32,16,8,4,2,1 + encode_residual_lpc_sub(frame, lpcs, iWindow, max_order, ch); + for (i = lpc.MAX_LPC_ORDER; i >= min_order; i >>= 1) + if (i < max_order) + encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch); + break; + default: + throw new Exception("unknown ordermethod"); + } + } + } + + unsafe void output_frame_header(ALACFrame frame, BitWriter bitwriter) + { + bitwriter.writebits(3, channels - 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 < channels; 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]); + } + } + } + + void output_frame_footer(BitWriter bitwriter) + { + bitwriter.writebits(3, 7); + bitwriter.flush(); + } + + unsafe void window_welch(double* window, int L) + { + int N = L - 1; + double N2 = (double)N / 2.0; + + for (int n = 0; n <= N; n++) + { + double k = 1 / N2 - 1.0 - Math.Min(n, N - n); + //double k = ((double)n - N2) / N2; + window[n] = 1.0 - k * k; + } + } + + unsafe void window_rectangle(double* window, int L) + { + for (int n = 0; n < L; n++) + window[n] = 1.0; + } + + unsafe void window_flattop(double* window, int L) + { + int N = L - 1; + for (int n = 0; n < L; n++) + window[n] = 1.0 - 1.93 * Math.Cos(2.0 * Math.PI * n / N) + 1.29 * Math.Cos(4.0 * Math.PI * n / N) - 0.388 * Math.Cos(6.0 * Math.PI * n / N) + 0.0322 * Math.Cos(8.0 * Math.PI * n / N); + } + + unsafe void window_tukey(double* window, int L) + { + window_rectangle(window, L); + double p = 0.5; + int Np = (int)(p / 2.0 * L) - 1; + if (Np > 0) + { + for (int n = 0; n <= Np; n++) + { + window[n] = 0.5 - 0.5 * Math.Cos(Math.PI * n / Np); + window[L - Np - 1 + n] = 0.5 - 0.5 * Math.Cos(Math.PI * (n + Np) / Np); + } + } + } + + unsafe void window_hann(double* window, int L) + { + int N = L - 1; + for (int n = 0; n < L; n++) + window[n] = 0.5 - 0.5 * Math.Cos(2.0 * Math.PI * n / N); + } + + unsafe void encode_residual_pass1(ALACFrame frame, int ch) + { + int max_prediction_order = eparams.max_prediction_order; + int estimation_depth = eparams.estimation_depth; + int min_modifier = eparams.min_modifier; + eparams.max_prediction_order = Math.Min(8,eparams.max_prediction_order); + eparams.estimation_depth = 1; + eparams.min_modifier = eparams.max_modifier; + encode_residual(frame, ch, OrderMethod.Estimate, 1); + eparams.max_prediction_order = max_prediction_order; + eparams.estimation_depth = estimation_depth; + eparams.min_modifier = min_modifier; + } + + unsafe void encode_residual_pass2(ALACFrame frame, int ch) + { + encode_residual(frame, ch, eparams.order_method, 2); + } + + unsafe void encode_residual_onepass(ALACFrame frame, int ch) + { + if (_windowcount > 1) + { + encode_residual_pass1(frame, ch); + encode_residual_pass2(frame, ch); + } + else + encode_residual(frame, ch, eparams.order_method, 0); + } + + unsafe void estimate_frame(ALACFrame frame, bool do_midside) + { + int subframes = do_midside ? 5 : channels; + + switch (eparams.stereo_method) + { + case StereoMethod.Evaluate: + for (int ch = 0; ch < subframes; ch++) + { + int windowcount = _windowcount; + _windowcount = 1; + encode_residual_pass1(frame, ch); + _windowcount = windowcount; + } + break; + case StereoMethod.Search: + for (int ch = 0; ch < subframes; ch++) + encode_residual_onepass(frame, ch); + 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 < channels; ch++) + total += frame.subframes[ch].best.size; + + return total; + } + + unsafe void encode_estimated_frame(ALACFrame frame) + { + switch (eparams.stereo_method) + { + case StereoMethod.Evaluate: + for (int ch = 0; ch < channels; ch++) + { + if (_windowcount > 1) + encode_residual_pass1(frame, ch); + encode_residual_pass2(frame, ch); + } + break; + case StereoMethod.Search: + break; + } + } + + unsafe delegate void window_function(double* window, int size); + + unsafe void calculate_window(double* window, window_function func, WindowFunction flag) + { + if ((eparams.window_function & flag) == 0 || _windowcount == lpc.MAX_LPC_WINDOWS) + return; + int sz = _windowsize; + double* pos = window + _windowcount * Alac.MAX_BLOCKSIZE * 2; + do + { + func(pos, sz); + if ((sz & 1) != 0) + break; + pos += sz; + sz >>= 1; + } while (sz >= 32); + _windowcount++; + } + + unsafe int encode_frame(ref int size) + { + fixed (int* s = samplesBuffer, r = residualBuffer) + fixed (double* window = windowBuffer) + { + frame.InitSize(size); + + if (frame.blocksize != _windowsize && frame.blocksize > 4) + { + _windowsize = frame.blocksize; + _windowcount = 0; + calculate_window(window, window_welch, WindowFunction.Welch); + calculate_window(window, window_tukey, WindowFunction.Tukey); + calculate_window(window, window_hann, WindowFunction.Hann); + calculate_window(window, window_flattop, WindowFunction.Flattop); + if (_windowcount == 0) + throw new Exception("invalid windowfunction"); + } + frame.window_buffer = window; + + int bps = (int)bits_per_sample + channels - 1; + if (channels != 2 || frame.blocksize <= 32 || eparams.stereo_method == StereoMethod.Independent) + { + frame.current.residual = r + channels * Alac.MAX_BLOCKSIZE; + + for (int ch = 0; ch < channels; ch++) + frame.subframes[ch].Init(s + ch * Alac.MAX_BLOCKSIZE, r + ch * Alac.MAX_BLOCKSIZE); + + for (int ch = 0; ch < channels; ch++) + encode_residual_onepass(frame, ch); + } + else if (eparams.stereo_method == StereoMethod.Estimate || eparams.stereo_method == StereoMethod.Estimate2) + { + int* sl = s; + int* sr = s + Alac.MAX_BLOCKSIZE; + int n = frame.blocksize; + ulong lsum = 0, rsum = 0, dsum = 0, s31 = 0, s1 = 0, s2 = 0, s3 = 0; + if (eparams.stereo_method == StereoMethod.Estimate) + for (int i = 2; i < n; i++) + { + int lt = sl[i] - 2 * sl[i - 1] + sl[i - 2]; + int rt = sr[i] - 2 * sr[i - 1] + sr[i - 2]; + int df = lt - rt; + lsum += (ulong)Math.Abs(lt); + rsum += (ulong)Math.Abs(rt); + dsum += (ulong)Math.Abs(df); + s1 += (ulong)Math.Abs(rt + (df >> 1)); + s31 += (ulong)Math.Abs(rt + (df >> 31)); + } + else + for (int i = 2; i < n; i++) + { + int lt = sl[i] - 2 * sl[i - 1] + sl[i - 2]; + int rt = sr[i] - 2 * sr[i - 1] + sr[i - 2]; + int df = lt - rt; + lsum += (ulong)Math.Abs(lt); + rsum += (ulong)Math.Abs(rt); + dsum += (ulong)Math.Abs(df); + s1 += (ulong)Math.Abs(rt + (df >> 1)); + s2 += (ulong)Math.Abs(rt + (df >> 2)); + s3 += (ulong)Math.Abs(rt + (df * 3 >> 2)); + s31 += (ulong)Math.Abs(rt + (df >> 31)); + } + frame.interlacing_leftweight = 0; + frame.interlacing_shift = 0; + ulong score = lsum + rsum; + if (lsum + dsum < score) //leftside + { + frame.interlacing_leftweight = 1; + frame.interlacing_shift = 0; + score = lsum + dsum; + } + if (s1 + dsum < score) // midside + { + frame.interlacing_leftweight = 1; + frame.interlacing_shift = 1; + score = s1 + dsum; + } + if (s31 + dsum < score) // rightside + { + frame.interlacing_leftweight = 1; + frame.interlacing_shift = 31; + score = s31 + dsum; + } + if (eparams.stereo_method == StereoMethod.Estimate2) + { + if (s2 + dsum < score) // close to rightside + { + frame.interlacing_leftweight = 1; + frame.interlacing_shift = 2; + score = s2 + dsum; + } + if (s3 + dsum < score) // close to leftside + { + frame.interlacing_leftweight = 3; + frame.interlacing_shift = 2; + score = s3 + dsum; + } + } + if (frame.interlacing_leftweight == 0) + { + frame.current.residual = r + channels * Alac.MAX_BLOCKSIZE; + for (int ch = 0; ch < channels; ch++) + frame.subframes[ch].Init(s + ch * Alac.MAX_BLOCKSIZE, r + ch * Alac.MAX_BLOCKSIZE); + } + else + { + frame.current.residual = r + 2 * channels * Alac.MAX_BLOCKSIZE; + channel_decorrelation(s, s + Alac.MAX_BLOCKSIZE, s + 2 * Alac.MAX_BLOCKSIZE, s + 3 * Alac.MAX_BLOCKSIZE, frame.blocksize, + frame.interlacing_leftweight, frame.interlacing_shift); + for (int ch = 0; ch < channels; ch++) + frame.subframes[ch].Init(s + (channels + ch) * Alac.MAX_BLOCKSIZE, r + (channels + ch) * Alac.MAX_BLOCKSIZE); + } + + for (int ch = 0; ch < channels; ch++) + encode_residual_onepass(frame, ch); + } + else + { + channel_decorrelation(s, s + Alac.MAX_BLOCKSIZE, s + 2 * Alac.MAX_BLOCKSIZE, s + 3 * Alac.MAX_BLOCKSIZE, frame.blocksize, 1, 1); + channel_decorrelation(s, s + Alac.MAX_BLOCKSIZE, s + 4 * Alac.MAX_BLOCKSIZE, s + 3 * Alac.MAX_BLOCKSIZE, frame.blocksize, 1, 31); + frame.current.residual = r + 5 * Alac.MAX_BLOCKSIZE; + for (int ch = 0; ch < 5; ch++) + frame.subframes[ch].Init(s + ch * Alac.MAX_BLOCKSIZE, r + ch * Alac.MAX_BLOCKSIZE); + estimate_frame(frame, true); + measure_frame_size(frame, true); + frame.ChooseSubframes(); + encode_estimated_frame(frame); + } + uint fs = measure_frame_size(frame, false); + frame.type = ((int)fs > frame.blocksize * channels * bps) ? FrameType.Verbatim : FrameType.Compressed; + BitWriter bitwriter = new BitWriter(frame_buffer, 0, max_frame_size); + output_frame_header(frame, bitwriter); + if (frame.type == FrameType.Verbatim) + { + for (int i = 0; i < frame.blocksize; i++) + for (int ch = 0; ch < channels; ch++) + bitwriter.writebits_signed((int)bits_per_sample, frame.subframes[ch].samples[i]); + } + else if (frame.type == FrameType.Compressed) + { + for (int ch = 0; ch < channels; ch++) + alac_entropy_coder(bitwriter, frame.subframes[ch].best.residual, frame.blocksize, + bps, frame.subframes[ch].best.ricemodifier); + } + output_frame_footer(bitwriter); + + _sample_byte_size[frame_count++] = (uint)bitwriter.Length; + + size = frame.blocksize; + return bitwriter.Length; + } + } + + unsafe int output_frame(int blocksize) + { + if (verify != null) + { + fixed (int* s = verifyBuffer, r = samplesBuffer) + for (int ch = 0; ch < channels; ch++) + AudioSamples.MemCpy(s + ch * Alac.MAX_BLOCKSIZE, r + ch * Alac.MAX_BLOCKSIZE, eparams.block_size); + } + + //if (0 != eparams.variable_block_size && 0 == (eparams.block_size & 7) && eparams.block_size >= 128) + // fs = encode_frame_vbs(); + //else + int bs = blocksize; + int fs = encode_frame(ref bs); + + _position += bs; + _IO.Write(frame_buffer, 0, fs); + _totalSize += fs; + + if (verify != null) + { + int decoded = verify.DecodeFrame(frame_buffer, 0, fs); + if (decoded != fs || verify.Remaining != (ulong)bs) + throw new Exception("validation failed!"); + int [,] deinterlaced = new int[bs,channels]; + verify.deinterlace(deinterlaced, 0, (uint)bs); + fixed (int* s = verifyBuffer, r = deinterlaced) + { + for (int i = 0; i < bs; i++) + for (int ch = 0; ch < channels; ch++) + if (r[i * channels + ch] != s[ch * Alac.MAX_BLOCKSIZE + i]) + throw new Exception("validation failed!"); + } + } + + if (bs < blocksize) + { + fixed (int* s = samplesBuffer) + for (int ch = 0; ch < channels; ch++) + AudioSamples.MemCpy(s + ch * Alac.MAX_BLOCKSIZE, s + bs + ch * Alac.MAX_BLOCKSIZE, eparams.block_size - bs); + } + + samplesInBuffer -= bs; + + return bs; + } + + public void Write(int[,] buff, int pos, int sampleCount) + { + if (!inited) + { + if (_IO == null) + _IO = new FileStream(_path, FileMode.Create, FileAccess.Write, FileShare.Read); + int header_size = encode_init(); + _IO.Write(header, 0, header_size); + if (_IO.CanSeek) + first_frame_offset = _IO.Position; + inited = true; + } + + int len = sampleCount; + while (len > 0) + { + int block = Math.Min(len, eparams.block_size - samplesInBuffer); + + copy_samples(buff, pos, block); + + len -= block; + pos += block; + + while (samplesInBuffer >= eparams.block_size) + output_frame(eparams.block_size); + } + } + + public string Path { get { return _path; } } + + string vendor_string = "CUETools.2.05"; + + int select_blocksize(int samplerate, int time_ms) + { + int target = (samplerate * time_ms) / 1000; + int blocksize = 1024; + while (target >= blocksize) + blocksize <<= 1; + return blocksize >> 1; + } + + void write_chunk_mvhd(BitWriter bitwriter, TimeSpan UnixTime) + { + chunk_start(bitwriter); + { + bitwriter.write('m', 'v', 'h', 'd'); + bitwriter.writebits(32, 0); + bitwriter.writebits(32, (int)UnixTime.TotalSeconds); + bitwriter.writebits(32, (int)UnixTime.TotalSeconds); + bitwriter.writebits(32, 1000); + bitwriter.writebits(32, sample_count); + bitwriter.writebits(32, 0x00010000); // reserved (preferred rate) 1.0 = normal + bitwriter.writebits(16, 0x0100); // reserved (preferred volume) 1.0 = normal + bitwriter.writebytes(10, 0); // reserved + bitwriter.writebits(32, 0x00010000); // reserved (matrix structure) + bitwriter.writebits(32, 0x00000000); // reserved (matrix structure) + bitwriter.writebits(32, 0x00000000); // reserved (matrix structure) + bitwriter.writebits(32, 0x00000000); // reserved (matrix structure) + bitwriter.writebits(32, 0x00010000); // reserved (matrix structure) + bitwriter.writebits(32, 0x00000000); // reserved (matrix structure) + bitwriter.writebits(32, 0x00000000); // reserved (matrix structure) + bitwriter.writebits(32, 0x00000000); // reserved (matrix structure) + bitwriter.writebits(32, 0x40000000); // reserved (matrix structure) + bitwriter.writebits(32, 0); // preview time + bitwriter.writebits(32, 0); // preview duration + bitwriter.writebits(32, 0); // poster time + bitwriter.writebits(32, 0); // selection time + bitwriter.writebits(32, 0); // selection duration + bitwriter.writebits(32, 0); // current time + bitwriter.writebits(32, 2); // next track ID + } + chunk_end(bitwriter); + } + + void write_chunk_minf(BitWriter bitwriter) + { + chunk_start(bitwriter); + { + bitwriter.write('m', 'i', 'n', 'f'); + chunk_start(bitwriter); + { + bitwriter.write('s', 'm', 'h', 'd'); + bitwriter.writebits(32, 0); // version & flags + bitwriter.writebits(16, 0); // reserved (balance) + bitwriter.writebits(16, 0); // reserved + } + chunk_end(bitwriter); + chunk_start(bitwriter); + { + bitwriter.write('d', 'i', 'n', 'f'); + chunk_start(bitwriter); + { + bitwriter.write('d', 'r', 'e', 'f'); + bitwriter.writebits(32, 0); // version & flags + bitwriter.writebits(32, 1); // entry count + chunk_start(bitwriter); + { + bitwriter.write('u', 'r', 'l', ' '); + bitwriter.writebits(32, 1); // version & flags + } + chunk_end(bitwriter); + } + chunk_end(bitwriter); + } + chunk_end(bitwriter); + chunk_start(bitwriter); + { + bitwriter.write('s', 't', 'b', 'l'); + chunk_start(bitwriter); + { + bitwriter.write('s', 't', 's', 'd'); + bitwriter.writebits(32, 0); // version & flags + bitwriter.writebits(32, 1); // entry count + chunk_start(bitwriter); + { + bitwriter.write('a', 'l', 'a', 'c'); + bitwriter.writebits(32, 0); // reserved + bitwriter.writebits(16, 0); // reserved + bitwriter.writebits(16, 1); // data reference index + bitwriter.writebits(16, 0); // version + bitwriter.writebits(16, 0); // revision + bitwriter.writebits(32, 0); // reserved + bitwriter.writebits(16, 2); // reserved channels + bitwriter.writebits(16, 16); // reserved bps + bitwriter.writebits(16, 0); // reserved compression ID + bitwriter.writebits(16, 0); // packet size + bitwriter.writebits(16, sample_rate); // time scale + bitwriter.writebits(16, 0); // reserved + chunk_start(bitwriter); + { + bitwriter.write('a', 'l', 'a', 'c'); + bitwriter.writebits(32, 0); // reserved + bitwriter.writebits(32, eparams.block_size); // max frame size + bitwriter.writebits(8, 0); // reserved + bitwriter.writebits(8, bits_per_sample); + bitwriter.writebits(8, history_mult); + bitwriter.writebits(8, initial_history); + bitwriter.writebits(8, k_modifier); + bitwriter.writebits(8, channels); // channels + bitwriter.writebits(16, 0); // reserved + bitwriter.writebits(32, max_frame_size); + bitwriter.writebits(32, sample_rate * channels * (int)bits_per_sample); // average bitrate + bitwriter.writebits(32, sample_rate); + } + chunk_end(bitwriter); + } + chunk_end(bitwriter); + } + chunk_end(bitwriter); + chunk_start(bitwriter); + { + bitwriter.write('s', 't', 't', 's'); + bitwriter.writebits(32, 0); // version & flags + if (sample_count % eparams.block_size == 0) + { + bitwriter.writebits(32, 1); // entries + bitwriter.writebits(32, sample_count / eparams.block_size); + bitwriter.writebits(32, eparams.block_size); + } + else + { + bitwriter.writebits(32, 2); // entries + bitwriter.writebits(32, sample_count / eparams.block_size); + bitwriter.writebits(32, eparams.block_size); + bitwriter.writebits(32, 1); + bitwriter.writebits(32, sample_count % eparams.block_size); + } + } + chunk_end(bitwriter); + chunk_start(bitwriter); + { + bitwriter.write('s', 't', 's', 'c'); + bitwriter.writebits(32, 0); // version & flags + bitwriter.writebits(32, 1); // entry count + bitwriter.writebits(32, 1); // first chunk + bitwriter.writebits(32, 1); // samples in chunk + bitwriter.writebits(32, 1); // sample description index + } + chunk_end(bitwriter); + chunk_start(bitwriter); + { + bitwriter.write('s', 't', 's', 'z'); + bitwriter.writebits(32, 0); // version & flags + bitwriter.writebits(32, 0); // sample size (0 == variable) + bitwriter.writebits(32, frame_count); // entry count + for (int i = 0; i < frame_count; i++) + bitwriter.writebits(32, _sample_byte_size[i]); + } + chunk_end(bitwriter); + chunk_start(bitwriter); + { + bitwriter.write('s', 't', 'c', 'o'); + bitwriter.writebits(32, 0); // version & flags + bitwriter.writebits(32, frame_count); // entry count + uint pos = (uint)mdat_pos + 8; + for (int i = 0; i < frame_count; i++) + { + bitwriter.writebits(32, pos); + pos += _sample_byte_size[i]; + } + } + chunk_end(bitwriter); + } + chunk_end(bitwriter); + } + chunk_end(bitwriter); + } + + void write_chunk_mdia(BitWriter bitwriter, TimeSpan UnixTime) + { + chunk_start(bitwriter); + { + bitwriter.write('m', 'd', 'i', 'a'); + chunk_start(bitwriter); + { + bitwriter.write('m', 'd', 'h', 'd'); + bitwriter.writebits(32, 0); // version & flags + bitwriter.writebits(32, (int)UnixTime.TotalSeconds); + bitwriter.writebits(32, (int)UnixTime.TotalSeconds); + bitwriter.writebits(32, sample_rate); + bitwriter.writebits(32, sample_count); + bitwriter.writebits(16, 0x55c4); // language + bitwriter.writebits(16, 0); // quality + } + chunk_end(bitwriter); + chunk_start(bitwriter); + { + bitwriter.write('h', 'd', 'l', 'r'); + bitwriter.writebits(32, 0); // version & flags + bitwriter.writebits(32, 0); // hdlr + bitwriter.write('s', 'o', 'u', 'n'); + bitwriter.writebits(32, 0); // reserved + bitwriter.writebits(32, 0); // reserved + bitwriter.writebits(32, 0); // reserved + bitwriter.writebits(8, "SoundHandler".Length); + bitwriter.write("SoundHandler"); + } + chunk_end(bitwriter); + write_chunk_minf(bitwriter); + } + chunk_end(bitwriter); + } + + void write_chunk_trak(BitWriter bitwriter, TimeSpan UnixTime) + { + chunk_start(bitwriter); + { + bitwriter.write('t', 'r', 'a', 'k'); + chunk_start(bitwriter); + { + bitwriter.write('t', 'k', 'h', 'd'); + bitwriter.writebits(32, 15); // version + bitwriter.writebits(32, (int)UnixTime.TotalSeconds); + bitwriter.writebits(32, (int)UnixTime.TotalSeconds); + bitwriter.writebits(32, 1); // track ID + bitwriter.writebits(32, 0); // reserved + bitwriter.writebits(32, sample_count / sample_rate); + bitwriter.writebits(32, 0); // reserved + bitwriter.writebits(32, 0); // reserved + bitwriter.writebits(32, 0); // reserved (layer & alternate group) + bitwriter.writebits(16, 0x0100); // reserved (preferred volume) 1.0 = normal + bitwriter.writebits(16, 0); // reserved + bitwriter.writebits(32, 0x00010000); // reserved (matrix structure) + bitwriter.writebits(32, 0x00000000); // reserved (matrix structure) + bitwriter.writebits(32, 0x00000000); // reserved (matrix structure) + bitwriter.writebits(32, 0x00000000); // reserved (matrix structure) + bitwriter.writebits(32, 0x00010000); // reserved (matrix structure) + bitwriter.writebits(32, 0x00000000); // reserved (matrix structure) + bitwriter.writebits(32, 0x00000000); // reserved (matrix structure) + bitwriter.writebits(32, 0x00000000); // reserved (matrix structure) + bitwriter.writebits(32, 0x40000000); // reserved (matrix structure) + bitwriter.writebits(32, 0); // reserved (width) + bitwriter.writebits(32, 0); // reserved (height) + } + chunk_end(bitwriter); + write_chunk_mdia(bitwriter, UnixTime); + } + chunk_end(bitwriter); + } + + void write_chunk_udta(BitWriter bitwriter) + { + chunk_start(bitwriter); + { + bitwriter.write('u', 'd', 't', 'a'); + chunk_start(bitwriter); + { + bitwriter.write('m', 'e', 't', 'a'); + bitwriter.writebits(32, 0); + chunk_start(bitwriter); + { + bitwriter.write('h', 'd', 'l', 'r'); + bitwriter.writebits(32, 0); + bitwriter.writebits(32, 0); + bitwriter.write('m', 'd', 'i', 'r'); + bitwriter.write('a', 'p', 'p', 'l'); + bitwriter.writebits(32, 0); + bitwriter.writebits(32, 0); + bitwriter.writebits(16, 0); + } + chunk_end(bitwriter); + chunk_start(bitwriter); + { + bitwriter.write('i', 'l', 's', 't'); + chunk_start(bitwriter); + { + bitwriter.write((char)0xA9, 't', 'o', 'o'); + chunk_start(bitwriter); + { + bitwriter.write('d', 'a', 't', 'a'); + bitwriter.writebits(32, 1); + bitwriter.writebits(32, 0); + bitwriter.write(vendor_string); + } + chunk_end(bitwriter); + } + chunk_end(bitwriter); + } + chunk_end(bitwriter); + } + chunk_end(bitwriter); + } + chunk_end(bitwriter); + } + + int write_trailers() + { + TimeSpan UnixTime = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0).ToLocalTime(); + header = new byte[0x1000 + frame_count * 8]; // FIXME!!! Possible buffer overrun + BitWriter bitwriter = new BitWriter(header, 0, header.Length); + chunk_start(bitwriter); + { + bitwriter.write('m', 'o', 'o', 'v'); + write_chunk_mvhd(bitwriter, UnixTime); + write_chunk_trak(bitwriter, UnixTime); + write_chunk_udta(bitwriter); + } + chunk_end(bitwriter); + return bitwriter.Length; + } + + int write_headers() + { + BitWriter bitwriter = new BitWriter(header, 0, header.Length); + + chunk_start(bitwriter); + bitwriter.write('f', 't', 'y', 'p'); + bitwriter.write('M', '4', 'A', ' '); + bitwriter.writebits(32, 0x200); // minor version + bitwriter.write('M', '4', 'A', ' '); + bitwriter.write('m', 'p', '4', '2'); + bitwriter.write('i', 's', 'o', 'm'); + bitwriter.writebits(32, 0); + chunk_end(bitwriter); + + chunk_start(bitwriter); // padding in case we need extended mdat len + bitwriter.write('f', 'r', 'e', 'e'); + chunk_end(bitwriter); + + mdat_pos = bitwriter.Length; + + chunk_start(bitwriter); // mdat len placeholder + bitwriter.write('m', 'd', 'a', 't'); + chunk_end(bitwriter); + + return bitwriter.Length; + } + + int encode_init() + { + int i, header_len; + + //if(flake_validate_params(s) < 0) + + ch_code = channels - 1; + + // FIXME: For now, only 44100 samplerate is supported + if (sample_rate != 44100) + throw new Exception("non-standard samplerate"); + + // FIXME: For now, only 16-bit encoding is supported + if (bits_per_sample != 16) + throw new Exception("non-standard bps"); + + if (_blocksize == 0) + { + if (eparams.block_size == 0) + eparams.block_size = select_blocksize(sample_rate, eparams.block_time_ms); + _blocksize = eparams.block_size; + } + else + eparams.block_size = _blocksize; + + // set maximum encoded frame size (if larger, re-encodes in verbatim mode) + if (channels == 2) + max_frame_size = 16 + ((eparams.block_size * (int)(bits_per_sample + bits_per_sample + 1) + 7) >> 3); + else + max_frame_size = 16 + ((eparams.block_size * channels * (int)bits_per_sample + 7) >> 3); + + //if (_IO.CanSeek && eparams.do_seektable) + //{ + //} + + // output header bytes + header = new byte[eparams.padding_size + 0x1000]; + header_len = write_headers(); + + frame_buffer = new byte[max_frame_size]; + _sample_byte_size = new uint[sample_count / eparams.block_size + 1]; + + initial_history = 10; + history_mult = 40; + k_modifier = 14; + + if (eparams.do_verify) + { + verify = new ALACReader(channels, (int)bits_per_sample, history_mult, initial_history, k_modifier, eparams.block_size); + verifyBuffer = new int[Alac.MAX_BLOCKSIZE * channels]; + } + + return header_len; + } + } + + struct ALACEncodeParams + { + // compression quality + // set by user prior to calling encode_init + // standard values are 0 to 8 + // 0 is lower compression, faster encoding + // 8 is higher compression, slower encoding + // extended values 9 to 12 are slower and/or use + // higher prediction orders + public int compression; + + // prediction order selection method + // set by user prior to calling encode_init + // if set to less than 0, it is chosen based on compression. + // valid values are 0 to 5 + // 0 = use maximum order only + // 1 = use estimation + // 2 = 2-level + // 3 = 4-level + // 4 = 8-level + // 5 = full search + // 6 = log search + public OrderMethod order_method; + + + // stereo decorrelation method + // set by user prior to calling encode_init + // if set to less than 0, it is chosen based on compression. + // valid values are 0 to 2 + // 0 = independent L+R channels + // 1 = mid-side encoding + public StereoMethod stereo_method; + + // block size in samples + // set by the user prior to calling encode_init + // if set to 0, a block size is chosen based on block_time_ms + // can also be changed by user before encoding a frame + public int block_size; + + // block time in milliseconds + // set by the user prior to calling encode_init + // used to calculate block_size based on sample rate + // can also be changed by user before encoding a frame + public int block_time_ms; + + // padding size in bytes + // set by the user prior to calling encode_init + // if set to less than 0, defaults to 4096 + public int padding_size; + + // minimum LPC order + // set by user prior to calling encode_init + // if set to less than 0, it is chosen based on compression. + // valid values are 1 to 32 + public int min_prediction_order; + + // maximum LPC order + // set by user prior to calling encode_init + // if set to less than 0, it is chosen based on compression. + // valid values are 1 to 32 + public int max_prediction_order; + + // Number of LPC orders to try (for estimate mode) + // set by user prior to calling encode_init + // if set to less than 0, it is chosen based on compression. + // valid values are 1 to 32 + public int estimation_depth; + + public int min_modifier, max_modifier; + + public WindowFunction window_function; + + public bool do_verify; + public bool do_seektable; + + public int set_defaults(int lvl) + { + compression = lvl; + + if ((lvl < 0 || lvl > 12) && (lvl != 99)) + { + return -1; + } + + // default to level 5 params + window_function = WindowFunction.Flattop | WindowFunction.Tukey; + order_method = OrderMethod.Estimate; + stereo_method = StereoMethod.Evaluate; + block_size = 0; + block_time_ms = 105; + min_modifier = 4; + max_modifier = 4; + min_prediction_order = 1; + max_prediction_order = 12; + estimation_depth = 1; + do_verify = false; + do_seektable = false; + + // differences from level 6 + switch (lvl) + { + case 0: + block_time_ms = 53; + stereo_method = StereoMethod.Independent; + window_function = WindowFunction.Welch; + max_prediction_order = 6; + break; + case 1: + stereo_method = StereoMethod.Independent; + window_function = WindowFunction.Welch; + max_prediction_order = 8; + break; + case 2: + stereo_method = StereoMethod.Estimate; + window_function = WindowFunction.Welch; + max_prediction_order = 6; + break; + case 3: + stereo_method = StereoMethod.Estimate; + window_function = WindowFunction.Welch; + max_prediction_order = 8; + break; + case 4: + stereo_method = StereoMethod.Estimate2; + window_function = WindowFunction.Welch; + break; + case 5: + stereo_method = StereoMethod.Estimate2; + break; + case 6: + break; + case 7: + estimation_depth = 3; + min_modifier = 3; + break; + case 8: + estimation_depth = 5; + max_prediction_order = 30; + min_modifier = 2; + break; + } + + return 0; + } + } +} diff --git a/CUETools.Codecs.ALAC/CUETools.Codecs.ALAC.csproj b/CUETools.Codecs.ALAC/CUETools.Codecs.ALAC.csproj index 117f506..8c93164 100644 --- a/CUETools.Codecs.ALAC/CUETools.Codecs.ALAC.csproj +++ b/CUETools.Codecs.ALAC/CUETools.Codecs.ALAC.csproj @@ -84,6 +84,8 @@ + + diff --git a/CUETools.Codecs.FLAKE/CUETools.Codecs.FLAKE.csproj b/CUETools.Codecs.FLAKE/CUETools.Codecs.FLAKE.csproj index 50608c2..cdb17e7 100644 --- a/CUETools.Codecs.FLAKE/CUETools.Codecs.FLAKE.csproj +++ b/CUETools.Codecs.FLAKE/CUETools.Codecs.FLAKE.csproj @@ -35,12 +35,9 @@ - - - diff --git a/CUETools.Codecs.FLAKE/Flake.cs b/CUETools.Codecs.FLAKE/Flake.cs index b04a2b9..33c30f6 100644 --- a/CUETools.Codecs.FLAKE/Flake.cs +++ b/CUETools.Codecs.FLAKE/Flake.cs @@ -22,6 +22,7 @@ using System; using System.Collections.Generic; using System.Text; +using CUETools.Codecs; namespace CUETools.Codecs.FLAKE { @@ -32,8 +33,6 @@ namespace CUETools.Codecs.FLAKE public const int MAX_PARTITION_ORDER = 8; public const int MAX_PARTITIONS = 1 << MAX_PARTITION_ORDER; - public const uint UINT32_MAX = 0xffffffff; - public const int FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */ public const int FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = 64; /* bits */ public const int FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */ @@ -46,25 +45,6 @@ namespace CUETools.Codecs.FLAKE public static readonly int[] flac_blocksizes = new int[15] { 0, 192, 576, 1152, 2304, 4608, 0, 0, 256, 512, 1024, 2048, 4096, 8192, 16384 }; public static readonly int[] flac_bitdepths = new int[8] { 0, 8, 12, 0, 16, 20, 24, 0 }; - public static int log2i(int v) - { - return log2i((uint)v); - } - public static int log2i(uint v) - { - //int i; - int n = 0; - if (0 != (v & 0xffff0000)) { v >>= 16; n += 16; } - if (0 != (v & 0xff00)) { v >>= 8; n += 8; } - if (0 != v) return n + 7 - BitReader.byte_to_unary_table[v]; - //for (i = 2; i < 256; i <<= 1) - //{ - // if (v >= i) n++; - // else break; - //} - return n; - } - public static PredictionType LookupPredictionType(string name) { return (PredictionType)(Enum.Parse(typeof(PredictionType), name, true)); @@ -84,45 +64,6 @@ namespace CUETools.Codecs.FLAKE { return (WindowFunction)(Enum.Parse(typeof(WindowFunction), name, true)); } - - unsafe public static bool memcmp(int* res, int* smp, int n) - { - for (int i = n; i > 0; i--) - if (*(res++) != *(smp++)) - return true; - return false; - } - unsafe public static void memcpy(int* res, int* smp, int n) - { - for (int i = n; i > 0; i--) - *(res++) = *(smp++); - } - unsafe public static void memcpy(byte* res, byte* smp, int n) - { - for (int i = n; i > 0; i--) - *(res++) = *(smp++); - } - unsafe public static void memset(int* res, int smp, int n) - { - for (int i = n; i > 0; i--) - *(res++) = smp; - } - unsafe public static void interlace(int* res, int* src1, int* src2, int n) - { - for (int i = n; i > 0; i--) - { - *(res++) = *(src1++); - *(res++) = *(src2++); - } - } - unsafe public static void deinterlace(int* dst1, int* dst2, int* src, int n) - { - for (int i = n; i > 0; i--) - { - *(dst1++) = *(src++); - *(dst2++) = *(src++); - } - } } unsafe class RiceContext @@ -186,7 +127,7 @@ namespace CUETools.Codecs.FLAKE wbits = w; best.residual = r; best.type = SubframeType.Verbatim; - best.size = Flake.UINT32_MAX; + best.size = AudioSamples.UINT32_MAX; for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++) lpc_ctx[iWindow].Reset(); done_fixed = 0; diff --git a/CUETools.Codecs.FLAKE/FlakeReader.cs b/CUETools.Codecs.FLAKE/FlakeReader.cs index 2a21ae0..7d10597 100644 --- a/CUETools.Codecs.FLAKE/FlakeReader.cs +++ b/CUETools.Codecs.FLAKE/FlakeReader.cs @@ -223,7 +223,7 @@ namespace CUETools.Codecs.FLAKE if (channels == 2) { fixed (int* res = &buff[offset, 0], src = &samplesBuffer[_samplesBufferOffset]) - Flake.interlace(res, src, src + Flake.MAX_BLOCKSIZE, count); + AudioSamples.Interlace(res, src, src + Flake.MAX_BLOCKSIZE, count); } else { @@ -280,7 +280,7 @@ namespace CUETools.Codecs.FLAKE else if (_framesBufferLength < _framesBuffer.Length / 2 && _framesBufferOffset >= _framesBuffer.Length / 2) { fixed (byte* buff = _framesBuffer) - Flake.memcpy(buff, buff + _framesBufferOffset, _framesBufferLength); + AudioSamples.MemCpy(buff, buff + _framesBufferOffset, _framesBufferLength); _framesBufferOffset = 0; } while (_framesBufferLength < _framesBuffer.Length / 2) @@ -495,7 +495,7 @@ namespace CUETools.Codecs.FLAKE { FlacSubframeInfo sub = frame.subframes[ch]; - Flake.memcpy(sub.samples, sub.best.residual, sub.best.order); + AudioSamples.MemCpy(sub.samples, sub.best.residual, sub.best.order); int* data = sub.samples + sub.best.order; int* residual = sub.best.residual + sub.best.order; int data_len = frame.blocksize - sub.best.order; @@ -503,7 +503,7 @@ namespace CUETools.Codecs.FLAKE switch (sub.best.order) { case 0: - Flake.memcpy(data, residual, data_len); + AudioSamples.MemCpy(data, residual, data_len); break; case 1: s1 = data[-1]; @@ -559,10 +559,10 @@ namespace CUETools.Codecs.FLAKE switch (frame.subframes[ch].best.type) { case SubframeType.Constant: - Flake.memset(frame.subframes[ch].samples, frame.subframes[ch].best.residual[0], frame.blocksize); + AudioSamples.MemSet(frame.subframes[ch].samples, frame.subframes[ch].best.residual[0], frame.blocksize); break; case SubframeType.Verbatim: - Flake.memcpy(frame.subframes[ch].samples, frame.subframes[ch].best.residual, frame.blocksize); + AudioSamples.MemCpy(frame.subframes[ch].samples, frame.subframes[ch].best.residual, frame.blocksize); break; case SubframeType.Fixed: restore_samples_fixed(frame, ch); diff --git a/CUETools.Codecs.FLAKE/FlakeWriter.cs b/CUETools.Codecs.FLAKE/FlakeWriter.cs index e9f556c..82b1d74 100644 --- a/CUETools.Codecs.FLAKE/FlakeWriter.cs +++ b/CUETools.Codecs.FLAKE/FlakeWriter.cs @@ -25,7 +25,7 @@ using System.IO; using System.Collections.Generic; using System.Collections.Specialized; using System.Security.Cryptography; -using System.Runtime.InteropServices; +//using System.Runtime.InteropServices; using CUETools.Codecs; namespace CUETools.Codecs.FLAKE @@ -161,10 +161,10 @@ namespace CUETools.Codecs.FLAKE } } - [DllImport("kernel32.dll")] - static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime); - [DllImport("kernel32.dll")] - static extern IntPtr GetCurrentThread(); + //[DllImport("kernel32.dll")] + //static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime); + //[DllImport("kernel32.dll")] + //static extern IntPtr GetCurrentThread(); void DoClose() { @@ -196,9 +196,9 @@ namespace CUETools.Codecs.FLAKE inited = false; } - long fake, KernelStart, UserStart; - GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart); - _userProcessorTime = new TimeSpan(UserStart); + //long fake, KernelStart, UserStart; + //GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart); + //_userProcessorTime = new TimeSpan(UserStart); } public void Close() @@ -481,7 +481,7 @@ namespace CUETools.Codecs.FLAKE fixed (int* fsamples = samplesBuffer, src = &samples[pos, 0]) { if (channels == 2) - Flake.deinterlace(fsamples + samplesInBuffer, fsamples + Flake.MAX_BLOCKSIZE + samplesInBuffer, src, block); + AudioSamples.Deinterlace(fsamples + samplesInBuffer, fsamples + Flake.MAX_BLOCKSIZE + samplesInBuffer, src, block); else for (int ch = 0; ch < channels; ch++) { @@ -557,7 +557,7 @@ namespace CUETools.Codecs.FLAKE unsafe void encode_residual_verbatim(int* res, int* smp, uint n) { - Flake.memcpy(res, smp, (int) n); + AudioSamples.MemCpy(res, smp, (int) n); } unsafe void encode_residual_fixed(int* res, int* smp, int n, int order) @@ -567,7 +567,7 @@ namespace CUETools.Codecs.FLAKE switch (order) { case 0: - Flake.memcpy(res, smp, n); + AudioSamples.MemCpy(res, smp, n); return; case 1: *(res++) = s1 = *(smp++); @@ -678,7 +678,7 @@ namespace CUETools.Codecs.FLAKE calc_sums(pmin, pmax, udata, n, pred_order, sums); int opt_porder = pmin; - uint opt_bits = Flake.UINT32_MAX; + uint opt_bits = AudioSamples.UINT32_MAX; for (int i = pmin; i <= pmax; i++) { uint bits = calc_optimal_rice_params(ref tmp_rc, i, sums + i * Flake.MAX_PARTITIONS, n, pred_order); @@ -697,9 +697,9 @@ namespace CUETools.Codecs.FLAKE static int get_max_p_order(int max_porder, int n, int order) { - int porder = Math.Min(max_porder, Flake.log2i(n ^ (n - 1))); + int porder = Math.Min(max_porder, BitReader.log2i(n ^ (n - 1))); if (order > 0) - porder = Math.Min(porder, Flake.log2i(n / order)); + porder = Math.Min(porder, BitReader.log2i(n / order)); return porder; } @@ -752,7 +752,7 @@ namespace CUETools.Codecs.FLAKE fixed (int* coefs = frame.current.coefs) { lpc.quantize_lpc_coefs(lpcs + (frame.current.order - 1) * lpc.MAX_LPC_ORDER, - frame.current.order, cbits, coefs, out frame.current.shift); + frame.current.order, cbits, coefs, out frame.current.shift, 15, 0); if (frame.current.shift < 0 || frame.current.shift > 15) throw new Exception("negative shift"); @@ -1201,11 +1201,11 @@ namespace CUETools.Codecs.FLAKE unsafe uint measure_frame_size(FlacFrame frame, bool do_midside) { // crude estimation of header/footer size - uint total = (uint)(32 + ((Flake.log2i(frame_count) + 4) / 5) * 8 + (eparams.variable_block_size != 0 ? 16 : 0) + 16); + uint total = (uint)(32 + ((BitReader.log2i(frame_count) + 4) / 5) * 8 + (eparams.variable_block_size != 0 ? 16 : 0) + 16); if (do_midside) { - uint bitsBest = Flake.UINT32_MAX; + uint bitsBest = AudioSamples.UINT32_MAX; ChannelMode modeBest = ChannelMode.LeftRight; if (bitsBest > frame.subframes[2].best.size + frame.subframes[3].best.size) @@ -1244,7 +1244,7 @@ namespace CUETools.Codecs.FLAKE case StereoMethod.Estimate: for (int ch = 0; ch < channels; ch++) { - frame.subframes[ch].best.size = Flake.UINT32_MAX; + frame.subframes[ch].best.size = AudioSamples.UINT32_MAX; encode_residual_onepass(frame, ch); } break; @@ -1389,7 +1389,7 @@ namespace CUETools.Codecs.FLAKE { fixed (int* s = verifyBuffer, r = samplesBuffer) for (int ch = 0; ch < channels; ch++) - Flake.memcpy(s + ch * Flake.MAX_BLOCKSIZE, r + ch * Flake.MAX_BLOCKSIZE, eparams.block_size); + AudioSamples.MemCpy(s + ch * Flake.MAX_BLOCKSIZE, r + ch * Flake.MAX_BLOCKSIZE, eparams.block_size); } int fs, bs; @@ -1427,7 +1427,7 @@ namespace CUETools.Codecs.FLAKE fixed (int* s = verifyBuffer, r = verify.Samples) { for (int ch = 0; ch < channels; ch++) - if (Flake.memcmp(s + ch * Flake.MAX_BLOCKSIZE, r + ch * Flake.MAX_BLOCKSIZE, bs)) + if (AudioSamples.MemCmp(s + ch * Flake.MAX_BLOCKSIZE, r + ch * Flake.MAX_BLOCKSIZE, bs)) throw new Exception("validation failed!"); } } @@ -1436,7 +1436,7 @@ namespace CUETools.Codecs.FLAKE { fixed (int* s = samplesBuffer) for (int ch = 0; ch < channels; ch++) - Flake.memcpy(s + ch * Flake.MAX_BLOCKSIZE, s + bs + ch * Flake.MAX_BLOCKSIZE, eparams.block_size - bs); + AudioSamples.MemCpy(s + ch * Flake.MAX_BLOCKSIZE, s + bs + ch * Flake.MAX_BLOCKSIZE, eparams.block_size - bs); } samplesInBuffer -= bs; @@ -1771,11 +1771,6 @@ namespace CUETools.Codecs.FLAKE // if set to less than 0, defaults to 4096 public int padding_size; - // maximum encoded frame size - // this is set by flake_encode_init based on input audio format - // it can be used by the user to allocate an output buffer - public int max_frame_size; - // minimum LPC order // set by user prior to calling flake_encode_init // if set to less than 0, it is chosen based on compression. diff --git a/CUETools.Codecs.FLAKE/BitReader.cs b/CUETools.Codecs/BitReader.cs similarity index 83% rename from CUETools.Codecs.FLAKE/BitReader.cs rename to CUETools.Codecs/BitReader.cs index 0a0afff..0818ad8 100644 --- a/CUETools.Codecs.FLAKE/BitReader.cs +++ b/CUETools.Codecs/BitReader.cs @@ -1,8 +1,6 @@ /** - * CUETools.Flake: pure managed FLAC audio encoder + * CUETools.Codecs: common audio encoder/decoder routines * Copyright (c) 2009 Gregory S. Chudov - * Based on Flake encoder, http://flake-enc.sourceforge.net/ - * Copyright (c) 2006-2009 Justin Ruggles * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,15 +21,28 @@ using System; using System.Collections.Generic; using System.Text; -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs { - unsafe class BitReader + unsafe public class BitReader { byte* buffer; int pos, len; int _bitaccumulator; uint cache; + public static int log2i(int v) + { + return log2i((uint)v); + } + + public static int log2i(uint v) + { + int n = 0; + if (0 != (v & 0xffff0000)) { v >>= 16; n += 16; } + if (0 != (v & 0xff00)) { v >>= 8; n += 8; } + return n + byte_to_log2_table[v]; + } + public static readonly byte[] byte_to_unary_table = new byte[] { 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, @@ -52,6 +63,26 @@ namespace CUETools.Codecs.FLAKE 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + public static readonly byte[] byte_to_log2_table = new byte[] + { + 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 + }; + public int Position { get { return pos; } diff --git a/CUETools.Codecs.FLAKE/BitWriter.cs b/CUETools.Codecs/BitWriter.cs similarity index 87% rename from CUETools.Codecs.FLAKE/BitWriter.cs rename to CUETools.Codecs/BitWriter.cs index 10e9c7e..85870c9 100644 --- a/CUETools.Codecs.FLAKE/BitWriter.cs +++ b/CUETools.Codecs/BitWriter.cs @@ -1,8 +1,6 @@ /** - * CUETools.Flake: pure managed FLAC audio encoder + * CUETools.Codecs: common audio encoder/decoder routines * Copyright (c) 2009 Gregory S. Chudov - * Based on Flake encoder, http://flake-enc.sourceforge.net/ - * Copyright (c) 2006-2009 Justin Ruggles * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,9 +21,9 @@ using System; using System.Collections.Generic; using System.Text; -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs { - class BitWriter + public class BitWriter { uint bit_buf; int bit_left; @@ -44,6 +42,24 @@ namespace CUETools.Codecs.FLAKE eof = false; } + public void writebytes(int bytes, byte c) + { + for (; bytes > 0; bytes--) + writebits(8, c); + } + + public void write(params char [] chars) + { + foreach (char c in chars) + writebits(8, (byte)c); + } + + public void write(string s) + { + for (int i = 0; i < s.Length; i++) + writebits(8, (byte)s[i]); + } + public void writebits_signed(int bits, int val) { writebits(bits, val & ((1 << bits) - 1)); @@ -148,7 +164,7 @@ namespace CUETools.Codecs.FLAKE writebits(8, val); return; } - int bytes = (Flake.log2i(val) + 4) / 5; + int bytes = (BitReader.log2i(val) + 4) / 5; int shift = (bytes - 1) * 6; writebits(8, (256U - (256U >> bytes)) | (val >> shift)); while (shift >= 6) @@ -276,6 +292,11 @@ namespace CUETools.Codecs.FLAKE { return buf_ptr - buf_start; } + set + { + flush(); + buf_ptr = buf_start + value; + } } } } diff --git a/CUETools.Codecs/CUETools.Codecs.csproj b/CUETools.Codecs/CUETools.Codecs.csproj index b521b8e..be64207 100644 --- a/CUETools.Codecs/CUETools.Codecs.csproj +++ b/CUETools.Codecs/CUETools.Codecs.csproj @@ -18,6 +18,7 @@ DEBUG;TRACE prompt 4 + true pdbonly @@ -82,11 +83,14 @@ + + + diff --git a/CUETools.Codecs/Codecs.cs b/CUETools.Codecs/Codecs.cs index b8a0558..6c518c1 100644 --- a/CUETools.Codecs/Codecs.cs +++ b/CUETools.Codecs/Codecs.cs @@ -1,3 +1,22 @@ +/** + * CUETools.Codecs: common audio encoder/decoder routines + * Copyright (c) 2009 Gregory S. Chudov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + using System; using System.IO; using System.Text; @@ -177,6 +196,52 @@ namespace CUETools.Codecs if (samplesRead != toRead) throw new Exception("samples read != requested"); return buff; } + + unsafe public static void Interlace(int* res, int* src1, int* src2, int n) + { + for (int i = n; i > 0; i--) + { + *(res++) = *(src1++); + *(res++) = *(src2++); + } + } + + unsafe public static void Deinterlace(int* dst1, int* dst2, int* src, int n) + { + for (int i = n; i > 0; i--) + { + *(dst1++) = *(src++); + *(dst2++) = *(src++); + } + } + + unsafe public static bool MemCmp(int* res, int* smp, int n) + { + for (int i = n; i > 0; i--) + if (*(res++) != *(smp++)) + return true; + return false; + } + + unsafe public static void MemCpy(int* res, int* smp, int n) + { + for (int i = n; i > 0; i--) + *(res++) = *(smp++); + } + + unsafe public static void MemCpy(byte* res, byte* smp, int n) + { + for (int i = n; i > 0; i--) + *(res++) = *(smp++); + } + + unsafe public static void MemSet(int* res, int smp, int n) + { + for (int i = n; i > 0; i--) + *(res++) = smp; + } + + public const uint UINT32_MAX = 0xffffffff; } public class DummyWriter : IAudioDest diff --git a/CUETools.Codecs.FLAKE/lpc.cs b/CUETools.Codecs/LPC.cs similarity index 92% rename from CUETools.Codecs.FLAKE/lpc.cs rename to CUETools.Codecs/LPC.cs index 6c74dd5..e39419a 100644 --- a/CUETools.Codecs.FLAKE/lpc.cs +++ b/CUETools.Codecs/LPC.cs @@ -1,8 +1,6 @@ /** - * CUETools.Flake: pure managed FLAC audio encoder + * CUETools.Codecs: common audio encoder/decoder routines * Copyright (c) 2009 Gregory S. Chudov - * Based on Flake encoder, http://flake-enc.sourceforge.net/ - * Copyright (c) 2006-2009 Justin Ruggles * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,9 +21,9 @@ using System; using System.Collections.Generic; using System.Text; -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs { - class lpc + public class lpc { public const int MAX_LPC_ORDER = 32; public const int MAX_LPC_WINDOWS = 4; @@ -223,7 +221,7 @@ namespace CUETools.Codecs.FLAKE */ public static unsafe void quantize_lpc_coefs(double* lpc_in, int order, uint precision, int* lpc_out, - out int shift) + out int shift, int max_shift, int zero_shift) { int i; double d, cmax, error; @@ -242,16 +240,16 @@ namespace CUETools.Codecs.FLAKE cmax = d; } // if maximum value quantizes to zero, return all zeros - if (cmax * (1 << 15) < 1.0) + if (cmax * (1 << max_shift) < 1.0) { - shift = 0; + shift = zero_shift; for (i = 0; i < order; i++) lpc_out[i] = 0; return; } // calculate level shift which scales max coeff to available bits - sh = 15; + sh = max_shift; while ((cmax * (1 << sh) > qmax) && (sh > 0)) { sh--; @@ -282,25 +280,6 @@ namespace CUETools.Codecs.FLAKE shift = sh; } - /** - * Calculate LPC coefficients for multiple orders - */ - public static unsafe uint - calc_coefs(/*const*/ int* samples, uint blocksize, uint max_order, OrderMethod omethod, double* lpcs, double* window) - { - double* autoc = stackalloc double[MAX_LPC_ORDER + 1]; - - compute_autocorr(samples, blocksize, 0, max_order, autoc, window); - - uint opt_order = max_order; - if (omethod == OrderMethod.Estimate) - opt_order = compute_lpc_coefs_est(autoc, max_order, lpcs); - else - compute_lpc_coefs(autoc, max_order, null, lpcs); - - return opt_order; - } - public static unsafe void encode_residual(int* res, int* smp, int n, int order, int* coefs, int shift) @@ -808,7 +787,7 @@ namespace CUETools.Codecs.FLAKE /// /// Context for LPC coefficients calculation and order estimation /// - unsafe class LpcContext + unsafe public class LpcContext { public LpcContext() { @@ -875,6 +854,14 @@ namespace CUETools.Codecs.FLAKE double[] reflection_coeffs; int autocorr_order; + public double[] Reflection + { + get + { + return reflection_coeffs; + } + } + public uint[] done_lpcs; } diff --git a/CUETools.Converter/Program.cs b/CUETools.Converter/Program.cs index 2d01a7d..e688de6 100644 --- a/CUETools.Converter/Program.cs +++ b/CUETools.Converter/Program.cs @@ -45,28 +45,7 @@ namespace CUETools.Converter #endif { IAudioSource audioSource = AudioReadWrite.GetAudioSource(sourceFile, null, config); - IAudioDest audioDest; - FlakeWriter flake = null; - if (destFile == "$flaketest$") - { - flake = new FlakeWriter("", audioSource.BitsPerSample, audioSource.ChannelCount, audioSource.SampleRate, new NullStream()); - //((FlakeWriter)audioDest).CompressionLevel = 6; - flake.PredictionType = Flake.LookupPredictionType(args[2]); - flake.StereoMethod = Flake.LookupStereoMethod(args[3]); - flake.OrderMethod = Flake.LookupOrderMethod(args[4]); - flake.WindowFunction = Flake.LookupWindowFunction(args[5]); - flake.MinPartitionOrder = Int32.Parse(args[6]); - flake.MaxPartitionOrder = Int32.Parse(args[7]); - flake.MinLPCOrder = Int32.Parse(args[8]); - flake.MaxLPCOrder = Int32.Parse(args[9]); - flake.MinFixedOrder = Int32.Parse(args[10]); - flake.MaxFixedOrder = Int32.Parse(args[11]); - flake.MaxPrecisionSearch = Int32.Parse(args[12]); - flake.BlockSize = Int32.Parse(args[13]); - audioDest = new BufferedWriter(flake, 512 * 1024); - } - else - audioDest = AudioReadWrite.GetAudioDest(AudioEncoderType.Lossless, destFile, (long)audioSource.Length, audioSource.BitsPerSample, audioSource.SampleRate, 8192, config); + IAudioDest audioDest = AudioReadWrite.GetAudioDest(AudioEncoderType.Lossless, destFile, (long)audioSource.Length, audioSource.BitsPerSample, audioSource.SampleRate, 8192, config); int[,] buff = new int[0x4000, audioSource.ChannelCount]; Console.WriteLine("Filename : {0}", sourceFile); @@ -99,39 +78,14 @@ namespace CUETools.Converter audioSource.Close(); audioDest.Close(); - if (destFile != "$flaketest$") + TagLib.UserDefined.AdditionalFileTypes.Config = config; + TagLib.File sourceInfo = TagLib.File.Create(new TagLib.File.LocalFileAbstraction(sourceFile)); + TagLib.File destInfo = TagLib.File.Create(new TagLib.File.LocalFileAbstraction(destFile)); + if (Tagging.UpdateTags(destInfo, Tagging.Analyze(sourceInfo), config)) { - TagLib.UserDefined.AdditionalFileTypes.Config = config; - TagLib.File sourceInfo = TagLib.File.Create(new TagLib.File.LocalFileAbstraction(sourceFile)); - TagLib.File destInfo = TagLib.File.Create(new TagLib.File.LocalFileAbstraction(destFile)); - if (Tagging.UpdateTags(destInfo, Tagging.Analyze(sourceInfo), config)) - { - sourceInfo.Tag.CopyTo(destInfo.Tag, true); - destInfo.Tag.Pictures = sourceInfo.Tag.Pictures; - destInfo.Save(); - } - } - else - { - Console.SetOut(stdout); - //Console.Out.WriteLine("{0}\t{6}\t{1}\t{2}\t{3}\t{4}\t{5}", - // "Size ", "MaxPart", "MaxPred", "Pred ", "Stereo", "Order", "Time "); - Console.Out.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}..{7}\t{8}..{9}\t{10}..{11}\t{12}\t{13}", - flake.TotalSize, - flake.UserProcessorTime.TotalSeconds, - flake.PredictionType.ToString().PadRight(15), - flake.StereoMethod.ToString().PadRight(15), - flake.OrderMethod.ToString().PadRight(15), - flake.WindowFunction, - flake.MinPartitionOrder, - flake.MaxPartitionOrder, - flake.MinLPCOrder, - flake.MaxLPCOrder, - flake.MinFixedOrder, - flake.MaxFixedOrder, - flake.MaxPrecisionSearch, - flake.BlockSize - ); + sourceInfo.Tag.CopyTo(destInfo.Tag, true); + destInfo.Tag.Pictures = sourceInfo.Tag.Pictures; + destInfo.Save(); } } #if !DEBUG diff --git a/CUETools.Processor/AudioReadWrite.cs b/CUETools.Processor/AudioReadWrite.cs index 2215991..5a51cef 100644 --- a/CUETools.Processor/AudioReadWrite.cs +++ b/CUETools.Processor/AudioReadWrite.cs @@ -94,6 +94,18 @@ namespace CUETools.Processor case "WAVWriter": dest = new WAVWriter(path, bitsPerSample, channelCount, sampleRate, null); break; + case "FlakeWriter": + dest = new FlakeWriter(path, bitsPerSample, channelCount, sampleRate, null); + ((FlakeWriter)dest).PaddingLength = padding; + ((FlakeWriter)dest).CompressionLevel = encoder.DefaultModeIndex; + dest = new BufferedWriter(dest, 128 * 1024); + break; + case "ALACWriter": + dest = new ALACWriter(path, bitsPerSample, channelCount, sampleRate, null); + ((ALACWriter)dest).PaddingLength = padding; + ((ALACWriter)dest).CompressionLevel = encoder.DefaultModeIndex; + //dest = new BufferedWriter(dest, 128 * 1024); + break; #if !MONO case "FLACWriter": dest = new FLACWriter(path, bitsPerSample, channelCount, sampleRate); @@ -102,12 +114,6 @@ namespace CUETools.Processor ((FLACWriter)dest).Verify = config.flacVerify; ((FLACWriter)dest).DisableAsm = config.disableAsm; break; - case "FlakeWriter": - dest = new FlakeWriter(path, bitsPerSample, channelCount, sampleRate, null); - ((FlakeWriter)dest).PaddingLength = padding; - ((FlakeWriter)dest).CompressionLevel = encoder.DefaultModeIndex; - dest = new BufferedWriter(dest, 128 * 1024); - break; case "WavPackWriter": dest = new WavPackWriter(path, bitsPerSample, channelCount, sampleRate); ((WavPackWriter)dest).CompressionMode = encoder.DefaultModeIndex; diff --git a/CUETools.Processor/Processor.cs b/CUETools.Processor/Processor.cs index 598e1fe..6b91342 100644 --- a/CUETools.Processor/Processor.cs +++ b/CUETools.Processor/Processor.cs @@ -911,11 +911,12 @@ namespace CUETools.Processor encoders = new CUEToolsUDCList(); #if !MONO encoders.Add(new CUEToolsUDC("libFLAC", "flac", true, "0 1 2 3 4 5 6 7 8", "5", "FLACWriter")); - encoders.Add(new CUEToolsUDC("libFlake", "flac", true, "0 1 2 3 4 5 6 7 8 9 10 11", "7", "FlakeWriter")); encoders.Add(new CUEToolsUDC("libwavpack", "wv", true, "fast normal high high+", "normal", "WavPackWriter")); encoders.Add(new CUEToolsUDC("MAC_SDK", "ape", true, "fast normal high extra insane", "high", "APEWriter")); encoders.Add(new CUEToolsUDC("ttalib", "tta", true, "", "", "TTAWriter")); #endif + encoders.Add(new CUEToolsUDC("libFlake", "flac", true, "0 1 2 3 4 5 6 7 8 9 10 11", "7", "FlakeWriter")); + encoders.Add(new CUEToolsUDC("libALAC", "m4a", true, "0 1 2 3 4 5 6 7 8", "3", "ALACWriter")); encoders.Add(new CUEToolsUDC("builtin wav", "wav", true, "", "", "WAVWriter")); encoders.Add(new CUEToolsUDC("flake", "flac", true, "0 1 2 3 4 5 6 7 8 9 10 11", "10", "flake.exe", "-%M - -o %O -p %P")); encoders.Add(new CUEToolsUDC("takc", "tak", true, "0 1 2 2e 2m 3 3e 3m 4 4e 4m", "2", "takc.exe", "-e -p%M -overwrite - %O")); diff --git a/CUETools/CUETools.csproj b/CUETools/CUETools.csproj index 64f6ffe..46ea11a 100644 --- a/CUETools/CUETools.csproj +++ b/CUETools/CUETools.csproj @@ -12,7 +12,7 @@ cue2.ico 611F49E043614FEDCFF1B128868CC8DC0B6656C5 CUETools_TemporaryKey.pfx - true + false true true ftp://cuetools.net/cuetools.net/install/cuetools/ @@ -33,6 +33,7 @@ publish.htm 2.0.3.%2a true + LocalIntranet true @@ -279,6 +280,7 @@ + diff --git a/CUETools/CUETools.sln b/CUETools/CUETools.sln index 508ec66..09e6817 100644 --- a/CUETools/CUETools.sln +++ b/CUETools/CUETools.sln @@ -93,6 +93,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "flake", "..\flake\flake.vcp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CUETools.Flake", "..\CUETools.Flake\CUETools.Flake.csproj", "{2379BAAF-A406-4477-BF53-2D6A326C24C8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CUETools.ALACEnc", "..\CUETools.ALACEnc\CUETools.ALACEnc.csproj", "{8E6E1763-39AE-491D-A10F-44C8844ABA5B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -291,8 +293,8 @@ Global {6458A13A-30EF-45A9-9D58-E5031B17BEE2}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {6458A13A-30EF-45A9-9D58-E5031B17BEE2}.Debug|Mixed Platforms.Build.0 = Debug|x86 {6458A13A-30EF-45A9-9D58-E5031B17BEE2}.Debug|Win32.ActiveCfg = Debug|x86 - {6458A13A-30EF-45A9-9D58-E5031B17BEE2}.Debug|x64.ActiveCfg = Debug|x64 - {6458A13A-30EF-45A9-9D58-E5031B17BEE2}.Debug|x64.Build.0 = Debug|x64 + {6458A13A-30EF-45A9-9D58-E5031B17BEE2}.Debug|x64.ActiveCfg = Debug|Any CPU + {6458A13A-30EF-45A9-9D58-E5031B17BEE2}.Debug|x64.Build.0 = Debug|Any CPU {6458A13A-30EF-45A9-9D58-E5031B17BEE2}.Debug|x86.ActiveCfg = Debug|x86 {6458A13A-30EF-45A9-9D58-E5031B17BEE2}.Debug|x86.Build.0 = Debug|x86 {6458A13A-30EF-45A9-9D58-E5031B17BEE2}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -309,8 +311,8 @@ Global {F2EC7193-D5E5-4252-9803-5CEB407E910F}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {F2EC7193-D5E5-4252-9803-5CEB407E910F}.Debug|Mixed Platforms.Build.0 = Debug|x86 {F2EC7193-D5E5-4252-9803-5CEB407E910F}.Debug|Win32.ActiveCfg = Debug|x86 - {F2EC7193-D5E5-4252-9803-5CEB407E910F}.Debug|x64.ActiveCfg = Debug|x64 - {F2EC7193-D5E5-4252-9803-5CEB407E910F}.Debug|x64.Build.0 = Debug|x64 + {F2EC7193-D5E5-4252-9803-5CEB407E910F}.Debug|x64.ActiveCfg = Debug|Any CPU + {F2EC7193-D5E5-4252-9803-5CEB407E910F}.Debug|x64.Build.0 = Debug|Any CPU {F2EC7193-D5E5-4252-9803-5CEB407E910F}.Debug|x86.ActiveCfg = Debug|x86 {F2EC7193-D5E5-4252-9803-5CEB407E910F}.Debug|x86.Build.0 = Debug|x86 {F2EC7193-D5E5-4252-9803-5CEB407E910F}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -650,6 +652,7 @@ Global {082D6B9E-326E-4D15-9798-EDAE9EDE70A6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {082D6B9E-326E-4D15-9798-EDAE9EDE70A6}.Debug|Win32.ActiveCfg = Debug|Any CPU {082D6B9E-326E-4D15-9798-EDAE9EDE70A6}.Debug|x64.ActiveCfg = Debug|Any CPU + {082D6B9E-326E-4D15-9798-EDAE9EDE70A6}.Debug|x64.Build.0 = Debug|Any CPU {082D6B9E-326E-4D15-9798-EDAE9EDE70A6}.Debug|x86.ActiveCfg = Debug|Any CPU {082D6B9E-326E-4D15-9798-EDAE9EDE70A6}.Debug|x86.Build.0 = Debug|Any CPU {082D6B9E-326E-4D15-9798-EDAE9EDE70A6}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -666,7 +669,7 @@ Global {F53335A2-C013-4354-98CC-83E612EAEB60}.Debug|Mixed Platforms.Build.0 = Debug|Win32 {F53335A2-C013-4354-98CC-83E612EAEB60}.Debug|Win32.ActiveCfg = Debug|Win32 {F53335A2-C013-4354-98CC-83E612EAEB60}.Debug|Win32.Build.0 = Debug|Win32 - {F53335A2-C013-4354-98CC-83E612EAEB60}.Debug|x64.ActiveCfg = Debug|Win32 + {F53335A2-C013-4354-98CC-83E612EAEB60}.Debug|x64.ActiveCfg = Debug|x64 {F53335A2-C013-4354-98CC-83E612EAEB60}.Debug|x86.ActiveCfg = Debug|x64 {F53335A2-C013-4354-98CC-83E612EAEB60}.Release|Any CPU.ActiveCfg = Release|Win32 {F53335A2-C013-4354-98CC-83E612EAEB60}.Release|Mixed Platforms.ActiveCfg = Release|Win32 @@ -682,6 +685,7 @@ Global {2379BAAF-A406-4477-BF53-2D6A326C24C8}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {2379BAAF-A406-4477-BF53-2D6A326C24C8}.Debug|Win32.ActiveCfg = Debug|Any CPU {2379BAAF-A406-4477-BF53-2D6A326C24C8}.Debug|x64.ActiveCfg = Debug|Any CPU + {2379BAAF-A406-4477-BF53-2D6A326C24C8}.Debug|x64.Build.0 = Debug|Any CPU {2379BAAF-A406-4477-BF53-2D6A326C24C8}.Debug|x86.ActiveCfg = Debug|x86 {2379BAAF-A406-4477-BF53-2D6A326C24C8}.Debug|x86.Build.0 = Debug|x86 {2379BAAF-A406-4477-BF53-2D6A326C24C8}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -693,6 +697,22 @@ Global {2379BAAF-A406-4477-BF53-2D6A326C24C8}.Release|x64.Build.0 = Release|Any CPU {2379BAAF-A406-4477-BF53-2D6A326C24C8}.Release|x86.ActiveCfg = Release|x86 {2379BAAF-A406-4477-BF53-2D6A326C24C8}.Release|x86.Build.0 = Release|x86 + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Debug|Win32.ActiveCfg = Debug|Any CPU + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Debug|x64.ActiveCfg = Debug|Any CPU + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Debug|x64.Build.0 = Debug|Any CPU + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Debug|x86.ActiveCfg = Debug|Any CPU + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Release|Any CPU.Build.0 = Release|Any CPU + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Release|Win32.ActiveCfg = Release|Any CPU + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Release|x64.ActiveCfg = Release|Any CPU + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Release|x64.Build.0 = Release|Any CPU + {8E6E1763-39AE-491D-A10F-44C8844ABA5B}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -714,6 +734,7 @@ Global {A5A8D8FA-9E32-4010-8AAF-AE580C5AF728} = {4B59E09C-A51F-4B80-91BE-987904DCEF7D} {115CC5B0-0385-41CD-8A23-6A7EA4C51926} = {4B59E09C-A51F-4B80-91BE-987904DCEF7D} {2379BAAF-A406-4477-BF53-2D6A326C24C8} = {4B59E09C-A51F-4B80-91BE-987904DCEF7D} + {8E6E1763-39AE-491D-A10F-44C8844ABA5B} = {4B59E09C-A51F-4B80-91BE-987904DCEF7D} {B75FA7AD-968E-4990-B342-1B4B17C850DF} = {B36BE134-D85A-437E-AB61-2DA1CCDE06C1} {F2DFEB00-BB35-4665-85EA-CB8C7729A6B7} = {B36BE134-D85A-437E-AB61-2DA1CCDE06C1} {A05B6AA6-0EC3-495D-BCC4-ECE1210071A8} = {B36BE134-D85A-437E-AB61-2DA1CCDE06C1}