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}