diff --git a/Aaru.Compression/Aaru.Compression.csproj b/Aaru.Compression/Aaru.Compression.csproj
index 77d68b8cd..c4efe1dfc 100644
--- a/Aaru.Compression/Aaru.Compression.csproj
+++ b/Aaru.Compression/Aaru.Compression.csproj
@@ -36,8 +36,8 @@
CS1591;CS1574
-
-
+
+
$(Version)+{chash:8}
@@ -50,25 +50,25 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -76,14 +76,14 @@
-
-
-
-
-
-
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/AudioDecoder.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/AudioDecoder.cs
new file mode 100644
index 000000000..44b123071
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/AudioDecoder.cs
@@ -0,0 +1,767 @@
+/**
+ * CUETools.Flake: pure managed FLAC audio encoder
+ * Copyright (c) 2009-2021 Grigory 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
+ * 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;
+using System.IO;
+
+namespace CUETools.Codecs.Flake
+{
+ public class AudioDecoder: IAudioSource
+ {
+ int[] samplesBuffer;
+ int[] residualBuffer;
+
+ byte[] _framesBuffer;
+ int _framesBufferLength = 0, _framesBufferOffset = 0;
+ long first_frame_offset;
+
+ SeekPoint[] seek_table;
+
+ Crc8 crc8;
+ FlacFrame frame;
+ BitReader framereader;
+ AudioPCMConfig pcm;
+
+ uint min_block_size = 0;
+ uint max_block_size = 0;
+ uint min_frame_size = 0;
+ uint max_frame_size = 0;
+
+ int _samplesInBuffer, _samplesBufferOffset;
+ long _sampleCount = 0, _sampleOffset = 0;
+
+ bool do_crc = true;
+
+ string _path;
+ Stream _IO;
+
+ public bool DoCRC
+ {
+ get
+ {
+ return do_crc;
+ }
+ set
+ {
+ do_crc = value;
+ }
+ }
+
+ public int[] Samples
+ {
+ get
+ {
+ return samplesBuffer;
+ }
+ }
+
+ public AudioDecoder(DecoderSettings settings, string path, Stream IO = null)
+ {
+ m_settings = settings;
+
+ _path = path;
+ _IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000);
+
+ crc8 = new Crc8();
+
+ _framesBuffer = new byte[0x20000];
+ decode_metadata();
+
+ frame = new FlacFrame(PCM.ChannelCount);
+ framereader = new BitReader();
+
+ //max_frame_size = 16 + ((Flake.MAX_BLOCKSIZE * PCM.BitsPerSample * PCM.ChannelCount + 1) + 7) >> 3);
+ if (((int)max_frame_size * PCM.BitsPerSample * PCM.ChannelCount * 2 >> 3) > _framesBuffer.Length)
+ {
+ byte[] temp = _framesBuffer;
+ _framesBuffer = new byte[((int)max_frame_size * PCM.BitsPerSample * PCM.ChannelCount * 2 >> 3)];
+ if (_framesBufferLength > 0)
+ Array.Copy(temp, _framesBufferOffset, _framesBuffer, 0, _framesBufferLength);
+ _framesBufferOffset = 0;
+ }
+ _samplesInBuffer = 0;
+
+ if (PCM.BitsPerSample != 16 && PCM.BitsPerSample != 24)
+ throw new Exception("invalid flac file");
+
+ samplesBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * PCM.ChannelCount];
+ residualBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * PCM.ChannelCount];
+ }
+
+ public AudioDecoder(AudioPCMConfig _pcm)
+ {
+ pcm = _pcm;
+ crc8 = new Crc8();
+
+ samplesBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * PCM.ChannelCount];
+ residualBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * PCM.ChannelCount];
+ frame = new FlacFrame(PCM.ChannelCount);
+ framereader = new BitReader();
+ }
+
+ private DecoderSettings m_settings;
+ public IAudioDecoderSettings Settings => m_settings;
+
+ public void Close()
+ {
+ _IO.Close();
+ }
+
+ public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
+
+ public long Length
+ {
+ get
+ {
+ return _sampleCount;
+ }
+ }
+
+ public long Remaining
+ {
+ get
+ {
+ return Length - Position;
+ }
+ }
+
+ public long Position
+ {
+ get
+ {
+ return _sampleOffset - _samplesInBuffer;
+ }
+ set
+ {
+ if (value > _sampleCount)
+ throw new Exception("seeking past end of stream");
+ if (value < Position || value > _sampleOffset)
+ {
+ if (seek_table != null && _IO.CanSeek)
+ {
+ int best_st = -1;
+ for (int st = 0; st < seek_table.Length; st++)
+ {
+ if (seek_table[st].number <= value &&
+ (best_st == -1 || seek_table[st].number > seek_table[best_st].number))
+ best_st = st;
+ }
+ if (best_st != -1)
+ {
+ _framesBufferLength = 0;
+ _samplesInBuffer = 0;
+ _samplesBufferOffset = 0;
+ _IO.Position = (long)seek_table[best_st].offset + first_frame_offset;
+ _sampleOffset = seek_table[best_st].number;
+ }
+ }
+ if (value < Position)
+ throw new Exception("cannot seek backwards without seek table");
+ }
+ while (value > _sampleOffset)
+ {
+ _samplesInBuffer = 0;
+ _samplesBufferOffset = 0;
+
+ fill_frames_buffer();
+ if (_framesBufferLength == 0)
+ throw new Exception("seek failed");
+
+ int bytesDecoded = DecodeFrame(_framesBuffer, _framesBufferOffset, _framesBufferLength);
+ _framesBufferLength -= bytesDecoded;
+ _framesBufferOffset += bytesDecoded;
+
+ _sampleOffset += _samplesInBuffer;
+ };
+ int diff = _samplesInBuffer - (int)(_sampleOffset - value);
+ _samplesInBuffer -= diff;
+ _samplesBufferOffset += diff;
+ }
+ }
+
+ public AudioPCMConfig PCM
+ {
+ get
+ {
+ return pcm;
+ }
+ }
+
+ public string Path
+ {
+ get
+ {
+ return _path;
+ }
+ }
+
+ unsafe void interlace(AudioBuffer buff, int offset, int count)
+ {
+ if (PCM.ChannelCount == 2)
+ {
+ fixed (int* src = &samplesBuffer[_samplesBufferOffset])
+ buff.Interlace(offset, src, src + FlakeConstants.MAX_BLOCKSIZE, count);
+ }
+ else
+ {
+ for (int ch = 0; ch < PCM.ChannelCount; ch++)
+ fixed (int* res = &buff.Samples[offset, ch], src = &samplesBuffer[_samplesBufferOffset + ch * FlakeConstants.MAX_BLOCKSIZE])
+ {
+ int* psrc = src;
+ for (int i = 0; i < count; i++)
+ res[i * PCM.ChannelCount] = *(psrc++);
+ }
+ }
+ }
+
+ public int Read(AudioBuffer buff, int maxLength)
+ {
+ buff.Prepare(this, maxLength);
+
+ int offset = 0;
+ int sampleCount = buff.Length;
+
+ while (_samplesInBuffer < sampleCount)
+ {
+ if (_samplesInBuffer > 0)
+ {
+ interlace(buff, offset, _samplesInBuffer);
+ sampleCount -= _samplesInBuffer;
+ offset += _samplesInBuffer;
+ _samplesInBuffer = 0;
+ _samplesBufferOffset = 0;
+ }
+
+ fill_frames_buffer();
+
+ if (_framesBufferLength == 0)
+ return buff.Length = offset;
+
+ int bytesDecoded = DecodeFrame(_framesBuffer, _framesBufferOffset, _framesBufferLength);
+ _framesBufferLength -= bytesDecoded;
+ _framesBufferOffset += bytesDecoded;
+
+ _samplesInBuffer -= _samplesBufferOffset; // can be set by Seek, otherwise zero
+ _sampleOffset += _samplesInBuffer;
+ }
+
+ interlace(buff, offset, sampleCount);
+ _samplesInBuffer -= sampleCount;
+ _samplesBufferOffset += sampleCount;
+ if (_samplesInBuffer == 0)
+ _samplesBufferOffset = 0;
+ return buff.Length = offset + sampleCount;
+ }
+
+ unsafe void fill_frames_buffer()
+ {
+ if (_framesBufferLength == 0)
+ _framesBufferOffset = 0;
+ else if (_framesBufferLength < _framesBuffer.Length / 2 && _framesBufferOffset >= _framesBuffer.Length / 2)
+ {
+ fixed (byte* buff = _framesBuffer)
+ AudioSamples.MemCpy(buff, buff + _framesBufferOffset, _framesBufferLength);
+ _framesBufferOffset = 0;
+ }
+ while (_framesBufferLength < _framesBuffer.Length / 2)
+ {
+ int read = _IO.Read(_framesBuffer, _framesBufferOffset + _framesBufferLength, _framesBuffer.Length - _framesBufferOffset - _framesBufferLength);
+ _framesBufferLength += read;
+ if (read == 0)
+ break;
+ }
+ }
+
+ unsafe void decode_frame_header(BitReader bitreader, FlacFrame frame)
+ {
+ int header_start = bitreader.Position;
+
+ if (bitreader.readbits(15) != 0x7FFC)
+ throw new Exception("invalid frame");
+ uint vbs = bitreader.readbit();
+ frame.bs_code0 = (int) bitreader.readbits(4);
+ uint sr_code0 = bitreader.readbits(4);
+ frame.ch_mode = (ChannelMode)bitreader.readbits(4);
+ uint bps_code = bitreader.readbits(3);
+ if (FlakeConstants.flac_bitdepths[bps_code] != PCM.BitsPerSample)
+ throw new Exception("unsupported bps coding");
+ uint t1 = bitreader.readbit(); // == 0?????
+ if (t1 != 0)
+ throw new Exception("unsupported frame coding");
+ frame.frame_number = (int)bitreader.read_utf8();
+
+ // custom block size
+ if (frame.bs_code0 == 6)
+ {
+ frame.bs_code1 = (int)bitreader.readbits(8);
+ frame.blocksize = frame.bs_code1 + 1;
+ }
+ else if (frame.bs_code0 == 7)
+ {
+ frame.bs_code1 = (int)bitreader.readbits(16);
+ frame.blocksize = frame.bs_code1 + 1;
+ }
+ else
+ frame.blocksize = FlakeConstants.flac_blocksizes[frame.bs_code0];
+
+ // custom sample rate
+ if (sr_code0 < 1 || sr_code0 > 11)
+ {
+ // sr_code0 == 12 -> sr == bitreader.readbits(8) * 1000;
+ // sr_code0 == 13 -> sr == bitreader.readbits(16);
+ // sr_code0 == 14 -> sr == bitreader.readbits(16) * 10;
+ throw new Exception("invalid sample rate mode");
+ }
+
+ int frame_channels = (int)frame.ch_mode + 1;
+ if (frame_channels > 11)
+ throw new Exception("invalid channel mode");
+ if (frame_channels == 2 || frame_channels > 8) // Mid/Left/Right Side Stereo
+ frame_channels = 2;
+ else
+ frame.ch_mode = ChannelMode.NotStereo;
+ if (frame_channels != PCM.ChannelCount)
+ throw new Exception("invalid channel mode");
+
+ // CRC-8 of frame header
+ byte crc = do_crc ? crc8.ComputeChecksum(bitreader.Buffer, header_start, bitreader.Position - header_start) : (byte)0;
+ frame.crc8 = (byte)bitreader.readbits(8);
+ if (do_crc && frame.crc8 != crc)
+ throw new Exception("header crc mismatch");
+ }
+
+ unsafe void decode_subframe_constant(BitReader bitreader, FlacFrame frame, int ch)
+ {
+ int obits = frame.subframes[ch].obits;
+ frame.subframes[ch].best.residual[0] = bitreader.readbits_signed(obits);
+ }
+
+ unsafe void decode_subframe_verbatim(BitReader bitreader, FlacFrame frame, int ch)
+ {
+ int obits = frame.subframes[ch].obits;
+ for (int i = 0; i < frame.blocksize; i++)
+ frame.subframes[ch].best.residual[i] = bitreader.readbits_signed(obits);
+ }
+
+ unsafe void decode_residual(BitReader bitreader, FlacFrame frame, int ch)
+ {
+ // rice-encoded block
+ // coding method
+ frame.subframes[ch].best.rc.coding_method = (int)bitreader.readbits(2); // ????? == 0
+ if (frame.subframes[ch].best.rc.coding_method != 0 && frame.subframes[ch].best.rc.coding_method != 1)
+ throw new Exception("unsupported residual coding");
+ // partition order
+ frame.subframes[ch].best.rc.porder = (int)bitreader.readbits(4);
+ if (frame.subframes[ch].best.rc.porder > 8)
+ throw new Exception("invalid partition order");
+ int psize = frame.blocksize >> frame.subframes[ch].best.rc.porder;
+ int res_cnt = psize - frame.subframes[ch].best.order;
+
+ int rice_len = 4 + frame.subframes[ch].best.rc.coding_method;
+ // residual
+ int j = frame.subframes[ch].best.order;
+ int* r = frame.subframes[ch].best.residual + j;
+ for (int p = 0; p < (1 << frame.subframes[ch].best.rc.porder); p++)
+ {
+ if (p == 1) res_cnt = psize;
+ int n = Math.Min(res_cnt, frame.blocksize - j);
+
+ int k = frame.subframes[ch].best.rc.rparams[p] = (int)bitreader.readbits(rice_len);
+ if (k == (1 << rice_len) - 1)
+ {
+ k = frame.subframes[ch].best.rc.esc_bps[p] = (int)bitreader.readbits(5);
+ for (int i = n; i > 0; i--)
+ *(r++) = bitreader.readbits_signed((int)k);
+ }
+ else
+ {
+ bitreader.read_rice_block(n, (int)k, r);
+ r += n;
+ }
+ j += n;
+ }
+ }
+
+ unsafe void decode_subframe_fixed(BitReader bitreader, FlacFrame frame, int ch)
+ {
+ // warm-up samples
+ int obits = frame.subframes[ch].obits;
+ for (int i = 0; i < frame.subframes[ch].best.order; i++)
+ frame.subframes[ch].best.residual[i] = bitreader.readbits_signed(obits);
+
+ // residual
+ decode_residual(bitreader, frame, ch);
+ }
+
+ unsafe void decode_subframe_lpc(BitReader bitreader, FlacFrame frame, int ch)
+ {
+ // warm-up samples
+ int obits = frame.subframes[ch].obits;
+ for (int i = 0; i < frame.subframes[ch].best.order; i++)
+ frame.subframes[ch].best.residual[i] = bitreader.readbits_signed(obits);
+
+ // LPC coefficients
+ frame.subframes[ch].best.cbits = (int)bitreader.readbits(4) + 1; // lpc_precision
+ if (frame.subframes[ch].best.cbits >= 16)
+ throw new Exception("cbits >= 16");
+ frame.subframes[ch].best.shift = bitreader.readbits_signed(5);
+ if (frame.subframes[ch].best.shift < 0)
+ throw new Exception("negative shift");
+ for (int i = 0; i < frame.subframes[ch].best.order; i++)
+ frame.subframes[ch].best.coefs[i] = bitreader.readbits_signed(frame.subframes[ch].best.cbits);
+
+ // residual
+ decode_residual(bitreader, frame, ch);
+ }
+
+ unsafe void decode_subframes(BitReader bitreader, FlacFrame frame)
+ {
+ fixed (int *r = residualBuffer, s = samplesBuffer)
+ for (int ch = 0; ch < PCM.ChannelCount; ch++)
+ {
+ // subframe header
+ uint t1 = bitreader.readbit(); // ?????? == 0
+ if (t1 != 0)
+ throw new Exception("unsupported subframe coding (ch == " + ch.ToString() + ")");
+ int type_code = (int)bitreader.readbits(6);
+ frame.subframes[ch].wbits = (int)bitreader.readbit();
+ if (frame.subframes[ch].wbits != 0)
+ frame.subframes[ch].wbits += (int)bitreader.read_unary();
+
+ frame.subframes[ch].obits = PCM.BitsPerSample - frame.subframes[ch].wbits;
+ switch (frame.ch_mode)
+ {
+ case ChannelMode.MidSide: frame.subframes[ch].obits += ch; break;
+ case ChannelMode.LeftSide: frame.subframes[ch].obits += ch; break;
+ case ChannelMode.RightSide: frame.subframes[ch].obits += 1 - ch; break;
+ }
+
+ frame.subframes[ch].best.type = (SubframeType)type_code;
+ frame.subframes[ch].best.order = 0;
+
+ if ((type_code & (uint)SubframeType.LPC) != 0)
+ {
+ frame.subframes[ch].best.order = (type_code - (int)SubframeType.LPC) + 1;
+ frame.subframes[ch].best.type = SubframeType.LPC;
+ }
+ else if ((type_code & (uint)SubframeType.Fixed) != 0)
+ {
+ frame.subframes[ch].best.order = (type_code - (int)SubframeType.Fixed);
+ frame.subframes[ch].best.type = SubframeType.Fixed;
+ }
+
+ frame.subframes[ch].best.residual = r + ch * FlakeConstants.MAX_BLOCKSIZE;
+ frame.subframes[ch].samples = s + ch * FlakeConstants.MAX_BLOCKSIZE;
+
+ // subframe
+ switch (frame.subframes[ch].best.type)
+ {
+ case SubframeType.Constant:
+ decode_subframe_constant(bitreader, frame, ch);
+ break;
+ case SubframeType.Verbatim:
+ decode_subframe_verbatim(bitreader, frame, ch);
+ break;
+ case SubframeType.Fixed:
+ decode_subframe_fixed(bitreader, frame, ch);
+ break;
+ case SubframeType.LPC:
+ decode_subframe_lpc(bitreader, frame, ch);
+ break;
+ default:
+ throw new Exception("invalid subframe type");
+ }
+ }
+ }
+
+ unsafe void restore_samples_fixed(FlacFrame frame, int ch)
+ {
+ FlacSubframeInfo sub = frame.subframes[ch];
+
+ 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;
+ int s0, s1, s2;
+ switch (sub.best.order)
+ {
+ case 0:
+ AudioSamples.MemCpy(data, residual, data_len);
+ break;
+ case 1:
+ s1 = data[-1];
+ for (int i = data_len; i > 0; i--)
+ {
+ s1 += *(residual++);
+ *(data++) = s1;
+ }
+ //data[i] = residual[i] + data[i - 1];
+ break;
+ case 2:
+ s2 = data[-2];
+ s1 = data[-1];
+ for (int i = data_len; i > 0; i--)
+ {
+ s0 = *(residual++) + (s1 << 1) - s2;
+ *(data++) = s0;
+ s2 = s1;
+ s1 = s0;
+ }
+ //data[i] = residual[i] + data[i - 1] * 2 - data[i - 2];
+ break;
+ case 3:
+ for (int i = 0; i < data_len; i++)
+ data[i] = residual[i] + (((data[i - 1] - data[i - 2]) << 1) + (data[i - 1] - data[i - 2])) + data[i - 3];
+ break;
+ case 4:
+ for (int i = 0; i < data_len; i++)
+ data[i] = residual[i] + ((data[i - 1] + data[i - 3]) << 2) - ((data[i - 2] << 2) + (data[i - 2] << 1)) - data[i - 4];
+ break;
+ }
+ }
+
+ unsafe void restore_samples_lpc(FlacFrame frame, int ch)
+ {
+ FlacSubframeInfo sub = frame.subframes[ch];
+ ulong csum = 0;
+ fixed (int* coefs = sub.best.coefs)
+ {
+ for (int i = sub.best.order; i > 0; i--)
+ csum += (ulong)Math.Abs(coefs[i - 1]);
+ if ((csum << sub.obits) >= 1UL << 32)
+ lpc.decode_residual_long(sub.best.residual, sub.samples, frame.blocksize, sub.best.order, coefs, sub.best.shift);
+ else
+ lpc.decode_residual(sub.best.residual, sub.samples, frame.blocksize, sub.best.order, coefs, sub.best.shift);
+ }
+ }
+
+ unsafe void restore_samples(FlacFrame frame)
+ {
+ for (int ch = 0; ch < PCM.ChannelCount; ch++)
+ {
+ switch (frame.subframes[ch].best.type)
+ {
+ case SubframeType.Constant:
+ AudioSamples.MemSet(frame.subframes[ch].samples, frame.subframes[ch].best.residual[0], frame.blocksize);
+ break;
+ case SubframeType.Verbatim:
+ AudioSamples.MemCpy(frame.subframes[ch].samples, frame.subframes[ch].best.residual, frame.blocksize);
+ break;
+ case SubframeType.Fixed:
+ restore_samples_fixed(frame, ch);
+ break;
+ case SubframeType.LPC:
+ restore_samples_lpc(frame, ch);
+ break;
+ }
+ if (frame.subframes[ch].wbits != 0)
+ {
+ int* s = frame.subframes[ch].samples;
+ int x = (int) frame.subframes[ch].wbits;
+ for (int i = frame.blocksize; i > 0; i--)
+ *(s++) <<= x;
+ }
+ }
+ if (frame.ch_mode != ChannelMode.NotStereo)
+ {
+ int* l = frame.subframes[0].samples;
+ int* r = frame.subframes[1].samples;
+ switch (frame.ch_mode)
+ {
+ case ChannelMode.LeftRight:
+ break;
+ case ChannelMode.MidSide:
+ for (int i = frame.blocksize; i > 0; i--)
+ {
+ int mid = *l;
+ int side = *r;
+ mid <<= 1;
+ mid |= (side & 1); /* i.e. if 'side' is odd... */
+ *(l++) = (mid + side) >> 1;
+ *(r++) = (mid - side) >> 1;
+ }
+ break;
+ case ChannelMode.LeftSide:
+ for (int i = frame.blocksize; i > 0; i--)
+ {
+ int _l = *(l++), _r = *r;
+ *(r++) = _l - _r;
+ }
+ break;
+ case ChannelMode.RightSide:
+ for (int i = frame.blocksize; i > 0; i--)
+ *(l++) += *(r++);
+ break;
+ }
+ }
+ }
+
+ public unsafe int DecodeFrame(byte[] buffer, int pos, int len)
+ {
+ fixed (byte* buf = buffer)
+ {
+ framereader.Reset(buf, pos, len);
+ decode_frame_header(framereader, frame);
+ decode_subframes(framereader, frame);
+ framereader.flush();
+ ushort crc_1 = framereader.get_crc16();
+ ushort crc_2 = framereader.read_ushort();
+ if (do_crc && crc_1 != crc_2)
+ throw new Exception("frame crc mismatch");
+ restore_samples(frame);
+ _samplesInBuffer = frame.blocksize;
+ return framereader.Position - pos;
+ }
+ }
+
+
+ bool skip_bytes(int bytes)
+ {
+ for (int j = 0; j < bytes; j++)
+ if (0 == _IO.Read(_framesBuffer, 0, 1))
+ return false;
+ return true;
+ }
+
+ unsafe void decode_metadata()
+ {
+ byte x;
+ int i, id;
+ //bool first = true;
+ byte[] FLAC__STREAM_SYNC_STRING = new byte[] { (byte)'f', (byte)'L', (byte)'a', (byte)'C' };
+ byte[] ID3V2_TAG_ = new byte[] { (byte)'I', (byte)'D', (byte)'3' };
+
+ for (i = id = 0; i < 4; )
+ {
+ if (_IO.Read(_framesBuffer, 0, 1) == 0)
+ throw new Exception("FLAC stream not found");
+ x = _framesBuffer[0];
+ if (x == FLAC__STREAM_SYNC_STRING[i])
+ {
+ //first = true;
+ i++;
+ id = 0;
+ continue;
+ }
+ if (id < 3 && x == ID3V2_TAG_[id])
+ {
+ id++;
+ i = 0;
+ if (id == 3)
+ {
+ if (!skip_bytes(3))
+ throw new Exception("FLAC stream not found");
+ int skip = 0;
+ for (int j = 0; j < 4; j++)
+ {
+ if (0 == _IO.Read(_framesBuffer, 0, 1))
+ throw new Exception("FLAC stream not found");
+ skip <<= 7;
+ skip |= ((int)_framesBuffer[0] & 0x7f);
+ }
+ if (!skip_bytes(skip))
+ throw new Exception("FLAC stream not found");
+ }
+ continue;
+ }
+ id = 0;
+ if (x == 0xff) /* MAGIC NUMBER for the first 8 frame sync bits */
+ {
+ do
+ {
+ if (_IO.Read(_framesBuffer, 0, 1) == 0)
+ throw new Exception("FLAC stream not found");
+ x = _framesBuffer[0];
+ } while (x == 0xff);
+ if (x >> 2 == 0x3e) /* MAGIC NUMBER for the last 6 sync bits */
+ {
+ //_IO.Position -= 2;
+ // state = frame
+ throw new Exception("headerless file unsupported");
+ }
+ }
+ throw new Exception("FLAC stream not found");
+ }
+
+ do
+ {
+ fill_frames_buffer();
+ fixed (byte* buf = _framesBuffer)
+ {
+ BitReader bitreader = new BitReader(buf, _framesBufferOffset, _framesBufferLength - _framesBufferOffset);
+ bool is_last = bitreader.readbit() != 0;
+ MetadataType type = (MetadataType)bitreader.readbits(7);
+ int len = (int)bitreader.readbits(24);
+
+ if (type == MetadataType.StreamInfo)
+ {
+ const int FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = 24; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = 24; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = 20; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = 3; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = 5; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = 36; /* bits */
+ const int FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = 128; /* bits */
+
+ min_block_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN);
+ max_block_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN);
+ min_frame_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN);
+ max_frame_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN);
+ int sample_rate = (int)bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN);
+ int channels = 1 + (int)bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN);
+ int bits_per_sample = 1 + (int)bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN);
+ pcm = new AudioPCMConfig(bits_per_sample, channels, sample_rate);
+ _sampleCount = (long)bitreader.readbits64(FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN);
+ bitreader.skipbits(FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN);
+ }
+ else if (type == MetadataType.Seektable)
+ {
+ int num_entries = len / 18;
+ seek_table = new SeekPoint[num_entries];
+ for (int e = 0; e < num_entries; e++)
+ {
+ seek_table[e].number = bitreader.read_long();
+ seek_table[e].offset = bitreader.read_long();
+ seek_table[e].framesize = (int)bitreader.read_ushort();
+ }
+ }
+ if (_framesBufferLength < 4 + len)
+ {
+ _IO.Position += 4 + len - _framesBufferLength;
+ _framesBufferLength = 0;
+ }
+ else
+ {
+ _framesBufferLength -= 4 + len;
+ _framesBufferOffset += 4 + len;
+ }
+ if (is_last)
+ break;
+ }
+ } while (true);
+ first_frame_offset = _IO.Position - _framesBufferLength;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/AudioEncoder.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/AudioEncoder.cs
new file mode 100644
index 000000000..54f42e062
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/AudioEncoder.cs
@@ -0,0 +1,2402 @@
+/**
+ * CUETools.Flake: pure managed FLAC audio encoder
+ * Copyright (c) 2009-2021 Grigory 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
+ * 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
+ */
+
+#define NOINTEROP
+#define VARIANT1
+
+using System;
+using System.ComponentModel;
+using System.Text;
+using System.IO;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+#if INTEROP
+using System.Runtime.InteropServices;
+#endif
+using CUETools.Codecs;
+using Newtonsoft.Json;
+
+namespace CUETools.Codecs.Flake
+{
+ public class AudioEncoder : IAudioDest
+ {
+ Stream _IO = null;
+ string _path;
+ long _position;
+
+ // number of audio channels
+ // set by user prior to calling flake_encode_init
+ // valid values are 1 to 8
+ int channels, ch_code;
+
+ // audio sample rate in Hz
+ // set by user prior to calling flake_encode_init
+ int sr_code0, sr_code1;
+
+ // sample size in bits
+ // set by user prior to calling flake_encode_init
+ // only 16-bit is currently supported
+ int bps_code;
+
+ // total stream samples
+ // set by user prior to calling flake_encode_init
+ // if 0, stream length is unknown
+ int sample_count = -1;
+
+ FlakeEncodeParams eparams;
+
+ // maximum frame size in bytes
+ // set by flake_encode_init
+ // this can be used to allocate memory for output
+ int max_frame_size;
+
+ byte[] frame_buffer = null;
+
+ int frame_count = 0;
+
+ long first_frame_offset = 0;
+
+#if INTEROP
+ TimeSpan _userProcessorTime;
+#endif
+
+ // header bytes
+ // allocated by flake_encode_init and freed by flake_encode_close
+ byte[] header;
+
+ int[] samplesBuffer;
+ int[] verifyBuffer;
+ int[] residualBuffer;
+ float[] windowBuffer;
+ double[] windowScale;
+ LpcWindowSection[, ,] windowSections;
+
+ WindowFunction[] windowType;
+ int samplesInBuffer = 0;
+
+ int m_blockSize = 0;
+ int _totalSize = 0;
+ int _windowsize = 0, _windowcount = 0;
+
+ Crc8 crc8;
+ MD5 md5;
+
+ FlacFrame frame;
+ AudioDecoder verify;
+
+ SeekPoint[] seek_table;
+ int seek_table_offset = -1;
+
+ bool inited = false;
+
+ public AudioEncoder(EncoderSettings settings, string path, Stream IO = null)
+ {
+ m_settings = settings.Clone() as EncoderSettings;
+ m_settings.Validate();
+
+ //if (Settings.PCM.BitsPerSample != 16)
+ // throw new Exception("Bits per sample must be 16.");
+ //if (Settings.PCM.ChannelCount != 2)
+ // throw new Exception("ChannelCount must be 2.");
+
+ channels = Settings.PCM.ChannelCount;
+
+ // flake_validate_params
+
+ _path = path;
+ _IO = IO;
+
+ samplesBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * (channels == 2 ? 4 : channels)];
+ residualBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * (channels == 2 ? 10 : channels + 1)];
+ windowBuffer = new float[FlakeConstants.MAX_BLOCKSIZE * 2 * lpc.MAX_LPC_WINDOWS];
+ windowScale = new double[lpc.MAX_LPC_WINDOWS];
+ windowType = new WindowFunction[lpc.MAX_LPC_WINDOWS];
+ windowSections = new LpcWindowSection[12, lpc.MAX_LPC_WINDOWS, lpc.MAX_LPC_SECTIONS];
+
+ eparams.flake_set_defaults(m_settings);
+
+ crc8 = new Crc8();
+ frame = new FlacFrame(channels * 2);
+ }
+
+ public int TotalSize
+ {
+ get
+ {
+ return _totalSize;
+ }
+ }
+
+ EncoderSettings m_settings;
+
+ public IAudioEncoderSettings Settings => m_settings;
+
+#if INTEROP
+ [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();
+#endif
+
+ void DoClose()
+ {
+ if (inited)
+ {
+ while (samplesInBuffer > 0)
+ {
+ m_blockSize = samplesInBuffer;
+ output_frame();
+ }
+
+ if (_IO.CanSeek)
+ {
+ if (sample_count <= 0 && _position != 0)
+ {
+ BitWriter bitwriter = new BitWriter(header, 0, 4);
+ bitwriter.writebits(32, (int)_position);
+ bitwriter.flush();
+ _IO.Position = 22;
+ _IO.Write(header, 0, 4);
+ }
+
+ if (md5 != null)
+ {
+ md5.TransformFinalBlock(frame_buffer, 0, 0);
+ _IO.Position = 26;
+ _IO.Write(md5.Hash, 0, md5.Hash.Length);
+ }
+
+ if (seek_table != null)
+ {
+ _IO.Position = seek_table_offset;
+ int len = write_seekpoints(header, 0, 0);
+ _IO.Write(header, 4, len - 4);
+ }
+ }
+ _IO.Close();
+ inited = false;
+ }
+
+#if INTEROP
+ long fake, KernelStart, UserStart;
+ GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart);
+ _userProcessorTime = new TimeSpan(UserStart);
+#endif
+ }
+
+ public void Close()
+ {
+ DoClose();
+ if (sample_count > 0 && _position != sample_count)
+ throw new Exception(Properties.Resources.ExceptionSampleCount);
+ }
+
+ 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 OrderMethod OrderMethod
+ {
+ get { return eparams.order_method; }
+ set { eparams.order_method = value; }
+ }
+
+ public int DevelopmentMode
+ {
+ get { return eparams.development_mode; }
+ set { eparams.development_mode = value; }
+ }
+
+ public bool DoSeekTable
+ {
+ get { return eparams.do_seektable; }
+ set { eparams.do_seektable = value; }
+ }
+
+ public int VBRMode
+ {
+ get { return eparams.variable_block_size; }
+ set { eparams.variable_block_size = value; }
+ }
+
+ public TimeSpan UserProcessorTime
+ {
+ get
+ {
+#if INTEROP
+ return _userProcessorTime;
+#else
+ return new TimeSpan(0);
+#endif
+ }
+ }
+
+ unsafe int get_wasted_bits(int* signal, int samples)
+ {
+ int i, shift;
+ int x = 0;
+
+ for (i = 0; i < samples && 0 == (x & 1); i++)
+ x |= signal[i];
+
+ if (x == 0)
+ {
+ shift = 0;
+ }
+ else
+ {
+ for (shift = 0; 0 == (x & 1); shift++)
+ x >>= 1;
+ }
+
+ if (shift > 0)
+ {
+ for (i = 0; i < samples; i++)
+ signal[i] >>= shift;
+ }
+
+ return shift;
+ }
+
+ ///
+ /// 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)
+ {
+ if (m_settings.StereoMethod == StereoMethod.Independent)
+ AudioSamples.Deinterlace(fsamples + samplesInBuffer, fsamples + FlakeConstants.MAX_BLOCKSIZE + samplesInBuffer, src, block);
+ else
+ {
+ int* left = fsamples + samplesInBuffer;
+ int* right = left + FlakeConstants.MAX_BLOCKSIZE;
+ int* leftM = right + FlakeConstants.MAX_BLOCKSIZE;
+ int* rightM = leftM + FlakeConstants.MAX_BLOCKSIZE;
+ for (int i = 0; i < block; i++)
+ {
+ int l = src[2 * i];
+ int r = src[2 * i + 1];
+ left[i] = l;
+ right[i] = r;
+ leftM[i] = (l + r) >> 1;
+ rightM[i] = l - r;
+ }
+ }
+ }
+ else
+ for (int ch = 0; ch < channels; ch++)
+ {
+ int* psamples = fsamples + ch * FlakeConstants.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 void encode_residual_verbatim(int* res, int* smp, uint n)
+ {
+ AudioSamples.MemCpy(res, smp, (int) n);
+ }
+
+ unsafe static ulong encode_residual_fixed_partition(int* res, int* smp, int* end, int order, int* last_errors)
+ {
+ ulong sum = 0UL;
+ switch (order)
+ {
+ case 0:
+ {
+ while (smp < end)
+ {
+ int error = *(res++) = *(smp++);
+ sum += (uint)((error << 1) ^ (error >> 31));
+ }
+ break;
+ }
+ case 1:
+ {
+ int last_error_0 = last_errors[0];
+ while (smp < end)
+ {
+ int error, save;
+ error = *(smp++); save = error;
+ error -= last_error_0; *(res++) = error; last_error_0 = save;
+ sum += (uint)((error << 1) ^ (error >> 31));
+ }
+ last_errors[0] = last_error_0;
+ break;
+ }
+ case 2:
+ {
+ int last_error_0 = last_errors[0], last_error_1 = last_errors[1];
+ while (smp < end)
+ {
+ int error, save;
+ error = *(smp++); save = error;
+ error -= last_error_0; last_error_0 = save; save = error;
+ error -= last_error_1; *(res++) = error; last_error_1 = save;
+ sum += (uint)((error << 1) ^ (error >> 31));
+ }
+ last_errors[0] = last_error_0; last_errors[1] = last_error_1; ;
+ break;
+ }
+ case 3:
+ {
+ int last_error_0 = last_errors[0], last_error_1 = last_errors[1], last_error_2 = last_errors[2];
+ while (smp < end)
+ {
+ int error, save;
+ error = *(smp++); save = error;
+ error -= last_error_0; last_error_0 = save; save = error;
+ error -= last_error_1; last_error_1 = save; save = error;
+ error -= last_error_2; *(res++) = error; last_error_2 = save;
+ sum += (uint)((error << 1) ^ (error >> 31));
+ }
+ last_errors[0] = last_error_0; last_errors[1] = last_error_1; last_errors[2] = last_error_2;
+ break;
+ }
+ case 4:
+ {
+ int last_error_0 = last_errors[0], last_error_1 = last_errors[1], last_error_2 = last_errors[2], last_error_3 = last_errors[3];
+ while (smp < end)
+ {
+ int error, save;
+ error = *(smp++); save = error;
+ error -= last_error_0; last_error_0 = save; save = error;
+ error -= last_error_1; last_error_1 = save; save = error;
+ error -= last_error_2; last_error_2 = save; save = error;
+ error -= last_error_3; *(res++) = error; last_error_3 = save;
+ sum += (uint)((error << 1) ^ (error >> 31));
+ }
+ last_errors[0] = last_error_0; last_errors[1] = last_error_1; last_errors[2] = last_error_2; last_errors[3] = last_error_3;
+ break;
+ }
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ return sum;
+ }
+
+ unsafe static void encode_residual_fixed(int* res, int* smp, int n, int order, ulong* sums, int pmax)
+ {
+ int* last_errors = stackalloc int[4];
+ int* end = smp + n;
+ int* seg_end = smp + (n >> pmax);
+
+ if (order > 4)
+ throw new ArgumentOutOfRangeException();
+
+ for (int i = 0; i < order; i++)
+ {
+ int* next_errors = stackalloc int[4];
+ next_errors[0] = *(res++) = *(smp++);
+ for (int j = 0; j < i; j++)
+ next_errors[j + 1] = next_errors[j] - last_errors[j];
+ for (int j = 0; j <= i; j++)
+ last_errors[j] = next_errors[j];
+ }
+
+ while (smp < end)
+ {
+ *(sums++) = encode_residual_fixed_partition(res, smp, seg_end, order, last_errors);
+ res += seg_end - smp;
+ smp = seg_end;
+ seg_end += n >> pmax;
+ }
+ }
+
+#if XXX
+ unsafe static int encode_residual_fixed_estimate_best_order(int* res, int* smp, int n, int order)
+ {
+ int next_error_0, next_error_1, next_error_2, next_error_3, next_error_4;
+ int last_error_0, last_error_1, last_error_2, last_error_3;
+ int* end = smp + n;
+ ulong total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0;
+
+ if (order == 0)
+ {
+ AudioSamples.MemCpy(res, smp, n);
+ return 0;
+ }
+
+ next_error_0 = *(res++) = *(smp++);
+ last_error_0 = next_error_0;
+
+ if (order == 1)
+ {
+ while (smp < end)
+ {
+ next_error_0 = *(smp++);
+ next_error_1 = next_error_0 - last_error_0;
+
+ last_error_0 = next_error_0;
+
+ total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31));
+ total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31));
+
+ *(res++) = (int)next_error_1;
+ }
+
+ if ((total_error_0 < total_error_1))
+ return 0;
+ return 1;
+ }
+
+ next_error_0 = *(res++) = *(smp++);
+ next_error_1 = next_error_0 - last_error_0;
+ last_error_0 = next_error_0;
+ last_error_1 = next_error_1;
+
+ if (order == 2)
+ {
+ while (smp < end)
+ {
+ next_error_0 = *(smp++);
+ next_error_1 = next_error_0 - last_error_0;
+ next_error_2 = next_error_1 - last_error_1;
+
+ last_error_0 = next_error_0;
+ last_error_1 = next_error_1;
+
+ total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31));
+ total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31));
+ total_error_2 += (ulong)((next_error_2 << 1) ^ (next_error_2 >> 31));
+
+ *(res++) = (int)next_error_2;
+ }
+
+ if ((total_error_0 < total_error_1) & (total_error_0 < total_error_2))
+ return 0;
+ else if ((total_error_1 < total_error_2))
+ return 1;
+ return 2;
+ }
+
+ next_error_0 = *(res++) = *(smp++);
+ next_error_1 = next_error_0 - last_error_0;
+ next_error_2 = next_error_1 - last_error_1;
+ last_error_0 = next_error_0;
+ last_error_1 = next_error_1;
+ last_error_2 = next_error_2;
+
+ if (order == 3)
+ {
+ while (smp < end)
+ {
+ next_error_0 = *(smp++);
+ next_error_1 = next_error_0 - last_error_0;
+ next_error_2 = next_error_1 - last_error_1;
+ next_error_3 = next_error_2 - last_error_2;
+
+ last_error_0 = next_error_0;
+ last_error_1 = next_error_1;
+ last_error_2 = next_error_2;
+
+ total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31));
+ total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31));
+ total_error_2 += (ulong)((next_error_2 << 1) ^ (next_error_2 >> 31));
+ total_error_3 += (ulong)((next_error_3 << 1) ^ (next_error_3 >> 31));
+
+ *(res++) = (int)next_error_3;
+ }
+
+ if ((total_error_0 < total_error_1) & (total_error_0 < total_error_2) & (total_error_0 < total_error_3))
+ return 0;
+ else if ((total_error_1 < total_error_2) & (total_error_1 < total_error_3))
+ return 1;
+ else if ((total_error_2 < total_error_3))
+ return 2;
+ return 3;
+ }
+
+ next_error_0 = *(res++) = *(smp++);
+ next_error_1 = next_error_0 - last_error_0;
+ next_error_2 = next_error_1 - last_error_1;
+ next_error_3 = next_error_2 - last_error_2;
+ last_error_0 = next_error_0;
+ last_error_1 = next_error_1;
+ last_error_2 = next_error_2;
+ last_error_3 = next_error_3;
+
+ if (order == 4)
+ {
+ while (smp < end)
+ {
+ next_error_0 = *(smp++);
+ next_error_1 = next_error_0 - last_error_0;
+ next_error_2 = next_error_1 - last_error_1;
+ next_error_3 = next_error_2 - last_error_2;
+ next_error_4 = next_error_3 - last_error_3;
+
+ last_error_0 = next_error_0;
+ last_error_1 = next_error_1;
+ last_error_2 = next_error_2;
+ last_error_3 = next_error_3;
+
+ total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31));
+ total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31));
+ total_error_2 += (ulong)((next_error_2 << 1) ^ (next_error_2 >> 31));
+ total_error_3 += (ulong)((next_error_3 << 1) ^ (next_error_3 >> 31));
+ total_error_4 += (ulong)((next_error_4 << 1) ^ (next_error_4 >> 31));
+
+ *(res++) = (int)next_error_4;
+ }
+
+ if ((total_error_0 < total_error_1) & (total_error_0 < total_error_2) & (total_error_0 < total_error_3) & (total_error_0 < total_error_4))
+ return 0;
+ else if ((total_error_1 < total_error_2) & (total_error_1 < total_error_3) & (total_error_1 < total_error_4))
+ return 1;
+ else if ((total_error_2 < total_error_3) & (total_error_2 < total_error_4))
+ return 2;
+ else if (total_error_3 < total_error_4)
+ return 3;
+ return 4;
+ }
+
+ throw new ArgumentOutOfRangeException();
+ }
+#endif
+ static unsafe uint calc_optimal_rice_params(int porder, int* parm, ulong* sums, uint n, uint pred_order, ref int method)
+ {
+ uint part = (1U << porder);
+ uint cnt = (n >> porder) - pred_order;
+ int maxK = method > 0 ? 30 : FlakeConstants.MAX_RICE_PARAM;
+ int k = cnt > 0 ? Math.Min(maxK, BitReader.log2i(sums[0] / cnt)) : 0;
+ int realMaxK0 = k;
+ ulong all_bits = cnt * ((uint)k + 1U) + (sums[0] >> k);
+ parm[0] = k;
+ cnt = (n >> porder);
+ int logcnt = BitReader.log2i(cnt);
+ if (cnt == 1 << logcnt)
+ {
+ for (uint i = 1; i < part; i++)
+ {
+ ulong s = sums[i];
+ ulong u = s >> logcnt;
+ k = u >> maxK != 0 ? maxK : BitReader.log2i((uint)u);
+ realMaxK0 = Math.Max(realMaxK0, k);
+ all_bits += ((uint)k << logcnt) + (s >> k);
+ parm[i] = k;
+ }
+ }
+ else
+ {
+ for (uint i = 1; i < part; i++)
+ {
+ ulong s = sums[i];
+ ulong u = s / cnt;
+ k = u >> maxK != 0 ? maxK : BitReader.log2i((uint)u);
+ realMaxK0 = Math.Max(realMaxK0, k);
+ all_bits += cnt * (uint)k + (s >> k);
+ parm[i] = k;
+ }
+ }
+ all_bits += cnt * (part - 1U);
+ method = realMaxK0 > FlakeConstants.MAX_RICE_PARAM ? 1 : 0;
+ return (uint)all_bits + ((4U + (uint)method) * part);
+ }
+
+ static unsafe void calc_lower_sums(int pmin, int pmax, ulong* sums)
+ {
+ for (int i = pmax - 1; i >= pmin; i--)
+ {
+ for (int j = 0; j < (1 << i); j++)
+ {
+ sums[i * FlakeConstants.MAX_PARTITIONS + j] =
+ sums[(i + 1) * FlakeConstants.MAX_PARTITIONS + 2 * j] +
+ sums[(i + 1) * FlakeConstants.MAX_PARTITIONS + 2 * j + 1];
+ }
+ }
+ }
+
+ static unsafe uint calc_rice_params_sums(RiceContext rc, int pmin, int pmax, ulong* sums, uint n, uint pred_order, int bps)
+ {
+ int* parm = stackalloc int[(pmax + 1) * FlakeConstants.MAX_PARTITIONS];
+ //uint* bits = stackalloc uint[FlakeConstants.MAX_PARTITION_ORDER];
+
+ //assert(pmin >= 0 && pmin <= FlakeConstants.MAX_PARTITION_ORDER);
+ //assert(pmax >= 0 && pmax <= FlakeConstants.MAX_PARTITION_ORDER);
+ //assert(pmin <= pmax);
+
+ // sums for lower levels
+ calc_lower_sums(pmin, pmax, sums);
+
+ uint opt_bits = AudioSamples.UINT32_MAX;
+ int opt_porder = pmin;
+ int opt_method = 0;
+ for (int i = pmin; i <= pmax; i++)
+ {
+ int method = bps > 16 ? 1 : 0;
+ uint bits = calc_optimal_rice_params(i, parm + i * FlakeConstants.MAX_PARTITIONS, sums + i * FlakeConstants.MAX_PARTITIONS, n, pred_order, ref method);
+ if (bits <= opt_bits)
+ {
+ opt_bits = bits;
+ opt_porder = i;
+ opt_method = method;
+ }
+ }
+
+ rc.porder = opt_porder;
+ rc.coding_method = opt_method;
+ fixed (int* rparms = rc.rparams)
+ AudioSamples.MemCpy(rparms, parm + opt_porder * FlakeConstants.MAX_PARTITIONS, (1 << opt_porder));
+
+ return opt_bits;
+ }
+
+ static int get_max_p_order(int max_porder, int n, int order)
+ {
+ int porder = Math.Min(max_porder, BitReader.log2i(n ^ (n - 1)));
+ if (order > 0)
+ porder = Math.Min(porder, BitReader.log2i(n / order));
+ return porder;
+ }
+
+// private static int[,] best_x = new int[14,8193];
+ private static int[][] good_x = new int[][] {
+new int[] {}, // 0
+new int[] { // 1
+0x03,0x01,0x00,0x02
+},
+new int[] {// 2
+0x01,0x07,0x06,0x02, 0x03,0x04,0x00,0x05
+},
+new int[] { // 3
+0x0b,0x0f,0x0e,0x0d, 0x03,0x01,0x05,0x02
+},
+new int[] { //4
+0x17,0x09,0x03,0x0a, 0x06,0x1d,0x1f,0x05, 0x1c,0x0d,0x07,0x0c,
+},
+new int[] { // 5
+0x2b,0x3d,0x37,0x07, 0x11,0x15,0x36,0x3f,
+},
+new int[] { // 6
+0x6b,0x15,0x7e,0x31, 0x07,0x1a,0x29,0x26, 0x5d,0x23,0x6f,0x19, 0x56,0x75
+},
+new int[] { // 7
+0xdb,0xef,0xb5,0x47, 0xee,0x63,0x0b,0xfd, 0x31,0xbe,0xed,0x33, 0xff,0xfb,0xd6,0xbb
+},
+new int[] { // 8
+0x1bb,0x1c7,0x069,0x087, 0x1fd,0x16e,0x095,0x1de, 0x066,0x071,0x055,0x09a,
+},
+new int[] { // 9
+0x36b,0x3bd,0x097,0x0c3, 0x0e3,0x0b1,0x107,0x2de, 0x3ef,0x2fb,0x3d5,0x139
+},
+new int[] { // 10
+//0x0e3,0x199,0x383,0x307, 0x1e3,0x01f,0x269,0x0f1, 0x266,0x03f,0x2cd,0x1c3, 0x19a,0x387,0x339,0x259,
+0x6eb,0x187,0x77d,0x271, 0x195,0x259,0x5ae,0x169,
+},
+new int[] { // 11
+0xddb,0xf77,0xb6d,0x587, 0x2c3,0x03b,0xef5,0x1e3, 0xdbe,
+},
+new int[] { // 12
+0x1aeb,0x0587,0x0a71,0x1dbd, 0x0559,0x0aa5,0x0a2e,0x0d43, 0x05aa,0x00f3,0x0696,0x03c6,
+},
+new int[] { // 13
+0x35d7,0x2f6f,0x0aa3,0x1569, 0x150f,0x3d79,0x0dc3,0x309f/*?*/,
+},
+new int[] { // 14
+0x75d7,0x5f7b,0x6a8f,0x29a3,
+},
+new int[] { // 15
+0xddd7,0xaaaf,0x55c3,0xf77b,
+},
+new int[] { // 16
+0x1baeb,0x1efaf,0x1d5bf,0x1cff3,
+},
+new int[] { // 17
+0x36dd7,0x3bb7b,0x3df6f,0x2d547,
+},
+new int[] { // 18
+0x75dd7,0x6f77b,0x7aaaf,0x5ddd3,
+},
+new int[] { // 19
+0xdddd7,0xf777b,0xd5547,0xb6ddb,
+},
+new int[] { // 20
+0x1baeeb,0x1efbaf,0x1aaabf,0x17bbeb,
+},
+new int[] { // 21
+0x376dd7,0x3ddf7b,0x2d550f,0x0aaaa3,
+},
+new int[] { // 22
+0x6eddd7,0x77777b,0x5dcd4f,0x5d76f9,
+},
+new int[] { // 23
+0xdeddd7,0xb5b6eb,0x55552b,0x2aaac3,
+},
+new int[] { // 24
+0x1dddbb7,0x1b76eeb,0x17bbf5f,0x1eeaa9f,
+},
+new int[] { // 25
+},
+new int[] { // 26
+},
+new int[] { // 27
+},
+new int[] { // 28
+},
+new int[] { // 29
+},
+new int[] { // 30
+},
+ };
+
+ unsafe void postprocess_coefs(FlacFrame frame, FlacSubframe sf, int ch)
+ {
+ if (eparams.development_mode < 0)
+ return;
+ if (sf.type != SubframeType.LPC || sf.order > 30)
+ return;
+ int orig_window = sf.window;
+ int orig_order = sf.order;
+ int orig_shift = sf.shift;
+ int orig_cbits = sf.cbits;
+ uint orig_size = sf.size;
+ var orig_coefs = stackalloc int[orig_order];
+ for (int i = 0; i < orig_order; i++) orig_coefs[i] = sf.coefs[i];
+ int orig_xx = -1;
+ int orig_seq = 0;
+ int maxxx = Math.Min(good_x[orig_order].Length, eparams.development_mode);
+ var pmax = get_max_p_order(m_settings.MaxPartitionOrder, frame.blocksize, orig_order);
+ var pmin = Math.Min(m_settings.MinPartitionOrder, pmax);
+ ulong* sums = stackalloc ulong[(pmax + 1) * FlakeConstants.MAX_PARTITIONS];
+
+ while (true)
+ {
+ var best_coefs = stackalloc int[orig_order];
+ int best_shift = orig_shift;
+ int best_cbits = orig_cbits;
+ uint best_size = orig_size;
+ int best_xx = -1;
+ for (int xx = -1; xx < maxxx; xx++)
+ {
+ int x = xx;
+ if (xx < 0)
+ {
+ if (orig_xx < 0 || maxxx < 1/*3*/)// || (orig_xx >> orig_order) != 0)
+ continue;
+ x = orig_xx;
+ orig_seq++;
+ }
+ else
+ {
+ orig_seq = 0;
+ if (orig_order < good_x.Length && good_x[orig_order] != null)
+ x = good_x[orig_order][xx];
+ }
+
+ frame.current.type = SubframeType.LPC;
+ frame.current.order = orig_order;
+ frame.current.window = orig_window;
+ frame.current.shift = orig_shift;
+ frame.current.cbits = orig_cbits;
+
+ if (((x >> orig_order) & 1) != 0)
+ {
+ frame.current.shift--;
+ frame.current.cbits--;
+ if (frame.current.shift < 0 || frame.current.cbits < 2)
+ continue;
+ }
+
+ ulong csum = 0;
+ int qmax = (1 << (frame.current.cbits - 1)) - 1;
+ for (int i = 0; i < frame.current.order; i++)
+ {
+ int shift = (x >> orig_order) & 1;
+ int increment = (x == 1 << orig_order) ? 0 : (((x >> i) & 1) << 1) - 1;
+ frame.current.coefs[i] = (orig_coefs[i] + (increment << orig_seq)) >> shift;
+ if (frame.current.coefs[i] < -(qmax + 1)) frame.current.coefs[i] = -(qmax + 1);
+ if (frame.current.coefs[i] > qmax) frame.current.coefs[i] = qmax;
+ csum += (ulong)Math.Abs(frame.current.coefs[i]);
+ }
+
+ fixed (int* coefs = frame.current.coefs)
+ {
+ if ((csum << frame.subframes[ch].obits) >= 1UL << 32)
+ lpc.encode_residual_long(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax);
+ else
+ lpc.encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax);
+ }
+
+ var cur_size = calc_rice_params_sums(frame.current.rc, pmin, pmax, sums, (uint)frame.blocksize, (uint)frame.current.order, Settings.PCM.BitsPerSample);
+ frame.current.size = (uint)(frame.current.order * frame.subframes[ch].obits + 4 + 5 + frame.current.order * frame.current.cbits + 6 + (int)cur_size);
+
+ if (frame.current.size < best_size)
+ {
+ //var dif = best_size - frame.current.size;
+ for (int i = 0; i < frame.current.order; i++) best_coefs[i] = frame.current.coefs[i];
+ best_shift = frame.current.shift;
+ best_cbits = frame.current.cbits;
+ best_size = frame.current.size;
+ best_xx = x;
+ frame.ChooseBestSubframe(ch);
+ //if (dif > orig_order * 5)
+ // break;
+ }
+
+ if (xx < 0 && best_size < orig_size)
+ break;
+ }
+
+ if (best_size < orig_size)
+ {
+ //if (best_xx >= 0) best_x[order, best_xx]++;
+ //if (orig_size != 0x7FFFFFFF)
+ // System.Console.Write(string.Format(" {0}[{1:x}]", orig_size - best_size, best_xx));
+ for (int i = 0; i < orig_order; i++) orig_coefs[i] = best_coefs[i];
+ orig_shift = best_shift;
+ orig_cbits = best_cbits;
+ orig_size = best_size;
+ orig_xx = best_xx;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ //if (orig_size != 0x7FFFFFFF)
+ // System.Console.WriteLine();
+
+ //if (frame_count % 0x400 == 0)
+ //{
+ // for (int o = 0; o < best_x.GetLength(0); o++)
+ // {
+ // //for (int x = 0; x <= (1 << o); x++)
+ // // if (best_x[o, x] != 0)
+ // // System.Console.WriteLine(string.Format("{0:x2}\t{1:x4}\t{2}", o, x, best_x[o, x]));
+ // var s = new List>();
+ // for (int x = 0; x < (1 << o); x++)
+ // if (best_x[o, x] != 0)
+ // s.Add(new KeyValuePair(x, best_x[o, x]));
+ // s.Sort((x, y) => y.Value.CompareTo(x.Value));
+ // foreach (var x in s)
+ // System.Console.WriteLine(string.Format("{0:x2}\t{1:x4}\t{2}", o, x.Key, x.Value));
+ // int i = 0;
+ // foreach (var x in s)
+ // {
+ // System.Console.Write(string.Format(o <= 8 ? "0x{0:x2}," : "0x{0:x3},", x.Key));
+ // if ((++i) % 16 == 0)
+ // System.Console.WriteLine();
+ // }
+ // System.Console.WriteLine();
+ // }
+ //}
+ }
+
+ public static void SetCoefs(int order, int[] coefs)
+ {
+ good_x[order] = new int[coefs.Length];
+ for (int i = 0; i < coefs.Length; i++)
+ good_x[order][i] = coefs[i];
+ }
+
+ unsafe void encode_residual_lpc_sub(FlacFrame frame, float* lpcs, int iWindow, int order, int ch)
+ {
+ // select LPC precision based on block size
+ uint lpc_precision;
+ 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;
+ else lpc_precision = 15;
+
+ for (int i_precision = m_settings.MinPrecisionSearch; i_precision <= m_settings.MaxPrecisionSearch && 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.type = SubframeType.LPC;
+ frame.current.order = order;
+ frame.current.window = iWindow;
+ frame.current.cbits = (int)cbits;
+
+ int pmax = get_max_p_order(m_settings.MaxPartitionOrder, frame.blocksize, frame.current.order);
+ int pmin = Math.Min(m_settings.MinPartitionOrder, pmax);
+ ulong* sums = stackalloc ulong[(pmax + 1) * FlakeConstants.MAX_PARTITIONS];
+ ulong csum = 0;
+ 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, 15, 0);
+
+ if (frame.current.shift < 0 || frame.current.shift > 15)
+ throw new Exception("negative shift");
+
+ for (int i = frame.current.order; i > 0; i--)
+ csum += (ulong)Math.Abs(coefs[i - 1]);
+
+ if ((csum << frame.subframes[ch].obits) >= 1UL << 32)
+ lpc.encode_residual_long(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax);
+ else
+ lpc.encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax);
+
+ }
+ uint best_size = calc_rice_params_sums(frame.current.rc, pmin, pmax, sums, (uint)frame.blocksize, (uint)frame.current.order, Settings.PCM.BitsPerSample);
+ frame.current.size = (uint)(frame.current.order * frame.subframes[ch].obits + 4 + 5 + frame.current.order * (int)cbits + 6 + (int)best_size);
+ frame.ChooseBestSubframe(ch);
+ //if (frame.current.size >= frame.subframes[ch].best.size)
+ // postprocess_coefs(frame, frame.current, ch);
+ //else
+ //{
+ // frame.ChooseBestSubframe(ch);
+ // postprocess_coefs(frame, frame.subframes[ch].best, ch);
+ //}
+ }
+ }
+
+ unsafe void encode_residual_fixed_sub(FlacFrame frame, int order, int ch)
+ {
+ if ((frame.subframes[ch].done_fixed & (1U << order)) != 0)
+ return; // already calculated;
+
+ frame.current.order = order;
+ frame.current.type = SubframeType.Fixed;
+
+#if XXX
+ int best_order = order;
+ if (frame.subframes[ch].done_fixed == 0)
+ {
+ best_order = encode_residual_fixed_estimate_best_order(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order);
+ if (best_order != order)
+ {
+ //frame.subframes[ch].done_fixed |= (1U << order);
+ order = best_order;
+ frame.current.order = order;
+ encode_residual_fixed(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order);
+ }
+ }
+ else
+#endif
+ int pmax = get_max_p_order(m_settings.MaxPartitionOrder, frame.blocksize, frame.current.order);
+ int pmin = Math.Min(m_settings.MinPartitionOrder, pmax);
+ ulong* sums = stackalloc ulong[(pmax + 1) * FlakeConstants.MAX_PARTITIONS];
+ encode_residual_fixed(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax);
+
+ frame.current.size = (uint)(frame.current.order * frame.subframes[ch].obits) + 6
+ + calc_rice_params_sums(frame.current.rc, pmin, pmax, sums, (uint)frame.blocksize, (uint)frame.current.order, Settings.PCM.BitsPerSample);
+
+ frame.subframes[ch].done_fixed |= (1U << order);
+
+ frame.ChooseBestSubframe(ch);
+ }
+
+ unsafe void fixed_compute_best_predictor(int* data, uint data_len, ulong* errors)//, float* residual_bits_per_sample)
+ {
+ long last_error_0 = data[-1];
+ long last_error_1 = data[-1] - data[-2];
+ long last_error_2 = last_error_1 - (data[-2] - data[-3]);
+ long last_error_3 = last_error_2 - (data[-2] - 2 * data[-3] + data[-4]);
+ ulong total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0;
+
+#if VARIANT1
+ long error, save;
+ int* finish = data + data_len;
+ while (data < finish)
+ {
+ error = *(data++); total_error_0 += (ulong)((error << 1) ^ (error >> 63)); save = error;
+ error -= last_error_0; total_error_1 += (ulong)((error << 1) ^ (error >> 63)); last_error_0 = save; save = error;
+ error -= last_error_1; total_error_2 += (ulong)((error << 1) ^ (error >> 63)); last_error_1 = save; save = error;
+ error -= last_error_2; total_error_3 += (ulong)((error << 1) ^ (error >> 63)); last_error_2 = save; save = error;
+ error -= last_error_3; total_error_4 += (ulong)((error << 1) ^ (error >> 63)); last_error_3 = save;
+ }
+#else
+ int* finish = data + data_len;
+ while (data < finish)
+ {
+ long next_error_0 = *(data++);
+ long next_error_1 = next_error_0 - last_error_0;
+ long next_error_2 = next_error_1 - last_error_1;
+ long next_error_3 = next_error_2 - last_error_2;
+ long next_error_4 = next_error_3 - last_error_3;
+
+ last_error_0 = next_error_0;
+ last_error_1 = next_error_1;
+ last_error_2 = next_error_2;
+ last_error_3 = next_error_3;
+
+ total_error_0 += (ulong)((last_error_0 << 1) ^ (last_error_0 >> 63));
+ total_error_1 += (ulong)((last_error_1 << 1) ^ (last_error_1 >> 63));
+ total_error_2 += (ulong)((last_error_2 << 1) ^ (last_error_2 >> 63));
+ total_error_3 += (ulong)((last_error_3 << 1) ^ (last_error_3 >> 63));
+ total_error_4 += (ulong)((next_error_4 << 1) ^ (next_error_4 >> 63));
+ }
+#endif
+
+ errors[0] = total_error_0;
+ errors[1] = total_error_1;
+ errors[2] = total_error_2;
+ errors[3] = total_error_3;
+ errors[4] = total_error_4;
+
+ //residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0);
+ //residual_bits_per_sample[1] = (float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0);
+ //residual_bits_per_sample[2] = (float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0);
+ //residual_bits_per_sample[3] = (float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0);
+ //residual_bits_per_sample[4] = (float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0);
+ }
+
+ unsafe int fixed_compute_best_predictor_order(ulong* error)
+ {
+ int order;
+ if ((error[0] < error[1]) & (error[0] < error[2]) & (error[0] < error[3]) & (error[0] < error[4]))
+ order = 0;
+ else if ((error[1] < error[2]) & (error[1] < error[3]) & (error[1] < error[4]))
+ order = 1;
+ else if ((error[2] < error[3]) & (error[2] < error[4]))
+ order = 2;
+ else if (error[3] < error[4])
+ order = 3;
+ else
+ order = 4;
+ return order;
+ }
+
+ unsafe void encode_residual(FlacFrame frame, int ch, PredictionType predict, OrderMethod omethod, int pass, int windows_mask)
+ {
+ int* smp = frame.subframes[ch].samples;
+ int i, n = frame.blocksize;
+ // save best.window, because we can overwrite it later with fixed frame
+
+ // CONSTANT
+ for (i = 1; i < n; i++)
+ {
+ if (smp[i] != smp[0]) break;
+ }
+ if (i == n)
+ {
+ frame.subframes[ch].best.type = SubframeType.Constant;
+ frame.subframes[ch].best.residual[0] = smp[0];
+ frame.subframes[ch].best.size = (uint)frame.subframes[ch].obits;
+ return;
+ }
+
+ // VERBATIM
+ frame.current.type = SubframeType.Verbatim;
+ frame.current.size = (uint)(frame.subframes[ch].obits * frame.blocksize);
+ frame.ChooseBestSubframe(ch);
+
+ if (n < 5 || predict == PredictionType.None)
+ return;
+
+ // LPC
+ if (n > m_settings.MaxLPCOrder &&
+ (predict == PredictionType.Levinson ||
+ predict == PredictionType.Search)
+ //predict == PredictionType.Search ||
+ //(pass == 2 && frame.subframes[ch].best.type == SubframeType.LPC))
+ )
+ {
+ float* lpcs = stackalloc float[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER];
+ int min_order = m_settings.MinLPCOrder;
+ int max_order = m_settings.MaxLPCOrder;
+
+ for (int iWindow = 0; iWindow < _windowcount; iWindow++)
+ {
+ if ((windows_mask & (1 << iWindow)) == 0)
+ continue;
+
+ LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow];
+ fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, iWindow, 0])
+ lpc_ctx.GetReflection(
+ frame.subframes[ch].sf, max_order, frame.blocksize,
+ smp, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2, sections);
+ lpc_ctx.ComputeLPC(lpcs);
+
+ //int frameSize = n;
+ //float* F = stackalloc float[frameSize];
+ //float* B = stackalloc float[frameSize];
+ //float* PE = stackalloc float[max_order + 1];
+ //float* arp = stackalloc float[max_order];
+ //float* rc = stackalloc float[max_order];
+
+ //for (int j = 0; j < frameSize; j++)
+ // F[j] = B[j] = smp[j];
+
+ //for (int K = 1; K <= max_order; K++)
+ //{
+ // // BURG:
+ // float denominator = 0.0f;
+ // //float denominator = F[K - 1] * F[K - 1] + B[frameSize - K] * B[frameSize - K];
+ // for (int j = 0; j < frameSize - K; j++)
+ // denominator += F[j + K] * F[j + K] + B[j] * B[j];
+ // denominator /= 2;
+
+ // // Estimate error
+ // PE[K - 1] = denominator / (frameSize - K);
+
+ // float reflectionCoeff = 0.0f;
+ // for (int j = 0; j < frameSize - K; j++)
+ // reflectionCoeff += F[j + K] * B[j];
+ // reflectionCoeff /= denominator;
+ // rc[K - 1] = arp[K - 1] = reflectionCoeff;
+
+ // // Levinson-Durbin
+ // for (int j = 0; j < (K - 1) >> 1; j++)
+ // {
+ // float arptmp = arp[j];
+ // arp[j] -= reflectionCoeff * arp[K - 2 - j];
+ // arp[K - 2 - j] -= reflectionCoeff * arptmp;
+ // }
+ // if (((K - 1) & 1) != 0)
+ // arp[(K - 1) >> 1] -= reflectionCoeff * arp[(K - 1) >> 1];
+
+ // for (int j = 0; j < frameSize - K; j++)
+ // {
+ // float f = F[j + K];
+ // float b = B[j];
+ // F[j + K] = f - reflectionCoeff * b;
+ // B[j] = b - reflectionCoeff * f;
+ // }
+
+ // for (int j = 0; j < K; j++)
+ // lpcs[(K - 1) * lpc.MAX_LPC_ORDER + j] = (float)arp[j];
+ //}
+
+ switch (omethod)
+ {
+ case OrderMethod.Akaike:
+ //lpc_ctx.SortOrdersAkaike(frame.blocksize, m_settings.EstimationDepth, max_order, 7.1, 0.0);
+ lpc_ctx.SortOrdersAkaike(frame.blocksize, m_settings.EstimationDepth, min_order, max_order, 4.5, 0);
+ break;
+ default:
+ throw new Exception("unknown order method");
+ }
+
+ for (i = 0; i < m_settings.EstimationDepth && i < max_order; i++)
+ encode_residual_lpc_sub(frame, lpcs, iWindow, lpc_ctx.best_orders[i], ch);
+ }
+
+ postprocess_coefs(frame, frame.subframes[ch].best, ch);
+ }
+
+ // FIXED
+ if (predict == PredictionType.Fixed ||
+ (predict == PredictionType.Search && pass != 1) ||
+ //predict == PredictionType.Search ||
+ //(pass == 2 && frame.subframes[ch].best.type == SubframeType.Fixed) ||
+ (n > m_settings.MaxFixedOrder && n <= m_settings.MaxLPCOrder))
+ {
+ int max_fixed_order = Math.Min(m_settings.MaxFixedOrder, 4);
+ int min_fixed_order = Math.Min(m_settings.MinFixedOrder, max_fixed_order);
+
+ if (min_fixed_order == 0 && max_fixed_order == 4)
+ {
+ fixed (ulong* fixed_errors = frame.subframes[ch].best_fixed)
+ {
+ if ((frame.subframes[ch].done_fixed & (1U << 5)) == 0)
+ {
+ fixed_compute_best_predictor(smp + 4, (uint)n - 4, fixed_errors);
+ frame.subframes[ch].done_fixed |= (1U << 5);
+ }
+ i = fixed_compute_best_predictor_order(fixed_errors);
+ encode_residual_fixed_sub(frame, i, ch);
+ }
+ }
+ else
+ {
+ for (i = max_fixed_order; i >= min_fixed_order; i--)
+ encode_residual_fixed_sub(frame, i, ch);
+ }
+ }
+
+ }
+
+ unsafe void output_frame_header(FlacFrame frame, BitWriter bitwriter)
+ {
+ bitwriter.writebits(15, 0x7FFC);
+ bitwriter.writebits(1, eparams.variable_block_size > 0 ? 1 : 0);
+ bitwriter.writebits(4, frame.bs_code0);
+ bitwriter.writebits(4, sr_code0);
+ if (frame.ch_mode == ChannelMode.NotStereo)
+ bitwriter.writebits(4, ch_code);
+ else
+ bitwriter.writebits(4, (int) frame.ch_mode);
+ bitwriter.writebits(3, bps_code);
+ bitwriter.writebits(1, 0);
+ bitwriter.write_utf8(frame_count);
+
+ // custom block size
+ if (frame.bs_code1 >= 0)
+ {
+ if (frame.bs_code1 < 256)
+ bitwriter.writebits(8, frame.bs_code1);
+ else
+ bitwriter.writebits(16, frame.bs_code1);
+ }
+
+ // custom sample rate
+ if (sr_code1 > 0)
+ {
+ if (sr_code1 < 256)
+ bitwriter.writebits(8, sr_code1);
+ else
+ bitwriter.writebits(16, sr_code1);
+ }
+
+ // CRC-8 of frame header
+ bitwriter.flush();
+ byte crc = crc8.ComputeChecksum(frame_buffer, 0, bitwriter.Length);
+ bitwriter.writebits(8, crc);
+ }
+
+ unsafe void output_residual(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub)
+ {
+ // rice-encoded block
+ bitwriter.writebits(2, sub.best.rc.coding_method);
+
+ // partition order
+ int porder = sub.best.rc.porder;
+ int psize = frame.blocksize >> porder;
+ //assert(porder >= 0);
+ bitwriter.writebits(4, porder);
+ int res_cnt = psize - sub.best.order;
+
+ int rice_len = 4 + sub.best.rc.coding_method;
+ // residual
+ int j = sub.best.order;
+ fixed (byte* fixbuf = &frame_buffer[0])
+ for (int p = 0; p < (1 << porder); p++)
+ {
+ int k = sub.best.rc.rparams[p];
+ bitwriter.writebits(rice_len, k);
+ if (p == 1) res_cnt = psize;
+ int cnt = Math.Min(res_cnt, frame.blocksize - j);
+ bitwriter.write_rice_block_signed(fixbuf, k, sub.best.residual + j, cnt);
+ j += cnt;
+ }
+ }
+
+ unsafe void
+ output_subframe_constant(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub)
+ {
+ bitwriter.writebits_signed(sub.obits, sub.best.residual[0]);
+ }
+
+ unsafe void
+ output_subframe_verbatim(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub)
+ {
+ int n = frame.blocksize;
+ for (int i = 0; i < n; i++)
+ bitwriter.writebits_signed(sub.obits, sub.samples[i]);
+ // Don't use residual here, because we don't copy samples to residual for verbatim frames.
+ }
+
+ unsafe void
+ output_subframe_fixed(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub)
+ {
+ // warm-up samples
+ for (int i = 0; i < sub.best.order; i++)
+ bitwriter.writebits_signed(sub.obits, sub.best.residual[i]);
+
+ // residual
+ output_residual(frame, bitwriter, sub);
+ }
+
+ unsafe void
+ output_subframe_lpc(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub)
+ {
+ // warm-up samples
+ for (int i = 0; i < sub.best.order; i++)
+ bitwriter.writebits_signed(sub.obits, sub.best.residual[i]);
+
+ // LPC coefficients
+ int cbits = 1;
+ for (int i = 0; i < sub.best.order; i++)
+ while (cbits < 16 && sub.best.coefs[i] != (sub.best.coefs[i] << (32 - cbits)) >> (32 - cbits))
+ cbits++;
+ bitwriter.writebits(4, cbits - 1);
+ bitwriter.writebits_signed(5, sub.best.shift);
+ for (int i = 0; i < sub.best.order; i++)
+ bitwriter.writebits_signed(cbits, sub.best.coefs[i]);
+
+ // residual
+ output_residual(frame, bitwriter, sub);
+ }
+
+ unsafe void output_subframes(FlacFrame frame, BitWriter bitwriter)
+ {
+ for (int ch = 0; ch < channels; ch++)
+ {
+ FlacSubframeInfo sub = frame.subframes[ch];
+ // subframe header
+ int type_code = (int) sub.best.type;
+ if (sub.best.type == SubframeType.Fixed)
+ type_code |= sub.best.order;
+ if (sub.best.type == SubframeType.LPC)
+ type_code |= sub.best.order - 1;
+ bitwriter.writebits(1, 0);
+ bitwriter.writebits(6, type_code);
+ bitwriter.writebits(1, sub.wbits != 0 ? 1 : 0);
+ if (sub.wbits > 0)
+ bitwriter.writebits((int)sub.wbits, 1);
+
+ // subframe
+ switch (sub.best.type)
+ {
+ case SubframeType.Constant:
+ output_subframe_constant(frame, bitwriter, sub);
+ break;
+ case SubframeType.Verbatim:
+ output_subframe_verbatim(frame, bitwriter, sub);
+ break;
+ case SubframeType.Fixed:
+ output_subframe_fixed(frame, bitwriter, sub);
+ break;
+ case SubframeType.LPC:
+ output_subframe_lpc(frame, bitwriter, sub);
+ break;
+ }
+ }
+ }
+
+ void output_frame_footer(BitWriter bitwriter)
+ {
+ bitwriter.flush();
+ ushort crc = bitwriter.get_crc16();
+ bitwriter.writebits(16, crc);
+ bitwriter.flush();
+ }
+
+ unsafe void encode_residual_pass1(FlacFrame frame, int ch, int windows_mask)
+ {
+ int max_prediction_order = m_settings.MaxLPCOrder;
+ //int max_fixed_order = m_settings.MaxFixedOrder;
+ //int min_fixed_order = m_settings.MinFixedOrder;
+ int lpc_min_precision_search = m_settings.MinPrecisionSearch;
+ int lpc_max_precision_search = m_settings.MaxPrecisionSearch;
+ int max_partition_order = m_settings.MaxPartitionOrder;
+ int estimation_depth = m_settings.EstimationDepth;
+ var development_mode = eparams.development_mode;
+ //m_settings.MinFixedOrder = 2;
+ //m_settings.MaxFixedOrder = 2;
+ m_settings.MinPrecisionSearch = m_settings.MaxPrecisionSearch;
+ m_settings.MaxLPCOrder = Math.Min(m_settings.MaxLPCOrder, Math.Max(m_settings.MinLPCOrder, 8));
+ m_settings.EstimationDepth = 1;
+ eparams.development_mode = Math.Min(eparams.development_mode, -1);
+ encode_residual(frame, ch, m_settings.PredictionType, OrderMethod.Akaike, 1, windows_mask);
+ //m_settings.MinFixedOrder = min_fixed_order;
+ //m_settings.MaxFixedOrder = max_fixed_order;
+ m_settings.MaxLPCOrder = max_prediction_order;
+ m_settings.MinPrecisionSearch = lpc_min_precision_search;
+ m_settings.MaxPrecisionSearch = lpc_max_precision_search;
+ m_settings.MaxPartitionOrder = max_partition_order;
+ m_settings.EstimationDepth = estimation_depth;
+ eparams.development_mode = development_mode;
+ }
+
+ unsafe void encode_residual_pass2(FlacFrame frame, int ch)
+ {
+ encode_residual(frame, ch, m_settings.PredictionType, eparams.order_method, 2, estimate_best_windows(frame, ch));
+ }
+
+ unsafe int estimate_best_windows_akaike(FlacFrame frame, int ch, int count, bool onePerType)
+ {
+ int* windows_present = stackalloc int[_windowcount];
+ for (int i = 0; i < _windowcount; i++)
+ windows_present[i] = 0;
+ if (onePerType)
+ {
+ for (int i = 0; i < _windowcount; i++)
+ for (int j = 0; j < _windowcount; j++)
+ if (windowType[j] == windowType[i])
+ windows_present[j]++;
+ }
+
+ float* err = stackalloc float[lpc.MAX_LPC_ORDER];
+ for (int i = 0; i < _windowcount; i++)
+ {
+ LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[i];
+ if (onePerType && windows_present[i] <= count)
+ {
+ err[i] = 0;
+ continue;
+ }
+ int estimate_order = 4;
+ fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, i, 0])
+ lpc_ctx.GetReflection(
+ frame.subframes[ch].sf, estimate_order, frame.blocksize,
+ frame.subframes[ch].samples, frame.window_buffer + i * FlakeConstants.MAX_BLOCKSIZE * 2, sections);
+ lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, estimate_order, 4.5, 0.0);
+ //err[i] = (float)(lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0));
+ //err[i] = (float)((frame.blocksize * lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1] / windowScale[i]) + lpc_ctx.best_orders[0] * 4.5);
+ //err[i] = (float)((frame.blocksize * lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1] / windowScale[i]) + lpc_ctx.best_orders[0] * frame.subframes[ch].obits);
+
+ // realistic
+ //err[i] = (float)(frame.blocksize * Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1]) / Math.Log(2) / 2.5
+ //- windowScale[i] / 2 + lpc_ctx.best_orders[0] * frame.subframes[ch].obits / 2);
+
+ //err[i] = (float)(frame.blocksize * Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1]) / Math.Log(2) / 2.5
+ //- frame.blocksize * Math.Log(lpc_ctx.autocorr_values[0]) / 2.1
+ //+ Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5 / 2.5 / Math.Log(2));
+
+ // Akaike
+ //err[i] = (float)(frame.blocksize * (Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1])) + Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5);
+
+ //err[i] = (float)(lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0) - frame.blocksize * (frame.subframes[ch].obits + Math.Log(windowScale[i] / frame.blocksize) / 2));
+
+ // tested good
+ err[i] = (float)(lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0) - frame.blocksize * Math.Log(lpc_ctx.autocorr_values[0]) / 2);
+ }
+ int* best_windows = stackalloc int[lpc.MAX_LPC_ORDER];
+ for (int i = 0; i < _windowcount; i++)
+ best_windows[i] = i;
+ for (int i = 0; i < _windowcount; i++)
+ {
+ for (int j = i + 1; j < _windowcount; j++)
+ {
+ if (err[best_windows[i]] > err[best_windows[j]])
+ {
+ int tmp = best_windows[j];
+ best_windows[j] = best_windows[i];
+ best_windows[i] = tmp;
+ }
+ }
+ }
+ int window_mask = 0;
+ if (onePerType)
+ {
+ for (int i = 0; i < _windowcount; i++)
+ windows_present[i] = count;
+ for (int i = 0; i < _windowcount; i++)
+ {
+ int w = best_windows[i];
+ if (windows_present[w] > 0)
+ {
+ for (int j = 0; j < _windowcount; j++)
+ if (windowType[j] == windowType[w])
+ windows_present[j]--;
+ window_mask |= 1 << w;
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < _windowcount && i < count; i++)
+ window_mask |= 1 << best_windows[i];
+ }
+ return window_mask;
+ }
+
+ unsafe int estimate_best_windows(FlacFrame frame, int ch)
+ {
+ if (_windowcount == 1 || m_settings.PredictionType == PredictionType.Fixed)
+ return 1;
+ switch (m_settings.WindowMethod)
+ {
+ case WindowMethod.Estimate:
+ return estimate_best_windows_akaike(frame, ch, 1, false);
+ case WindowMethod.Estimate2:
+ return estimate_best_windows_akaike(frame, ch, 2, false);
+ case WindowMethod.Estimate3:
+ return estimate_best_windows_akaike(frame, ch, 3, false);
+ case WindowMethod.EstimateN:
+ return estimate_best_windows_akaike(frame, ch, 1, true);
+ case WindowMethod.Evaluate2:
+ encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 2, false));
+ return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0;
+ case WindowMethod.Evaluate3:
+ encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 3, false));
+ return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0;
+ case WindowMethod.EvaluateN:
+ encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 1, true));
+#if XXX
+ if (frame.subframes[ch].best.type == SubframeType.LPC && frame.subframes[ch].best.order <= 4)
+ {
+ LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[frame.subframes[ch].best.window];
+ double err = lpc_ctx.prediction_error[frame.subframes[ch].best.order - 1] / lpc_ctx.autocorr_values[0];
+ double est = frame.blocksize * (frame.subframes[ch].obits * (1 - err));
+ double est1 = frame.blocksize * (frame.subframes[ch].obits * (err));
+ if (est < 0 || est1 < 0) return -1;
+ }
+#endif
+ return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0;
+ case WindowMethod.Evaluate2N:
+ encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 2, true));
+ return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0;
+ case WindowMethod.Evaluate3N:
+ encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 3, true));
+ return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0;
+ case WindowMethod.Evaluate:
+ encode_residual_pass1(frame, ch, -1);
+ return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0;
+ case WindowMethod.Search:
+ return -1;
+ }
+ return -1;
+ }
+
+ unsafe void estimate_frame(FlacFrame frame, bool do_midside)
+ {
+ int subframes = do_midside ? channels * 2 : channels;
+
+ switch (m_settings.StereoMethod)
+ {
+ case StereoMethod.Estimate:
+ for (int ch = 0; ch < subframes; ch++)
+ {
+ LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[0];
+ int estimate_order = 4;
+ int iWindow = 0;
+ fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, iWindow, 0])
+ lpc_ctx.GetReflection(
+ frame.subframes[ch].sf, estimate_order, frame.blocksize,
+ frame.subframes[ch].samples, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2, sections);
+ lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, estimate_order, 4.5, 0.0);
+ frame.subframes[ch].best.size
+ = (uint)Math.Max(0, frame.blocksize * (Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1])) + Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5
+ //= (uint)Math.Max(0, lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0)
+ //* 2.0 / Math.Log(windowScale[0] / frame.blocksize)
+ + 7.1 * frame.subframes[ch].obits * m_settings.MaxLPCOrder);
+ }
+ break;
+ case StereoMethod.EstimateFixed:
+ for (int ch = 0; ch < subframes; ch++)
+ {
+ fixed (ulong* fixed_errors = frame.subframes[ch].best_fixed)
+ {
+ if ((frame.subframes[ch].done_fixed & (1U << 5)) == 0)
+ {
+ fixed_compute_best_predictor(frame.subframes[ch].samples + 4, (uint)frame.blocksize - 4, fixed_errors);
+ frame.subframes[ch].done_fixed |= (1U << 5);
+ }
+ int best_order = fixed_compute_best_predictor_order(fixed_errors);
+ //residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0);
+ frame.subframes[ch].best.size = (uint)fixed_errors[best_order];
+ }
+ }
+ break;
+ case StereoMethod.EstimateX:
+ for (int ch = 0; ch < subframes; ch++)
+ {
+ for (int iWindow = 0; iWindow < _windowcount; iWindow++)
+ {
+ LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow];
+ int estimate_order = 4;
+ fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, iWindow, 0])
+ lpc_ctx.GetReflection(
+ frame.subframes[ch].sf, estimate_order, frame.blocksize,
+ frame.subframes[ch].samples, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2, sections);
+ lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, estimate_order, 4.5, 0.0);
+ uint estimate
+ = (uint)Math.Max(0, frame.blocksize * (Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1])) + Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5
+ //= (uint)Math.Max(0, lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0)
+ //* 2.0 / Math.Log(windowScale[0] / frame.blocksize)
+ + 7.1 * frame.subframes[ch].obits * m_settings.MaxLPCOrder);
+ if (iWindow == 0 || frame.subframes[ch].best.size > estimate)
+ frame.subframes[ch].best.size = estimate;
+ }
+ }
+ break;
+ case StereoMethod.Evaluate:
+ for (int ch = 0; ch < subframes; ch++)
+ encode_residual_pass1(frame, ch, 1);
+ break;
+ case StereoMethod.EvaluateX:
+ for (int ch = 0; ch < subframes; ch++)
+ encode_residual_pass1(frame, ch,
+ estimate_best_windows_akaike(frame, ch, 1, false));
+ break;
+ case StereoMethod.Search:
+ for (int ch = 0; ch < subframes; ch++)
+ encode_residual_pass2(frame, ch);
+ break;
+ }
+ }
+
+ unsafe uint measure_frame_size(FlacFrame frame, bool do_midside)
+ {
+ // crude estimation of header/footer size
+ uint total = (uint)(32 + ((BitReader.log2i(frame_count) + 4) / 5) * 8 + (eparams.variable_block_size != 0 ? 16 : 0) + 16);
+
+ if (do_midside)
+ {
+ uint bitsBest = AudioSamples.UINT32_MAX;
+ ChannelMode modeBest = ChannelMode.LeftRight;
+
+ if (bitsBest > frame.subframes[2].best.size + frame.subframes[3].best.size)
+ {
+ bitsBest = frame.subframes[2].best.size + frame.subframes[3].best.size;
+ modeBest = ChannelMode.MidSide;
+ }
+ if (bitsBest > frame.subframes[3].best.size + frame.subframes[1].best.size)
+ {
+ bitsBest = frame.subframes[3].best.size + frame.subframes[1].best.size;
+ modeBest = ChannelMode.RightSide;
+ }
+ if (bitsBest > frame.subframes[3].best.size + frame.subframes[0].best.size)
+ {
+ bitsBest = frame.subframes[3].best.size + frame.subframes[0].best.size;
+ modeBest = ChannelMode.LeftSide;
+ }
+ if (bitsBest > frame.subframes[0].best.size + frame.subframes[1].best.size)
+ {
+ bitsBest = frame.subframes[0].best.size + frame.subframes[1].best.size;
+ modeBest = ChannelMode.LeftRight;
+ }
+ frame.ch_mode = modeBest;
+ return total + bitsBest;
+ }
+
+ for (int ch = 0; ch < channels; ch++)
+ total += frame.subframes[ch].best.size;
+ return total;
+ }
+
+ unsafe void encode_estimated_frame(FlacFrame frame)
+ {
+ switch (m_settings.StereoMethod)
+ {
+ case StereoMethod.Estimate:
+ case StereoMethod.EstimateX:
+ case StereoMethod.EstimateFixed:
+ for (int ch = 0; ch < channels; ch++)
+ {
+ frame.subframes[ch].best.size = AudioSamples.UINT32_MAX;
+ encode_residual_pass2(frame, ch);
+ }
+ break;
+ case StereoMethod.Evaluate:
+ case StereoMethod.EvaluateX:
+ for (int ch = 0; ch < channels; ch++)
+ encode_residual_pass2(frame, ch);
+ break;
+ case StereoMethod.Search:
+ break;
+ }
+ }
+
+ unsafe delegate void window_function(float* window, int size);
+
+ unsafe void calculate_window(float* window, window_function func, WindowFunction flag)
+ {
+ if ((m_settings.WindowFunctions & flag) == 0 || _windowcount == lpc.MAX_LPC_WINDOWS)
+ return;
+ int sz = _windowsize;
+ float* pos1 = window + _windowcount * FlakeConstants.MAX_BLOCKSIZE * 2;
+ float* pos = pos1;
+ int nSeg = 0;
+ do
+ {
+ windowSections[nSeg, _windowcount, 0].setData(0, sz);
+ for (int j = 1; j < lpc.MAX_LPC_SECTIONS; j++)
+ windowSections[nSeg, _windowcount, j].setZero(sz, sz);
+
+ fixed (LpcWindowSection* sections = &windowSections[nSeg, _windowcount, 0])
+ func(pos, sz);
+ if ((sz & 1) != 0)
+ break;
+ nSeg++;
+ pos += sz;
+ sz >>= 1;
+ } while (sz >= 32);
+ double scale = 0.0;
+ for (int i = 0; i < _windowsize; i++)
+ scale += pos1[i] * pos1[i];
+ windowScale[_windowcount] = scale;
+ windowType[_windowcount] = flag;
+ _windowcount++;
+ }
+
+ class PunchoutTukeyVariant
+ {
+ public PunchoutTukeyVariant(
+ WindowFunction _type,
+ int _parts, double _overlap, double _p)
+ {
+ parts = _parts;
+ type = _type;
+ overlap = _overlap;
+ p = _p;
+ }
+ public WindowFunction type;
+ public int parts;
+ public double overlap;
+ public double p;
+ };
+
+ unsafe int encode_frame(out int size)
+ {
+ fixed (int* s = samplesBuffer, r = residualBuffer)
+ fixed (float* window = windowBuffer)
+ {
+ frame.InitSize(m_blockSize, eparams.variable_block_size != 0);
+
+ if (frame.blocksize != _windowsize && frame.blocksize > 4 && m_settings.PredictionType != PredictionType.Fixed)
+ {
+ _windowsize = frame.blocksize;
+ _windowcount = 0;
+ calculate_window(window, lpc.window_welch, WindowFunction.Welch);
+ calculate_window(window, lpc.window_flattop, WindowFunction.Flattop);
+ calculate_window(window, lpc.window_hann, WindowFunction.Hann);
+ calculate_window(window, lpc.window_bartlett, WindowFunction.Bartlett);
+ var tukeys = new PunchoutTukeyVariant[]
+ {
+ new PunchoutTukeyVariant(WindowFunction.Tukey4, 4, 0, 0.03),
+ new PunchoutTukeyVariant(WindowFunction.Tukey4A, 4, 0, 0.03),
+ new PunchoutTukeyVariant(WindowFunction.Tukey4B, 4, 0, 0.03),
+ new PunchoutTukeyVariant(WindowFunction.Tukey4X, 4, m_settings.TukeyOverlap, m_settings.TukeyP),
+ new PunchoutTukeyVariant(WindowFunction.Tukey3, 3, 1.0/3, 0.03),
+ new PunchoutTukeyVariant(WindowFunction.Tukey3A, 3, 1.0/3, 0.03),
+ new PunchoutTukeyVariant(WindowFunction.Tukey3B, 3, 1.0/3, 0.03),
+ new PunchoutTukeyVariant(WindowFunction.Tukey3X, 3, m_settings.TukeyOverlap, m_settings.TukeyP),
+ new PunchoutTukeyVariant(WindowFunction.Tukey2, 2, 0.25, 0.03),
+ new PunchoutTukeyVariant(WindowFunction.Tukey2A, 2, 0.25, 0.03),
+ new PunchoutTukeyVariant(WindowFunction.Tukey2B, 2, 0.25, 0.03),
+ new PunchoutTukeyVariant(WindowFunction.Tukey2X, 2, m_settings.TukeyOverlap, m_settings.TukeyP),
+ new PunchoutTukeyVariant(WindowFunction.Tukey, 1, 0.0, 0.03),
+ new PunchoutTukeyVariant(WindowFunction.Tukey1A, 1, 0.0, 0.03),
+ new PunchoutTukeyVariant(WindowFunction.Tukey1B, 1, 0.0, 0.03),
+ new PunchoutTukeyVariant(WindowFunction.Tukey1X, 1, m_settings.TukeyOverlap, m_settings.TukeyP),
+ };
+
+ foreach (var tukey in tukeys)
+ {
+ if (tukey.parts == 0 || (m_settings.WindowFunctions & tukey.type) == 0) continue;
+ if (tukey.parts == 1)
+ {
+ calculate_window(window, (w, wsz) =>
+ {
+ lpc.window_tukey(w, wsz, tukey.p);
+ }, tukey.type);
+ continue;
+ }
+ double overlap = tukey.overlap;
+ double overlap_units = overlap / (1.0 - overlap);
+ for (int m = 0; m < tukey.parts; m++)
+ calculate_window(window, (w, wsz) =>
+ {
+ lpc.window_punchout_tukey(w, wsz, tukey.p, tukey.p,
+ m / (tukey.parts + overlap_units),
+ (m + 1 + overlap_units) / (tukey.parts + overlap_units));
+ }, tukey.type);
+ }
+
+ if (_windowcount == 0)
+ throw new Exception("invalid windowfunction");
+ int nSeg = 0;
+ int sz = _windowsize;
+ float* window_segment = window;
+ do
+ {
+ fixed (LpcWindowSection* sections = &windowSections[nSeg, 0, 0])
+ LpcWindowSection.Detect(_windowcount, window_segment, FlakeConstants.MAX_BLOCKSIZE * 2, sz, Settings.PCM.BitsPerSample, sections);
+ if ((sz & 1) != 0)
+ break;
+ window_segment += sz;
+ nSeg++;
+ sz >>= 1;
+ } while (sz >= 32);
+#if NONONO
+ using (TextWriter tx = File.CreateText(@"H:\ubuntu\flac\w.txt"))
+ {
+#if !NONONO
+ int totaltotal = 0;
+ for (int i = 0; i < _windowcount; i++)
+ {
+ int total = 0;
+ for (int sec = 0; sec < lpc.MAX_LPC_SECTIONS; sec++)
+ if (windowSections[0, i, sec].m_type != LpcWindowSection.SectionType.Zero || windowSections[0, i, sec].m_start != windowSections[0, i, sec].m_end)
+ {
+ tx.WriteLine("{0}\t{1}\t{2}\t{3}", windowSections[0, i, sec].m_start, windowSections[0, i, sec].m_end, windowSections[0, i, sec].m_type, windowSections[0, i, sec].m_id);
+ if (windowSections[0, i, sec].m_type != LpcWindowSection.SectionType.One)
+ total += windowSections[0, i, sec].m_end - windowSections[0, i, sec].m_start;
+ }
+ totaltotal += total;
+ tx.WriteLine("{0} total window data", total);
+ }
+ tx.WriteLine("{0} grand total window data", totaltotal);
+#endif
+ for (int x = 0; x < frame.blocksize; x++)
+ {
+ tx.Write("{0}", x);
+ for (int i = 0; i < _windowcount; i++)
+ tx.Write("\t{0}", window[i * FlakeConstants.MAX_BLOCKSIZE * 2 + x]);
+ tx.WriteLine();
+ }
+ }
+#endif
+ }
+
+ if (channels != 2 || frame.blocksize <= 32 || m_settings.StereoMethod == StereoMethod.Independent)
+ {
+ frame.window_buffer = window;
+ frame.nSeg = 0;
+ frame.current.residual = r + channels * FlakeConstants.MAX_BLOCKSIZE;
+ frame.ch_mode = channels != 2 ? ChannelMode.NotStereo : ChannelMode.LeftRight;
+ for (int ch = 0; ch < channels; ch++)
+ frame.subframes[ch].Init(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE,
+ Settings.PCM.BitsPerSample, get_wasted_bits(s + ch * FlakeConstants.MAX_BLOCKSIZE, frame.blocksize));
+
+ for (int ch = 0; ch < channels; ch++)
+ encode_residual_pass2(frame, ch);
+ }
+ else
+ {
+ //channel_decorrelation(s, s + FlakeConstants.MAX_BLOCKSIZE, s + 2 * FlakeConstants.MAX_BLOCKSIZE, s + 3 * FlakeConstants.MAX_BLOCKSIZE, frame.blocksize);
+ frame.window_buffer = window;
+ frame.nSeg = 0;
+ frame.current.residual = r + 4 * FlakeConstants.MAX_BLOCKSIZE;
+ for (int ch = 0; ch < 4; ch++)
+ frame.subframes[ch].Init(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE,
+ Settings.PCM.BitsPerSample + (ch == 3 ? 1 : 0), get_wasted_bits(s + ch * FlakeConstants.MAX_BLOCKSIZE, frame.blocksize));
+
+ //for (int ch = 0; ch < 4; ch++)
+ // for (int iWindow = 0; iWindow < _windowcount; iWindow++)
+ // frame.subframes[ch].lpc_ctx[iWindow].GetReflection(32, frame.subframes[ch].samples, frame.blocksize, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2);
+
+ estimate_frame(frame, true);
+ uint fs = measure_frame_size(frame, true);
+
+ if (0 != eparams.variable_block_size)
+ {
+ FlacFrame frame2 = new FlacFrame(channels * 2);
+ FlacFrame frame3 = new FlacFrame(channels * 2);
+ int tumbler = 1;
+ while ((frame.blocksize & 1) == 0 && frame.blocksize >= 1024)
+ {
+ frame2.InitSize(frame.blocksize / 2, true);
+ frame2.window_buffer = frame.window_buffer + frame.blocksize;
+ frame2.nSeg = frame.nSeg + 1;
+ frame2.current.residual = r + tumbler * 5 * FlakeConstants.MAX_BLOCKSIZE;
+ for (int ch = 0; ch < 4; ch++)
+ frame2.subframes[ch].Init(frame.subframes[ch].samples, frame2.current.residual + (ch + 1) * frame2.blocksize,
+ frame.subframes[ch].obits + frame.subframes[ch].wbits, frame.subframes[ch].wbits);
+ estimate_frame(frame2, true);
+ //measure_frame_size(frame2, true);
+ //frame2.ChooseSubframes();
+ //encode_estimated_frame(frame2);
+ //uint fs2 = measure_frame_size(frame2, false);
+ uint fs2 = measure_frame_size(frame2, true);
+ uint fs3 = fs2;
+ if (eparams.variable_block_size == 2 || eparams.variable_block_size == 4)
+ {
+ frame3.InitSize(frame2.blocksize, true);
+ frame3.window_buffer = frame2.window_buffer;
+ frame3.nSeg = frame2.nSeg;
+ frame3.current.residual = frame2.current.residual + 5 * frame2.blocksize;
+ for (int ch = 0; ch < 4; ch++)
+ frame3.subframes[ch].Init(frame2.subframes[ch].samples + frame2.blocksize, frame3.current.residual + (ch + 1) * frame3.blocksize,
+ frame.subframes[ch].obits + frame.subframes[ch].wbits, frame.subframes[ch].wbits);
+ estimate_frame(frame3, true);
+ fs3 = measure_frame_size(frame3, true);
+ }
+ if (fs2 + fs3 > fs)
+ break;
+ FlacFrame tmp = frame;
+ frame = frame2;
+ frame2 = tmp;
+ fs = fs2;
+ if (eparams.variable_block_size <= 2)
+ break;
+ tumbler = 1 - tumbler;
+ }
+ }
+
+ frame.ChooseSubframes();
+ encode_estimated_frame(frame);
+ }
+
+ BitWriter bitwriter = new BitWriter(frame_buffer, 0, max_frame_size);
+
+ output_frame_header(frame, bitwriter);
+ output_subframes(frame, bitwriter);
+ output_frame_footer(bitwriter);
+
+ if (bitwriter.Length >= max_frame_size)
+ throw new Exception("buffer overflow");
+
+ if (frame_buffer != null)
+ {
+ if (eparams.variable_block_size > 0)
+ frame_count += frame.blocksize;
+ else
+ frame_count++;
+ }
+ size = frame.blocksize;
+ return bitwriter.Length;
+ }
+ }
+
+ unsafe int output_frame()
+ {
+ if (verify != null)
+ {
+ fixed (int* s = verifyBuffer, r = samplesBuffer)
+ for (int ch = 0; ch < channels; ch++)
+ AudioSamples.MemCpy(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE, m_blockSize);
+ }
+
+ int fs, bs;
+ //if (0 != eparams.variable_block_size && 0 == (m_blockSize & 7) && m_blockSize >= 128)
+ // fs = encode_frame_vbs();
+ //else
+ fs = encode_frame(out bs);
+
+ if (seek_table != null && _IO.CanSeek)
+ {
+ for (int sp = 0; sp < seek_table.Length; sp++)
+ {
+ if (seek_table[sp].framesize != 0)
+ continue;
+ if (seek_table[sp].number > _position + bs)
+ break;
+ if (seek_table[sp].number >= _position)
+ {
+ seek_table[sp].number = _position;
+ seek_table[sp].offset = _IO.Position - first_frame_offset;
+ seek_table[sp].framesize = bs;
+ }
+ }
+ }
+
+ _position += bs;
+ _IO.Write(frame_buffer, 0, fs);
+ _totalSize += fs;
+
+ if (verify != null)
+ try
+ {
+ int decoded = verify.DecodeFrame(frame_buffer, 0, fs);
+ if (decoded != fs || verify.Remaining != bs)
+ throw new Exception(Properties.Resources.ExceptionValidationFailed);
+ fixed (int* s = verifyBuffer, r = verify.Samples)
+ {
+ for (int ch = 0; ch < channels; ch++)
+ if (AudioSamples.MemCmp(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE, bs))
+ throw new Exception(Properties.Resources.ExceptionValidationFailed);
+ }
+ }
+ catch (Exception ex)
+ {
+ //if (channels == 2)
+ //{
+ // var sw = new WAVWriter(string.Format("verify_{0}.wav", this.frame_count), new WAVWriterSettings(this.Settings.PCM));
+ // sw.FinalSampleCount = this.frame.blocksize;
+ // var ab = new AudioBuffer(this.Settings.PCM, this.frame.blocksize);
+ // ab.Prepare(this.frame.blocksize);
+ // fixed (int* abs = ab.Samples, s = verifyBuffer)
+ // AudioSamples.Interlace(abs, s, s + FlakeConstants.MAX_BLOCKSIZE, this.frame.blocksize);
+ // sw.Write(ab);
+ // sw.Close();
+ //} else
+ throw ex;
+ }
+
+ if (bs < m_blockSize)
+ {
+ for (int ch = 0; ch < (channels == 2 ? 4 : channels); ch++)
+ Buffer.BlockCopy(samplesBuffer, (bs + ch * FlakeConstants.MAX_BLOCKSIZE) * sizeof(int), samplesBuffer, ch * FlakeConstants.MAX_BLOCKSIZE * sizeof(int), (m_blockSize - bs) * sizeof(int));
+ //fixed (int* s = samplesBuffer)
+ // for (int ch = 0; ch < channels; ch++)
+ // AudioSamples.MemCpy(s + ch * FlakeConstants.MAX_BLOCKSIZE, s + bs + ch * FlakeConstants.MAX_BLOCKSIZE, m_blockSize - bs);
+ }
+
+ samplesInBuffer -= bs;
+
+ return bs;
+ }
+
+ public void Write(AudioBuffer buff)
+ {
+ if (!inited)
+ {
+ if (_IO == null)
+ _IO = new FileStream(_path, FileMode.Create, FileAccess.Write, FileShare.Read, 0x20000);
+ inited = true;
+ int header_size = flake_encode_init();
+ _IO.Write(header, 0, header_size);
+ if (_IO.CanSeek)
+ first_frame_offset = _IO.Position;
+ }
+
+ buff.Prepare(this);
+
+ int pos = 0;
+ while (pos < buff.Length)
+ {
+ int block = Math.Min(buff.Length - pos, m_blockSize - samplesInBuffer);
+
+ copy_samples(buff.Samples, pos, block);
+
+ pos += block;
+
+ while (samplesInBuffer >= m_blockSize)
+ output_frame();
+ }
+
+ if (md5 != null)
+ md5.TransformBlock(buff.Bytes, 0, buff.ByteLength, null, 0);
+ }
+
+ public string Path { get { return _path; } }
+
+ public static string Vendor
+ {
+ get
+ {
+ var version = typeof(AudioEncoder).Assembly.GetName().Version;
+ return vendor_string ?? "CUETools " + version.Major + "." + version.Minor + "." + version.Build;
+ }
+ set
+ {
+ vendor_string = value;
+ }
+ }
+
+ static string vendor_string = null;
+
+ int select_blocksize(int samplerate, int time_ms)
+ {
+ int blocksize = FlakeConstants.flac_blocksizes[1];
+ int target = (samplerate * time_ms) / 1000;
+ if (eparams.variable_block_size > 0)
+ {
+ blocksize = 1024;
+ while (target >= blocksize)
+ blocksize <<= 1;
+ return blocksize >> 1;
+ }
+
+ for (int i = 8; i < FlakeConstants.flac_blocksizes.Length - 1; i++)
+ if (target >= FlakeConstants.flac_blocksizes[i] && FlakeConstants.flac_blocksizes[i] > blocksize)
+ {
+ blocksize = FlakeConstants.flac_blocksizes[i];
+ }
+ return blocksize;
+ }
+
+ void write_streaminfo(byte[] header, int pos, int last)
+ {
+ Array.Clear(header, pos, 38);
+ BitWriter bitwriter = new BitWriter(header, pos, 38);
+
+ // metadata header
+ bitwriter.writebits(1, last);
+ bitwriter.writebits(7, (int)MetadataType.StreamInfo);
+ bitwriter.writebits(24, 34);
+
+ if (eparams.variable_block_size > 0)
+ bitwriter.writebits(16, 0);
+ else
+ bitwriter.writebits(16, m_blockSize);
+
+ bitwriter.writebits(16, m_blockSize);
+ bitwriter.writebits(24, 0);
+ bitwriter.writebits(24, max_frame_size);
+ bitwriter.writebits(20, Settings.PCM.SampleRate);
+ bitwriter.writebits(3, channels - 1);
+ bitwriter.writebits(5, Settings.PCM.BitsPerSample - 1);
+
+ // total samples
+ if (sample_count > 0)
+ {
+ bitwriter.writebits(4, 0);
+ bitwriter.writebits(32, sample_count);
+ }
+ else
+ {
+ bitwriter.writebits(4, 0);
+ bitwriter.writebits(32, 0);
+ }
+ bitwriter.flush();
+ }
+
+ /**
+ * Write vorbis comment metadata block to byte array.
+ * Just writes the vendor string for now.
+ */
+ int write_vorbis_comment(byte[] comment, int pos, int len, int last)
+ {
+ BitWriter bitwriter = new BitWriter(comment, pos, len);
+ Encoding enc = new UTF8Encoding();
+ byte[] str = enc.GetBytes(Vendor);
+
+ // metadata header
+ bitwriter.writebits(1, last);
+ bitwriter.writebits(7, (int)MetadataType.VorbisComment);
+ int tagsLen = 0;
+ if (m_settings.Tags != null)
+ foreach (var t in m_settings.Tags)
+ tagsLen += 4 + enc.GetByteCount(t);
+ bitwriter.writebits(24, 8 + str.Length + tagsLen);
+ for (int i = 0; i < 4; i++)
+ bitwriter.writebits(8, (str.Length >> (i * 8)) & 0xff);
+ bitwriter.write(str);
+ int nTags = m_settings.Tags != null ? m_settings.Tags.Length : 0;
+ for (int i = 0; i < 4; i++)
+ bitwriter.writebits(8, (nTags >> (i * 8)) & 0xff);
+ if (m_settings.Tags != null)
+ foreach (var tag in m_settings.Tags)
+ {
+ str = enc.GetBytes(tag);
+ for (int i = 0; i < 4; i++)
+ bitwriter.writebits(8, (str.Length >> (i * 8)) & 0xff);
+ bitwriter.write(str);
+ }
+ bitwriter.flush();
+ return bitwriter.Length;
+ }
+
+ int write_seekpoints(byte[] header, int pos, int last)
+ {
+ seek_table_offset = pos + 4;
+
+ BitWriter bitwriter = new BitWriter(header, pos, 4 + 18 * seek_table.Length);
+
+ // metadata header
+ bitwriter.writebits(1, last);
+ bitwriter.writebits(7, (int)MetadataType.Seektable);
+ bitwriter.writebits(24, 18 * seek_table.Length);
+ for (int i = 0; i < seek_table.Length; i++)
+ {
+ bitwriter.writebits(FlakeConstants.FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN, (ulong)seek_table[i].number);
+ bitwriter.writebits(FlakeConstants.FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN, (ulong)seek_table[i].offset);
+ bitwriter.writebits(FlakeConstants.FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN, seek_table[i].framesize);
+ }
+ bitwriter.flush();
+ return 4 + 18 * seek_table.Length;
+ }
+
+ /**
+ * Write padding metadata block to byte array.
+ */
+ int
+ write_padding(byte[] padding, int pos, int last, int padlen)
+ {
+ BitWriter bitwriter = new BitWriter(padding, pos, 4);
+
+ // metadata header
+ bitwriter.writebits(1, last);
+ bitwriter.writebits(7, (int)MetadataType.Padding);
+ bitwriter.writebits(24, padlen);
+
+ bitwriter.flush();
+ return padlen + 4;
+ }
+
+ int write_headers()
+ {
+ int header_size = 0;
+ int last = 0;
+
+ // stream marker
+ header[0] = 0x66;
+ header[1] = 0x4C;
+ header[2] = 0x61;
+ header[3] = 0x43;
+ header_size += 4;
+
+ // streaminfo
+ write_streaminfo(header, header_size, last);
+ header_size += 38;
+
+ // seek table
+ if (_IO.CanSeek && seek_table != null)
+ header_size += write_seekpoints(header, header_size, last);
+
+ // vorbis comments
+ if (m_settings.Padding == 0) last = 1;
+ header_size += write_vorbis_comment(header, header_size, header.Length - header_size, last);
+
+ // padding
+ if (m_settings.Padding > 0)
+ {
+ last = 1;
+ header_size += write_padding(header, header_size, last, m_settings.Padding);
+ }
+
+ return header_size;
+ }
+
+ int flake_encode_init()
+ {
+ int i, header_len;
+
+ //if(flake_validate_params(s) < 0)
+
+ ch_code = channels - 1;
+
+ // find samplerate in table
+ for (i = 1; i < 12; i++)
+ {
+ if (Settings.PCM.SampleRate == FlakeConstants.flac_samplerates[i])
+ {
+ sr_code0 = i;
+ break;
+ }
+ }
+
+ // if not in table, samplerate is non-standard
+ if (i == 12)
+ throw new Exception("non-standard samplerate");
+
+ for (i = 1; i < 8; i++)
+ {
+ if (Settings.PCM.BitsPerSample == FlakeConstants.flac_bitdepths[i])
+ {
+ bps_code = i;
+ break;
+ }
+ }
+ if (i == 8)
+ throw new Exception("non-standard bps");
+
+ m_blockSize = m_settings.BlockSize != 0 ? m_settings.BlockSize :
+ select_blocksize(Settings.PCM.SampleRate, eparams.block_time_ms);
+
+ // set maximum encoded frame size (if larger, re-encodes in verbatim mode)
+ if (channels == 2)
+ max_frame_size = 16 + ((m_blockSize * (Settings.PCM.BitsPerSample + Settings.PCM.BitsPerSample + 1) + 7) >> 3);
+ else
+ max_frame_size = 16 + ((m_blockSize * channels * Settings.PCM.BitsPerSample + 7) >> 3);
+
+ if (_IO.CanSeek && eparams.do_seektable && sample_count > 0)
+ {
+ int seek_points_distance = Settings.PCM.SampleRate * 10;
+ int num_seek_points = 1 + sample_count / seek_points_distance; // 1 seek point per 10 seconds
+ if (sample_count % seek_points_distance == 0)
+ num_seek_points--;
+ seek_table = new SeekPoint[num_seek_points];
+ for (int sp = 0; sp < num_seek_points; sp++)
+ {
+ seek_table[sp].framesize = 0;
+ seek_table[sp].offset = 0;
+ seek_table[sp].number = sp * seek_points_distance;
+ }
+ }
+
+ // output header bytes
+ int tagsLen = 0;
+ Encoding enc = new UTF8Encoding();
+ if (m_settings.Tags != null)
+ foreach (var t in m_settings.Tags)
+ tagsLen += 4 + enc.GetByteCount(t);
+ header = new byte[m_settings.Padding + 1024 + (seek_table == null ? 0 : seek_table.Length * 18) + tagsLen];
+ header_len = write_headers();
+
+ // initialize CRC & MD5
+ if (_IO.CanSeek && m_settings.DoMD5)
+ md5 = new MD5CryptoServiceProvider();
+
+ if (m_settings.DoVerify)
+ {
+ verify = new AudioDecoder(Settings.PCM);
+ verifyBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * channels];
+ }
+
+ frame_buffer = new byte[max_frame_size];
+
+ return header_len;
+ }
+ }
+
+ struct FlakeEncodeParams
+ {
+ // prediction order selection method
+ // set by user prior to calling flake_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;
+
+ // block time in milliseconds
+ // set by the user prior to calling flake_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;
+
+ // whether to use variable block sizes
+ // set by user prior to calling flake_encode_init
+ // 0 = fixed block size
+ // 1 = variable block size
+ public int variable_block_size;
+
+ public bool do_seektable;
+
+ public int development_mode;
+
+ public int flake_set_defaults(EncoderSettings settings)
+ {
+ order_method = OrderMethod.Akaike;
+ block_time_ms = 105;
+ variable_block_size = 0;
+ do_seektable = true;
+ development_mode = -1;
+
+ if (settings.GetEncoderModeIndex() == 11)
+ variable_block_size = 4;
+
+ return 0;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/COPYING b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/COPYING
new file mode 100644
index 000000000..69352d1d3
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/COPYING
@@ -0,0 +1,503 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ 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 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ , 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/CUETools.Codecs.Flake.csproj b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/CUETools.Codecs.Flake.csproj
new file mode 100644
index 000000000..a7db18bb0
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/CUETools.Codecs.Flake.csproj
@@ -0,0 +1,29 @@
+
+
+
+ netstandard2.0
+ 2.1.9.0
+ CUETools.Codecs.Flake
+ CUETools.Codecs.Flake
+ CUETools
+ A library for encoding and decoding FLAC.
+ Copyright (c) 2008-2021 Grigory Chudov
+ Grigory Chudov
+ true
+ ..\bin\$(Configuration)\plugins
+ https://github.com/gchudov/cuetools.net
+ git
+
+
+
+
+
+ False
+
+
+
+
+
+
+
+
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/ChannelMode.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/ChannelMode.cs
new file mode 100644
index 000000000..e2544a336
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/ChannelMode.cs
@@ -0,0 +1,11 @@
+namespace CUETools.Codecs.Flake
+{
+ public enum ChannelMode
+ {
+ NotStereo = 0,
+ LeftRight = 1,
+ LeftSide = 8,
+ RightSide = 9,
+ MidSide = 10
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/DecoderSettings.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/DecoderSettings.cs
new file mode 100644
index 000000000..dea9e8865
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/DecoderSettings.cs
@@ -0,0 +1,36 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Text;
+
+namespace CUETools.Codecs.Flake
+{
+ [JsonObject(MemberSerialization.OptIn)]
+ public class DecoderSettings : IAudioDecoderSettings
+ {
+ #region IAudioDecoderSettings implementation
+ [Browsable(false)]
+ public string Extension => "flac";
+
+ [Browsable(false)]
+ public string Name => "cuetools";
+
+ [Browsable(false)]
+ public Type DecoderType => typeof(AudioDecoder);
+
+ [Browsable(false)]
+ public int Priority => 2;
+
+ public IAudioDecoderSettings Clone()
+ {
+ return MemberwiseClone() as IAudioDecoderSettings;
+ }
+ #endregion
+
+ public DecoderSettings()
+ {
+ this.Init();
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/EncoderSettings.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/EncoderSettings.cs
new file mode 100644
index 000000000..ba782b6e4
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/EncoderSettings.cs
@@ -0,0 +1,271 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Text;
+
+namespace CUETools.Codecs.Flake
+{
+ [JsonObject(MemberSerialization.OptIn)]
+ public class EncoderSettings : IAudioEncoderSettings
+ {
+ #region IAudioEncoderSettings implementation
+ [Browsable(false)]
+ public string Extension => "flac";
+
+ [Browsable(false)]
+ public string Name => "cuetools";
+
+ [Browsable(false)]
+ public Type EncoderType => typeof(AudioEncoder);
+
+ [Browsable(false)]
+ public bool Lossless => true;
+
+ [Browsable(false)]
+ public int Priority => 4;
+
+ [Browsable(false)]
+ public string SupportedModes => this.AllowNonSubset || (this.PCM != null && this.PCM.SampleRate > 48000) ? "0 1 2 3 4 5 6 7 8 9 10 11" : "0 1 2 3 4 5 6 7 8";
+
+ [Browsable(false)]
+ public string DefaultMode => "5";
+
+ [Browsable(false)]
+ [DefaultValue("")]
+ [JsonProperty]
+ public string EncoderMode { get; set; }
+
+ [Browsable(false)]
+ public AudioPCMConfig PCM { get; set; }
+
+ [Browsable(false)]
+ public int BlockSize { get; set; }
+
+ [Browsable(false)]
+ [DefaultValue(4096)]
+ public int Padding { get; set; }
+
+ public IAudioEncoderSettings Clone()
+ {
+ return MemberwiseClone() as IAudioEncoderSettings;
+ }
+ #endregion
+
+ public EncoderSettings()
+ {
+ this.Init();
+ }
+
+ public bool IsSubset()
+ {
+ return (BlockSize == 0 || (BlockSize <= 16384 && (PCM.SampleRate > 48000 || BlockSize <= 4608)))
+ && (PCM.SampleRate > 48000 || MaxLPCOrder <= 12)
+ && MaxPartitionOrder <= 8
+ ;
+ //The blocksize bits in the frame header must be 0001-1110. The blocksize must be <=16384; if the sample rate is <= 48000Hz, the blocksize must be <=4608.
+ //The sample rate bits in the frame header must be 0001-1110.
+ //The bits-per-sample bits in the frame header must be 001-111.
+ //If the sample rate is <= 48000Hz, the filter order in LPC subframes must be less than or equal to 12, i.e. the subframe type bits in the subframe header may not be 101100-111111.
+ //The Rice partition order in a Rice-coded residual section must be less than or equal to 8.
+ }
+
+ public void Validate()
+ {
+ if (this.GetEncoderModeIndex() < 0)
+ throw new Exception("unsupported encoder mode");
+ this.SetDefaultValuesForMode();
+ if (Padding < 0)
+ throw new Exception("unsupported padding value " + Padding.ToString());
+ if (BlockSize != 0 && (BlockSize < 256 || BlockSize >= FlakeConstants.MAX_BLOCKSIZE))
+ throw new Exception("unsupported block size " + BlockSize.ToString());
+ if (MinLPCOrder > MaxLPCOrder || MaxLPCOrder > lpc.MAX_LPC_ORDER)
+ throw new Exception("invalid MaxLPCOrder " + MaxLPCOrder.ToString());
+ if (MinFixedOrder < 0 || MinFixedOrder > 4)
+ throw new Exception("invalid MinFixedOrder " + MinFixedOrder.ToString());
+ if (MaxFixedOrder < 0 || MaxFixedOrder > 4)
+ throw new Exception("invalid MaxFixedOrder " + MaxFixedOrder.ToString());
+ if (MinPartitionOrder < 0)
+ throw new Exception("invalid MinPartitionOrder " + MinPartitionOrder.ToString());
+ if (MinPartitionOrder > MaxPartitionOrder || MaxPartitionOrder > 8)
+ throw new Exception("invalid MaxPartitionOrder " + MaxPartitionOrder.ToString());
+ if (PredictionType == PredictionType.None)
+ throw new Exception("invalid PredictionType " + PredictionType.ToString());
+ if (PredictionType != PredictionType.Fixed)
+ {
+ if (WindowMethod == WindowMethod.Invalid)
+ throw new InvalidOperationException("invalid WindowMethod " + WindowMethod.ToString());
+ if (WindowFunctions == WindowFunction.None)
+ throw new InvalidOperationException("invalid WindowFunctions " + WindowFunctions.ToString());
+ if (EstimationDepth > 32 || EstimationDepth < 1)
+ throw new InvalidOperationException("invalid EstimationDepth " + EstimationDepth.ToString());
+ if (MinPrecisionSearch < 0 || MinPrecisionSearch >= lpc.MAX_LPC_PRECISIONS)
+ throw new Exception("unsupported MinPrecisionSearch value");
+ if (MaxPrecisionSearch < 0 || MaxPrecisionSearch >= lpc.MAX_LPC_PRECISIONS)
+ throw new Exception("unsupported MaxPrecisionSearch value");
+ if (MaxPrecisionSearch < MinPrecisionSearch)
+ throw new Exception("unsupported MaxPrecisionSearch value");
+ }
+ if (!AllowNonSubset && !IsSubset())
+ throw new Exception("the encoding parameters specified do not conform to the FLAC Subset");
+ }
+
+ [DefaultValue(-1)]
+ [DefaultValueForMode(2, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0)]
+ [Browsable(false)]
+ [DisplayName("MinFixedOrder")]
+ [SRDescription(typeof(Properties.Resources), "MinFixedOrderDescription")]
+ public int MinFixedOrder { get; set; }
+
+ [DefaultValue(-1)]
+ [DefaultValueForMode(2, 4, 4, 4, 2, 2, 4, 4, 4, 4, 4, 4)]
+ [Browsable(false)]
+ [DisplayName("MaxFixedOrder")]
+ [SRDescription(typeof(Properties.Resources), "MaxFixedOrderDescription")]
+ public int MaxFixedOrder { get; set; }
+
+ [DefaultValue(1)]
+ [Browsable(false)]
+ [DisplayName("MinLPCOrder")]
+ [SRDescription(typeof(Properties.Resources), "MinLPCOrderDescription")]
+ public int MinLPCOrder { get; set; }
+
+ [DefaultValue(-1)]
+ [DefaultValueForMode(8, 8, 8, 12, 12, 12, 12, 12, 12, 32, 32, 32)]
+ [Browsable(false)]
+ [DisplayName("MaxLPCOrder")]
+ [SRDescription(typeof(Properties.Resources), "MaxLPCOrderDescription")]
+ public int MaxLPCOrder { get; set; }
+
+ [DefaultValue(0)]
+ [DisplayName("MinPartitionOrder")]
+ [Browsable(false)]
+ [SRDescription(typeof(Properties.Resources), "MinPartitionOrderDescription")]
+ public int MinPartitionOrder { get; set; }
+
+ [DefaultValue(-1)]
+ [DefaultValueForMode(6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 8)]
+ [DisplayName("MaxPartitionOrder")]
+ [Browsable(false)]
+ [SRDescription(typeof(Properties.Resources), "MaxPartitionOrderDescription")]
+ public int MaxPartitionOrder { get; set; }
+
+ [DefaultValue(false)]
+ [DisplayName("Verify")]
+ [SRDescription(typeof(Properties.Resources), "DoVerifyDescription")]
+ [JsonProperty]
+ public bool DoVerify { get; set; }
+
+ [DefaultValue(true)]
+ [DisplayName("MD5")]
+ [SRDescription(typeof(Properties.Resources), "DoMD5Description")]
+ [JsonProperty]
+ public bool DoMD5 { get; set; }
+
+ [DefaultValue(false)]
+ [DisplayName("Allow Non-subset")]
+ [SRDescription(typeof(Properties.Resources), "AllowNonSubsetDescription")]
+ [JsonProperty]
+ public bool AllowNonSubset { get; set; }
+
+ [DefaultValue(StereoMethod.Invalid)]
+ [DefaultValueForMode(
+ /* 0 */ StereoMethod.Independent,
+ /* 1 */ StereoMethod.EstimateFixed,
+ /* 2 */ StereoMethod.Estimate,
+ /* 3 */ StereoMethod.Estimate,
+ /* 4 */ StereoMethod.Evaluate,
+ /* 5 */ StereoMethod.Evaluate,
+ /* 6 */ StereoMethod.Evaluate,
+ /* 7 */ StereoMethod.Evaluate,
+ /* 8 */ StereoMethod.Evaluate,
+ /* 9 */ StereoMethod.Evaluate,
+ /* 10 */ StereoMethod.Evaluate,
+ /* 11 */ StereoMethod.Evaluate)]
+ [Browsable(false)]
+ public StereoMethod StereoMethod { get; set; }
+
+ [DefaultValue(PredictionType.None)]
+ [DefaultValueForMode(
+ /* 0 */ PredictionType.Fixed,
+ /* 1 */ PredictionType.Fixed,
+ /* 2 */ PredictionType.Levinson,
+ /* 3 */ PredictionType.Levinson,
+ /* 4 */ PredictionType.Search,
+ /* 5 */ PredictionType.Search,
+ /* 6 */ PredictionType.Search,
+ /* 7 */ PredictionType.Search,
+ /* 8 */ PredictionType.Search,
+ /* 9 */ PredictionType.Levinson,
+ /* 10 */ PredictionType.Search,
+ /* 11 */ PredictionType.Search)]
+ [Browsable(false)]
+ public PredictionType PredictionType { get; set; }
+
+ [DefaultValue(WindowMethod.Invalid)]
+ [DefaultValueForMode(
+ /* 0 */ WindowMethod.Invalid,
+ /* 1 */ WindowMethod.Invalid,
+ /* 2 */ WindowMethod.Estimate,
+ /* 3 */ WindowMethod.Estimate,
+ /* 4 */ WindowMethod.Estimate,
+ /* 5 */ WindowMethod.EvaluateN,
+ /* 6 */ WindowMethod.EvaluateN,
+ /* 7 */ WindowMethod.EvaluateN,
+ /* 8 */ WindowMethod.EvaluateN,
+ /* 9 */ WindowMethod.EvaluateN,
+ /* 10 */ WindowMethod.EvaluateN,
+ /* 11 */ WindowMethod.EvaluateN)]
+ [Browsable(false)]
+ public WindowMethod WindowMethod { get; set; }
+
+ [DefaultValue(WindowFunction.None)]
+ [DefaultValueForMode(
+ /* 0 */ WindowFunction.None,
+ /* 1 */ WindowFunction.None,
+ /* 2 */ WindowFunction.Tukey3,
+ /* 3 */ WindowFunction.Tukey4,
+ /* 4 */ WindowFunction.Tukey4,
+ /* 5 */ WindowFunction.Tukey4 | WindowFunction.Tukey3,
+ /* 6 */ WindowFunction.Tukey4 | WindowFunction.Tukey3 | WindowFunction.Tukey,
+ /* 7 */ WindowFunction.Tukey4 | WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey,
+ /* 8 */ WindowFunction.Tukey4 | WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey,
+ /* 9 */ WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey,
+ /* 10 */ WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey,
+ /* 11 */ WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey)]
+ [Browsable(false)]
+ [DisplayName("WindowFunctions")]
+ [SRDescription(typeof(Properties.Resources), "WindowFunctionsDescription")]
+ public WindowFunction WindowFunctions { get; set; }
+
+ [DefaultValue(0)]
+ [DefaultValueForMode(0, 0, 1, 1, 1, 1, 1, 1, 3, 1, 1, 5)]
+ [Browsable(false)]
+ public int EstimationDepth { get; set; }
+
+ [DefaultValue(-1)]
+ [DefaultValueForMode(1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1)]
+ [Browsable(false)]
+ public int MinPrecisionSearch { get; set; }
+
+ [DefaultValue(-1)]
+ [DefaultValueForMode(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)]
+ [Browsable(false)]
+ public int MaxPrecisionSearch { get; set; }
+
+ [DefaultValue(0)]
+ [Browsable(false)]
+ public int TukeyParts { get; set; }
+
+ [DefaultValue(1.0)]
+ [Browsable(false)]
+ public double TukeyOverlap { get; set; }
+
+ [DefaultValue(1.0)]
+ [Browsable(false)]
+ public double TukeyP { get; set; }
+
+ [Browsable(false)]
+ public string[] Tags { get; set; }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/FlacFrame.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/FlacFrame.cs
new file mode 100644
index 000000000..9397fdd99
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/FlacFrame.cs
@@ -0,0 +1,96 @@
+namespace CUETools.Codecs.Flake
+{
+ unsafe public class FlacFrame
+ {
+ public int blocksize;
+ public int bs_code0, bs_code1;
+ public ChannelMode ch_mode;
+ //public int ch_order0, ch_order1;
+ public byte crc8;
+ public FlacSubframeInfo[] subframes;
+ public int frame_number;
+ public FlacSubframe current;
+ public float* window_buffer;
+ public int nSeg = 0;
+
+ public BitWriter writer = null;
+ public int writer_offset = 0;
+
+ public FlacFrame(int subframes_count)
+ {
+ subframes = new FlacSubframeInfo[subframes_count];
+ for (int ch = 0; ch < subframes_count; ch++)
+ subframes[ch] = new FlacSubframeInfo();
+ current = new FlacSubframe();
+ }
+
+ public void InitSize(int bs, bool vbs)
+ {
+ blocksize = bs;
+ int i = 15;
+ if (!vbs)
+ {
+ for (i = 0; i < 15; i++)
+ {
+ if (bs == FlakeConstants.flac_blocksizes[i])
+ {
+ bs_code0 = i;
+ bs_code1 = -1;
+ break;
+ }
+ }
+ }
+ if (i == 15)
+ {
+ if (blocksize <= 256)
+ {
+ bs_code0 = 6;
+ bs_code1 = blocksize - 1;
+ }
+ else
+ {
+ bs_code0 = 7;
+ bs_code1 = blocksize - 1;
+ }
+ }
+ }
+
+ public void ChooseBestSubframe(int ch)
+ {
+ if (current.size >= subframes[ch].best.size)
+ return;
+ FlacSubframe tmp = subframes[ch].best;
+ subframes[ch].best = current;
+ current = tmp;
+ }
+
+ public void SwapSubframes(int ch1, int ch2)
+ {
+ FlacSubframeInfo 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()
+ {
+ switch (ch_mode)
+ {
+ case ChannelMode.MidSide:
+ SwapSubframes(0, 2);
+ SwapSubframes(1, 3);
+ break;
+ case ChannelMode.RightSide:
+ SwapSubframes(0, 3);
+ break;
+ case ChannelMode.LeftSide:
+ SwapSubframes(1, 3);
+ break;
+ }
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/FlacSubframe.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/FlacSubframe.cs
new file mode 100644
index 000000000..4b69fd758
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/FlacSubframe.cs
@@ -0,0 +1,21 @@
+namespace CUETools.Codecs.Flake
+{
+ unsafe public class FlacSubframe
+ {
+ public FlacSubframe()
+ {
+ rc = new RiceContext();
+ coefs = new int[lpc.MAX_LPC_ORDER];
+ }
+ public SubframeType type;
+ public int order;
+ public int* residual;
+ public RiceContext rc;
+ public uint size;
+
+ public int cbits;
+ public int shift;
+ public int[] coefs;
+ public int window;
+ };
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/FlacSubframeInfo.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/FlacSubframeInfo.cs
new file mode 100644
index 000000000..4f6bd26de
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/FlacSubframeInfo.cs
@@ -0,0 +1,45 @@
+using System;
+
+namespace CUETools.Codecs.Flake
+{
+ unsafe public class FlacSubframeInfo
+ {
+ public FlacSubframeInfo()
+ {
+ best = new FlacSubframe();
+ sf = new LpcSubframeInfo();
+ best_fixed = new ulong[5];
+ 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, int bps, int w)
+ {
+ if (w > bps)
+ throw new Exception("internal error");
+ samples = s;
+ obits = bps - w;
+ wbits = w;
+ for (int o = 0; o <= 4; o++)
+ best_fixed[o] = 0;
+ best.residual = r;
+ best.type = SubframeType.Verbatim;
+ best.size = AudioSamples.UINT32_MAX;
+ sf.Reset();
+ for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++)
+ lpc_ctx[iWindow].Reset();
+ //sf.obits = obits;
+ done_fixed = 0;
+ }
+
+ public FlacSubframe best;
+ public int obits;
+ public int wbits;
+ public int* samples;
+ public uint done_fixed;
+ public ulong[] best_fixed;
+ public LpcContext[] lpc_ctx;
+ public LpcSubframeInfo sf;
+ };
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/Flake.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/Flake.cs
new file mode 100644
index 000000000..b4a33d9d2
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/Flake.cs
@@ -0,0 +1,74 @@
+/**
+ * CUETools.Flake: pure managed FLAC audio encoder
+ * Copyright (c) 2009-2021 Grigory 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
+ * 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;
+
+namespace CUETools.Codecs.Flake
+{
+ public class FlakeConstants
+ {
+ 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 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 */
+
+ public static readonly int[] flac_samplerates = new int[16] {
+ 0, 88200, 176400, 192000,
+ 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000,
+ 0, 0, 0, 0
+ };
+ //1100 : get 8 bit sample rate (in kHz) from end of header
+ //1101 : get 16 bit sample rate (in Hz) from end of header
+ //1110 : get 16 bit sample rate (in tens of Hz) from end of header
+ public static readonly int[] flac_blocksizes = new int[15] { 0, 192, 576, 1152, 2304, 4608, 0, 0, 256, 512, 1024, 2048, 4096, 8192, 16384 };
+ //0110 : get 8 bit (blocksize-1) from end of header
+ //0111 : get 16 bit (blocksize-1) from end of header
+ public static readonly int[] flac_bitdepths = new int[8] { 0, 8, 12, 0, 16, 20, 24, 0 };
+
+ public static PredictionType LookupPredictionType(string name)
+ {
+ return (PredictionType)(Enum.Parse(typeof(PredictionType), name, true));
+ }
+
+ public static StereoMethod LookupStereoMethod(string name)
+ {
+ return (StereoMethod)(Enum.Parse(typeof(StereoMethod), name, true));
+ }
+
+ public static WindowMethod LookupWindowMethod(string name)
+ {
+ return (WindowMethod)(Enum.Parse(typeof(WindowMethod), 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));
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/MetadataType.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/MetadataType.cs
new file mode 100644
index 000000000..8f7f7055d
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/MetadataType.cs
@@ -0,0 +1,45 @@
+namespace CUETools.Codecs.Flake
+{
+ public enum MetadataType
+ {
+ ///
+ /// STREAMINFO block
+ ///
+ StreamInfo = 0,
+
+ ///
+ /// PADDING block
+ ///
+ Padding = 1,
+
+ ///
+ /// APPLICATION block
+ ///
+ Application = 2,
+
+ ///
+ /// SEEKTABLE block
+ ///
+ Seektable = 3,
+
+ ///
+ /// VORBISCOMMENT block (a.k.a. FLAC tags)
+ ///
+ VorbisComment = 4,
+
+ ///
+ /// CUESHEET block
+ ///
+ CUESheet = 5,
+
+ ///
+ /// PICTURE block
+ ///
+ Picture = 6,
+
+ ///
+ /// marker to denote beginning of undefined type range; this number will increase as new metadata types are added
+ ///
+ Undefined = 7
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/OrderMethod.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/OrderMethod.cs
new file mode 100644
index 000000000..25f38ba49
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/OrderMethod.cs
@@ -0,0 +1,10 @@
+namespace CUETools.Codecs.Flake
+{
+ public enum OrderMethod
+ {
+ ///
+ /// Select orders based on Akaike's criteria
+ ///
+ Akaike = 0
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/PredictionType.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/PredictionType.cs
new file mode 100644
index 000000000..d41ef8257
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/PredictionType.cs
@@ -0,0 +1,25 @@
+namespace CUETools.Codecs.Flake
+{
+ ///
+ /// Type of linear prediction
+ ///
+ public enum PredictionType
+ {
+ ///
+ /// Verbatim
+ ///
+ None = 0,
+ ///
+ /// Fixed prediction only
+ ///
+ Fixed = 1,
+ ///
+ /// Levinson-Durbin recursion
+ ///
+ Levinson = 2,
+ ///
+ /// Exhaustive search
+ ///
+ Search = 3
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/Properties/Resources.Designer.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..dab1dc995
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/Properties/Resources.Designer.cs
@@ -0,0 +1,108 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.18033
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace CUETools.Codecs.Flake.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CUETools.Codecs.Flake.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Allow non-subset modes, which allow for greater compression, but are less compatible.
+ ///
+ internal static string AllowNonSubsetDescription {
+ get {
+ return ResourceManager.GetString("AllowNonSubsetDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Calculate MD5 hash for audio stream.
+ ///
+ internal static string DoMD5Description {
+ get {
+ return ResourceManager.GetString("DoMD5Description", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Decode each frame and compare with original.
+ ///
+ internal static string DoVerifyDescription {
+ get {
+ return ResourceManager.GetString("DoVerifyDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Samples written differs from the expected sample count.
+ ///
+ internal static string ExceptionSampleCount {
+ get {
+ return ResourceManager.GetString("ExceptionSampleCount", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Validation failed.
+ ///
+ internal static string ExceptionValidationFailed {
+ get {
+ return ResourceManager.GetString("ExceptionValidationFailed", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/Properties/Resources.de-DE.resx b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/Properties/Resources.de-DE.resx
new file mode 100644
index 000000000..40b3cd6dd
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/Properties/Resources.de-DE.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Non-subset Modi erlauben, die eine höhere Kompression ermöglichen, allerdings weniger kompatibel sind
+
+
+ MD5-Prüfsumme für Audiodaten berechnen
+
+
+ Jeden Frame dekodieren und mit dem Original vergleichen
+
+
+ Die geschriebenen Samples unterscheiden sich von der erwarteten Anzahl
+
+
+ Validierung fehlgeschlagen
+
+
\ No newline at end of file
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/Properties/Resources.resx b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/Properties/Resources.resx
new file mode 100644
index 000000000..1fdb388fc
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/Properties/Resources.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Allow non-subset modes, which allow for greater compression, but are less compatible
+
+
+ Calculate MD5 hash for audio stream
+
+
+ Decode each frame and compare with original
+
+
+ Samples written differs from the expected sample count
+
+
+ Validation failed
+
+
\ No newline at end of file
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/Properties/Resources.ru-RU.resx b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/Properties/Resources.ru-RU.resx
new file mode 100644
index 000000000..cbe431646
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/Properties/Resources.ru-RU.resx
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Вычислять MD5-хеш аудиопотока
+
+
+ Декодировать каждый кадр и сравнивать с оригиналом
+
+
+ Количество записанных сэмплов отличается от ожидаемого
+
+
+ Ошибка верификации
+
+
+ Разрешить non-subset режимы допускающие большее сжатие,
+но меньшую совместимость с декодерами
+
+
\ No newline at end of file
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/RiceContext.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/RiceContext.cs
new file mode 100644
index 000000000..c00aafbdc
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/RiceContext.cs
@@ -0,0 +1,30 @@
+namespace CUETools.Codecs.Flake
+{
+ unsafe public class RiceContext
+ {
+ public RiceContext()
+ {
+ rparams = new int[FlakeConstants.MAX_PARTITIONS];
+ esc_bps = new int[FlakeConstants.MAX_PARTITIONS];
+ }
+ ///
+ /// partition order
+ ///
+ public int porder;
+
+ ///
+ /// coding method: rice parameters use 4 bits for coding_method 0 and 5 bits for coding_method 1
+ ///
+ public int coding_method;
+
+ ///
+ /// Rice parameters
+ ///
+ public int[] rparams;
+
+ ///
+ /// bps if using escape code
+ ///
+ public int[] esc_bps;
+ };
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/SeekPoint.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/SeekPoint.cs
new file mode 100644
index 000000000..f17bd7429
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/SeekPoint.cs
@@ -0,0 +1,9 @@
+namespace CUETools.Codecs.Flake
+{
+ public struct SeekPoint
+ {
+ public long number;
+ public long offset;
+ public int framesize;
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/StereoMethod.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/StereoMethod.cs
new file mode 100644
index 000000000..ddcc61d10
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/StereoMethod.cs
@@ -0,0 +1,15 @@
+
+namespace CUETools.Codecs.Flake
+{
+ public enum StereoMethod
+ {
+ Invalid,
+ Independent,
+ Estimate,
+ Evaluate,
+ Search,
+ EstimateX,
+ EvaluateX,
+ EstimateFixed,
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/SubframeType.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/SubframeType.cs
new file mode 100644
index 000000000..3c5cadda5
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/SubframeType.cs
@@ -0,0 +1,10 @@
+namespace CUETools.Codecs.Flake
+{
+ public enum SubframeType
+ {
+ Constant = 0,
+ Verbatim = 1,
+ Fixed = 8,
+ LPC = 32
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/WindowFunction.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/WindowFunction.cs
new file mode 100644
index 000000000..9723b56d4
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/WindowFunction.cs
@@ -0,0 +1,27 @@
+namespace CUETools.Codecs.Flake
+{
+ public enum WindowFunction
+ {
+ None = 0,
+ Welch = 1,
+ Tukey = 2,
+ Hann = 4,
+ Flattop = 8,
+ Bartlett = 16,
+ Tukey2 = 32,
+ Tukey3 = 64,
+ Tukey4 = (1 << 7),
+ Tukey2A = (1 << 9),
+ Tukey2B = (1 << 10),
+ Tukey3A = (1 << 11),
+ Tukey3B = (1 << 12),
+ Tukey4A = (1 << 13),
+ Tukey4B = (1 << 14),
+ Tukey1A = (1 << 15),
+ Tukey1B = (1 << 16),
+ Tukey1X = (1 << 17),
+ Tukey2X = (1 << 18),
+ Tukey3X = (1 << 19),
+ Tukey4X = (1 << 20),
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/WindowMethod.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/WindowMethod.cs
new file mode 100644
index 000000000..de4cc239a
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs.Flake/WindowMethod.cs
@@ -0,0 +1,18 @@
+namespace CUETools.Codecs.Flake
+{
+ public enum WindowMethod
+ {
+ Invalid,
+ Evaluate,
+ Search,
+ Estimate,
+ Estimate2,
+ Estimate3,
+ EstimateN,
+ Evaluate2,
+ Evaluate2N,
+ Evaluate3,
+ Evaluate3N,
+ EvaluateN,
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioBuffer.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioBuffer.cs
new file mode 100644
index 000000000..88fdce6b6
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioBuffer.cs
@@ -0,0 +1,484 @@
+using System;
+
+namespace CUETools.Codecs
+{
+ public class AudioBuffer
+ {
+ #region Static Methods
+
+ public static unsafe void FLACSamplesToBytes_16(int[,] inSamples, int inSampleOffset,
+ byte* outSamples, int sampleCount, int channelCount)
+ {
+ int loopCount = sampleCount * channelCount;
+
+ if (inSamples.GetLength(0) - inSampleOffset < sampleCount)
+ throw new IndexOutOfRangeException();
+
+ fixed (int* pInSamplesFixed = &inSamples[inSampleOffset, 0])
+ {
+ int* pInSamples = pInSamplesFixed;
+ short* pOutSamples = (short*)outSamples;
+ for (int i = 0; i < loopCount; i++)
+ pOutSamples[i] = (short)pInSamples[i];
+ //*(pOutSamples++) = (short)*(pInSamples++);
+ }
+ }
+
+ public static unsafe void FLACSamplesToBytes_16(int[,] inSamples, int inSampleOffset,
+ byte[] outSamples, int outByteOffset, int sampleCount, int channelCount)
+ {
+ int loopCount = sampleCount * channelCount;
+
+ if ((inSamples.GetLength(0) - inSampleOffset < sampleCount) ||
+ (outSamples.Length - outByteOffset < loopCount * 2))
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ fixed (byte* pOutSamplesFixed = &outSamples[outByteOffset])
+ FLACSamplesToBytes_16(inSamples, inSampleOffset, pOutSamplesFixed, sampleCount, channelCount);
+ }
+
+ public static unsafe void FLACSamplesToBytes_24(int[,] inSamples, int inSampleOffset,
+ byte[] outSamples, int outByteOffset, int sampleCount, int channelCount, int wastedBits)
+ {
+ int loopCount = sampleCount * channelCount;
+
+ if ((inSamples.GetLength(0) - inSampleOffset < sampleCount) ||
+ (outSamples.Length - outByteOffset < loopCount * 3))
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ fixed (int* pInSamplesFixed = &inSamples[inSampleOffset, 0])
+ {
+ fixed (byte* pOutSamplesFixed = &outSamples[outByteOffset])
+ {
+ int* pInSamples = pInSamplesFixed;
+ byte* pOutSamples = pOutSamplesFixed;
+
+ for (int i = 0; i < loopCount; i++)
+ {
+ uint sample_out = (uint)*(pInSamples++) << wastedBits;
+ *(pOutSamples++) = (byte)(sample_out & 0xFF);
+ sample_out >>= 8;
+ *(pOutSamples++) = (byte)(sample_out & 0xFF);
+ sample_out >>= 8;
+ *(pOutSamples++) = (byte)(sample_out & 0xFF);
+ }
+ }
+ }
+ }
+
+ public static unsafe void FloatToBytes_16(float[,] inSamples, int inSampleOffset,
+ byte[] outSamples, int outByteOffset, int sampleCount, int channelCount)
+ {
+ int loopCount = sampleCount * channelCount;
+
+ if ((inSamples.GetLength(0) - inSampleOffset < sampleCount) ||
+ (outSamples.Length - outByteOffset < loopCount * 2))
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ fixed (float* pInSamplesFixed = &inSamples[inSampleOffset, 0])
+ {
+ fixed (byte* pOutSamplesFixed = &outSamples[outByteOffset])
+ {
+ float* pInSamples = pInSamplesFixed;
+ short* pOutSamples = (short*)pOutSamplesFixed;
+
+ for (int i = 0; i < loopCount; i++)
+ {
+ *(pOutSamples++) = (short)(32758 * (*(pInSamples++)));
+ }
+ }
+ }
+ }
+
+ public static unsafe void FloatToBytes(float[,] inSamples, int inSampleOffset,
+ byte[] outSamples, int outByteOffset, int sampleCount, int channelCount, int bitsPerSample)
+ {
+ if (bitsPerSample == 16)
+ FloatToBytes_16(inSamples, inSampleOffset, outSamples, outByteOffset, sampleCount, channelCount);
+ //else if (bitsPerSample > 16 && bitsPerSample <= 24)
+ // FLACSamplesToBytes_24(inSamples, inSampleOffset, outSamples, outByteOffset, sampleCount, channelCount, 24 - bitsPerSample);
+ else if (bitsPerSample == 32)
+ Buffer.BlockCopy(inSamples, inSampleOffset * 4 * channelCount, outSamples, outByteOffset, sampleCount * 4 * channelCount);
+ else
+ throw new Exception("Unsupported bitsPerSample value");
+ }
+
+ public static unsafe void FLACSamplesToBytes(int[,] inSamples, int inSampleOffset,
+ byte[] outSamples, int outByteOffset, int sampleCount, int channelCount, int bitsPerSample)
+ {
+ if (bitsPerSample == 16)
+ FLACSamplesToBytes_16(inSamples, inSampleOffset, outSamples, outByteOffset, sampleCount, channelCount);
+ else if (bitsPerSample > 16 && bitsPerSample <= 24)
+ FLACSamplesToBytes_24(inSamples, inSampleOffset, outSamples, outByteOffset, sampleCount, channelCount, 24 - bitsPerSample);
+ else
+ throw new Exception("Unsupported bitsPerSample value");
+ }
+
+ public static unsafe void FLACSamplesToBytes(int[,] inSamples, int inSampleOffset,
+ byte* outSamples, int sampleCount, int channelCount, int bitsPerSample)
+ {
+ if (bitsPerSample == 16)
+ FLACSamplesToBytes_16(inSamples, inSampleOffset, outSamples, sampleCount, channelCount);
+ else
+ throw new Exception("Unsupported bitsPerSample value");
+ }
+
+ public static unsafe void Bytes16ToFloat(byte[] inSamples, int inByteOffset,
+ float[,] outSamples, int outSampleOffset, int sampleCount, int channelCount)
+ {
+ int loopCount = sampleCount * channelCount;
+
+ if ((inSamples.Length - inByteOffset < loopCount * 2) ||
+ (outSamples.GetLength(0) - outSampleOffset < sampleCount))
+ throw new IndexOutOfRangeException();
+
+ fixed (byte* pInSamplesFixed = &inSamples[inByteOffset])
+ {
+ fixed (float* pOutSamplesFixed = &outSamples[outSampleOffset, 0])
+ {
+ short* pInSamples = (short*)pInSamplesFixed;
+ float* pOutSamples = pOutSamplesFixed;
+ for (int i = 0; i < loopCount; i++)
+ *(pOutSamples++) = *(pInSamples++) / 32768.0f;
+ }
+ }
+ }
+
+ public static unsafe void BytesToFLACSamples_16(byte[] inSamples, int inByteOffset,
+ int[,] outSamples, int outSampleOffset, int sampleCount, int channelCount)
+ {
+ int loopCount = sampleCount * channelCount;
+
+ if ((inSamples.Length - inByteOffset < loopCount * 2) ||
+ (outSamples.GetLength(0) - outSampleOffset < sampleCount))
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ fixed (byte* pInSamplesFixed = &inSamples[inByteOffset])
+ {
+ fixed (int* pOutSamplesFixed = &outSamples[outSampleOffset, 0])
+ {
+ short* pInSamples = (short*)pInSamplesFixed;
+ int* pOutSamples = pOutSamplesFixed;
+
+ for (int i = 0; i < loopCount; i++)
+ {
+ *(pOutSamples++) = (int)*(pInSamples++);
+ }
+ }
+ }
+ }
+
+ public static unsafe void BytesToFLACSamples_24(byte[] inSamples, int inByteOffset,
+ int[,] outSamples, int outSampleOffset, int sampleCount, int channelCount, int wastedBits)
+ {
+ int loopCount = sampleCount * channelCount;
+
+ if ((inSamples.Length - inByteOffset < loopCount * 3) ||
+ (outSamples.GetLength(0) - outSampleOffset < sampleCount))
+ throw new IndexOutOfRangeException();
+
+ fixed (byte* pInSamplesFixed = &inSamples[inByteOffset])
+ {
+ fixed (int* pOutSamplesFixed = &outSamples[outSampleOffset, 0])
+ {
+ byte* pInSamples = (byte*)pInSamplesFixed;
+ int* pOutSamples = pOutSamplesFixed;
+ for (int i = 0; i < loopCount; i++)
+ {
+ int sample = (int)*(pInSamples++);
+ sample += (int)*(pInSamples++) << 8;
+ sample += (int)*(pInSamples++) << 16;
+ *(pOutSamples++) = (sample << 8) >> (8 + wastedBits);
+ }
+ }
+ }
+ }
+
+ public static unsafe void BytesToFLACSamples(byte[] inSamples, int inByteOffset,
+ int[,] outSamples, int outSampleOffset, int sampleCount, int channelCount, int bitsPerSample)
+ {
+ if (bitsPerSample == 16)
+ BytesToFLACSamples_16(inSamples, inByteOffset, outSamples, outSampleOffset, sampleCount, channelCount);
+ else if (bitsPerSample > 16 && bitsPerSample <= 24)
+ BytesToFLACSamples_24(inSamples, inByteOffset, outSamples, outSampleOffset, sampleCount, channelCount, 24 - bitsPerSample);
+ else
+ throw new Exception("Unsupported bitsPerSample value");
+ }
+
+ #endregion
+
+ private int[,] samples;
+ private float[,] fsamples;
+ private byte[] bytes;
+ private int length;
+ private int size;
+ private AudioPCMConfig pcm;
+ private bool dataInSamples = false;
+ private bool dataInBytes = false;
+ private bool dataInFloat = false;
+
+ public int Length
+ {
+ get { return length; }
+ set { length = value; }
+ }
+
+ public int Size
+ {
+ get { return size; }
+ }
+
+ public AudioPCMConfig PCM { get { return pcm; } }
+
+ public int ByteLength
+ {
+ get
+ {
+ return length * pcm.BlockAlign;
+ }
+ }
+
+ public int[,] Samples
+ {
+ get
+ {
+ if (samples == null || samples.GetLength(0) < length)
+ samples = new int[size, pcm.ChannelCount];
+ if (!dataInSamples && dataInBytes && length != 0)
+ BytesToFLACSamples(bytes, 0, samples, 0, length, pcm.ChannelCount, pcm.BitsPerSample);
+ dataInSamples = true;
+ return samples;
+ }
+ }
+
+ public float[,] Float
+ {
+ get
+ {
+ if (fsamples == null || fsamples.GetLength(0) < length)
+ fsamples = new float[size, pcm.ChannelCount];
+ if (!dataInFloat && dataInBytes && length != 0)
+ {
+ if (pcm.BitsPerSample == 16)
+ Bytes16ToFloat(bytes, 0, fsamples, 0, length, pcm.ChannelCount);
+ //else if (pcm.BitsPerSample > 16 && PCM.BitsPerSample <= 24)
+ // BytesToFLACSamples_24(bytes, 0, fsamples, 0, length, pcm.ChannelCount, 24 - pcm.BitsPerSample);
+ else if (pcm.BitsPerSample == 32)
+ Buffer.BlockCopy(bytes, 0, fsamples, 0, length * 4 * pcm.ChannelCount);
+ else
+ throw new Exception("Unsupported bitsPerSample value");
+ }
+ dataInFloat = true;
+ return fsamples;
+ }
+ }
+
+ public byte[] Bytes
+ {
+ get
+ {
+ if (bytes == null || bytes.Length < length * pcm.BlockAlign)
+ bytes = new byte[size * pcm.BlockAlign];
+ if (!dataInBytes && length != 0)
+ {
+ if (dataInSamples)
+ FLACSamplesToBytes(samples, 0, bytes, 0, length, pcm.ChannelCount, pcm.BitsPerSample);
+ else if (dataInFloat)
+ FloatToBytes(fsamples, 0, bytes, 0, length, pcm.ChannelCount, pcm.BitsPerSample);
+ }
+ dataInBytes = true;
+ return bytes;
+ }
+ }
+
+ public AudioBuffer(AudioPCMConfig _pcm, int _size)
+ {
+ pcm = _pcm;
+ size = _size;
+ length = 0;
+ }
+
+ public AudioBuffer(AudioPCMConfig _pcm, int[,] _samples, int _length)
+ {
+ pcm = _pcm;
+ // assert _samples.GetLength(1) == pcm.ChannelCount
+ Prepare(_samples, _length);
+ }
+
+ public AudioBuffer(AudioPCMConfig _pcm, byte[] _bytes, int _length)
+ {
+ pcm = _pcm;
+ Prepare(_bytes, _length);
+ }
+
+ public AudioBuffer(IAudioSource source, int _size)
+ {
+ pcm = source.PCM;
+ size = _size;
+ }
+
+ public void Prepare(IAudioDest dest)
+ {
+ if (dest.Settings.PCM.ChannelCount != pcm.ChannelCount || dest.Settings.PCM.BitsPerSample != pcm.BitsPerSample)
+ throw new Exception("AudioBuffer format mismatch");
+ }
+
+ public void Prepare(IAudioSource source, int maxLength)
+ {
+ if (source.PCM.ChannelCount != pcm.ChannelCount || source.PCM.BitsPerSample != pcm.BitsPerSample)
+ throw new Exception("AudioBuffer format mismatch");
+ length = size;
+ if (maxLength >= 0)
+ length = Math.Min(length, maxLength);
+ if (source.Remaining >= 0)
+ length = (int)Math.Min((long)length, source.Remaining);
+ dataInBytes = false;
+ dataInSamples = false;
+ dataInFloat = false;
+ }
+
+ public void Prepare(int maxLength)
+ {
+ length = size;
+ if (maxLength >= 0)
+ length = Math.Min(length, maxLength);
+ dataInBytes = false;
+ dataInSamples = false;
+ dataInFloat = false;
+ }
+
+ public void Prepare(int[,] _samples, int _length)
+ {
+ length = _length;
+ size = _samples.GetLength(0);
+ samples = _samples;
+ dataInSamples = true;
+ dataInBytes = false;
+ dataInFloat = false;
+ if (length > size)
+ throw new Exception("Invalid length");
+ }
+
+ public void Prepare(byte[] _bytes, int _length)
+ {
+ length = _length;
+ size = _bytes.Length / PCM.BlockAlign;
+ bytes = _bytes;
+ dataInSamples = false;
+ dataInBytes = true;
+ dataInFloat = false;
+ if (length > size)
+ throw new Exception("Invalid length");
+ }
+
+ internal unsafe void Load(int dstOffset, AudioBuffer src, int srcOffset, int copyLength)
+ {
+ if (dataInBytes)
+ Buffer.BlockCopy(src.Bytes, srcOffset * pcm.BlockAlign, Bytes, dstOffset * pcm.BlockAlign, copyLength * pcm.BlockAlign);
+ if (dataInSamples)
+ Buffer.BlockCopy(src.Samples, srcOffset * pcm.ChannelCount * 4, Samples, dstOffset * pcm.ChannelCount * 4, copyLength * pcm.ChannelCount * 4);
+ if (dataInFloat)
+ Buffer.BlockCopy(src.Float, srcOffset * pcm.ChannelCount * 4, Float, dstOffset * pcm.ChannelCount * 4, copyLength * pcm.ChannelCount * 4);
+ }
+
+ public unsafe void Prepare(AudioBuffer _src, int _offset, int _length)
+ {
+ length = Math.Min(size, _src.Length - _offset);
+ if (_length >= 0)
+ length = Math.Min(length, _length);
+ dataInBytes = false;
+ dataInFloat = false;
+ dataInSamples = false;
+ if (_src.dataInBytes)
+ dataInBytes = true;
+ else if (_src.dataInSamples)
+ dataInSamples = true;
+ else if (_src.dataInFloat)
+ dataInFloat = true;
+ Load(0, _src, _offset, length);
+ }
+
+ public void Swap(AudioBuffer buffer)
+ {
+ if (pcm.BitsPerSample != buffer.PCM.BitsPerSample || pcm.ChannelCount != buffer.PCM.ChannelCount)
+ throw new Exception("AudioBuffer format mismatch");
+
+ int[,] samplesTmp = samples;
+ float[,] floatsTmp = fsamples;
+ byte[] bytesTmp = bytes;
+
+ fsamples = buffer.fsamples;
+ samples = buffer.samples;
+ bytes = buffer.bytes;
+ length = buffer.length;
+ size = buffer.size;
+ dataInSamples = buffer.dataInSamples;
+ dataInBytes = buffer.dataInBytes;
+ dataInFloat = buffer.dataInFloat;
+
+ buffer.samples = samplesTmp;
+ buffer.bytes = bytesTmp;
+ buffer.fsamples = floatsTmp;
+ buffer.length = 0;
+ buffer.dataInSamples = false;
+ buffer.dataInBytes = false;
+ buffer.dataInFloat = false;
+ }
+
+ unsafe public void Interlace(int pos, int* src1, int* src2, int n)
+ {
+ if (PCM.ChannelCount != 2)
+ {
+ throw new Exception("Must be stereo");
+ }
+ if (PCM.BitsPerSample == 16)
+ {
+ fixed (byte* bs = Bytes)
+ {
+ int* res = ((int*)bs) + pos;
+ for (int i = n; i > 0; i--)
+ *(res++) = (*(src1++) & 0xffff) ^ (*(src2++) << 16);
+ }
+ }
+ else if (PCM.BitsPerSample == 24)
+ {
+ fixed (byte* bs = Bytes)
+ {
+ byte* res = bs + pos * 6;
+ for (int i = n; i > 0; i--)
+ {
+ uint sample_out = (uint)*(src1++);
+ *(res++) = (byte)(sample_out & 0xFF);
+ sample_out >>= 8;
+ *(res++) = (byte)(sample_out & 0xFF);
+ sample_out >>= 8;
+ *(res++) = (byte)(sample_out & 0xFF);
+ sample_out = (uint)*(src2++);
+ *(res++) = (byte)(sample_out & 0xFF);
+ sample_out >>= 8;
+ *(res++) = (byte)(sample_out & 0xFF);
+ sample_out >>= 8;
+ *(res++) = (byte)(sample_out & 0xFF);
+ }
+ }
+ }
+ else
+ {
+ throw new Exception("Unsupported BPS");
+ }
+ }
+
+ //public void Clear()
+ //{
+ // length = 0;
+ //}
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioDecoderSettings.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioDecoderSettings.cs
new file mode 100644
index 000000000..52a8234ce
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioDecoderSettings.cs
@@ -0,0 +1,54 @@
+using System;
+using System.ComponentModel;
+using System.Collections.Generic;
+using System.Xml.Serialization;
+using System.Text;
+using Newtonsoft.Json;
+using System.IO;
+
+namespace CUETools.Codecs
+{
+ public interface IAudioDecoderSettings
+ {
+ string Name { get; }
+
+ string Extension { get; }
+
+ Type DecoderType { get; }
+
+ int Priority { get; }
+
+ IAudioDecoderSettings Clone();
+ }
+
+ public static class IAudioDecoderSettingsExtensions
+ {
+ public static bool HasBrowsableAttributes(this IAudioDecoderSettings settings)
+ {
+ bool hasBrowsable = false;
+ foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(settings))
+ {
+ bool isBrowsable = true;
+ foreach (var attribute in property.Attributes)
+ {
+ var browsable = attribute as BrowsableAttribute;
+ isBrowsable &= browsable == null || browsable.Browsable;
+ }
+ hasBrowsable |= isBrowsable;
+ }
+ return hasBrowsable;
+ }
+
+ public static void Init(this IAudioDecoderSettings settings)
+ {
+ // Iterate through each property and call ResetValue()
+ foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(settings))
+ property.ResetValue(settings);
+ }
+
+ public static IAudioSource Open(this IAudioDecoderSettings settings, string path, Stream IO = null)
+ {
+ return Activator.CreateInstance(settings.DecoderType, settings, path, IO) as IAudioSource;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioEncoderSettings.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioEncoderSettings.cs
new file mode 100644
index 000000000..7d01b2f07
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioEncoderSettings.cs
@@ -0,0 +1,124 @@
+using System;
+using System.ComponentModel;
+using System.Collections.Generic;
+using System.Text;
+using Newtonsoft.Json;
+
+#if NET20
+ namespace System.Runtime.CompilerServices
+ {
+ [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class
+ | AttributeTargets.Method)]
+ public sealed class ExtensionAttribute : Attribute { }
+ }
+#endif
+
+namespace CUETools.Codecs
+{
+ public interface IAudioEncoderSettings
+ {
+ string Name { get; }
+
+ string Extension { get; }
+
+ Type EncoderType { get; }
+
+ bool Lossless { get; }
+
+ int Priority { get; }
+
+ string SupportedModes { get; }
+
+ string DefaultMode { get; }
+
+ string EncoderMode { get; set; }
+
+ AudioPCMConfig PCM { get; set; }
+
+ int BlockSize { get; set; }
+
+ int Padding { get; set; }
+
+ IAudioEncoderSettings Clone();
+ }
+
+ public static class IAudioEncoderSettingsExtensions
+ {
+ public static bool HasBrowsableAttributes(this IAudioEncoderSettings settings)
+ {
+ bool hasBrowsable = false;
+ foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(settings))
+ {
+ bool isBrowsable = true;
+ foreach (var attribute in property.Attributes)
+ {
+ var browsable = attribute as BrowsableAttribute;
+ isBrowsable &= browsable == null || browsable.Browsable;
+ }
+ hasBrowsable |= isBrowsable;
+ }
+ return hasBrowsable;
+ }
+
+ public static int GetEncoderModeIndex(this IAudioEncoderSettings settings)
+ {
+ return new List(settings.SupportedModes.Split(' ')).FindIndex(m => m == settings.EncoderMode);
+ }
+
+ public static void SetEncoderModeIndex(this IAudioEncoderSettings settings, int value)
+ {
+ string[] modes = settings.SupportedModes.Split(' ');
+ if (modes.Length == 0 && value < 0)
+ return;
+ if (value < 0 || value >= modes.Length)
+ throw new IndexOutOfRangeException();
+ settings.EncoderMode = modes[value];
+ }
+
+ public static void SetDefaultValuesForMode(this IAudioEncoderSettings settings)
+ {
+ foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(settings))
+ if (!property.CanResetValue(settings))
+ foreach (var attribute in property.Attributes)
+ if (attribute is DefaultValueForModeAttribute)
+ {
+ var defaultValueForMode = attribute as DefaultValueForModeAttribute;
+ property.SetValue(settings, defaultValueForMode.m_values[settings.GetEncoderModeIndex()]);
+ }
+ }
+
+ public static bool HasDefaultValuesForMode(this IAudioEncoderSettings settings, int index)
+ {
+ bool res = true;
+ foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(settings))
+ foreach (var attribute in property.Attributes)
+ if (attribute is DefaultValueForModeAttribute)
+ {
+ var defaultValueForMode = attribute as DefaultValueForModeAttribute;
+ res &= property.GetValue(settings).Equals(defaultValueForMode.m_values[index]);
+ }
+ return res;
+ }
+
+ public static int GuessEncoderMode(this IAudioEncoderSettings settings)
+ {
+ // return new List(settings.SupportedModes.Split(' ')).FindIndex(m => settings.HasDefaultValuesForMode(m));
+ string[] modes = settings.SupportedModes.Split(' ');
+ if (modes == null || modes.Length < 1)
+ return -1;
+ for (int i = 0; i < modes.Length; i++)
+ if (settings.HasDefaultValuesForMode(i))
+ return i;
+ return -1;
+ }
+
+ public static void Init(this IAudioEncoderSettings settings, AudioPCMConfig pcm = null)
+ {
+ // Iterate through each property and call ResetValue()
+ foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(settings))
+ property.ResetValue(settings);
+ settings.EncoderMode = settings.DefaultMode;
+ settings.PCM = pcm;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioPCMConfig.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioPCMConfig.cs
new file mode 100644
index 000000000..d0e2e72cd
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioPCMConfig.cs
@@ -0,0 +1,138 @@
+namespace CUETools.Codecs
+{
+ public class AudioPCMConfig
+ {
+ public static readonly AudioPCMConfig RedBook = new AudioPCMConfig(16, 2, 44100);
+ public enum SpeakerConfig
+ {
+ SPEAKER_FRONT_LEFT = 0x1,
+ SPEAKER_FRONT_RIGHT = 0x2,
+ SPEAKER_FRONT_CENTER = 0x4,
+ SPEAKER_LOW_FREQUENCY = 0x8,
+ SPEAKER_BACK_LEFT = 0x10,
+ SPEAKER_BACK_RIGHT = 0x20,
+ SPEAKER_FRONT_LEFT_OF_CENTER = 0x40,
+ SPEAKER_FRONT_RIGHT_OF_CENTER = 0x80,
+ SPEAKER_BACK_CENTER = 0x100,
+ SPEAKER_SIDE_LEFT = 0x200,
+ SPEAKER_SIDE_RIGHT = 0x400,
+ SPEAKER_TOP_CENTER = 0x800,
+ SPEAKER_TOP_FRONT_LEFT = 0x1000,
+ SPEAKER_TOP_FRONT_CENTER = 0x2000,
+ SPEAKER_TOP_FRONT_RIGHT = 0x4000,
+ SPEAKER_TOP_BACK_LEFT = 0x8000,
+ SPEAKER_TOP_BACK_CENTER = 0x10000,
+ SPEAKER_TOP_BACK_RIGHT = 0x20000,
+
+ DIRECTOUT = 0,
+ KSAUDIO_SPEAKER_MONO = (SPEAKER_FRONT_CENTER),
+ KSAUDIO_SPEAKER_STEREO = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT),
+ KSAUDIO_SPEAKER_QUAD = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT),
+ KSAUDIO_SPEAKER_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER),
+ KSAUDIO_SPEAKER_5POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT),
+ KSAUDIO_SPEAKER_5POINT1_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT),
+ KSAUDIO_SPEAKER_7POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER),
+ KSAUDIO_SPEAKER_7POINT1_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT),
+
+ DVDAUDIO_GR1_0 = SPEAKER_FRONT_CENTER,
+ DVDAUDIO_GR1_1 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
+ DVDAUDIO_GR1_2 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
+ DVDAUDIO_GR1_3 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
+ DVDAUDIO_GR1_4 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
+ DVDAUDIO_GR1_5 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
+ DVDAUDIO_GR1_6 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
+ DVDAUDIO_GR1_7 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
+ DVDAUDIO_GR1_8 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
+ DVDAUDIO_GR1_9 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
+ DVDAUDIO_GR1_10 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
+ DVDAUDIO_GR1_11 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
+ DVDAUDIO_GR1_12 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
+ DVDAUDIO_GR1_13 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
+ DVDAUDIO_GR1_14 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
+ DVDAUDIO_GR1_15 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
+ DVDAUDIO_GR1_16 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
+ DVDAUDIO_GR1_17 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
+ DVDAUDIO_GR1_18 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
+ DVDAUDIO_GR1_19 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
+ DVDAUDIO_GR1_20 = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
+
+ DVDAUDIO_GR2_0 = 0,
+ DVDAUDIO_GR2_1 = 0,
+ DVDAUDIO_GR2_2 = SPEAKER_BACK_CENTER,
+ DVDAUDIO_GR2_3 = SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
+ DVDAUDIO_GR2_4 = SPEAKER_LOW_FREQUENCY,
+ DVDAUDIO_GR2_5 = SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_CENTER,
+ DVDAUDIO_GR2_6 = SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
+ DVDAUDIO_GR2_7 = SPEAKER_FRONT_CENTER,
+ DVDAUDIO_GR2_8 = SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER,
+ DVDAUDIO_GR2_9 = SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
+ DVDAUDIO_GR2_10 = SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY,
+ DVDAUDIO_GR2_11 = SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_CENTER,
+ DVDAUDIO_GR2_12 = SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
+ DVDAUDIO_GR2_13 = SPEAKER_BACK_CENTER,
+ DVDAUDIO_GR2_14 = SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
+ DVDAUDIO_GR2_15 = SPEAKER_LOW_FREQUENCY,
+ DVDAUDIO_GR2_16 = SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_CENTER,
+ DVDAUDIO_GR2_17 = SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
+ DVDAUDIO_GR2_18 = SPEAKER_LOW_FREQUENCY,
+ DVDAUDIO_GR2_19 = SPEAKER_FRONT_CENTER,
+ DVDAUDIO_GR2_20 = SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY,
+ }
+
+ private int _bitsPerSample;
+ private int _channelCount;
+ private int _sampleRate;
+ private SpeakerConfig _channelMask;
+
+ public int BitsPerSample { get { return _bitsPerSample; } }
+ public int ChannelCount { get { return _channelCount; } }
+ public int SampleRate { get { return _sampleRate; } }
+ public int BlockAlign { get { return _channelCount * ((_bitsPerSample + 7) / 8); } }
+ public SpeakerConfig ChannelMask { get { return _channelMask; } }
+ public bool IsRedBook { get { return _bitsPerSample == 16 && _channelCount == 2 && _sampleRate == 44100; } }
+ public static int ChannelsInMask(SpeakerConfig mask)
+ {
+ int count = 0;
+ while (mask != 0)
+ {
+ count++;
+ mask &= (mask - 1);
+ }
+ return count;
+ }
+
+ public static SpeakerConfig GetDefaultChannelMask(int channelCount)
+ {
+ switch (channelCount)
+ {
+ case 1:
+ return SpeakerConfig.KSAUDIO_SPEAKER_MONO;
+ case 2:
+ return SpeakerConfig.KSAUDIO_SPEAKER_STEREO;
+ case 3:
+ return SpeakerConfig.KSAUDIO_SPEAKER_STEREO | SpeakerConfig.SPEAKER_LOW_FREQUENCY;
+ case 4:
+ return SpeakerConfig.KSAUDIO_SPEAKER_QUAD;
+ case 5:
+ //return SpeakerConfig.KSAUDIO_SPEAKER_5POINT1 & ~SpeakerConfig.SPEAKER_LOW_FREQUENCY;
+ return SpeakerConfig.KSAUDIO_SPEAKER_5POINT1_SURROUND & ~SpeakerConfig.SPEAKER_LOW_FREQUENCY;
+ case 6:
+ //return SpeakerConfig.KSAUDIO_SPEAKER_5POINT1;
+ return SpeakerConfig.KSAUDIO_SPEAKER_5POINT1_SURROUND;
+ case 7:
+ return SpeakerConfig.KSAUDIO_SPEAKER_5POINT1_SURROUND | SpeakerConfig.SPEAKER_BACK_CENTER;
+ case 8:
+ return SpeakerConfig.KSAUDIO_SPEAKER_7POINT1_SURROUND;
+ }
+ return (SpeakerConfig)((1 << channelCount) - 1);
+ }
+
+ public AudioPCMConfig(int bitsPerSample, int channelCount, int sampleRate, SpeakerConfig channelMask = SpeakerConfig.DIRECTOUT)
+ {
+ _bitsPerSample = bitsPerSample;
+ _channelCount = channelCount;
+ _sampleRate = sampleRate;
+ _channelMask = channelMask == 0 ? GetDefaultChannelMask(channelCount) : channelMask;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioPipe.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioPipe.cs
new file mode 100644
index 000000000..5715d8017
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioPipe.cs
@@ -0,0 +1,269 @@
+using System;
+using System.Threading;
+
+namespace CUETools.Codecs
+{
+ public class AudioPipe : IAudioSource//, IDisposable
+ {
+ private AudioBuffer _readBuffer, _writeBuffer;
+ private AudioPCMConfig pcm;
+ private long _sampleLen, _samplePos;
+ private int _maxLength;
+ private Thread _workThread;
+ private IAudioSource _source;
+ private bool _close = false;
+ private bool _haveData = false;
+ private int _bufferPos = 0;
+ private Exception _ex = null;
+ private bool own;
+ private ThreadPriority priority;
+
+ public IAudioDecoderSettings Settings => null;
+
+ public long Position
+ {
+ get
+ {
+ return _samplePos;
+ }
+ set
+ {
+ if (value == _samplePos)
+ return;
+
+ if (_source == null)
+ throw new NotSupportedException();
+
+ lock (this)
+ {
+ _close = true;
+ Monitor.Pulse(this);
+ }
+ if (_workThread != null)
+ {
+ _workThread.Join();
+ _workThread = null;
+ }
+ _source.Position = value;
+ _samplePos = value;
+ _bufferPos = 0;
+ _haveData = false;
+ _close = false;
+ //Go();
+ //throw new Exception("not supported");
+ }
+ }
+
+ public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
+
+ public long Length
+ {
+ get
+ {
+ return _sampleLen;
+ }
+ }
+
+ public long Remaining
+ {
+ get
+ {
+ return _sampleLen - _samplePos;
+ }
+ }
+
+ public AudioPCMConfig PCM
+ {
+ get
+ {
+ return pcm;
+ }
+ }
+
+ public string Path
+ {
+ get
+ {
+ if (_source == null)
+ return "";
+ return _source.Path;
+ }
+ }
+
+ public AudioPipe(AudioPCMConfig pcm, int size)
+ {
+ this.pcm = pcm;
+ _readBuffer = new AudioBuffer(pcm, size);
+ _writeBuffer = new AudioBuffer(pcm, size);
+ _maxLength = size;
+ _sampleLen = -1;
+ _samplePos = 0;
+ }
+
+ public AudioPipe(IAudioSource source, int size, bool own, ThreadPriority priority)
+ : this(source.PCM, size)
+ {
+ this.own = own;
+ this.priority = priority;
+ _source = source;
+ _sampleLen = _source.Length;
+ _samplePos = _source.Position;
+ }
+
+ public AudioPipe(IAudioSource source, int size)
+ : this(source, size, true, ThreadPriority.BelowNormal)
+ {
+ }
+
+ private void Decompress(object o)
+ {
+#if !DEBUG
+ try
+#endif
+ {
+ bool done = false;
+ do
+ {
+ done = _source.Read(_writeBuffer, -1) == 0;
+ lock (this)
+ {
+ while (_haveData && !_close)
+ Monitor.Wait(this);
+ if (_close)
+ break;
+ AudioBuffer temp = _writeBuffer;
+ _writeBuffer = _readBuffer;
+ _readBuffer = temp;
+ _haveData = true;
+ Monitor.Pulse(this);
+ }
+ } while (!done);
+ }
+#if !DEBUG
+ catch (Exception ex)
+ {
+ lock (this)
+ {
+ _ex = ex;
+ Monitor.Pulse(this);
+ }
+ }
+#endif
+ }
+
+ private void Go()
+ {
+ if (_workThread != null || _ex != null || _source == null) return;
+ _workThread = new Thread(Decompress);
+ _workThread.Priority = priority;
+ _workThread.IsBackground = true;
+ _workThread.Name = "AudioPipe";
+ _workThread.Start(null);
+ }
+
+ //public new void Dispose()
+ //{
+ // _buffer.Clear();
+ //}
+
+ public void Close()
+ {
+ lock (this)
+ {
+ _close = true;
+ Monitor.Pulse(this);
+ }
+ if (_workThread != null)
+ {
+ _workThread.Join();
+ _workThread = null;
+ }
+ if (_source != null)
+ {
+ if (own) _source.Close();
+ _source = null;
+ }
+ if (_readBuffer != null)
+ {
+ //_readBuffer.Clear();
+ _readBuffer = null;
+ }
+ if (_writeBuffer != null)
+ {
+ //_writeBuffer.Clear();
+ _writeBuffer = null;
+ }
+ }
+
+ public int Write(AudioBuffer buff)
+ {
+ if (_writeBuffer.Size < _writeBuffer.Length + buff.Length)
+ {
+ AudioBuffer realloced = new AudioBuffer(pcm, _writeBuffer.Size + buff.Size);
+ realloced.Prepare(_writeBuffer, 0, _writeBuffer.Length);
+ _writeBuffer = realloced;
+ }
+ if (_writeBuffer.Length == 0)
+ _writeBuffer.Prepare(buff, 0, buff.Length);
+ else
+ {
+ _writeBuffer.Load(_writeBuffer.Length, buff, 0, buff.Length);
+ _writeBuffer.Length += buff.Length;
+ }
+ lock (this)
+ {
+ if (!_haveData)
+ {
+ AudioBuffer temp = _writeBuffer;
+ _writeBuffer = _readBuffer;
+ _writeBuffer.Length = 0;
+ _readBuffer = temp;
+ _haveData = true;
+ Monitor.Pulse(this);
+ }
+ }
+ return _writeBuffer.Length;
+ }
+
+ public int Read(AudioBuffer buff, int maxLength)
+ {
+ Go();
+
+ bool needToCopy = false;
+ if (_bufferPos != 0)
+ needToCopy = true;
+ else
+ lock (this)
+ {
+ while (!_haveData && _ex == null)
+ Monitor.Wait(this);
+ if (_ex != null)
+ throw _ex;
+ if (_bufferPos == 0 && (maxLength < 0 || _readBuffer.Length <= maxLength))
+ {
+ buff.Swap(_readBuffer);
+ _haveData = false;
+ Monitor.Pulse(this);
+ }
+ else
+ needToCopy = true;
+ }
+ if (needToCopy)
+ {
+ buff.Prepare(_readBuffer, _bufferPos, maxLength);
+ _bufferPos += buff.Length;
+ if (_bufferPos == _readBuffer.Length)
+ {
+ _bufferPos = 0;
+ lock (this)
+ {
+ _haveData = false;
+ Monitor.Pulse(this);
+ }
+ }
+ }
+ _samplePos += buff.Length;
+ return buff.Length;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioSamples.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioSamples.cs
new file mode 100644
index 000000000..a8bf2c54e
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/AudioSamples.cs
@@ -0,0 +1,142 @@
+using System;
+
+namespace CUETools.Codecs
+{
+ public class AudioSamples
+ {
+ public const uint UINT32_MAX = 0xffffffff;
+
+ 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(uint* res, uint* smp, int n)
+ {
+ for (int i = n; i > 0; i--)
+ *(res++) = *(smp++);
+ }
+
+ 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(long* res, long* smp, int n)
+ {
+ for (int i = n; i > 0; i--)
+ *(res++) = *(smp++);
+ }
+
+ unsafe public static void MemCpy(short* res, short* smp, int n)
+ {
+ for (int i = n; i > 0; i--)
+ *(res++) = *(smp++);
+ }
+
+ unsafe public static void MemCpy(byte* res, byte* smp, int n)
+ {
+ if ((((IntPtr)smp).ToInt64() & 7) == (((IntPtr)res).ToInt64() & 7) && n > 32)
+ {
+ int delta = (int)((8 - (((IntPtr)smp).ToInt64() & 7)) & 7);
+ for (int i = delta; i > 0; i--)
+ *(res++) = *(smp++);
+ n -= delta;
+
+ MemCpy((long*)res, (long*)smp, n >> 3);
+ int n8 = (n >> 3) << 3;
+ n -= n8;
+ smp += n8;
+ res += n8;
+ }
+ if ((((IntPtr)smp).ToInt64() & 3) == (((IntPtr)res).ToInt64() & 3) && n > 16)
+ {
+ int delta = (int)((4 - (((IntPtr)smp).ToInt64() & 3)) & 3);
+ for (int i = delta; i > 0; i--)
+ *(res++) = *(smp++);
+ n -= delta;
+
+ MemCpy((int*)res, (int*)smp, n >> 2);
+ int n4 = (n >> 2) << 2;
+ n -= n4;
+ smp += n4;
+ res += n4;
+ }
+ 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 MemSet(long* res, long smp, int n)
+ {
+ for (int i = n; i > 0; i--)
+ *(res++) = smp;
+ }
+
+ unsafe public static void MemSet(byte* res, byte smp, int n)
+ {
+ if (IntPtr.Size == 8 && (((IntPtr)res).ToInt64() & 7) == 0 && smp == 0 && n > 8)
+ {
+ MemSet((long*)res, 0, n >> 3);
+ int n8 = (n >> 3) << 3;
+ n -= n8;
+ res += n8;
+ }
+ if ((((IntPtr)res).ToInt64() & 3) == 0 && smp == 0 && n > 4)
+ {
+ MemSet((int*)res, 0, n >> 2);
+ int n4 = (n >> 2) << 2;
+ n -= n4;
+ res += n4;
+ }
+ for (int i = n; i > 0; i--)
+ *(res++) = smp;
+ }
+
+ unsafe public static void MemSet(byte[] res, byte smp, int offs, int n)
+ {
+ fixed (byte* pres = &res[offs])
+ MemSet(pres, smp, n);
+ }
+
+ unsafe public static void MemSet(int[] res, int smp, int offs, int n)
+ {
+ fixed (int* pres = &res[offs])
+ MemSet(pres, smp, n);
+ }
+
+ unsafe public static void MemSet(long[] res, long smp, int offs, int n)
+ {
+ fixed (long* pres = &res[offs])
+ MemSet(pres, smp, n);
+ }
+ }
+
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/BitReader.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/BitReader.cs
new file mode 100644
index 000000000..d2960607b
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/BitReader.cs
@@ -0,0 +1,335 @@
+using System;
+
+namespace CUETools.Codecs
+{
+ unsafe public class BitReader
+ {
+ #region Static Methods
+
+ public static int log2i(int v)
+ {
+ return log2i((uint)v);
+ }
+
+ public static readonly byte[] MultiplyDeBruijnBitPosition = new byte[32]
+ {
+ 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
+ 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
+ };
+
+ public static int log2i(ulong v)
+ {
+ v |= v >> 1; // first round down to one less than a power of 2
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ if (v >> 32 == 0)
+ return MultiplyDeBruijnBitPosition[(uint)((uint)v * 0x07C4ACDDU) >> 27];
+ return 32 + MultiplyDeBruijnBitPosition[(uint)((uint)(v >> 32) * 0x07C4ACDDU) >> 27];
+ }
+
+ public static int log2i(uint v)
+ {
+ v |= v >> 1; // first round down to one less than a power of 2
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ return MultiplyDeBruijnBitPosition[(uint)(v * 0x07C4ACDDU) >> 27];
+ }
+
+ 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,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ #endregion
+
+ private byte* buffer_m;
+ private byte* bptr_m;
+ private int buffer_len_m;
+ private int have_bits_m;
+ private ulong cache_m;
+ private ushort crc16_m;
+
+ public int Position
+ {
+ get { return (int)(bptr_m - buffer_m - (have_bits_m >> 3)); }
+ }
+
+ public byte* Buffer
+ {
+ get
+ {
+ return buffer_m;
+ }
+ }
+
+ public BitReader()
+ {
+ buffer_m = null;
+ bptr_m = null;
+ buffer_len_m = 0;
+ have_bits_m = 0;
+ cache_m = 0;
+ crc16_m = 0;
+ }
+
+ public BitReader(byte* _buffer, int _pos, int _len)
+ {
+ Reset(_buffer, _pos, _len);
+ }
+
+ public void Reset(byte* _buffer, int _pos, int _len)
+ {
+ buffer_m = _buffer;
+ bptr_m = _buffer + _pos;
+ buffer_len_m = _len;
+ have_bits_m = 0;
+ cache_m = 0;
+ crc16_m = 0;
+ fill();
+ }
+
+ public void fill()
+ {
+ while (have_bits_m < 56)
+ {
+ have_bits_m += 8;
+ byte b = *(bptr_m++);
+ cache_m |= (ulong)b << (64 - have_bits_m);
+ crc16_m = (ushort)((crc16_m << 8) ^ Crc16.table[(crc16_m >> 8) ^ b]);
+ }
+ }
+
+ /* skip any number of bits */
+ public void skipbits(int bits)
+ {
+ while (bits > have_bits_m)
+ {
+ bits -= have_bits_m;
+ cache_m = 0;
+ have_bits_m = 0;
+ fill();
+ }
+ cache_m <<= bits;
+ have_bits_m -= bits;
+ }
+
+ public long read_long()
+ {
+ return ((long)readbits(32) << 32) | readbits(32);
+ }
+
+ public ulong read_ulong()
+ {
+ return ((ulong)readbits(32) << 32) | readbits(32);
+ }
+
+ public int read_int()
+ {
+ return (int)readbits(sizeof(int));
+ }
+
+ public uint read_uint()
+ {
+ return (uint)readbits(sizeof(uint));
+ }
+
+ public short read_short()
+ {
+ return (short)readbits(16);
+ }
+
+ public ushort read_ushort()
+ {
+ return (ushort)readbits(16);
+ }
+
+ /* supports reading 1 to 32 bits, in big endian format */
+ public uint readbits(int bits)
+ {
+ fill();
+ uint result = (uint)(cache_m >> 64 - bits);
+ skipbits(bits);
+ return result;
+ }
+
+ /* supports reading 1 to 64 bits, in big endian format */
+ public ulong readbits64(int bits)
+ {
+ if (bits <= 56)
+ return readbits(bits);
+ return ((ulong)readbits(32) << bits - 32) | readbits(bits - 32);
+ }
+
+ /* reads a single bit */
+ public uint readbit()
+ {
+ return readbits(1);
+ }
+
+ public uint read_unary()
+ {
+ fill();
+ uint val = 0;
+ ulong result = cache_m >> 56;
+ while (result == 0)
+ {
+ val += 8;
+ cache_m <<= 8;
+ byte b = *(bptr_m++);
+ cache_m |= (ulong)b << (64 - have_bits_m);
+ crc16_m = (ushort)((crc16_m << 8) ^ Crc16.table[(crc16_m >> 8) ^ b]);
+ result = cache_m >> 56;
+ }
+ val += byte_to_unary_table[result];
+ skipbits((int)(val & 7) + 1);
+ return val;
+ }
+
+ public void flush()
+ {
+ if ((have_bits_m & 7) > 0)
+ {
+ cache_m <<= have_bits_m & 7;
+ have_bits_m -= have_bits_m & 7;
+ }
+ }
+
+ public ushort get_crc16()
+ {
+ if (have_bits_m == 0)
+ return crc16_m;
+ ushort crc = 0;
+ int n = have_bits_m >> 3;
+ for (int i = 0; i < n; i++)
+ crc = (ushort)((crc << 8) ^ Crc16.table[(crc >> 8) ^ (byte)(cache_m >> (56 - (i << 3)))]);
+ return Crc16.Subtract(crc16_m, crc, n);
+ }
+
+ public int readbits_signed(int bits)
+ {
+ int val = (int)readbits(bits);
+ val <<= (32 - bits);
+ val >>= (32 - bits);
+ return val;
+ }
+
+ public uint read_utf8()
+ {
+ uint x = readbits(8);
+ uint v;
+ int i;
+ if (0 == (x & 0x80))
+ {
+ v = x;
+ i = 0;
+ }
+ else if (0xC0 == (x & 0xE0)) /* 110xxxxx */
+ {
+ v = x & 0x1F;
+ i = 1;
+ }
+ else if (0xE0 == (x & 0xF0)) /* 1110xxxx */
+ {
+ v = x & 0x0F;
+ i = 2;
+ }
+ else if (0xF0 == (x & 0xF8)) /* 11110xxx */
+ {
+ v = x & 0x07;
+ i = 3;
+ }
+ else if (0xF8 == (x & 0xFC)) /* 111110xx */
+ {
+ v = x & 0x03;
+ i = 4;
+ }
+ else if (0xFC == (x & 0xFE)) /* 1111110x */
+ {
+ v = x & 0x01;
+ i = 5;
+ }
+ else if (0xFE == x) /* 11111110 */
+ {
+ v = 0;
+ i = 6;
+ }
+ else
+ {
+ throw new Exception("invalid utf8 encoding");
+ }
+ for (; i > 0; i--)
+ {
+ x = readbits(8);
+ if (0x80 != (x & 0xC0)) /* 10xxxxxx */
+ throw new Exception("invalid utf8 encoding");
+ v <<= 6;
+ v |= (x & 0x3F);
+ }
+ return v;
+ }
+
+ public void read_rice_block(int n, int k, int* r)
+ {
+ fill();
+ fixed (byte* unary_table = byte_to_unary_table)
+ fixed (ushort* t = Crc16.table)
+ {
+ uint mask = (1U << k) - 1;
+ byte* bptr = bptr_m;
+ int have_bits = have_bits_m;
+ ulong cache = cache_m;
+ ushort crc = crc16_m;
+ for (int i = n; i > 0; i--)
+ {
+ uint bits;
+ byte* orig_bptr = bptr;
+ while ((bits = unary_table[cache >> 56]) == 8)
+ {
+ cache <<= 8;
+ byte b = *(bptr++);
+ cache |= (ulong)b << (64 - have_bits);
+ crc = (ushort)((crc << 8) ^ t[(crc >> 8) ^ b]);
+ }
+ uint msbs = bits + ((uint)(bptr - orig_bptr) << 3);
+ // assumes k <= 41 (have_bits < 41 + 7 + 1 + 8 == 57, so we don't loose bits here)
+ while (have_bits < 56)
+ {
+ have_bits += 8;
+ byte b = *(bptr++);
+ cache |= (ulong)b << (64 - have_bits);
+ crc = (ushort)((crc << 8) ^ t[(crc >> 8) ^ b]);
+ }
+
+ int btsk = k + (int)bits + 1;
+ uint uval = (msbs << k) | (uint)((cache >> (64 - btsk)) & mask);
+ cache <<= btsk;
+ have_bits -= btsk;
+ *(r++) = (int)(uval >> 1 ^ -(int)(uval & 1));
+ }
+ have_bits_m = have_bits;
+ cache_m = cache;
+ bptr_m = bptr;
+ crc16_m = crc;
+ }
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/BitWriter.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/BitWriter.cs
new file mode 100644
index 000000000..524c727ac
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/BitWriter.cs
@@ -0,0 +1,370 @@
+using System;
+
+namespace CUETools.Codecs
+{
+ public class BitWriter
+ {
+ private ushort crc16_m;
+ private ulong bit_buf_m;
+ private int bit_left_m;
+ private byte[] buffer;
+ private int buf_start, buf_ptr_m, buf_end;
+ private bool eof;
+
+ public byte[] Buffer
+ {
+ get
+ {
+ return buffer;
+ }
+ }
+
+ public int Length
+ {
+ get
+ {
+ return buf_ptr_m - buf_start;
+ }
+ set
+ {
+ flush();
+ buf_ptr_m = buf_start + value;
+ }
+ }
+
+ public int BitLength
+ {
+ get
+ {
+ return buf_ptr_m * 8 + 64 - bit_left_m;
+ }
+ }
+
+ public ushort get_crc16()
+ {
+ return crc16_m;
+ }
+
+ public BitWriter(byte[] buf, int pos, int len)
+ {
+ buffer = buf;
+ buf_start = pos;
+ buf_ptr_m = pos;
+ buf_end = pos + len;
+ bit_left_m = 64;
+ bit_buf_m = 0;
+ crc16_m = 0;
+ eof = false;
+ }
+
+ public void Reset()
+ {
+ buf_ptr_m = buf_start;
+ bit_left_m = 64;
+ bit_buf_m = 0;
+ crc16_m = 0;
+ eof = false;
+ }
+
+ public void writebytes(int bytes, byte c)
+ {
+ for (; bytes > 0; bytes--)
+ {
+ writebits(8, c);
+ }
+ }
+
+ public unsafe void writeints(int len, int pos, byte* buf)
+ {
+ int old_pos = BitLength;
+ int start = old_pos / 8;
+ int start1 = pos / 8;
+ int end = (old_pos + len) / 8;
+ int end1 = (pos + len) / 8;
+ flush();
+ byte start_val = old_pos % 8 != 0 ? buffer[start] : (byte)0;
+ fixed (byte* buf1 = &buffer[0])
+ {
+ if (old_pos % 8 != 0)
+ crc16_m = Crc16.Subtract(crc16_m, 0, 1);
+ crc16_m = Crc16.ComputeChecksum(crc16_m, buf + start1, end - start);
+ AudioSamples.MemCpy(buf1 + start, buf + start1, end - start);
+ buf1[start] |= start_val;
+ }
+ buf_ptr_m = end;
+ if ((old_pos + len) % 8 != 0)
+ writebits((old_pos + len) % 8, buf[end1] >> (8 - ((old_pos + len) % 8)));
+ }
+
+ 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 write(byte[] s)
+ {
+ for (int i = 0; i < s.Length; i++)
+ writebits(8, s[i]);
+ }
+
+ public void writebits_signed(int bits, int val)
+ {
+ writebits(bits, val & ((1 << bits) - 1));
+ }
+
+ public void writebits_signed(uint bits, int val)
+ {
+ writebits((int)bits, val & ((1 << (int)bits) - 1));
+ }
+
+ public void writebits(int bits, int val)
+ {
+ writebits(bits, (ulong)val);
+ }
+
+ public void writebits(DateTime val)
+ {
+ TimeSpan span = val.ToUniversalTime() - new DateTime(1904, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
+ writebits(32, (ulong)span.TotalSeconds);
+ }
+
+ public void writebits(int bits, uint val)
+ {
+ writebits(bits, (ulong)val);
+ }
+
+ public void writebits(int bits, ulong val)
+ {
+ //assert(bits == 32 || val < (1U << bits));
+
+ if (bits == 0 || eof) return;
+ if (bits < bit_left_m)
+ {
+ bit_left_m -= bits;
+ bit_buf_m |= val << bit_left_m;
+ }
+ else
+ {
+ ulong bb = bit_buf_m | (val >> (bits - bit_left_m));
+ if (buffer != null)
+ {
+ if (buf_ptr_m + 8 > buf_end)
+ {
+ eof = true;
+ return;
+ }
+
+ crc16_m = (ushort)((crc16_m << 8) ^ Crc16.table[(crc16_m >> 8) ^ (byte)(bb >> 56)]);
+ crc16_m = (ushort)((crc16_m << 8) ^ Crc16.table[(crc16_m >> 8) ^ (byte)(bb >> 48)]);
+ crc16_m = (ushort)((crc16_m << 8) ^ Crc16.table[(crc16_m >> 8) ^ (byte)(bb >> 40)]);
+ crc16_m = (ushort)((crc16_m << 8) ^ Crc16.table[(crc16_m >> 8) ^ (byte)(bb >> 32)]);
+ crc16_m = (ushort)((crc16_m << 8) ^ Crc16.table[(crc16_m >> 8) ^ (byte)(bb >> 24)]);
+ crc16_m = (ushort)((crc16_m << 8) ^ Crc16.table[(crc16_m >> 8) ^ (byte)(bb >> 16)]);
+ crc16_m = (ushort)((crc16_m << 8) ^ Crc16.table[(crc16_m >> 8) ^ (byte)(bb >> 8)]);
+ crc16_m = (ushort)((crc16_m << 8) ^ Crc16.table[(crc16_m >> 8) ^ (byte)(bb )]);
+
+ buffer[buf_ptr_m + 7] = (byte)(bb & 0xFF); bb >>= 8;
+ buffer[buf_ptr_m + 6] = (byte)(bb & 0xFF); bb >>= 8;
+ buffer[buf_ptr_m + 5] = (byte)(bb & 0xFF); bb >>= 8;
+ buffer[buf_ptr_m + 4] = (byte)(bb & 0xFF); bb >>= 8;
+ buffer[buf_ptr_m + 3] = (byte)(bb & 0xFF); bb >>= 8;
+ buffer[buf_ptr_m + 2] = (byte)(bb & 0xFF); bb >>= 8;
+ buffer[buf_ptr_m + 1] = (byte)(bb & 0xFF); bb >>= 8;
+ buffer[buf_ptr_m + 0] = (byte)(bb & 0xFF);
+ buf_ptr_m += 8;
+ }
+ // cannot do this in one shift, because bit_left_m can be 64,
+ //
+ bit_left_m += 64 - bits;
+ bit_buf_m = bit_left_m == 64 ? 0 : val << bit_left_m;
+ }
+ }
+
+ ///
+ /// Assumes there's enough space, buffer != null and bits is in range 1..31
+ ///
+ ///
+ ///
+// unsafe void writebits_fast(int bits, uint val, ref byte* buf)
+// {
+//#if DEBUG
+// if ((buf_ptr + 3) >= buf_end)
+// {
+// eof = true;
+// return;
+// }
+//#endif
+// if (bits < bit_left)
+// {
+// bit_buf = (bit_buf << bits) | val;
+// bit_left -= bits;
+// }
+// else
+// {
+// uint bb = (bit_buf << bit_left) | (val >> (bits - bit_left));
+// bit_left += (32 - bits);
+
+// *(buf++) = (byte)(bb >> 24);
+// *(buf++) = (byte)(bb >> 16);
+// *(buf++) = (byte)(bb >> 8);
+// *(buf++) = (byte)(bb);
+
+// bit_buf = val;
+// }
+// }
+
+ public void write_utf8(int val)
+ {
+ write_utf8((uint)val);
+ }
+
+ public void write_utf8(uint val)
+ {
+ if (val < 0x80)
+ {
+ writebits(8, val);
+ return;
+ }
+ int bytes = (BitReader.log2i(val) + 4) / 5;
+ int shift = (bytes - 1) * 6;
+ writebits(8, (256U - (256U >> bytes)) | (val >> shift));
+ while (shift >= 6)
+ {
+ shift -= 6;
+ writebits(8, 0x80 | ((val >> shift) & 0x3F));
+ }
+ }
+
+ public void write_unary_signed(int val)
+ {
+ // convert signed to unsigned
+ int v = -2 * val - 1;
+ v ^= (v >> 31);
+
+ // write quotient in unary
+ int q = v + 1;
+ while (q > 31)
+ {
+ writebits(31, 0);
+ q -= 31;
+ }
+ writebits(q, 1);
+ }
+
+ public void write_rice_signed(int k, int val)
+ {
+ // convert signed to unsigned
+ int v = -2 * val - 1;
+ v ^= (v >> 31);
+
+ // write quotient in unary
+ int q = (v >> k) + 1;
+ while (q + k > 31)
+ {
+ int b = Math.Min(q + k - 31, 31);
+ writebits(b, 0);
+ q -= b;
+ }
+
+ // write remainder in binary using 'k' bits
+ writebits(k + q, (v & ((1 << k) - 1)) | (1 << k));
+ }
+
+ public unsafe void write_rice_block_signed(byte* fixedbuf, int k, int* residual, int count)
+ {
+ byte* buf = &fixedbuf[buf_ptr_m];
+ ulong bit_buf = bit_buf_m;
+ int bit_left = bit_left_m;
+ ushort crc16 = crc16_m;
+ fixed (ushort *crc16_t = Crc16.table)
+ for (int i = count; i > 0; i--)
+ {
+ int vi = *(residual++);
+ uint v = (uint)((vi << 1) ^ (vi >> 31));
+
+ // write quotient in unary
+ int q = (int)(v >> k) + 1;
+ int bits = k + q;
+ while (bits > 64)
+ {
+#if DEBUG
+ if (buf + 1 > fixedbuf + buf_end)
+ {
+ eof = true;
+ return;
+ }
+#endif
+ crc16 = (ushort)((crc16 << 8) ^ crc16_t[(crc16 >> 8) ^ (*(buf++) = (byte)(bit_buf >> 56))]);
+ bit_buf <<= 8;
+ bits -= 8;
+ }
+
+ // write remainder in binary using 'k' bits
+ //writebits_fast(k + q, (uint)((v & ((1 << k) - 1)) | (1 << k)), ref buf);
+ ulong val = (uint)((v & ((1U << k) - 1)) | (1U << k));
+ if (bits < bit_left)
+ {
+ bit_left -= bits;
+ bit_buf |= val << bit_left;
+ }
+ else
+ {
+ ulong bb = bit_buf | (val >> (bits - bit_left));
+#if DEBUG
+ if (buf + 8 > fixedbuf + buf_end)
+ {
+ eof = true;
+ return;
+ }
+#endif
+
+ crc16 = (ushort)((crc16 << 8) ^ crc16_t[(crc16 >> 8) ^ (*(buf++) = (byte)(bb >> 56))]);
+ crc16 = (ushort)((crc16 << 8) ^ crc16_t[(crc16 >> 8) ^ (*(buf++) = (byte)(bb >> 48))]);
+ crc16 = (ushort)((crc16 << 8) ^ crc16_t[(crc16 >> 8) ^ (*(buf++) = (byte)(bb >> 40))]);
+ crc16 = (ushort)((crc16 << 8) ^ crc16_t[(crc16 >> 8) ^ (*(buf++) = (byte)(bb >> 32))]);
+ crc16 = (ushort)((crc16 << 8) ^ crc16_t[(crc16 >> 8) ^ (*(buf++) = (byte)(bb >> 24))]);
+ crc16 = (ushort)((crc16 << 8) ^ crc16_t[(crc16 >> 8) ^ (*(buf++) = (byte)(bb >> 16))]);
+ crc16 = (ushort)((crc16 << 8) ^ crc16_t[(crc16 >> 8) ^ (*(buf++) = (byte)(bb >> 8))]);
+ crc16 = (ushort)((crc16 << 8) ^ crc16_t[(crc16 >> 8) ^ (*(buf++) = (byte)(bb))]);
+
+ bit_left += 64 - bits;
+ bit_buf = (val << bit_left - 1) << 1;
+ }
+ }
+ crc16_m = crc16;
+ buf_ptr_m = (int)(buf - fixedbuf);
+ bit_buf_m = bit_buf;
+ bit_left_m = bit_left;
+ }
+
+ public void flush()
+ {
+ while (bit_left_m < 64 && !eof)
+ {
+ if (buf_ptr_m >= buf_end)
+ {
+ eof = true;
+ break;
+ }
+ if (buffer != null)
+ {
+ byte b = (byte)(bit_buf_m >> 56);
+ crc16_m = (ushort)((crc16_m << 8) ^ Crc16.table[(crc16_m >> 8) ^ b]);
+ buffer[buf_ptr_m] = b;
+ }
+ buf_ptr_m++;
+ bit_buf_m <<= 8;
+ bit_left_m += 8;
+ }
+ bit_left_m = 64;
+ bit_buf_m = 0;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/CRC/CRC16.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/CRC/CRC16.cs
new file mode 100644
index 000000000..85b321919
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/CRC/CRC16.cs
@@ -0,0 +1,164 @@
+using System;
+namespace CUETools.Codecs
+{
+ public static class Crc16
+ {
+ const int GF2_DIM = 16;
+ public static ushort[] table = new ushort[256];
+ private static readonly ushort[,] combineTable = new ushort[GF2_DIM, GF2_DIM];
+ private static readonly ushort[,] substractTable = new ushort[GF2_DIM, GF2_DIM];
+
+ public static unsafe ushort ComputeChecksum(ushort crc, byte[] bytes, int pos, int count)
+ {
+ fixed (byte* bs = bytes)
+ return ComputeChecksum(crc, bs + pos, count);
+ }
+
+ public static unsafe ushort ComputeChecksum(ushort crc, byte* bytes, int count)
+ {
+ fixed (ushort* t = table)
+ for (int i = count; i > 0; i--)
+ {
+ crc = (ushort)((crc << 8) ^ t[(crc >> 8) ^ *(bytes++)]);
+ }
+ return crc;
+ }
+
+ const ushort polynomial = 0x8005;
+ const ushort reversePolynomial = 0x4003;
+
+ static unsafe Crc16()
+ {
+ for (ushort i = 0; i < table.Length; i++)
+ {
+ int crc = i;
+ for (int j = 0; j < GF2_DIM; j++)
+ {
+ if ((crc & (1U << (GF2_DIM - 1))) != 0)
+ crc = ((crc << 1) ^ polynomial);
+ else
+ crc <<= 1;
+ }
+ table[i] = (ushort)(crc & ((1 << GF2_DIM) - 1));
+ }
+
+ combineTable[0, 0] = Crc16.Reflect(polynomial);
+ substractTable[0, GF2_DIM - 1] = reversePolynomial;
+ for (int n = 1; n < GF2_DIM; n++)
+ {
+ combineTable[0, n] = (ushort)(1 << (n - 1));
+ substractTable[0, n - 1] = (ushort)(1 << n);
+ }
+
+ fixed (ushort* ct = &combineTable[0, 0], st = &substractTable[0, 0])
+ {
+ //for (int i = 0; i < GF2_DIM; i++)
+ // st[32 + i] = ct[i];
+ //invert_binary_matrix(st + 32, st, GF2_DIM);
+
+ for (int i = 1; i < GF2_DIM; i++)
+ {
+ gf2_matrix_square(ct + i * GF2_DIM, ct + (i - 1) * GF2_DIM);
+ gf2_matrix_square(st + i * GF2_DIM, st + (i - 1) * GF2_DIM);
+ }
+ }
+ }
+
+ private static unsafe ushort gf2_matrix_times(ushort* mat, ushort uvec)
+ {
+ int vec = ((int) uvec) << 16;
+ return (ushort)(
+ (*(mat++) & ((vec << 15) >> 31)) ^
+ (*(mat++) & ((vec << 14) >> 31)) ^
+ (*(mat++) & ((vec << 13) >> 31)) ^
+ (*(mat++) & ((vec << 12) >> 31)) ^
+ (*(mat++) & ((vec << 11) >> 31)) ^
+ (*(mat++) & ((vec << 10) >> 31)) ^
+ (*(mat++) & ((vec << 09) >> 31)) ^
+ (*(mat++) & ((vec << 08) >> 31)) ^
+ (*(mat++) & ((vec << 07) >> 31)) ^
+ (*(mat++) & ((vec << 06) >> 31)) ^
+ (*(mat++) & ((vec << 05) >> 31)) ^
+ (*(mat++) & ((vec << 04) >> 31)) ^
+ (*(mat++) & ((vec << 03) >> 31)) ^
+ (*(mat++) & ((vec << 02) >> 31)) ^
+ (*(mat++) & ((vec << 01) >> 31)) ^
+ (*(mat++) & (vec >> 31)));
+ }
+
+ private static unsafe void gf2_matrix_square(ushort* square, ushort* mat)
+ {
+ for (int n = 0; n < GF2_DIM; n++)
+ square[n] = gf2_matrix_times(mat, mat[n]);
+ }
+
+ public static ushort Reflect(ushort crc)
+ {
+ return (ushort)Crc32.Reflect(crc, 16);
+ }
+
+ public static unsafe ushort Combine(ushort crc1, ushort crc2, long len2)
+ {
+ crc1 = Crc16.Reflect(crc1);
+ crc2 = Crc16.Reflect(crc2);
+
+ /* degenerate case */
+ if (len2 == 0)
+ return crc1;
+ if (crc1 == 0)
+ return crc2;
+ if (len2 < 0)
+ throw new ArgumentException("crc.Combine length cannot be negative", "len2");
+
+ fixed (ushort* ct = &combineTable[0, 0])
+ {
+ int n = 3;
+ do
+ {
+ /* apply zeros operator for this bit of len2 */
+ if ((len2 & 1) != 0)
+ crc1 = gf2_matrix_times(ct + GF2_DIM * n, crc1);
+ len2 >>= 1;
+ n = (n + 1) & (GF2_DIM - 1);
+ /* if no more bits set, then done */
+ } while (len2 != 0);
+ }
+
+ /* return combined crc */
+ crc1 ^= crc2;
+ crc1 = Crc16.Reflect(crc1);
+ return crc1;
+ }
+
+ public static unsafe ushort Subtract(ushort crc1, ushort crc2, long len2)
+ {
+ crc1 = Crc16.Reflect(crc1);
+ crc2 = Crc16.Reflect(crc2);
+ /* degenerate case */
+ if (len2 == 0)
+ return crc1;
+ if (len2 < 0)
+ throw new ArgumentException("crc.Combine length cannot be negative", "len2");
+
+ crc1 ^= crc2;
+
+ fixed (ushort* st = &substractTable[0, 0])
+ {
+ int n = 3;
+ do
+ {
+ /* apply zeros operator for this bit of len2 */
+ if ((len2 & 1) != 0)
+ crc1 = gf2_matrix_times(st + GF2_DIM * n, crc1);
+ len2 >>= 1;
+ n = (n + 1) & (GF2_DIM - 1);
+ /* if no more bits set, then done */
+ } while (len2 != 0);
+ }
+
+ /* return combined crc */
+ crc1 = Crc16.Reflect(crc1);
+ return crc1;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/CRC/CRC16CCITT.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/CRC/CRC16CCITT.cs
new file mode 100644
index 000000000..d38d8821b
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/CRC/CRC16CCITT.cs
@@ -0,0 +1,51 @@
+namespace CUETools.Codecs
+{
+ public enum InitialCrcValue { Zeros, NonZero1 = 0xffff, NonZero2 = 0x1D0F }
+
+ public class Crc16Ccitt
+ {
+ const ushort poly = 4129;
+ ushort[] table = new ushort[256];
+ ushort initialValue = 0;
+
+ public ushort ComputeChecksum(byte[] bytes, int pos, int count)
+ {
+ ushort crc = this.initialValue;
+ for (int i = pos; i < pos + count; i++)
+ {
+ crc = (ushort)((crc << 8) ^ table[((crc >> 8) ^ (0xff & bytes[i]))]);
+ }
+ return crc;
+ }
+
+ public byte[] ComputeChecksumBytes(byte[] bytes, int pos, int count)
+ {
+ ushort crc = ComputeChecksum(bytes, pos, count);
+ return new byte[] { (byte)(crc >> 8), (byte)(crc & 0x00ff) };
+ }
+
+ public Crc16Ccitt(InitialCrcValue initialValue)
+ {
+ this.initialValue = (ushort)initialValue;
+ ushort temp, a;
+ for (int i = 0; i < table.Length; i++)
+ {
+ temp = 0;
+ a = (ushort)(i << 8);
+ for (int j = 0; j < 8; j++)
+ {
+ if (((temp ^ a) & 0x8000) != 0)
+ {
+ temp = (ushort)((temp << 1) ^ poly);
+ }
+ else
+ {
+ temp <<= 1;
+ }
+ a <<= 1;
+ }
+ table[i] = temp;
+ }
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/CRC/CRC32.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/CRC/CRC32.cs
new file mode 100644
index 000000000..48a629f39
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/CRC/CRC32.cs
@@ -0,0 +1,259 @@
+using System;
+
+namespace CUETools.Codecs
+{
+ public static class Crc32
+ {
+ public static readonly uint[] table;
+
+ public static uint ComputeChecksum(uint crc, byte val)
+ {
+ return (crc >> 8) ^ table[(crc & 0xff) ^ val];
+ }
+
+ public static unsafe uint ComputeChecksum(uint crc, byte* bytes, int count)
+ {
+ fixed (uint *t = table)
+ for (int i = 0; i < count; i++)
+ crc = (crc >> 8) ^ t[(crc ^ bytes[i]) & 0xff];
+ return crc;
+ }
+
+ public static unsafe uint ComputeChecksum(uint crc, byte[] bytes, int pos, int count)
+ {
+ fixed (byte* pbytes = &bytes[pos])
+ return ComputeChecksum(crc, pbytes, count);
+ }
+
+ public static uint ComputeChecksum(uint crc, uint s)
+ {
+ return ComputeChecksum(ComputeChecksum(ComputeChecksum(ComputeChecksum(
+ crc, (byte)s), (byte)(s >> 8)), (byte)(s >> 16)), (byte)(s >> 24));
+ }
+
+ public static unsafe uint ComputeChecksum(uint crc, int* samples, int count)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ int s1 = samples[2 * i], s2 = samples[2 * i + 1];
+ crc = ComputeChecksum(ComputeChecksum(ComputeChecksum(ComputeChecksum(
+ crc, (byte)s1), (byte)(s1 >> 8)), (byte)s2), (byte)(s2 >> 8));
+ }
+ return crc;
+ }
+
+ internal static uint Reflect(uint val, int ch)
+ {
+ uint value = 0;
+ // Swap bit 0 for bit 7
+ // bit 1 for bit 6, etc.
+ for (int i = 1; i < (ch + 1); i++)
+ {
+ if (0 != (val & 1))
+ value |= 1U << (ch - i);
+ val >>= 1;
+ }
+ return value;
+ }
+
+ const uint uPolynomial = 0x04c11db7;
+ const uint uReversePolynomial = 0xedb88320;
+ const uint uReversePolynomial2 = 0xdb710641;
+
+ private static readonly uint[,] combineTable;
+ private static readonly uint[,] substractTable;
+
+#if need_invert_binary_matrix
+ static unsafe void invert_binary_matrix(uint* mat, uint* inv, int rows)
+ {
+ int cols, i, j;
+ uint tmp;
+
+ cols = rows;
+
+ for (i = 0; i < rows; i++) inv[i] = (1U << i);
+
+ /* First -- convert into upper triangular */
+
+ for (i = 0; i < cols; i++)
+ {
+
+ /* Swap rows if we ave a zero i,i element. If we can't swap, then the
+ matrix was not invertible */
+
+ if ((mat[i] & (1 << i)) == 0)
+ {
+ for (j = i + 1; j < rows && (mat[j] & (1 << i)) == 0; j++) ;
+ if (j == rows)
+ throw new Exception("Matrix not invertible");
+ tmp = mat[i]; mat[i] = mat[j]; mat[j] = tmp;
+ tmp = inv[i]; inv[i] = inv[j]; inv[j] = tmp;
+ }
+
+ /* Now for each j>i, add A_ji*Ai to Aj */
+ for (j = i + 1; j != rows; j++)
+ {
+ if ((mat[j] & (1 << i)) != 0)
+ {
+ mat[j] ^= mat[i];
+ inv[j] ^= inv[i];
+ }
+ }
+ }
+
+ /* Now the matrix is upper triangular. Start at the top and multiply down */
+
+ for (i = rows - 1; i >= 0; i--)
+ {
+ for (j = 0; j < i; j++)
+ {
+ if ((mat[j] & (1 << i)) != 0)
+ {
+ /* mat[j] ^= mat[i]; */
+ inv[j] ^= inv[i];
+ }
+ }
+ }
+ }
+#endif
+
+ static unsafe Crc32()
+ {
+ table = new uint[256];
+ for (uint i = 0; i < table.Length; i++)
+ {
+ table[i] = Reflect(i, 8) << 24;
+ for (int j = 0; j < 8; j++)
+ table[i] = (table[i] << 1) ^ ((table[i] & (1U << 31)) == 0 ? 0 : uPolynomial);
+ table[i] = Reflect(table[i], 32);
+ }
+ combineTable = new uint[GF2_DIM, GF2_DIM];
+ substractTable = new uint[GF2_DIM, GF2_DIM];
+ combineTable[0, 0] = uReversePolynomial;
+ substractTable[0, 31] = uReversePolynomial2;
+ for (int n = 1; n < GF2_DIM; n++)
+ {
+ combineTable[0, n] = 1U << (n - 1);
+ substractTable[0, n - 1] = 1U << n;
+ }
+ fixed (uint* ct = &combineTable[0, 0], st = &substractTable[0, 0])
+ {
+ //for (int i = 0; i < GF2_DIM; i++)
+ // st[32 + i] = ct[i];
+ //invert_binary_matrix(st + 32, st, GF2_DIM);
+
+ for (int i = 1; i < GF2_DIM; i++)
+ {
+ gf2_matrix_square(ct + i * 32, ct + (i - 1) * 32);
+ gf2_matrix_square(st + i * 32, st + (i - 1) * 32);
+ }
+ }
+ }
+
+ const int GF2_DIM = 32;
+ //const int GF2_DIM2 = 67;
+
+ private static unsafe uint gf2_matrix_times(uint* umat, uint uvec)
+ {
+ int vec = (int)uvec;
+ int* mat = (int*)umat;
+ return (uint)(
+ (*(mat++) & ((vec << 31) >> 31)) ^
+ (*(mat++) & ((vec << 30) >> 31)) ^
+ (*(mat++) & ((vec << 29) >> 31)) ^
+ (*(mat++) & ((vec << 28) >> 31)) ^
+ (*(mat++) & ((vec << 27) >> 31)) ^
+ (*(mat++) & ((vec << 26) >> 31)) ^
+ (*(mat++) & ((vec << 25) >> 31)) ^
+ (*(mat++) & ((vec << 24) >> 31)) ^
+ (*(mat++) & ((vec << 23) >> 31)) ^
+ (*(mat++) & ((vec << 22) >> 31)) ^
+ (*(mat++) & ((vec << 21) >> 31)) ^
+ (*(mat++) & ((vec << 20) >> 31)) ^
+ (*(mat++) & ((vec << 19) >> 31)) ^
+ (*(mat++) & ((vec << 18) >> 31)) ^
+ (*(mat++) & ((vec << 17) >> 31)) ^
+ (*(mat++) & ((vec << 16) >> 31)) ^
+ (*(mat++) & ((vec << 15) >> 31)) ^
+ (*(mat++) & ((vec << 14) >> 31)) ^
+ (*(mat++) & ((vec << 13) >> 31)) ^
+ (*(mat++) & ((vec << 12) >> 31)) ^
+ (*(mat++) & ((vec << 11) >> 31)) ^
+ (*(mat++) & ((vec << 10) >> 31)) ^
+ (*(mat++) & ((vec << 09) >> 31)) ^
+ (*(mat++) & ((vec << 08) >> 31)) ^
+ (*(mat++) & ((vec << 07) >> 31)) ^
+ (*(mat++) & ((vec << 06) >> 31)) ^
+ (*(mat++) & ((vec << 05) >> 31)) ^
+ (*(mat++) & ((vec << 04) >> 31)) ^
+ (*(mat++) & ((vec << 03) >> 31)) ^
+ (*(mat++) & ((vec << 02) >> 31)) ^
+ (*(mat++) & ((vec << 01) >> 31)) ^
+ (*(mat++) & (vec >> 31)));
+ }
+
+ /* ========================================================================= */
+ private static unsafe void gf2_matrix_square(uint *square, uint *mat)
+ {
+ for (int n = 0; n < GF2_DIM; n++)
+ square[n] = gf2_matrix_times(mat, mat[n]);
+ }
+
+ public static unsafe uint Combine(uint crc1, uint crc2, long len2)
+ {
+ /* degenerate case */
+ if (len2 == 0)
+ return crc1;
+ if (crc1 == 0)
+ return crc2;
+ if (len2 < 0)
+ throw new ArgumentException("crc.Combine length cannot be negative", "len2");
+
+ fixed (uint* ct = &combineTable[0, 0])
+ {
+ int n = 3;
+ do
+ {
+ /* apply zeros operator for this bit of len2 */
+ if ((len2 & 1) != 0)
+ crc1 = gf2_matrix_times(ct + 32 * n, crc1);
+ len2 >>= 1;
+ n = (n + 1) & (GF2_DIM - 1);
+ /* if no more bits set, then done */
+ } while (len2 != 0);
+ }
+
+ /* return combined crc */
+ crc1 ^= crc2;
+ return crc1;
+ }
+
+ public static unsafe uint Subtract(uint crc1, uint crc2, long len2)
+ {
+ /* degenerate case */
+ if (len2 == 0)
+ return crc1;
+ if (len2 < 0)
+ throw new ArgumentException("crc.Combine length cannot be negative", "len2");
+
+ crc1 ^= crc2;
+
+ fixed (uint* st = &substractTable[0, 0])
+ {
+ int n = 3;
+ do
+ {
+ /* apply zeros operator for this bit of len2 */
+ if ((len2 & 1) != 0)
+ crc1 = gf2_matrix_times(st + 32 * n, crc1);
+ len2 >>= 1;
+ n = (n + 1) & (GF2_DIM - 1);
+ /* if no more bits set, then done */
+ } while (len2 != 0);
+ }
+
+ /* return combined crc */
+ return crc1;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/CRC/CRC8.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/CRC/CRC8.cs
new file mode 100644
index 000000000..2b2914d0b
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/CRC/CRC8.cs
@@ -0,0 +1,43 @@
+namespace CUETools.Codecs
+{
+ public class Crc8
+ {
+ private const ushort poly8 = 0x07;
+
+ private ushort[] table = new ushort[256];
+
+ public Crc8()
+ {
+ int bits = 8;
+ ushort poly = (ushort) (poly8 + (1U << bits));
+ for (ushort i = 0; i < table.Length; i++)
+ {
+ ushort crc = i;
+ for (int j = 0; j < bits; j++)
+ {
+ if ((crc & (1U << (bits - 1))) != 0)
+ crc = (ushort)((crc << 1) ^ poly);
+ else
+ crc <<= 1;
+ }
+ table[i] = (ushort)(crc & 0x00ff);
+ }
+ }
+
+ public byte ComputeChecksum(byte[] bytes, int pos, int count)
+ {
+ ushort crc = 0;
+ for (int i = pos; i < pos + count; i++)
+ crc = table[crc ^ bytes[i]];
+ return (byte)crc;
+ }
+
+ public unsafe byte ComputeChecksum(byte* bytes, int pos, int count)
+ {
+ ushort crc = 0;
+ for (int i = pos; i < pos + count; i++)
+ crc = table[crc ^ bytes[i]];
+ return (byte)crc;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/CUETools.Codecs.csproj b/Aaru.Compression/cuetools.net/CUETools.Codecs/CUETools.Codecs.csproj
new file mode 100644
index 000000000..a7491076c
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/CUETools.Codecs.csproj
@@ -0,0 +1,23 @@
+
+
+
+ netstandard2.0
+ 2.1.9.0
+ CUETools.Codecs
+ CUETools.Codecs
+ CUETools
+ A library for encoding and decoding audio.
+ Copyright (c) 2008-2021 Grigory Chudov
+ Grigory Chudov
+ true
+ ..\bin\$(Configuration)\
+ https://github.com/gchudov/cuetools.net
+ git
+
+
+
+
+
+
+
+
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/CUEToolsCodecsConfig.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/CUEToolsCodecsConfig.cs
new file mode 100644
index 000000000..0bf0103a0
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/CUEToolsCodecsConfig.cs
@@ -0,0 +1,94 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Text;
+using System.Threading;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace CUETools.Codecs
+{
+ public class CUEToolsCodecsConfig
+ {
+ [JsonIgnore]
+ public Dictionary formats;
+ public List encoders;
+ public List decoders;
+ [JsonIgnore]
+ public EncoderListViewModel encodersViewModel;
+ [JsonIgnore]
+ public DecoderListViewModel decodersViewModel;
+
+ public CUEToolsCodecsConfig()
+ {
+ encoders = new List();
+ decoders = new List();
+ encodersViewModel = new EncoderListViewModel(encoders);
+ decodersViewModel = new DecoderListViewModel(decoders);
+ formats = new Dictionary();
+ }
+
+ public CUEToolsCodecsConfig(CUEToolsCodecsConfig src)
+ {
+ encoders = new List();
+ decoders = new List();
+ src.encoders.ForEach(item => encoders.Add(item.Clone()));
+ src.decoders.ForEach(item => decoders.Add(item.Clone()));
+ encodersViewModel = new EncoderListViewModel(encoders);
+ decodersViewModel = new DecoderListViewModel(decoders);
+ formats = new Dictionary();
+ foreach (var fmt in src.formats)
+ formats.Add(fmt.Key, fmt.Value.Clone(this));
+ }
+
+ public void Init(List src_encoders, List src_decoders)
+ {
+ encoders = new List();
+ decoders = new List();
+ src_encoders.ForEach(item => encoders.Add(item.Clone()));
+ src_decoders.ForEach(item => decoders.Add(item.Clone()));
+
+ if (Type.GetType("Mono.Runtime", false) == null)
+ {
+ encoders.Add(new CommandLine.EncoderSettings("flake.exe", "flac", true, "0 1 2 3 4 5 6 7 8 9 10 11 12", "8", "flake.exe", "-%M - -o %O -p %P"));
+ encoders.Add(new CommandLine.EncoderSettings("takc.exe", "tak", true, "0 1 2 2e 2m 3 3e 3m 4 4e 4m", "2", "takc.exe", "-e -p%M -overwrite - %O"));
+ encoders.Add(new CommandLine.EncoderSettings("ffmpeg.exe", "m4a", true, "", "", "ffmpeg.exe", "-i - -f ipod -acodec alac -y %O"));
+ encoders.Add(new CommandLine.EncoderSettings("lame.exe (VBR)", "mp3", false, "V9 V8 V7 V6 V5 V4 V3 V2 V1 V0", "V2", "lame.exe", "--vbr-new -%M - %O"));
+ encoders.Add(new CommandLine.EncoderSettings("lame.exe (CBR)", "mp3", false, "96 128 192 256 320", "256", "lame.exe", "-m s -q 0 -b %M --noreplaygain - %O"));
+ encoders.Add(new CommandLine.EncoderSettings("oggenc.exe", "ogg", false, "-1 -0.5 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5 5.5 6 6.5 7 7.5 8", "3", "oggenc.exe", "-q %M - -o %O"));
+ encoders.Add(new CommandLine.EncoderSettings("opusenc.exe", "opus", false, "6 16 32 48 64 96 128 192 256", "128", "opusenc.exe", "--bitrate %M - %O"));
+ encoders.Add(new CommandLine.EncoderSettings("neroAacEnc.exe", "m4a", false, "0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9", "0.4", "neroAacEnc.exe", "-q %M -if - -of %O"));
+ encoders.Add(new CommandLine.EncoderSettings("qaac.exe (tvbr)", "m4a", false, "10 20 30 40 50 60 70 80 90 100 110 127", "80", "qaac.exe", "-s -V %M -q 2 - -o %O"));
+
+ decoders.Add(new CommandLine.DecoderSettings("takc.exe", "tak", "takc.exe", "-d %I -"));
+ decoders.Add(new CommandLine.DecoderSettings("ffmpeg.exe", "m4a", "ffmpeg.exe", "-v 0 -i %I -f wav -"));
+ }
+ else
+ {
+ // !!!
+ }
+
+ encodersViewModel = new EncoderListViewModel(encoders);
+ decodersViewModel = new DecoderListViewModel(decoders);
+
+ formats = new Dictionary();
+ formats.Add("flac", new CUEToolsFormat("flac", CUEToolsTagger.TagLibSharp, true, false, true, true, encodersViewModel.GetDefault("flac", true), null, decodersViewModel.GetDefault("flac")));
+ formats.Add("wv", new CUEToolsFormat("wv", CUEToolsTagger.TagLibSharp, true, false, true, true, encodersViewModel.GetDefault("wv", true), null, decodersViewModel.GetDefault("wv")));
+ formats.Add("ape", new CUEToolsFormat("ape", CUEToolsTagger.TagLibSharp, true, false, true, true, encodersViewModel.GetDefault("ape", true), null, decodersViewModel.GetDefault("ape")));
+ formats.Add("tta", new CUEToolsFormat("tta", CUEToolsTagger.APEv2, true, false, false, true, encodersViewModel.GetDefault("tta", true), null, decodersViewModel.GetDefault("tta")));
+ formats.Add("m2ts", new CUEToolsFormat("m2ts", CUEToolsTagger.APEv2, true, false, false, true, null, null, decodersViewModel.GetDefault("m2ts")));
+ formats.Add("mpls", new CUEToolsFormat("mpls", CUEToolsTagger.APEv2, true, false, false, true, null, null, decodersViewModel.GetDefault("mpls")));
+ formats.Add("wav", new CUEToolsFormat("wav", CUEToolsTagger.TagLibSharp, true, false, false, true, encodersViewModel.GetDefault("wav", true), null, decodersViewModel.GetDefault("wav")));
+ formats.Add("m4a", new CUEToolsFormat("m4a", CUEToolsTagger.TagLibSharp, true, true, false, true, encodersViewModel.GetDefault("m4a", true), encodersViewModel.GetDefault("m4a", false), decodersViewModel.GetDefault("m4a")));
+ formats.Add("tak", new CUEToolsFormat("tak", CUEToolsTagger.APEv2, true, false, true, true, encodersViewModel.GetDefault("tak", true), null, decodersViewModel.GetDefault("tak")));
+ formats.Add("wma", new CUEToolsFormat("wma", CUEToolsTagger.TagLibSharp, true, true, false, true, encodersViewModel.GetDefault("wma", true), encodersViewModel.GetDefault("wma", false), decodersViewModel.GetDefault("wma")));
+ formats.Add("mp3", new CUEToolsFormat("mp3", CUEToolsTagger.TagLibSharp, false, true, false, true, null, encodersViewModel.GetDefault("mp3", false), null));
+ formats.Add("ogg", new CUEToolsFormat("ogg", CUEToolsTagger.TagLibSharp, false, true, false, true, null, encodersViewModel.GetDefault("ogg", false), null));
+ formats.Add("opus", new CUEToolsFormat("opus", CUEToolsTagger.TagLibSharp, false, true, false, true, null, encodersViewModel.GetDefault("opus", false), null));
+ formats.Add("mlp", new CUEToolsFormat("mlp", CUEToolsTagger.APEv2, true, false, false, false, null, null, decodersViewModel.GetDefault("mlp")));
+ formats.Add("aob", new CUEToolsFormat("aob", CUEToolsTagger.APEv2, true, false, false, false, null, null, decodersViewModel.GetDefault("aob")));
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/CUEToolsFormat.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/CUEToolsFormat.cs
new file mode 100644
index 000000000..b42cf9b30
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/CUEToolsFormat.cs
@@ -0,0 +1,55 @@
+namespace CUETools.Codecs
+{
+ public class CUEToolsFormat
+ {
+ public CUEToolsFormat(
+ string _extension,
+ CUEToolsTagger _tagger,
+ bool _allowLossless,
+ bool _allowLossy,
+ bool _allowEmbed,
+ bool _builtin,
+ AudioEncoderSettingsViewModel _encoderLossless,
+ AudioEncoderSettingsViewModel _encoderLossy,
+ AudioDecoderSettingsViewModel _decoder)
+ {
+ extension = _extension;
+ tagger = _tagger;
+ allowLossless = _allowLossless;
+ allowLossy = _allowLossy;
+ allowEmbed = _allowEmbed;
+ builtin = _builtin;
+ encoderLossless = _encoderLossless;
+ encoderLossy = _encoderLossy;
+ decoder = _decoder;
+ }
+ public string DotExtension
+ {
+ get
+ {
+ return "." + extension;
+ }
+ }
+
+ public CUEToolsFormat Clone(CUEToolsCodecsConfig cfg)
+ {
+ var res = this.MemberwiseClone() as CUEToolsFormat;
+ if (decoder != null) cfg.decodersViewModel.TryGetValue(decoder.Settings.Extension, decoder.Settings.Name, out res.decoder);
+ if (encoderLossy != null) cfg.encodersViewModel.TryGetValue(encoderLossy.Settings.Extension, encoderLossy.Lossless, encoderLossy.Settings.Name, out res.encoderLossy);
+ if (encoderLossless != null) cfg.encodersViewModel.TryGetValue(encoderLossless.Settings.Extension, encoderLossless.Lossless, encoderLossless.Settings.Name, out res.encoderLossless);
+ return res;
+ }
+
+ public override string ToString()
+ {
+ return extension;
+ }
+
+ public string extension;
+ public AudioEncoderSettingsViewModel encoderLossless;
+ public AudioEncoderSettingsViewModel encoderLossy;
+ public AudioDecoderSettingsViewModel decoder;
+ public CUEToolsTagger tagger;
+ public bool allowLossless, allowLossy, allowEmbed, builtin;
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/CUEToolsTagger.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/CUEToolsTagger.cs
new file mode 100644
index 000000000..d0354bf3b
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/CUEToolsTagger.cs
@@ -0,0 +1,9 @@
+namespace CUETools.Codecs
+{
+ public enum CUEToolsTagger
+ {
+ TagLibSharp = 0,
+ APEv2 = 1,
+ ID3v2 = 2,
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/CommandLine/AudioDecoder.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/CommandLine/AudioDecoder.cs
new file mode 100644
index 000000000..91ea5c186
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/CommandLine/AudioDecoder.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace CUETools.Codecs.CommandLine
+{
+ public class AudioDecoder : IAudioSource
+ {
+ string _path;
+ Process _decoderProcess;
+ WAV.AudioDecoder rdr;
+
+ private DecoderSettings m_settings;
+ public IAudioDecoderSettings Settings => m_settings;
+
+ public long Position
+ {
+ get
+ {
+ Initialize();
+ return rdr.Position;
+ }
+ set
+ {
+ Initialize();
+ rdr.Position = value;
+ }
+ }
+
+ public TimeSpan Duration
+ {
+ get
+ {
+ Initialize();
+ return rdr.Duration;
+ }
+ }
+
+ public long Length
+ {
+ get
+ {
+ Initialize();
+ return rdr.Length;
+ }
+ }
+
+ public long Remaining
+ {
+ get
+ {
+ Initialize();
+ return rdr.Remaining;
+ }
+ }
+
+ public AudioPCMConfig PCM
+ {
+ get
+ {
+ Initialize();
+ return rdr.PCM;
+ }
+ }
+
+ public string Path { get { return _path; } }
+
+ public AudioDecoder(DecoderSettings settings, string path, Stream IO)
+ {
+ m_settings = settings;
+ _path = path;
+ _decoderProcess = null;
+ rdr = null;
+ }
+
+ void Initialize()
+ {
+ if (_decoderProcess != null)
+ return;
+ _decoderProcess = new Process();
+ _decoderProcess.StartInfo.FileName = m_settings.Path;
+ _decoderProcess.StartInfo.Arguments = m_settings.Parameters.Replace("%I", "\"" + _path + "\"");
+ _decoderProcess.StartInfo.CreateNoWindow = true;
+ _decoderProcess.StartInfo.RedirectStandardOutput = true;
+ _decoderProcess.StartInfo.UseShellExecute = false;
+ bool started = false;
+ Exception ex = null;
+ try
+ {
+ started = _decoderProcess.Start();
+ if (started)
+ _decoderProcess.PriorityClass = Process.GetCurrentProcess().PriorityClass;
+ }
+ catch (Exception _ex)
+ {
+ ex = _ex;
+ }
+ if (!started)
+ {
+ _decoderProcess = null;
+ throw new Exception(m_settings.Path + ": " + (ex == null ? "please check the path" : ex.Message));
+ }
+ rdr = new WAV.AudioDecoder(new WAV.DecoderSettings(), _path, _decoderProcess.StandardOutput.BaseStream);
+ }
+
+ public void Close()
+ {
+ if (rdr != null)
+ rdr.Close();
+ if (_decoderProcess != null && !_decoderProcess.HasExited)
+ try { _decoderProcess.Kill(); _decoderProcess.WaitForExit(); }
+ catch { }
+ }
+
+ public int Read(AudioBuffer buff, int maxLength)
+ {
+ Initialize();
+ return rdr.Read(buff, maxLength);
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/CommandLine/AudioEncoder.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/CommandLine/AudioEncoder.cs
new file mode 100644
index 000000000..24d23ad2d
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/CommandLine/AudioEncoder.cs
@@ -0,0 +1,137 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace CUETools.Codecs.CommandLine
+{
+ public class AudioEncoder : IAudioDest
+ {
+ string _path;
+ Process _encoderProcess;
+ WAV.AudioEncoder wrt;
+ CyclicBuffer outputBuffer = null;
+ bool useTempFile = false;
+ string tempFile = null;
+ long _finalSampleCount = -1;
+ bool closed = false;
+
+ public long Position
+ {
+ get
+ {
+ return wrt.Position;
+ }
+ }
+
+ public long FinalSampleCount
+ {
+ set { _finalSampleCount = wrt.FinalSampleCount = value; }
+ }
+
+ // !!!! Must not start the process in constructor, so that we can set CompressionLevel via Settings!
+ private EncoderSettings m_settings;
+ public IAudioEncoderSettings Settings => m_settings;
+
+ public string Path { get { return _path; } }
+
+ public AudioEncoder(EncoderSettings settings, string path, Stream IO = null)
+ {
+ m_settings = settings;
+ _path = path;
+ useTempFile = m_settings.Parameters.Contains("%I");
+ tempFile = path + ".tmp.wav";
+
+ _encoderProcess = new Process();
+ _encoderProcess.StartInfo.FileName = m_settings.Path;
+ _encoderProcess.StartInfo.Arguments = m_settings.Parameters.Replace("%O", "\"" + path + "\"").Replace("%M", m_settings.EncoderMode).Replace("%P", m_settings.Padding.ToString()).Replace("%I", "\"" + tempFile + "\"");
+ _encoderProcess.StartInfo.CreateNoWindow = true;
+ if (!useTempFile)
+ _encoderProcess.StartInfo.RedirectStandardInput = true;
+ _encoderProcess.StartInfo.UseShellExecute = false;
+ if (!m_settings.Parameters.Contains("%O"))
+ _encoderProcess.StartInfo.RedirectStandardOutput = true;
+ if (useTempFile)
+ {
+ wrt = new WAV.AudioEncoder(new WAV.EncoderSettings(settings.PCM), tempFile);
+ return;
+ }
+ bool started = false;
+ Exception ex = null;
+ try
+ {
+ started = _encoderProcess.Start();
+ if (started)
+ _encoderProcess.PriorityClass = Process.GetCurrentProcess().PriorityClass;
+ }
+ catch (Exception _ex)
+ {
+ ex = _ex;
+ }
+ if (!started)
+ throw new Exception(m_settings.Path + ": " + (ex == null ? "please check the path" : ex.Message));
+ if (_encoderProcess.StartInfo.RedirectStandardOutput)
+ {
+ Stream outputStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
+ outputBuffer = new CyclicBuffer(2 * 1024 * 1024, _encoderProcess.StandardOutput.BaseStream, outputStream);
+ }
+ Stream inputStream = new CyclicBufferOutputStream(_encoderProcess.StandardInput.BaseStream, 128 * 1024);
+ wrt = new WAV.AudioEncoder(new WAV.EncoderSettings(settings.PCM), path, inputStream);
+ }
+
+ public void Close()
+ {
+ if (closed)
+ return;
+ closed = true;
+ wrt.Close();
+ if (useTempFile && (_finalSampleCount < 0 || wrt.Position == _finalSampleCount))
+ {
+ bool started = false;
+ Exception ex = null;
+ try
+ {
+ started = _encoderProcess.Start();
+ if (started)
+ _encoderProcess.PriorityClass = Process.GetCurrentProcess().PriorityClass;
+ }
+ catch (Exception _ex)
+ {
+ ex = _ex;
+ }
+ if (!started)
+ throw new Exception(m_settings.Path + ": " + (ex == null ? "please check the path" : ex.Message));
+ }
+ wrt = null;
+ if (!_encoderProcess.HasExited)
+ _encoderProcess.WaitForExit();
+ if (useTempFile)
+ File.Delete(tempFile);
+ if (outputBuffer != null)
+ outputBuffer.Close();
+ if (_encoderProcess.ExitCode != 0)
+ throw new Exception(String.Format("{0} returned error code {1}", m_settings.Path, _encoderProcess.ExitCode));
+ }
+
+ public void Delete()
+ {
+ Close();
+ File.Delete(_path);
+ }
+
+ public void Write(AudioBuffer buff)
+ {
+ try
+ {
+ wrt.Write(buff);
+ }
+ catch (IOException ex)
+ {
+ if (_encoderProcess.HasExited)
+ throw new IOException(string.Format("{0} has exited prematurely with code {1}", m_settings.Path, _encoderProcess.ExitCode), ex);
+ else
+ throw ex;
+ }
+ //_sampleLen += sampleCount;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/CommandLine/DecoderSettings.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/CommandLine/DecoderSettings.cs
new file mode 100644
index 000000000..9bdba0d58
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/CommandLine/DecoderSettings.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.ComponentModel;
+using Newtonsoft.Json;
+
+namespace CUETools.Codecs.CommandLine
+{
+ [JsonObject(MemberSerialization.OptIn)]
+ public class DecoderSettings : IAudioDecoderSettings
+ {
+ #region IAudioDecoderSettings implementation
+ [DefaultValue("")]
+ [JsonProperty]
+ public string Name { get; set; }
+
+ [DefaultValue("")]
+ [JsonProperty]
+ public string Extension { get; set; }
+
+ [Browsable(false)]
+ public Type DecoderType => typeof(AudioDecoder);
+
+ [Browsable(false)]
+ public int Priority => 2;
+
+ public IAudioDecoderSettings Clone()
+ {
+ return MemberwiseClone() as IAudioDecoderSettings;
+ }
+ #endregion
+
+ public DecoderSettings()
+ {
+ this.Init();
+ }
+
+ public DecoderSettings(
+ string name,
+ string extension,
+ string path,
+ string parameters)
+ : base()
+ {
+ Name = name;
+ Extension = extension;
+ Path = path;
+ Parameters = parameters;
+ }
+
+ [DefaultValue("")]
+ [JsonProperty]
+ public string Path
+ {
+ get;
+ set;
+ }
+
+ [DefaultValue("")]
+ [JsonProperty]
+ public string Parameters
+ {
+ get;
+ set;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/CommandLine/EncoderSettings.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/CommandLine/EncoderSettings.cs
new file mode 100644
index 000000000..004e7bfb0
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/CommandLine/EncoderSettings.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.ComponentModel;
+using Newtonsoft.Json;
+
+namespace CUETools.Codecs.CommandLine
+{
+ [JsonObject(MemberSerialization.OptIn)]
+ public class EncoderSettings : IAudioEncoderSettings
+ {
+ #region IAudioEncoderSettings implementation
+ [DefaultValue("")]
+ [JsonProperty]
+ public string Name { get; set; }
+
+ [DefaultValue("")]
+ [JsonProperty]
+ public string Extension { get; set; }
+
+ [Browsable(false)]
+ public Type EncoderType => typeof(AudioEncoder);
+
+ [JsonProperty]
+ public bool Lossless { get; set; }
+
+ [Browsable(false)]
+ public int Priority => 0;
+
+ [DefaultValue("")]
+ [JsonProperty]
+ public string SupportedModes { get; set; }
+
+ public string DefaultMode => EncoderMode;
+
+ [Browsable(false)]
+ [DefaultValue("")]
+ [JsonProperty]
+ public string EncoderMode { get; set; }
+
+ [Browsable(false)]
+ public AudioPCMConfig PCM { get; set; }
+
+ [Browsable(false)]
+ public int BlockSize { get; set; }
+
+ [Browsable(false)]
+ [DefaultValue(4096)]
+ public int Padding { get; set; }
+
+ public IAudioEncoderSettings Clone()
+ {
+ return MemberwiseClone() as IAudioEncoderSettings;
+ }
+ #endregion
+
+ public EncoderSettings()
+ {
+ this.Init();
+ }
+
+ public EncoderSettings(
+ string name,
+ string extension,
+ bool lossless,
+ string supportedModes,
+ string defaultMode,
+ string path,
+ string parameters
+ )
+ {
+ this.Init();
+ Name = name;
+ Extension = extension;
+ Lossless = lossless;
+ SupportedModes = supportedModes;
+ Path = path;
+ EncoderMode = defaultMode;
+ Parameters = parameters;
+ }
+
+ [DefaultValue("")]
+ [JsonProperty]
+ public string Path
+ {
+ get;
+ set;
+ }
+
+ [DefaultValue("")]
+ [JsonProperty]
+ public string Parameters
+ {
+ get;
+ set;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/CyclicBuffer.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/CyclicBuffer.cs
new file mode 100644
index 000000000..5c68c2c89
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/CyclicBuffer.cs
@@ -0,0 +1,231 @@
+using System;
+using System.IO;
+using System.Threading;
+
+namespace CUETools.Codecs
+{
+ public class CyclicBuffer
+ {
+ private byte[] _buffer;
+ private int _size;
+ private int _start = 0; // moved only by Write
+ private int _end = 0; // moved only by Read
+ private bool _eof = false;
+ private Thread _readThread = null, _writeThread = null;
+ private Exception _ex = null;
+
+ private int DataAvailable
+ {
+ get
+ {
+ return _end - _start;
+ }
+ }
+
+ private int FreeSpace
+ {
+ get
+ {
+ return _size - DataAvailable;
+ }
+ }
+
+ public CyclicBuffer(int len)
+ {
+ _size = len;
+ _buffer = new byte[len];
+ }
+
+ public CyclicBuffer(int len, Stream input, Stream output)
+ {
+ _size = len;
+ _buffer = new byte[len];
+ ReadFrom(input);
+ WriteTo(output);
+ }
+
+ public void ReadFrom(Stream input)
+ {
+ _readThread = new Thread(PumpRead);
+ _readThread.Priority = ThreadPriority.Highest;
+ _readThread.IsBackground = true;
+ _readThread.Start(input);
+ }
+
+ public void WriteTo(Stream output)
+ {
+ WriteTo(flushOutputToStream, closeOutputToStream, ThreadPriority.Highest, output);
+ }
+
+ public void WriteTo(FlushOutput flushOutputDelegate, CloseOutput closeOutputDelegate, ThreadPriority priority, object to)
+ {
+ if (flushOutputDelegate != null)
+ flushOutput += flushOutputDelegate;
+ if (closeOutputDelegate != null)
+ closeOutput += closeOutputDelegate;
+ _writeThread = new Thread(FlushThread);
+ _writeThread.Priority = priority;
+ _writeThread.IsBackground = true;
+ _writeThread.Start(to);
+ }
+
+ private void closeOutputToStream(object to)
+ {
+ ((Stream)to).Close();
+ }
+
+ private void flushOutputToStream(byte[] buffer, int pos, int chunk, object to)
+ {
+ ((Stream)to).Write(buffer, pos, chunk);
+ }
+
+ private void PumpRead(object o)
+ {
+ while (Read((Stream)o))
+ ;
+ SetEOF();
+ }
+
+ public void Close()
+ {
+ if (_readThread != null)
+ {
+ _readThread.Join();
+ _readThread = null;
+ }
+ SetEOF();
+ if (_writeThread != null)
+ {
+ _writeThread.Join();
+ _writeThread = null;
+ }
+ }
+
+ public void SetEOF()
+ {
+ lock (this)
+ {
+ _eof = true;
+ Monitor.Pulse(this);
+ }
+ }
+
+ public bool Read(Stream input)
+ {
+ int pos, chunk;
+ lock (this)
+ {
+ while (FreeSpace == 0 && _ex == null)
+ Monitor.Wait(this);
+ if (_ex != null)
+ throw _ex;
+ pos = _end % _size;
+ chunk = Math.Min(FreeSpace, _size - pos);
+ }
+ chunk = input.Read(_buffer, pos, chunk);
+ if (chunk == 0)
+ return false;
+ lock (this)
+ {
+ _end += chunk;
+ Monitor.Pulse(this);
+ }
+ return true;
+ }
+
+ public void Read(byte[] array, int offset, int count)
+ {
+ int pos, chunk;
+ while (count > 0)
+ {
+ lock (this)
+ {
+ while (FreeSpace == 0 && _ex == null)
+ Monitor.Wait(this);
+ if (_ex != null)
+ throw _ex;
+ pos = _end % _size;
+ chunk = Math.Min(FreeSpace, _size - pos);
+ chunk = Math.Min(chunk, count);
+ }
+ Array.Copy(array, offset, _buffer, pos, chunk);
+ lock (this)
+ {
+ _end += chunk;
+ Monitor.Pulse(this);
+ }
+ count -= chunk;
+ offset += chunk;
+ }
+ }
+
+ public void Write(byte[] buff, int offs, int count)
+ {
+ while (count > 0)
+ {
+ int pos, chunk;
+ lock (this)
+ {
+ while (DataAvailable == 0 && !_eof)
+ Monitor.Wait(this);
+ if (DataAvailable == 0)
+ break;
+ pos = _start % _size;
+ chunk = Math.Min(DataAvailable, _size - pos);
+ }
+ if (flushOutput != null)
+ Array.Copy(_buffer, pos, buff, offs, chunk);
+ offs += chunk;
+ lock (this)
+ {
+ _start += chunk;
+ Monitor.Pulse(this);
+ }
+ }
+ }
+
+ private void FlushThread(object to)
+ {
+ while (true)
+ {
+ int pos, chunk;
+ lock (this)
+ {
+ while (DataAvailable == 0 && !_eof)
+ Monitor.Wait(this);
+ if (DataAvailable == 0)
+ break;
+ pos = _start % _size;
+ chunk = Math.Min(DataAvailable, _size - pos);
+ }
+ if (flushOutput != null)
+ try
+ {
+ flushOutput(_buffer, pos, chunk, to);
+ }
+ catch (Exception ex)
+ {
+ lock (this)
+ {
+ _ex = ex;
+ Monitor.Pulse(this);
+ return;
+ }
+ }
+ lock (this)
+ {
+ _start += chunk;
+ Monitor.Pulse(this);
+ }
+ }
+ if (closeOutput != null)
+ closeOutput(to);
+ }
+
+ public delegate void FlushOutput(byte[] buffer, int pos, int chunk, object to);
+ public delegate void CloseOutput(object to);
+
+ public event FlushOutput flushOutput;
+ public event CloseOutput closeOutput;
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/CyclicBufferInputStream.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/CyclicBufferInputStream.cs
new file mode 100644
index 000000000..05309a03e
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/CyclicBufferInputStream.cs
@@ -0,0 +1,75 @@
+using System;
+using System.IO;
+
+namespace CUETools.Codecs
+{
+ public class CyclicBufferInputStream : Stream
+ {
+ private CyclicBuffer _buffer;
+
+ public override bool CanRead
+ {
+ get { return true; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return false; }
+ }
+
+ public override long Length
+ {
+ get
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ public override long Position
+ {
+ get { throw new NotSupportedException(); }
+ set { throw new NotSupportedException(); }
+ }
+
+ public CyclicBufferInputStream(CyclicBuffer buffer)
+ {
+ _buffer = buffer;
+ }
+
+ public override void Close()
+ {
+ _buffer.Close();
+ }
+
+ public override void Flush()
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override int Read(byte[] array, int offset, int count)
+ {
+ _buffer.Write(array, offset, count);
+ return count;
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void Write(byte[] array, int offset, int count)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/CyclicBufferOutputStream.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/CyclicBufferOutputStream.cs
new file mode 100644
index 000000000..4c18c0dd7
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/CyclicBufferOutputStream.cs
@@ -0,0 +1,80 @@
+using System;
+using System.IO;
+
+namespace CUETools.Codecs
+{
+ public class CyclicBufferOutputStream : Stream
+ {
+ private CyclicBuffer _buffer;
+
+ public override bool CanRead
+ {
+ get { return false; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return true; }
+ }
+
+ public override long Length
+ {
+ get
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ public override long Position
+ {
+ get { throw new NotSupportedException(); }
+ set { throw new NotSupportedException(); }
+ }
+
+ public CyclicBufferOutputStream(CyclicBuffer buffer)
+ {
+ _buffer = buffer;
+ }
+
+ public CyclicBufferOutputStream(Stream output, int size)
+ {
+ _buffer = new CyclicBuffer(size);
+ _buffer.WriteTo(output);
+ }
+
+ public override void Close()
+ {
+ _buffer.Close();
+ }
+
+ public override void Flush()
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override int Read(byte[] array, int offset, int count)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void Write(byte[] array, int offset, int count)
+ {
+ _buffer.Read(array, offset, count);
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/DefaultValueForMode.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/DefaultValueForMode.cs
new file mode 100644
index 000000000..f99c88679
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/DefaultValueForMode.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace CUETools.Codecs
+{
+ ///
+ /// Default property value for each encoder mode attribute
+ ///
+ [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
+ public class DefaultValueForModeAttribute : Attribute
+ {
+ ///
+ /// Resource manager to use;
+ ///
+ public object[] m_values;
+
+ ///
+ /// Construct the description attribute
+ ///
+ ///
+ public DefaultValueForModeAttribute(params object[] values)
+ {
+ this.m_values = values;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/IAudioDest.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/IAudioDest.cs
new file mode 100644
index 000000000..018d27ae7
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/IAudioDest.cs
@@ -0,0 +1,14 @@
+namespace CUETools.Codecs
+{
+ public interface IAudioDest
+ {
+ IAudioEncoderSettings Settings { get; }
+
+ string Path { get; }
+ long FinalSampleCount { set; }
+
+ void Write(AudioBuffer buffer);
+ void Close();
+ void Delete();
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/IAudioFilter.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/IAudioFilter.cs
new file mode 100644
index 000000000..2f1eeceb4
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/IAudioFilter.cs
@@ -0,0 +1,7 @@
+namespace CUETools.Codecs
+{
+ public interface IAudioFilter
+ {
+ IAudioDest AudioDest { set; }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/IAudioSource.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/IAudioSource.cs
new file mode 100644
index 000000000..ef88362dc
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/IAudioSource.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+
+namespace CUETools.Codecs
+{
+ public interface IAudioSource
+ {
+ IAudioDecoderSettings Settings { get; }
+
+ AudioPCMConfig PCM { get; }
+ string Path { get; }
+
+ TimeSpan Duration { get; }
+ long Length { get; }
+ long Position { get; set; }
+ long Remaining { get; }
+
+ int Read(AudioBuffer buffer, int maxLength);
+ void Close();
+ }
+
+ public interface IAudioTitle
+ {
+ List Chapters { get; }
+ AudioPCMConfig PCM { get; }
+ string Codec { get; }
+ string Language { get; }
+ int StreamId { get; }
+ //IAudioSource Open { get; }
+ }
+
+ public interface IAudioTitleSet
+ {
+ List AudioTitles { get; }
+ }
+
+ public static class IAudioTitleExtensions
+ {
+ public static TimeSpan GetDuration(this IAudioTitle title)
+ {
+ var chapters = title.Chapters;
+ return chapters[chapters.Count - 1];
+ }
+
+
+ public static string GetRateString(this IAudioTitle title)
+ {
+ var sr = title.PCM.SampleRate;
+ if (sr % 1000 == 0) return $"{sr / 1000}KHz";
+ if (sr % 100 == 0) return $"{sr / 100}.{(sr / 100) % 10}KHz";
+ return $"{sr}Hz";
+ }
+
+ public static string GetFormatString(this IAudioTitle title)
+ {
+ switch (title.PCM.ChannelCount)
+ {
+ case 1: return "mono";
+ case 2: return "stereo";
+ default: return "multi-channel";
+ }
+ }
+ }
+
+ public class SingleAudioTitle : IAudioTitle
+ {
+ public SingleAudioTitle(IAudioSource source) { this.source = source; }
+ public List Chapters => new List { TimeSpan.Zero, source.Duration };
+ public AudioPCMConfig PCM => source.PCM;
+ public string Codec => source.Settings.Extension;
+ public string Language => "";
+ public int StreamId => 0;
+ IAudioSource source;
+ }
+
+ public class SingleAudioTitleSet : IAudioTitleSet
+ {
+ public SingleAudioTitleSet(IAudioSource source) { this.source = source; }
+ public List AudioTitles => new List { new SingleAudioTitle(source) };
+ IAudioSource source;
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/IWavePlayer.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/IWavePlayer.cs
new file mode 100644
index 000000000..6133d853c
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/IWavePlayer.cs
@@ -0,0 +1,41 @@
+using System;
+
+namespace CUETools.Codecs
+{
+ ///
+ /// Represents the interface to a device that can play a WaveFile
+ ///
+ public interface IWavePlayer : IDisposable, IAudioDest
+ {
+ ///
+ /// Begin playback
+ ///
+ void Play();
+
+ ///
+ /// Stop playback
+ ///
+ void Stop();
+
+ ///
+ /// Pause Playback
+ ///
+ void Pause();
+
+ ///
+ /// Current playback state
+ ///
+ PlaybackState PlaybackState { get; }
+
+ ///
+ /// The volume 1.0 is full scale
+ ///
+ float Volume { get; set; }
+
+ ///
+ /// Indicates that playback has gone into a stopped state due to
+ /// reaching the end of the input stream
+ ///
+ event EventHandler PlaybackStopped;
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/LPC.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/LPC.cs
new file mode 100644
index 000000000..90cc56d28
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/LPC.cs
@@ -0,0 +1,1182 @@
+using System;
+
+namespace CUETools.Codecs
+{
+ public class lpc
+ {
+ public const int MAX_LPC_ORDER = 32;
+ public const int MAX_LPC_WINDOWS = 16;
+ public const int MAX_LPC_PRECISIONS = 4;
+ public const int MAX_LPC_SECTIONS = 128;
+
+ public unsafe static void window_welch(float* window, int L)
+ {
+ int N = L - 1;
+ double N2 = (double)N / 2.0;
+
+ for (int n = 0; n <= N; n++)
+ {
+ double k = ((double)n - N2) / N2;
+ k = 1.0 - k * k;
+ window[n] = (float)(k);
+ }
+ }
+
+ public unsafe static void window_bartlett(float* window, int L)
+ {
+ int N = L - 1;
+ double N2 = (double)N / 2.0;
+ for (int n = 0; n <= N; n++)
+ {
+ double k = ((double)n - N2) / N2;
+ k = 1.0 - k * k;
+ window[n] = (float)(k * k);
+ }
+ }
+
+ public unsafe static void window_rectangle(float* window, int L)
+ {
+ for (int n = 0; n < L; n++)
+ window[n] = 1.0F;
+ }
+
+ public unsafe static void window_flattop(float* window, int L)
+ {
+ int N = L - 1;
+ for (int n = 0; n < L; n++)
+ window[n] = (float)(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));
+ }
+
+ public unsafe static void window_tukey(float* window, int L, double p)
+ {
+ int z = 0;
+ int Np = (int)(p / 2.0 * L) - z;
+ if (Np > 0)
+ {
+ for (int n = 0; n < z; n++)
+ window[n] = window[L - n - 1] = 0;
+ for (int n = 0; n < Np - 1; n++)
+ window[n + z] = window[L - n - 1 - z] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * (n + 1) / Np));
+ for (int n = z + Np - 1; n < L - z - Np + 1; n++)
+ window[n] = 1.0F;
+ }
+ }
+
+ public unsafe static void window_punchout_tukey(float* window, int L, double p, double p1, double start, double end)
+ {
+ int start_n = (int)(start * L);
+ int end_n = (int)(end * L);
+ int Np = (int)(p / 2.0 * L);
+ int Np1 = (int)(p1 / 2.0 * L);
+ int i, n = 0;
+
+ if (start_n != 0)
+ {
+ for (i = 1; n < Np; n++, i++)
+ window[n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * i / Np));
+ for (; n < start_n - Np1; n++)
+ window[n] = 1.0f;
+ for (i = Np1; n < start_n; n++, i--)
+ window[n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * i / Np1));
+ }
+ for (; n < end_n; n++)
+ window[n] = 0.0f;
+ if (end_n != L)
+ {
+ for (i = 1; n < end_n + Np1; n++, i++)
+ window[n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * i / Np1));
+ for (; n < L - Np; n++)
+ window[n] = 1.0f;
+ for (i = Np; n < L; n++, i--)
+ window[n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * i / Np));
+ }
+ }
+
+ public unsafe static void window_hann(float* window, int L)
+ {
+ int N = L - 1;
+ for (int n = 0; n < L; n++)
+ window[n] = (float)(0.5 - 0.5 * Math.Cos(2.0 * Math.PI * n / N));
+ }
+
+ private static short sign_only(int val)
+ {
+ return (short)((val >> 31) + ((val - 1) >> 31) + 1);
+ }
+
+#if XXX
+ static public unsafe void
+ compute_corr_int(/*const*/ short* data1, short* data2, int len, int min, int lag, int* autoc)
+ {
+ for (int i = min; i <= lag; ++i)
+ {
+ int temp = 0;
+ int temp2 = 0;
+
+ for (int j = 0; j <= lag - i; ++j)
+ temp += data1[j + i] * data2[j];
+
+ for (int j = lag + 1 - i; j < len - i; j += 2)
+ {
+ temp += data1[j + i] * data2[j];
+ temp2 += data1[j + i + 1] * data2[j + 1];
+ }
+ autoc[i] = temp + temp2;
+ }
+ }
+#endif
+
+ /**
+ * Calculates autocorrelation data from audio samples
+ * A window function is applied before calculation.
+ */
+ static public unsafe void
+ compute_autocorr(/*const*/ int* data, float* window, int len, int min, int lag, double* autoc)
+ {
+#if FPAC
+ short* data1 = stackalloc short[len + 1];
+ short* data2 = stackalloc short[len + 1];
+ int* c1 = stackalloc int[lpc.MAX_LPC_ORDER + 1];
+ int* c2 = stackalloc int[lpc.MAX_LPC_ORDER + 1];
+ int* c3 = stackalloc int[lpc.MAX_LPC_ORDER + 1];
+ int* c4 = stackalloc int[lpc.MAX_LPC_ORDER + 1];
+
+ for (int i = 0; i < len; i++)
+ {
+ int val = (int)(data[i] * window[i]);
+ data1[i] = (short)(sign_only(val) * (Math.Abs(val) >> 9));
+ data2[i] = (short)(sign_only(val) * (Math.Abs(val) & 0x1ff));
+ }
+ data1[len] = 0;
+ data2[len] = 0;
+
+ compute_corr_int(data1, data1, len, min, lag, c1);
+ compute_corr_int(data1, data2, len, min, lag, c2);
+ compute_corr_int(data2, data1, len, min, lag, c3);
+ compute_corr_int(data2, data2, len, min, lag, c4);
+
+ for (int coeff = min; coeff <= lag; coeff++)
+ autoc[coeff] = (c1[coeff] * (double)(1 << 18) + (c2[coeff] + c3[coeff]) * (double)(1 << 9) + c4[coeff]);
+#else
+#if XXX
+ if (min == 0 && lag >= 4)
+ {
+ int* pdata = data;
+ float* pwindow = window;
+
+ double temp0 = 1.0;
+ double temp1 = 1.0;
+ double temp2 = 1.0;
+ double temp3 = 1.0;
+ double temp4 = 1.0;
+
+ double c0 = *(pdata++) * *(pwindow++);
+ float c1 = *(pdata++) * *(pwindow++);
+ float c2 = *(pdata++) * *(pwindow++);
+ float c3 = *(pdata++) * *(pwindow++);
+ float c4 = *(pdata++) * *(pwindow++);
+
+ int* finish = data + len;
+
+ while (pdata <= finish)
+ {
+ temp0 += c0 * c0;
+ temp1 += c0 * c1;
+ temp2 += c0 * c2;
+ temp3 += c0 * c3;
+ temp4 += c0 * c4;
+
+ c0 = c1;
+ c1 = c2;
+ c2 = c3;
+ c3 = c4;
+ c4 = *(pdata++) * *(pwindow++);
+ }
+
+ temp0 += c0 * c0;
+ temp1 += c0 * c1;
+ temp2 += c0 * c2;
+ temp3 += c0 * c3;
+ c0 = c1;
+ c1 = c2;
+ c2 = c3;
+ temp0 += c0 * c0;
+ temp1 += c0 * c1;
+ temp2 += c0 * c2;
+ c0 = c1;
+ c1 = c2;
+ temp0 += c0 * c0;
+ temp1 += c0 * c1;
+ c0 = c1;
+ temp0 += c0 * c0;
+
+ autoc[0] += temp0;
+ autoc[1] += temp1;
+ autoc[2] += temp2;
+ autoc[3] += temp3;
+ autoc[4] += temp4;
+ min = 5;
+
+ if (lag < min) return;
+ }
+#endif
+ double* data1 = stackalloc double[len];
+ int i;
+
+ for (i = 0; i < len; i++)
+ data1[i] = data[i] * window[i];
+
+ for (i = min; i <= lag; ++i)
+ {
+ double temp = 0;
+ double temp2 = 0;
+ double* pdata = data1;
+ double* finish = data1 + len - 1 - i;
+
+ while (pdata < finish)
+ {
+ temp += pdata[i] * (*pdata++);
+ temp2 += pdata[i] * (*pdata++);
+ }
+ if (pdata <= finish)
+ temp += pdata[i] * (*pdata++);
+
+ autoc[i] += temp + temp2;
+ }
+#endif
+ }
+
+ static public unsafe void
+ compute_autocorr_windowless(/*const*/ int* data, int len, int min, int lag, double* autoc)
+ {
+ // if databits*2 + log2(len) <= 64
+#if !XXX
+#if XXX
+ if (min == 0 && lag >= 4)
+ {
+ long temp0 = 0;
+ long temp1 = 0;
+ long temp2 = 0;
+ long temp3 = 0;
+ long temp4 = 0;
+ int* pdata = data;
+ int* finish = data + len - 4;
+ while (pdata < finish)
+ {
+ long c0 = *(pdata++);
+ temp0 += c0 * c0;
+ temp1 += c0 * pdata[0];
+ temp2 += c0 * pdata[1];
+ temp3 += c0 * pdata[2];
+ temp4 += c0 * pdata[3];
+ }
+ {
+ long c0 = *(pdata++);
+ temp0 += c0 * c0;
+ temp1 += c0 * pdata[0];
+ temp2 += c0 * pdata[1];
+ temp3 += c0 * pdata[2];
+ }
+ {
+ long c0 = *(pdata++);
+ temp0 += c0 * c0;
+ temp1 += c0 * pdata[0];
+ temp2 += c0 * pdata[1];
+ }
+ {
+ long c0 = *(pdata++);
+ temp0 += c0 * c0;
+ temp1 += c0 * pdata[0];
+ }
+ {
+ long c0 = *(pdata++);
+ temp0 += c0 * c0;
+ }
+ autoc[0] += temp0;
+ autoc[1] += temp1;
+ autoc[2] += temp2;
+ autoc[3] += temp3;
+ autoc[4] += temp4;
+ min = 5;
+
+ if (lag < min) return;
+ }
+#endif
+ for (int i = min; i <= lag; ++i)
+ {
+ long temp = 0;
+ long temp2 = 0;
+ int* pdata = data;
+ int* finish = data + len - i - 1;
+ while (pdata < finish)
+ {
+ temp += (long)pdata[i] * (*pdata++);
+ temp2 += (long)pdata[i] * (*pdata++);
+ }
+ if (pdata <= finish)
+ temp += (long)pdata[i] * (*pdata++);
+ autoc[i] += temp + temp2;
+ }
+#else
+ for (int i = min; i <= lag; ++i)
+ {
+ double temp = 0;
+ double temp2 = 0;
+ int* pdata = data;
+ int* finish = data + len - i - 1;
+
+ while (pdata < finish)
+ {
+ temp += (double)pdata[i] * (double)(*pdata++);
+ temp2 += (double)pdata[i] * (double)(*pdata++);
+ }
+ if (pdata <= finish)
+ temp += (double)pdata[i] * (double)(*pdata++);
+ autoc[i] += temp + temp2;
+ }
+#endif
+ }
+
+ static public unsafe void
+ compute_autocorr_windowless_large(/*const*/ int* data, int len, int min, int lag, double* autoc)
+ {
+ for (int i = min; i <= lag; ++i)
+ {
+ double temp = 0;
+ double temp2 = 0;
+ int* pdata = data;
+ int* finish = data + len - i - 1;
+ while (pdata < finish)
+ {
+ temp += (long)pdata[i] * (*pdata++);
+ temp2 += (long)pdata[i] * (*pdata++);
+ }
+ if (pdata <= finish)
+ temp += (long)pdata[i] * (*pdata++);
+ autoc[i] += temp + temp2;
+ }
+ }
+
+ static public unsafe void
+ compute_autocorr_glue(/*const*/ int* data, float* window, int offs, int offs1, int min, int lag, double* autoc)
+ {
+ double* data1 = stackalloc double[lag + lag];
+ for (int i = -lag; i < lag; i++)
+ data1[i + lag] = offs + i >= 0 && offs + i < offs1 ? data[offs + i] * window[offs + i] : 0;
+ for (int i = min; i <= lag; ++i)
+ {
+ double temp = 0;
+ double* pdata = data1 + lag - i;
+ double* finish = data1 + lag;
+ while (pdata < finish)
+ temp += pdata[i] * (*pdata++);
+ autoc[i] += temp;
+ }
+ }
+
+ static public unsafe void
+ compute_autocorr_glue(/*const*/ int* data, int min, int lag, double* autoc)
+ {
+ for (int i = min; i <= lag; ++i)
+ {
+ long temp = 0;
+ int* pdata = data - i;
+ int* finish = data;
+ while (pdata < finish)
+ temp += (long)pdata[i] * (*pdata++);
+ autoc[i] += temp;
+ }
+ }
+
+ /**
+ * Levinson-Durbin recursion.
+ * Produces LPC coefficients from autocorrelation data.
+ */
+ public static unsafe void
+ compute_lpc_coefs(uint max_order, double* reff, float* lpc/*[][MAX_LPC_ORDER]*/)
+ {
+ double* lpc_tmp = stackalloc double[MAX_LPC_ORDER];
+
+ if (max_order > MAX_LPC_ORDER)
+ throw new Exception("weird");
+
+ for (int i = 0; i < max_order; i++)
+ lpc_tmp[i] = 0;
+
+ for (int i = 0; i < max_order; i++)
+ {
+ double r = reff[i];
+ int i2 = (i >> 1);
+ lpc_tmp[i] = r;
+ for (int j = 0; j < i2; j++)
+ {
+ double tmp = lpc_tmp[j];
+ lpc_tmp[j] += r * lpc_tmp[i - 1 - j];
+ lpc_tmp[i - 1 - j] += r * tmp;
+ }
+
+ if (0 != (i & 1))
+ lpc_tmp[i2] += lpc_tmp[i2] * r;
+
+ for (int j = 0; j <= i; j++)
+ lpc[i * MAX_LPC_ORDER + j] = (float)-lpc_tmp[j];
+ }
+ }
+
+ public static unsafe void
+ compute_schur_reflection(/*const*/ double* autoc, uint max_order,
+ double* reff/*[][MAX_LPC_ORDER]*/, double * err)
+ {
+ double* gen0 = stackalloc double[MAX_LPC_ORDER];
+ double* gen1 = stackalloc double[MAX_LPC_ORDER];
+
+ // Schur recursion
+ for (uint i = 0; i < max_order; i++)
+ gen0[i] = gen1[i] = autoc[i + 1];
+
+ double error = autoc[0];
+ reff[0] = -gen1[0] / error;
+ error += gen1[0] * reff[0];
+ err[0] = error;
+ for (uint i = 1; i < max_order; i++)
+ {
+ for (uint j = 0; j < max_order - i; j++)
+ {
+ gen1[j] = gen1[j + 1] + reff[i - 1] * gen0[j];
+ gen0[j] = gen1[j + 1] * reff[i - 1] + gen0[j];
+ }
+ reff[i] = -gen1[0] / error;
+ error += gen1[0] * reff[i];
+ err[i] = error;
+ }
+ }
+
+ /**
+ * Quantize LPC coefficients
+ */
+ public static unsafe void
+ quantize_lpc_coefs(float* lpc_in, int order, uint precision, int* lpc_out,
+ out int shift, int max_shift, int zero_shift)
+ {
+ int i;
+ float d, cmax, error;
+ int qmax;
+ int sh, q;
+
+ // define maximum levels
+ qmax = (1 << ((int)precision - 1)) - 1;
+
+ // find maximum coefficient value
+ cmax = 0.0F;
+ for (i = 0; i < order; i++)
+ {
+ d = Math.Abs(lpc_in[i]);
+ if (d > cmax)
+ cmax = d;
+ }
+ // if maximum value quantizes to zero, return all zeros
+ if (cmax * (1 << max_shift) < 1.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 = max_shift;
+ while ((cmax * (1 << sh) > qmax) && (sh > 0))
+ {
+ sh--;
+ }
+
+ // since negative shift values are unsupported in decoder, scale down
+ // coefficients instead
+ if (sh == 0 && cmax > qmax)
+ {
+ float scale = ((float)qmax) / cmax;
+ for (i = 0; i < order; i++)
+ {
+ lpc_in[i] *= scale;
+ }
+ }
+
+ // output quantized coefficients and level shift
+ error = 0;
+ for (i = 0; i < order; i++)
+ {
+ error += lpc_in[i] * (1 << sh);
+ q = (int)(error + 0.5);
+ if (q < -(qmax+1)) q = -(qmax + 1);
+ if (q > qmax) q = qmax;
+ error -= q;
+ lpc_out[i] = q;
+ }
+ shift = sh;
+ }
+
+ private static unsafe ulong
+ encode_residual_partition(int* s, int* r, int* seg_end, int* coefs, int shift, int order)
+ {
+ ulong sum = 0ul;
+ int c0 = coefs[0];
+ int c1 = coefs[1];
+ switch (order)
+ {
+ case 1:
+ while (s < seg_end)
+ {
+ int pred = c0 * *(s++);
+ //*(r++) = *s - (pred >> shift);
+ int d = *(r++) = *s - (pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ }
+ break;
+ case 2:
+ while (s < seg_end)
+ {
+ int pred = c1 * *(s++);
+ pred += c0 * *(s++);
+ int d = *(r++) = *(s--) - (pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ }
+ break;
+ case 3:
+ while (s < seg_end)
+ {
+ int pred = coefs[2] * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ int d = *(r++) = *s - (pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 2;
+ }
+ break;
+ case 4:
+ while (s < seg_end)
+ {
+ int* c = coefs + order - 1;
+ int pred =
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ int d = *(r++) = *s - (pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 3;
+ }
+ break;
+ case 5:
+ while (s < seg_end)
+ {
+ int* c = coefs + order - 1;
+ int pred =
+ *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ int d = *(r++) = *s - (pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 4;
+ }
+ break;
+ case 6:
+ while (s < seg_end)
+ {
+ int* c = coefs + order - 1;
+ int pred =
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ int d = *(r++) = *s - (pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 5;
+ }
+ break;
+ case 7:
+ while (s < seg_end)
+ {
+ int* c = coefs + order - 1;
+ int pred =
+ *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ int d = *(r++) = *s - (pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 6;
+ }
+ break;
+ case 8:
+ while (s < seg_end)
+ {
+ int* c = coefs + order - 1;
+ int pred =
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ int d = *(r++) = *s - (pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 7;
+ }
+ break;
+ case 9:
+ while (s < seg_end)
+ {
+ int* c = coefs + order - 1;
+ int pred =
+ *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ int d = *(r++) = *s - (pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 8;
+ }
+ break;
+ case 10:
+ while (s < seg_end)
+ {
+ int* c = coefs + order - 1;
+ int pred =
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ int d = *(r++) = *s - (pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 9;
+ }
+ break;
+ case 11:
+ while (s < seg_end)
+ {
+ int* c = coefs + order - 1;
+ int pred =
+ *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ int d = *(r++) = *s - (pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 10;
+ }
+ break;
+ case 12:
+ while (s < seg_end)
+ {
+ int* c = coefs + order - 1;
+ int pred =
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ int d = *(r++) = *s - (pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 11;
+ }
+ break;
+ default:
+ while (s < seg_end)
+ {
+ int pred = 0;
+ int* c = coefs + order - 1;
+ int* c11 = coefs + 11;
+ while (c > c11)
+ pred += *(c--) * *(s++);
+ pred +=
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ *(c--) * *(s++) + *(c--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ int d = *(r++) = *s - (pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= order - 1;
+ }
+ break;
+ }
+ return sum;
+ }
+
+ public static unsafe void
+ encode_residual(int* res, int* smp, int n, int order,
+ int* coefs, int shift, ulong* sums, int pmax)
+ {
+ for (int i = 0; i < order; i++)
+ res[i] = smp[i];
+
+ int* s = smp;
+ int* s_end = smp + n - order;
+ int* seg_end = s + (n >> pmax) - order;
+ int* r = res + order;
+ while (s < s_end)
+ {
+ *(sums++) = encode_residual_partition(s, r, seg_end, coefs, shift, order);
+ r += seg_end - s;
+ s = seg_end;
+ seg_end += n >> pmax;
+ }
+ }
+
+ private static unsafe ulong
+ encode_residual_long_partition(int* s, int* r, int* seg_end, int* coefs, int shift, int order)
+ {
+ ulong sum = 0ul;
+ int c0 = coefs[0];
+ int c1 = coefs[1];
+ switch (order)
+ {
+ case 1:
+ while (s < seg_end)
+ {
+ long pred = c0 * (long)*(s++);
+ int d = *(r++) = *s - (int)(pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ }
+ break;
+ case 2:
+ while (s < seg_end)
+ {
+ long pred = c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ int d = *(r++) = *(s--) - (int)(pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ }
+ break;
+ case 3:
+ while (s < seg_end)
+ {
+ long pred = coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ int d = *(r++) = *s - (int)(pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 2;
+ }
+ break;
+ case 4:
+ while (s < seg_end)
+ {
+ long pred = coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ int d = *(r++) = *s - (int)(pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 3;
+ }
+ break;
+ case 5:
+ while (s < seg_end)
+ {
+ long pred = coefs[4] * (long)*(s++);
+ pred += coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ int d = *(r++) = *s - (int)(pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 4;
+ }
+ break;
+ case 6:
+ while (s < seg_end)
+ {
+ long pred = coefs[5] * (long)*(s++);
+ pred += coefs[4] * (long)*(s++);
+ pred += coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ int d = *(r++) = *s - (int)(pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 5;
+ }
+ break;
+ case 7:
+ while (s < seg_end)
+ {
+ long pred = coefs[6] * (long)*(s++);
+ pred += coefs[5] * (long)*(s++);
+ pred += coefs[4] * (long)*(s++);
+ pred += coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ int d = *(r++) = *s - (int)(pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 6;
+ }
+ break;
+ case 8:
+ while (s < seg_end)
+ {
+ long pred = coefs[7] * (long)*(s++);
+ pred += coefs[6] * (long)*(s++);
+ pred += coefs[5] * (long)*(s++);
+ pred += coefs[4] * (long)*(s++);
+ pred += coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ int d = *(r++) = *s - (int)(pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= 7;
+ }
+ break;
+ default:
+ while (s < seg_end)
+ {
+ long pred = 0;
+ int* co = coefs + order - 1;
+ int* c7 = coefs + 7;
+ while (co > c7)
+ pred += *(co--) * (long)*(s++);
+ pred += coefs[7] * (long)*(s++);
+ pred += coefs[6] * (long)*(s++);
+ pred += coefs[5] * (long)*(s++);
+ pred += coefs[4] * (long)*(s++);
+ pred += coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ int d = *(r++) = *s - (int)(pred >> shift);
+ sum += (uint)((d << 1) ^ (d >> 31));
+ s -= order - 1;
+ }
+ break;
+ }
+ return sum;
+ }
+
+ public static unsafe void
+ encode_residual_long(int* res, int* smp, int n, int order,
+ int* coefs, int shift, ulong* sums, int pmax)
+ {
+ for (int i = 0; i < order; i++)
+ res[i] = smp[i];
+
+ int* s = smp;
+ int* s_end = smp + n - order;
+ int* seg_end = s + (n >> pmax) - order;
+ int* r = res + order;
+ while (s < s_end)
+ {
+ *(sums++) = encode_residual_long_partition(s, r, seg_end, coefs, shift, order);
+ r += seg_end - s;
+ s = seg_end;
+ seg_end += n >> pmax;
+ }
+ }
+
+ public static unsafe void
+ decode_residual(int* res, int* smp, int n, int order,
+ int* coefs, int shift)
+ {
+ for (int i = 0; i < order; i++)
+ smp[i] = res[i];
+
+ int* s = smp;
+ int* r = res + order;
+ int c0 = coefs[0];
+ int c1 = coefs[1];
+ switch (order)
+ {
+ case 1:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ }
+ break;
+ case 2:
+ for (int i = n - order; i > 0; i--)
+ {
+ int pred = c1 * *(s++) + c0 * *(s++);
+ *(s--) = *(r++) + (pred >> shift);
+ }
+ break;
+ case 3:
+ for (int i = n - order; i > 0; i--)
+ {
+ int* co = coefs + order - 1;
+ int pred =
+ *(co--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 2;
+ }
+ break;
+ case 4:
+ for (int i = n - order; i > 0; i--)
+ {
+ int* co = coefs + order - 1;
+ int pred =
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 3;
+ }
+ break;
+ case 5:
+ for (int i = n - order; i > 0; i--)
+ {
+ int* co = coefs + order - 1;
+ int pred =
+ *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 4;
+ }
+ break;
+ case 6:
+ for (int i = n - order; i > 0; i--)
+ {
+ int* co = coefs + order - 1;
+ int pred =
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 5;
+ }
+ break;
+ case 7:
+ for (int i = n - order; i > 0; i--)
+ {
+ int* co = coefs + order - 1;
+ int pred =
+ *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 6;
+ }
+ break;
+ case 8:
+ for (int i = n - order; i > 0; i--)
+ {
+ int* co = coefs + order - 1;
+ int pred =
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 7;
+ }
+ break;
+ case 9:
+ for (int i = n - order; i > 0; i--)
+ {
+ int* co = coefs + order - 1;
+ int pred =
+ *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 8;
+ }
+ break;
+ case 10:
+ for (int i = n - order; i > 0; i--)
+ {
+ int* co = coefs + order - 1;
+ int pred =
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 9;
+ }
+ break;
+ case 11:
+ for (int i = n - order; i > 0; i--)
+ {
+ int* co = coefs + order - 1;
+ int pred =
+ *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 10;
+ }
+ break;
+ case 12:
+ for (int i = n - order; i > 0; i--)
+ {
+ int* co = coefs + order - 1;
+ int pred =
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ *(co--) * *(s++) + *(co--) * *(s++) +
+ c1 * *(s++) + c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ s -= 11;
+ }
+ break;
+ default:
+ for (int i = order; i < n; i++)
+ {
+ s = smp + i - order;
+ int pred = 0;
+ int* co = coefs + order - 1;
+ int* c7 = coefs + 7;
+ while (co > c7)
+ pred += *(co--) * *(s++);
+ pred += coefs[7] * *(s++);
+ pred += coefs[6] * *(s++);
+ pred += coefs[5] * *(s++);
+ pred += coefs[4] * *(s++);
+ pred += coefs[3] * *(s++);
+ pred += coefs[2] * *(s++);
+ pred += c1 * *(s++);
+ pred += c0 * *(s++);
+ *s = *(r++) + (pred >> shift);
+ }
+ break;
+ }
+ }
+ public static unsafe void
+ decode_residual_long(int* res, int* smp, int n, int order,
+ int* coefs, int shift)
+ {
+ for (int i = 0; i < order; i++)
+ smp[i] = res[i];
+
+ int* s = smp;
+ int* r = res + order;
+ int c0 = coefs[0];
+ int c1 = coefs[1];
+ switch (order)
+ {
+ case 1:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ }
+ break;
+ case 2:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *(s--) = *(r++) + (int)(pred >> shift);
+ }
+ break;
+ case 3:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ s -= 2;
+ }
+ break;
+ case 4:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ s -= 3;
+ }
+ break;
+ case 5:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = coefs[4] * (long)*(s++);
+ pred += coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ s -= 4;
+ }
+ break;
+ case 6:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = coefs[5] * (long)*(s++);
+ pred += coefs[4] * (long)*(s++);
+ pred += coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ s -= 5;
+ }
+ break;
+ case 7:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = coefs[6] * (long)*(s++);
+ pred += coefs[5] * (long)*(s++);
+ pred += coefs[4] * (long)*(s++);
+ pred += coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ s -= 6;
+ }
+ break;
+ case 8:
+ for (int i = n - order; i > 0; i--)
+ {
+ long pred = coefs[7] * (long)*(s++);
+ pred += coefs[6] * (long)*(s++);
+ pred += coefs[5] * (long)*(s++);
+ pred += coefs[4] * (long)*(s++);
+ pred += coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ s -= 7;
+ }
+ break;
+ default:
+ for (int i = order; i < n; i++)
+ {
+ s = smp + i - order;
+ long pred = 0;
+ int* co = coefs + order - 1;
+ int* c7 = coefs + 7;
+ while (co > c7)
+ pred += *(co--) * (long)*(s++);
+ pred += coefs[7] * (long)*(s++);
+ pred += coefs[6] * (long)*(s++);
+ pred += coefs[5] * (long)*(s++);
+ pred += coefs[4] * (long)*(s++);
+ pred += coefs[3] * (long)*(s++);
+ pred += coefs[2] * (long)*(s++);
+ pred += c1 * (long)*(s++);
+ pred += c0 * (long)*(s++);
+ *s = *(r++) + (int)(pred >> shift);
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/License.txt b/Aaru.Compression/cuetools.net/CUETools.Codecs/License.txt
new file mode 100644
index 000000000..389f55a8b
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/License.txt
@@ -0,0 +1,16 @@
+CUETools.Codecs: common audio encoder/decoder routines
+Copyright (c) 2009-2021 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
\ No newline at end of file
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/LpcContext.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/LpcContext.cs
new file mode 100644
index 000000000..295a8c640
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/LpcContext.cs
@@ -0,0 +1,393 @@
+using System;
+using System.Collections.Generic;
+
+namespace CUETools.Codecs
+{
+ unsafe public class LpcSubframeInfo
+ {
+ public LpcSubframeInfo()
+ {
+ autocorr_section_values = new double[lpc.MAX_LPC_SECTIONS, lpc.MAX_LPC_ORDER + 1];
+ autocorr_section_orders = new int[lpc.MAX_LPC_SECTIONS];
+ }
+
+ // public LpcContext[] lpc_ctx;
+ public double[,] autocorr_section_values;
+ public int[] autocorr_section_orders;
+ //public int obits;
+
+ public void Reset()
+ {
+ for (int sec = 0; sec < autocorr_section_orders.Length; sec++)
+ autocorr_section_orders[sec] = 0;
+ }
+ }
+
+ unsafe public struct LpcWindowSection
+ {
+ public enum SectionType
+ {
+ Zero,
+ One,
+ OneLarge,
+ Data,
+ OneGlue,
+ Glue
+ };
+ public int m_start;
+ public int m_end;
+ public SectionType m_type;
+ public int m_id;
+ public LpcWindowSection(int end)
+ {
+ m_id = -1;
+ m_start = 0;
+ m_end = end;
+ m_type = SectionType.Data;
+ }
+ public void setData(int start, int end)
+ {
+ m_id = -1;
+ m_start = start;
+ m_end = end;
+ m_type = SectionType.Data;
+ }
+ public void setOne(int start, int end)
+ {
+ m_id = -1;
+ m_start = start;
+ m_end = end;
+ m_type = SectionType.One;
+ }
+ public void setGlue(int start)
+ {
+ m_id = -1;
+ m_start = start;
+ m_end = start;
+ m_type = SectionType.Glue;
+ }
+ public void setZero(int start, int end)
+ {
+ m_id = -1;
+ m_start = start;
+ m_end = end;
+ m_type = SectionType.Zero;
+ }
+
+ unsafe public void compute_autocorr(/*const*/ int* data, float* window, int min_order, int order, int blocksize, double* autoc)
+ {
+ if (m_type == SectionType.OneLarge)
+ lpc.compute_autocorr_windowless_large(data + m_start, m_end - m_start, min_order, order, autoc);
+ else if (m_type == SectionType.One)
+ lpc.compute_autocorr_windowless(data + m_start, m_end - m_start, min_order, order, autoc);
+ else if (m_type == SectionType.Data)
+ lpc.compute_autocorr(data + m_start, window + m_start, m_end - m_start, min_order, order, autoc);
+ else if (m_type == SectionType.Glue)
+ lpc.compute_autocorr_glue(data, window, m_start, m_end, min_order, order, autoc);
+ else if (m_type == SectionType.OneGlue)
+ lpc.compute_autocorr_glue(data + m_start, min_order, order, autoc);
+ }
+
+ unsafe public static void Detect(int _windowcount, float* window_segment, int stride, int sz, int bps, LpcWindowSection* sections)
+ {
+ int section_id = 0;
+ var boundaries = new List();
+ var types = new LpcWindowSection.SectionType[_windowcount, lpc.MAX_LPC_SECTIONS * 2];
+ var alias = new int[_windowcount, lpc.MAX_LPC_SECTIONS * 2];
+ var alias_set = new int[_windowcount, lpc.MAX_LPC_SECTIONS * 2];
+ for (int x = 0; x < sz; x++)
+ {
+ for (int i = 0; i < _windowcount; i++)
+ {
+ int a = alias[i, boundaries.Count];
+ float w = window_segment[i * stride + x];
+ float wa = window_segment[a * stride + x];
+ if (wa != w)
+ {
+ for (int i1 = i; i1 < _windowcount; i1++)
+ if (alias[i1, boundaries.Count] == a
+ && w == window_segment[i1 * stride + x])
+ alias[i1, boundaries.Count] = i;
+ }
+ if (boundaries.Count >= lpc.MAX_LPC_SECTIONS * 2) throw new IndexOutOfRangeException();
+ types[i, boundaries.Count] =
+ boundaries.Count >= lpc.MAX_LPC_SECTIONS * 2 - 2 ?
+ LpcWindowSection.SectionType.Data : w == 0.0 ?
+ LpcWindowSection.SectionType.Zero : w != 1.0 ?
+ LpcWindowSection.SectionType.Data : bps * 2 + BitReader.log2i(sz) >= 61 ?
+ LpcWindowSection.SectionType.OneLarge :
+ LpcWindowSection.SectionType.One ;
+ }
+ bool isBoundary = false;
+ for (int i = 0; i < _windowcount; i++)
+ {
+ isBoundary |= boundaries.Count == 0 ||
+ types[i, boundaries.Count - 1] != types[i, boundaries.Count];
+ }
+ if (isBoundary)
+ {
+ for (int i = 0; i < _windowcount; i++)
+ for (int i1 = 0; i1 < _windowcount; i1++)
+ if (i != i1 && alias[i, boundaries.Count] == alias[i1, boundaries.Count])
+ alias_set[i, boundaries.Count] |= 1 << i1;
+ boundaries.Add(x);
+ }
+ }
+ boundaries.Add(sz);
+ var secs = new int[_windowcount];
+ // Reconstruct segments list.
+ for (int j = 0; j < boundaries.Count - 1; j++)
+ {
+ for (int i = 0; i < _windowcount; i++)
+ {
+ LpcWindowSection* window_sections = sections + i * lpc.MAX_LPC_SECTIONS;
+ // leave room for glue
+ if (secs[i] >= lpc.MAX_LPC_SECTIONS - 1)
+ {
+ throw new IndexOutOfRangeException();
+ //window_sections[secs[i] - 1].m_type = LpcWindowSection.SectionType.Data;
+ //window_sections[secs[i] - 1].m_end = boundaries[j + 1];
+ //continue;
+ }
+ window_sections[secs[i]].setData(boundaries[j], boundaries[j + 1]);
+ window_sections[secs[i]++].m_type = types[i, j];
+ }
+ for (int i = 0; i < _windowcount; i++)
+ {
+ LpcWindowSection* window_sections = sections + i * lpc.MAX_LPC_SECTIONS;
+ int sec = secs[i] - 1;
+ if (sec > 0
+ && j > 0 && (alias_set[i, j] == alias_set[i, j - 1] || window_sections[sec].m_type == SectionType.Zero)
+ && window_sections[sec].m_start == boundaries[j]
+ && window_sections[sec].m_end == boundaries[j + 1]
+ && window_sections[sec - 1].m_end == boundaries[j]
+ && window_sections[sec - 1].m_type == window_sections[sec].m_type)
+ {
+ window_sections[sec - 1].m_end = window_sections[sec].m_end;
+ secs[i]--;
+ continue;
+ }
+ if (section_id >= lpc.MAX_LPC_SECTIONS) throw new IndexOutOfRangeException();
+ if (alias_set[i, j] != 0
+ && types[i, j] != SectionType.Zero
+ && section_id < lpc.MAX_LPC_SECTIONS)
+ {
+ for (int i1 = i; i1 < _windowcount; i1++)
+ if (alias[i1, j] == i && secs[i1] > 0)
+ sections[i1 * lpc.MAX_LPC_SECTIONS + secs[i1] - 1].m_id = section_id;
+ section_id++;
+ }
+ // TODO: section_id for glue? nontrivial, must be sure next sections are the same size
+ if (sec > 0
+ && (window_sections[sec].m_type == SectionType.One || window_sections[sec].m_type == SectionType.OneLarge)
+ && window_sections[sec].m_end - window_sections[sec].m_start >= lpc.MAX_LPC_ORDER
+ && (window_sections[sec - 1].m_type == SectionType.One || window_sections[sec - 1].m_type == SectionType.OneLarge)
+ && window_sections[sec - 1].m_end - window_sections[sec - 1].m_start >= lpc.MAX_LPC_ORDER)
+ {
+ window_sections[sec + 1] = window_sections[sec];
+ window_sections[sec].m_end = window_sections[sec].m_start;
+ window_sections[sec].m_type = SectionType.OneGlue;
+ window_sections[sec].m_id = -1;
+ secs[i]++;
+ continue;
+ }
+ if (sec > 0
+ && window_sections[sec].m_type != SectionType.Zero
+ && window_sections[sec - 1].m_type != SectionType.Zero)
+ {
+ window_sections[sec + 1] = window_sections[sec];
+ window_sections[sec].m_end = window_sections[sec].m_start;
+ window_sections[sec].m_type = SectionType.Glue;
+ window_sections[sec].m_id = -1;
+ secs[i]++;
+ continue;
+ }
+ }
+ }
+ for (int i = 0; i < _windowcount; i++)
+ {
+ for (int s = 0; s < secs[i]; s++)
+ {
+ LpcWindowSection* window_sections = sections + i * lpc.MAX_LPC_SECTIONS;
+ if (window_sections[s].m_type == SectionType.Glue
+ || window_sections[s].m_type == SectionType.OneGlue)
+ {
+ window_sections[s].m_end = window_sections[s + 1].m_end;
+ }
+ }
+ while (secs[i] < lpc.MAX_LPC_SECTIONS)
+ {
+ LpcWindowSection* window_sections = sections + i * lpc.MAX_LPC_SECTIONS;
+ window_sections[secs[i]++].setZero(sz, sz);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Context for LPC coefficients calculation and order estimation
+ ///
+ unsafe public class LpcContext
+ {
+ public LpcContext()
+ {
+ coefs = new int[lpc.MAX_LPC_ORDER];
+ reflection_coeffs = new double[lpc.MAX_LPC_ORDER];
+ prediction_error = new double[lpc.MAX_LPC_ORDER];
+ autocorr_values = new double[lpc.MAX_LPC_ORDER + 1];
+ best_orders = new int[lpc.MAX_LPC_ORDER];
+ done_lpcs = new uint[lpc.MAX_LPC_PRECISIONS];
+ }
+
+ ///
+ /// Reset to initial (blank) state
+ ///
+ public void Reset()
+ {
+ autocorr_order = 0;
+ for (int iPrecision = 0; iPrecision < lpc.MAX_LPC_PRECISIONS; iPrecision++)
+ done_lpcs[iPrecision] = 0;
+ }
+
+ ///
+ /// Calculate autocorrelation data and reflection coefficients.
+ /// Can be used to incrementaly compute coefficients for higher orders,
+ /// because it caches them.
+ ///
+ /// Maximum order
+ /// Samples pointer
+ /// Block size
+ /// Window function
+ public void GetReflection(LpcSubframeInfo subframe, int order, int blocksize, int* samples, float* window, LpcWindowSection* sections)
+ {
+ if (autocorr_order > order)
+ return;
+ fixed (double* reff = reflection_coeffs, autoc = autocorr_values, err = prediction_error)
+ {
+ for (int i = autocorr_order; i <= order; i++) autoc[i] = 0;
+ for (int section = 0; section < lpc.MAX_LPC_SECTIONS; section++)
+ {
+ if (sections[section].m_type == LpcWindowSection.SectionType.Zero)
+ {
+ continue;
+ }
+ if (sections[section].m_id >= 0)
+ {
+ if (subframe.autocorr_section_orders[sections[section].m_id] <= order)
+ {
+ fixed (double* autocsec = &subframe.autocorr_section_values[sections[section].m_id, 0])
+ {
+ int min_order = subframe.autocorr_section_orders[sections[section].m_id];
+ for (int i = min_order; i <= order; i++) autocsec[i] = 0;
+ sections[section].compute_autocorr(samples, window, min_order, order, blocksize, autocsec);
+ }
+ subframe.autocorr_section_orders[sections[section].m_id] = order + 1;
+ }
+ for (int i = autocorr_order; i <= order; i++)
+ autoc[i] += subframe.autocorr_section_values[sections[section].m_id, i];
+ }
+ else
+ {
+ sections[section].compute_autocorr(samples, window, autocorr_order, order, blocksize, autoc);
+ }
+ }
+ lpc.compute_schur_reflection(autoc, (uint)order, reff, err);
+ autocorr_order = order + 1;
+ }
+ }
+#if XXX
+ public void GetReflection1(int order, int* samples, int blocksize, float* window)
+ {
+ if (autocorr_order > order)
+ return;
+ fixed (double* reff = reflection_coeffs, autoc = autocorr_values, err = prediction_error)
+ {
+ lpc.compute_autocorr(samples, blocksize, 0, order + 1, autoc, window);
+ for (int i = 1; i <= order; i++)
+ autoc[i] = autoc[i + 1];
+ lpc.compute_schur_reflection(autoc, (uint)order, reff, err);
+ autocorr_order = order + 1;
+ }
+ }
+
+ public void ComputeReflection(int order, float* autocorr)
+ {
+ fixed (double* reff = reflection_coeffs, autoc = autocorr_values, err = prediction_error)
+ {
+ for (int i = 0; i <= order; i++)
+ autoc[i] = autocorr[i];
+ lpc.compute_schur_reflection(autoc, (uint)order, reff, err);
+ autocorr_order = order + 1;
+ }
+ }
+
+ public void ComputeReflection(int order, double* autocorr)
+ {
+ fixed (double* reff = reflection_coeffs, autoc = autocorr_values, err = prediction_error)
+ {
+ for (int i = 0; i <= order; i++)
+ autoc[i] = autocorr[i];
+ lpc.compute_schur_reflection(autoc, (uint)order, reff, err);
+ autocorr_order = order + 1;
+ }
+ }
+#endif
+ public double Akaike(int blocksize, int order, double alpha, double beta)
+ {
+ //return (blocksize - order) * (Math.Log(prediction_error[order - 1]) - Math.Log(1.0)) + Math.Log(blocksize) * order * (alpha + beta * order);
+ //return blocksize * (Math.Log(prediction_error[order - 1]) - Math.Log(autocorr_values[0]) / 2) + Math.Log(blocksize) * order * (alpha + beta * order);
+ return blocksize * (Math.Log(prediction_error[order - 1])) + Math.Log(blocksize) * order * (alpha + beta * order);
+ }
+
+ ///
+ /// Sorts orders based on Akaike's criteria
+ ///
+ /// Frame size
+ public void SortOrdersAkaike(int blocksize, int count, int min_order, int max_order, double alpha, double beta)
+ {
+ for (int i = min_order; i <= max_order; i++)
+ best_orders[i - min_order] = i;
+ int lim = max_order - min_order + 1;
+ for (int i = 0; i < lim && i < count; i++)
+ {
+ for (int j = i + 1; j < lim; j++)
+ {
+ if (Akaike(blocksize, best_orders[j], alpha, beta) < Akaike(blocksize, best_orders[i], alpha, beta))
+ {
+ int tmp = best_orders[j];
+ best_orders[j] = best_orders[i];
+ best_orders[i] = tmp;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Produces LPC coefficients from autocorrelation data.
+ ///
+ /// LPC coefficients buffer (for all orders)
+ public void ComputeLPC(float* lpcs)
+ {
+ fixed (double* reff = reflection_coeffs)
+ lpc.compute_lpc_coefs((uint)autocorr_order - 1, reff, lpcs);
+ }
+
+ public double[] autocorr_values;
+ double[] reflection_coeffs;
+ public double[] prediction_error;
+ public int[] best_orders;
+ public int[] coefs;
+ int autocorr_order;
+ public int shift;
+
+ public double[] Reflection
+ {
+ get
+ {
+ return reflection_coeffs;
+ }
+ }
+
+ public uint[] done_lpcs;
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/NULL/AudioDecoder.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/NULL/AudioDecoder.cs
new file mode 100644
index 000000000..651db6a22
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/NULL/AudioDecoder.cs
@@ -0,0 +1,65 @@
+using System;
+
+namespace CUETools.Codecs.NULL
+{
+ public class AudioDecoder : IAudioSource
+ {
+ private long _sampleOffset, _sampleCount;
+ private AudioPCMConfig pcm;
+ private int _sampleVal;
+
+ public IAudioDecoderSettings Settings => null;
+
+ public TimeSpan Duration => TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
+
+ public long Length
+ {
+ get { return _sampleCount; }
+ }
+
+ public long Remaining
+ {
+ get { return _sampleCount - _sampleOffset; }
+ }
+
+ public long Position
+ {
+ get { return _sampleOffset; }
+ set { _sampleOffset = value; }
+ }
+
+ public AudioPCMConfig PCM { get { return pcm; } }
+
+ public string Path { get { return null; } }
+
+ public AudioDecoder(AudioPCMConfig pcm, long sampleCount, int sampleVal)
+ {
+ this._sampleVal = sampleVal;
+ this._sampleOffset = 0;
+ this._sampleCount = sampleCount;
+ this.pcm = pcm;
+ }
+
+ public AudioDecoder(long sampleCount)
+ : this(AudioPCMConfig.RedBook, sampleCount, 0)
+ {
+ }
+
+ public int Read(AudioBuffer buff, int maxLength)
+ {
+ buff.Prepare(this, maxLength);
+
+ int[,] samples = buff.Samples;
+ for (int i = 0; i < buff.Length; i++)
+ for (int j = 0; j < PCM.ChannelCount; j++)
+ samples[i, j] = _sampleVal;
+
+ _sampleOffset += buff.Length;
+ return buff.Length;
+ }
+
+ public void Close()
+ {
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/NULL/AudioEncoder.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/NULL/AudioEncoder.cs
new file mode 100644
index 000000000..fbeb36749
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/NULL/AudioEncoder.cs
@@ -0,0 +1,35 @@
+using System;
+
+namespace CUETools.Codecs.NULL
+{
+ public class AudioEncoder : IAudioDest
+ {
+ IAudioEncoderSettings m_settings;
+
+ public AudioEncoder(string path, IAudioEncoderSettings settings)
+ {
+ m_settings = settings;
+ }
+
+ public void Close()
+ {
+ }
+
+ public void Delete()
+ {
+ }
+
+ public long FinalSampleCount
+ {
+ set { }
+ }
+
+ public IAudioEncoderSettings Settings => m_settings;
+
+ public void Write(AudioBuffer buff)
+ {
+ }
+
+ public string Path => null;
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/NullStream.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/NullStream.cs
new file mode 100644
index 000000000..a19b70a3d
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/NullStream.cs
@@ -0,0 +1,66 @@
+using System;
+using System.IO;
+
+namespace CUETools.Codecs
+{
+ public class NullStream : Stream
+ {
+ public override bool CanRead
+ {
+ get { return false; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return true; }
+ }
+
+ public override long Length
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override long Position
+ {
+ get { throw new NotSupportedException(); }
+ set { throw new NotSupportedException(); }
+ }
+
+ public NullStream()
+ {
+ }
+
+ public override void Close()
+ {
+ }
+
+ public override void Flush()
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override int Read(byte[] array, int offset, int count)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void Write(byte[] array, int offset, int count)
+ {
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/PlaybackState.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/PlaybackState.cs
new file mode 100644
index 000000000..e4a49d13b
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/PlaybackState.cs
@@ -0,0 +1,21 @@
+namespace CUETools.Codecs
+{
+ ///
+ /// Playback State
+ ///
+ public enum PlaybackState
+ {
+ ///
+ /// Stopped
+ ///
+ Stopped,
+ ///
+ /// Playing
+ ///
+ Playing,
+ ///
+ /// Paused
+ ///
+ Paused
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/SRDescriptionAttribute.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/SRDescriptionAttribute.cs
new file mode 100644
index 000000000..6c3e2fddd
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/SRDescriptionAttribute.cs
@@ -0,0 +1,56 @@
+using System;
+using System.ComponentModel;
+
+namespace CUETools.Codecs
+{
+ ///
+ /// Localized description attribute
+ ///
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
+ public class SRDescriptionAttribute : DescriptionAttribute
+ {
+ ///
+ /// Store a flag indicating whether this has been localized
+ ///
+ private bool localized;
+
+ ///
+ /// Resource manager to use;
+ ///
+ private Type SR;
+
+ ///
+ /// Construct the description attribute
+ ///
+ ///
+ public SRDescriptionAttribute(Type SR, string text)
+ : base(text)
+ {
+ this.localized = false;
+ this.SR = SR;
+ }
+
+ ///
+ /// Override the return of the description text to localize the text
+ ///
+ public override string Description
+ {
+ get
+ {
+ if (!localized)
+ {
+ localized = true;
+ this.DescriptionValue = SR.InvokeMember(
+ this.DescriptionValue,
+ System.Reflection.BindingFlags.GetProperty | System.Reflection.BindingFlags.Static |
+ System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic,
+ null,
+ null,
+ new object[] { }) as string;
+ }
+
+ return base.Description;
+ }
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/ViewModel/AudioDecoderSettingsViewModel.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/ViewModel/AudioDecoderSettingsViewModel.cs
new file mode 100644
index 000000000..a58d23e7e
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/ViewModel/AudioDecoderSettingsViewModel.cs
@@ -0,0 +1,97 @@
+using Newtonsoft.Json;
+using System;
+using System.ComponentModel;
+
+namespace CUETools.Codecs
+{
+ [JsonObject(MemberSerialization.OptIn)]
+ public class AudioDecoderSettingsViewModel : INotifyPropertyChanged
+ {
+ [JsonProperty]
+ public IAudioDecoderSettings Settings = null;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [JsonConstructor]
+ private AudioDecoderSettingsViewModel()
+ {
+ }
+
+ public AudioDecoderSettingsViewModel(IAudioDecoderSettings settings)
+ {
+ this.Settings = settings;
+ }
+
+ public override string ToString()
+ {
+ return Name;
+ }
+
+ public string FullName => Name + " [" + Extension + "]";
+
+ public string Path
+ {
+ get
+ {
+ if (Settings is CommandLine.DecoderSettings)
+ return (Settings as CommandLine.DecoderSettings).Path;
+ return "";
+ }
+ set
+ {
+ if (Settings is CommandLine.DecoderSettings)
+ (Settings as CommandLine.DecoderSettings).Path = value;
+ else throw new InvalidOperationException();
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Path"));
+ }
+ }
+ public string Parameters
+ {
+ get
+ {
+ if (Settings is CommandLine.DecoderSettings)
+ return (Settings as CommandLine.DecoderSettings).Parameters;
+ return "";
+ }
+ set
+ {
+ if (Settings is CommandLine.DecoderSettings)
+ (Settings as CommandLine.DecoderSettings).Parameters = value;
+ else throw new InvalidOperationException();
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Parameters"));
+ }
+ }
+
+ public string Name
+ {
+ get => Settings.Name;
+ set
+ {
+ if (Settings is CommandLine.DecoderSettings)
+ (Settings as CommandLine.DecoderSettings).Name = value;
+ else throw new InvalidOperationException();
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
+ }
+ }
+
+ public string Extension
+ {
+ get => Settings.Extension;
+ set
+ {
+ if (Settings is CommandLine.DecoderSettings)
+ (Settings as CommandLine.DecoderSettings).Extension = value;
+ else throw new InvalidOperationException();
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Extension"));
+ }
+ }
+
+ public string DotExtension => "." + Extension;
+
+ public bool CanBeDeleted => Settings is CommandLine.DecoderSettings;
+
+ public bool IsValid =>
+ (Settings != null)
+ && (Settings is CommandLine.DecoderSettings ? (Settings as CommandLine.DecoderSettings).Path != "" : true);
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/ViewModel/AudioEncoderSettingsViewModel.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/ViewModel/AudioEncoderSettingsViewModel.cs
new file mode 100644
index 000000000..a9e0f1062
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/ViewModel/AudioEncoderSettingsViewModel.cs
@@ -0,0 +1,137 @@
+using Newtonsoft.Json;
+using System;
+using System.ComponentModel;
+
+namespace CUETools.Codecs
+{
+ [JsonObject(MemberSerialization.OptIn)]
+ public class AudioEncoderSettingsViewModel : INotifyPropertyChanged
+ {
+ [JsonProperty]
+ public IAudioEncoderSettings Settings = null;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [JsonConstructor]
+ private AudioEncoderSettingsViewModel()
+ {
+ }
+
+ public AudioEncoderSettingsViewModel(IAudioEncoderSettings settings)
+ {
+ this.Settings = settings;
+ }
+
+ public override string ToString()
+ {
+ return Name;
+ }
+
+ public string FullName => Name + " [" + Extension + "]";
+
+ public string Path
+ {
+ get
+ {
+ if (Settings is CommandLine.EncoderSettings)
+ return (Settings as CommandLine.EncoderSettings).Path;
+ return "";
+ }
+ set
+ {
+ var settings = this.Settings as CommandLine.EncoderSettings;
+ if (settings == null) throw new InvalidOperationException();
+ settings.Path = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Path"));
+ }
+ }
+
+ public string Parameters
+ {
+ get
+ {
+ if (Settings is CommandLine.EncoderSettings)
+ return (Settings as CommandLine.EncoderSettings).Parameters;
+ return "";
+ }
+ set
+ {
+ var settings = this.Settings as CommandLine.EncoderSettings;
+ if (settings == null) throw new InvalidOperationException();
+ settings.Parameters = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Parameters"));
+ }
+ }
+
+ public bool Lossless
+ {
+ get => Settings.Lossless;
+ set
+ {
+ var settings = this.Settings as CommandLine.EncoderSettings;
+ if (settings == null) throw new InvalidOperationException();
+ settings.Lossless = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Lossless"));
+ }
+ }
+
+
+ public string Name
+ {
+ get => Settings.Name;
+ set
+ {
+ var settings = this.Settings as CommandLine.EncoderSettings;
+ if (settings == null) throw new InvalidOperationException();
+ settings.Name = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
+ }
+ }
+
+ public string Extension
+ {
+ get => Settings.Extension;
+ set
+ {
+ var settings = this.Settings as CommandLine.EncoderSettings;
+ if (settings == null) throw new InvalidOperationException();
+ settings.Extension = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Extension"));
+ }
+ }
+
+ public string DotExtension => "." + Extension;
+
+ public string SupportedModes
+ {
+ get => Settings.SupportedModes;
+ set
+ {
+ var settings = this.Settings as CommandLine.EncoderSettings;
+ if (settings == null) throw new InvalidOperationException();
+ settings.SupportedModes = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SupportedModes"));
+ }
+ }
+
+ public int EncoderModeIndex
+ {
+ get
+ {
+ string[] modes = this.SupportedModes.Split(' ');
+ if (modes == null || modes.Length < 2)
+ return -1;
+ for (int i = 0; i < modes.Length; i++)
+ if (modes[i] == this.Settings.EncoderMode)
+ return i;
+ return -1;
+ }
+ }
+
+ public bool CanBeDeleted => Settings is CommandLine.EncoderSettings;
+
+ public bool IsValid =>
+ (Settings != null)
+ && (Settings is CommandLine.EncoderSettings ? (Settings as CommandLine.EncoderSettings).Path != "" : true);
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/ViewModel/DecoderListViewModel.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/ViewModel/DecoderListViewModel.cs
new file mode 100644
index 000000000..0a790cf18
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/ViewModel/DecoderListViewModel.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace CUETools.Codecs
+{
+ public class DecoderListViewModel : BindingList
+ {
+ private List model;
+
+ public DecoderListViewModel(List model)
+ : base()
+ {
+ this.model = model;
+ model.ForEach(item => Add(new AudioDecoderSettingsViewModel(item)));
+ AddingNew += OnAddingNew;
+ }
+
+ private void OnAddingNew(object sender, AddingNewEventArgs e)
+ {
+ var item = new CommandLine.DecoderSettings("new", "wav", "", "");
+ model.Add(item);
+ e.NewObject = new AudioDecoderSettingsViewModel(item);
+ }
+
+ public bool TryGetValue(string extension, string name, out AudioDecoderSettingsViewModel result)
+ {
+ foreach (AudioDecoderSettingsViewModel udc in this)
+ {
+ if (udc.Settings.Extension == extension && udc.Settings.Name == name)
+ {
+ result = udc;
+ return true;
+ }
+ }
+ result = null;
+ return false;
+ }
+
+ public AudioDecoderSettingsViewModel GetDefault(string extension)
+ {
+ AudioDecoderSettingsViewModel result = null;
+ foreach (AudioDecoderSettingsViewModel udc in this)
+ {
+ if (udc.Settings.Extension == extension && (result == null || result.Settings.Priority < udc.Settings.Priority))
+ {
+ result = udc;
+ }
+ }
+ return result;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/ViewModel/EncoderListViewModel.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/ViewModel/EncoderListViewModel.cs
new file mode 100644
index 000000000..1c5ba2c20
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/ViewModel/EncoderListViewModel.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace CUETools.Codecs
+{
+ public class EncoderListViewModel : BindingList
+ {
+ private List model;
+
+ public EncoderListViewModel(List model)
+ : base()
+ {
+ this.model = model;
+ model.ForEach(item => Add(new AudioEncoderSettingsViewModel(item)));
+ AddingNew += OnAddingNew;
+ }
+
+ private void OnAddingNew(object sender, AddingNewEventArgs e)
+ {
+ var item = new CommandLine.EncoderSettings("new", "wav", true, "", "", "", "");
+ model.Add(item);
+ e.NewObject = new AudioEncoderSettingsViewModel(item);
+ }
+
+ public bool TryGetValue(string extension, bool lossless, string name, out AudioEncoderSettingsViewModel result)
+ {
+ //result = this.Where(udc => udc.settings.Extension == extension && udc.settings.Lossless == lossless && udc.settings.Name == name).First();
+ foreach (AudioEncoderSettingsViewModel udc in this)
+ {
+ if (udc.Settings.Extension == extension && udc.Settings.Lossless == lossless && udc.Settings.Name == name)
+ {
+ result = udc;
+ return true;
+ }
+ }
+ result = null;
+ return false;
+ }
+
+ public AudioEncoderSettingsViewModel GetDefault(string extension, bool lossless)
+ {
+ AudioEncoderSettingsViewModel result = null;
+ foreach (AudioEncoderSettingsViewModel udc in this)
+ {
+ if (udc.Settings.Extension == extension && udc.Settings.Lossless == lossless && (result == null || result.Settings.Priority < udc.Settings.Priority))
+ {
+ result = udc;
+ }
+ }
+ return result;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/WAV/AudioDecoder.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/WAV/AudioDecoder.cs
new file mode 100644
index 000000000..3cd32d0a3
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/WAV/AudioDecoder.cs
@@ -0,0 +1,271 @@
+using System;
+using System.IO;
+
+namespace CUETools.Codecs.WAV
+{
+ public class AudioDecoder : IAudioSource
+ {
+ Stream _IO;
+ BinaryReader _br;
+ long _dataOffset, _samplePos, _sampleLen;
+ private AudioPCMConfig pcm;
+ long _dataLen;
+ bool _largeFile;
+ string _path;
+
+ private DecoderSettings m_settings;
+ public IAudioDecoderSettings Settings => m_settings;
+
+ public long Position
+ {
+ get
+ {
+ return _samplePos;
+ }
+ set
+ {
+ long seekPos;
+
+ if (_samplePos == value)
+ {
+ return;
+ }
+
+ var oldSamplePos = _samplePos;
+ if (_sampleLen >= 0 && value > _sampleLen)
+ _samplePos = _sampleLen;
+ else
+ _samplePos = value;
+
+ if (_IO.CanSeek || _samplePos < oldSamplePos)
+ {
+ seekPos = _dataOffset + _samplePos * PCM.BlockAlign;
+ _IO.Seek(seekPos, SeekOrigin.Begin);
+ }
+ else
+ {
+ int offs = (int)(_samplePos - oldSamplePos) * PCM.BlockAlign;
+ while (offs > 0)
+ {
+ int chunk = Math.Min(offs, 16536);
+ _br.ReadBytes(chunk);
+ offs -= chunk;
+ }
+ }
+ }
+ }
+
+ public TimeSpan Duration => Length < 0 ? TimeSpan.Zero : TimeSpan.FromSeconds((double)Length / PCM.SampleRate);
+
+ public long Length
+ {
+ get
+ {
+ return _sampleLen;
+ }
+ }
+
+ public long Remaining
+ {
+ get
+ {
+ return _sampleLen - _samplePos;
+ }
+ }
+
+ public AudioPCMConfig PCM { get { return pcm; } }
+
+ public string Path { get { return _path; } }
+
+ public AudioDecoder(DecoderSettings settings, string path, Stream IO = null)
+ {
+ m_settings = settings;
+ _path = path;
+ _IO = IO ?? new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000, FileOptions.SequentialScan);
+ _br = new BinaryReader(_IO);
+
+ ParseHeaders();
+
+ if (_dataLen < 0 || m_settings.IgnoreChunkSizes)
+ _sampleLen = -1;
+ else
+ _sampleLen = _dataLen / pcm.BlockAlign;
+ }
+
+ public AudioDecoder(DecoderSettings settings, string path, Stream IO, AudioPCMConfig _pcm)
+ {
+ m_settings = settings;
+ _path = path;
+ _IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000, FileOptions.SequentialScan);
+ _br = new BinaryReader(_IO);
+
+ _largeFile = false;
+ _dataOffset = 0;
+ _samplePos = 0;
+ pcm = _pcm;
+ _dataLen = _IO.CanSeek ? _IO.Length : -1;
+ if (_dataLen < 0)
+ _sampleLen = -1;
+ else
+ {
+ _sampleLen = _dataLen / pcm.BlockAlign;
+ if ((_dataLen % pcm.BlockAlign) != 0)
+ throw new Exception("odd file size");
+ }
+ }
+
+ public static AudioBuffer ReadAllSamples(DecoderSettings settings, string path, Stream IO = null)
+ {
+ AudioDecoder reader = new AudioDecoder(settings, path, IO);
+ AudioBuffer buff = new AudioBuffer(reader, (int)reader.Length);
+ reader.Read(buff, -1);
+ if (reader.Remaining != 0)
+ throw new Exception("couldn't read the whole file");
+ reader.Close();
+ return buff;
+ }
+
+ public void Close()
+ {
+ if (_br != null)
+ {
+ _br.Close();
+ _br = null;
+ }
+ _IO = null;
+ }
+
+ private void ParseHeaders()
+ {
+ const long maxFileSize = 0x7FFFFFFEL;
+ const uint fccRIFF = 0x46464952;
+ const uint fccWAVE = 0x45564157;
+ const uint fccFormat = 0x20746D66;
+ const uint fccData = 0x61746164;
+
+ uint lenRIFF;
+ bool foundFormat, foundData;
+
+ if (_br.ReadUInt32() != fccRIFF)
+ {
+ throw new Exception("Not a valid RIFF file.");
+ }
+
+ lenRIFF = _br.ReadUInt32();
+
+ if (_br.ReadUInt32() != fccWAVE)
+ {
+ throw new Exception("Not a valid WAVE file.");
+ }
+
+ _largeFile = false;
+ foundFormat = false;
+ foundData = false;
+ long pos = 12;
+ do
+ {
+ uint ckID, ckSize, ckSizePadded;
+ long ckEnd;
+
+ ckID = _br.ReadUInt32();
+ ckSize = _br.ReadUInt32();
+ ckSizePadded = (ckSize + 1U) & ~1U;
+ pos += 8;
+ ckEnd = pos + (long)ckSizePadded;
+
+ if (ckID == fccFormat)
+ {
+ foundFormat = true;
+
+ uint fmtTag = _br.ReadUInt16();
+ int _channelCount = _br.ReadInt16();
+ int _sampleRate = _br.ReadInt32();
+ _br.ReadInt32(); // bytes per second
+ int _blockAlign = _br.ReadInt16();
+ int _bitsPerSample = _br.ReadInt16();
+ int _channelMask = 0;
+ pos += 16;
+
+ if (fmtTag == 0xFFFEU && ckSize >= 34) // WAVE_FORMAT_EXTENSIBLE
+ {
+ _br.ReadInt16(); // CbSize
+ _br.ReadInt16(); // ValidBitsPerSample
+ _channelMask = _br.ReadInt32();
+ fmtTag = _br.ReadUInt16();
+ pos += 10;
+ }
+
+ if (fmtTag != 1) // WAVE_FORMAT_PCM
+ throw new Exception("WAVE format tag not WAVE_FORMAT_PCM.");
+
+ pcm = new AudioPCMConfig(_bitsPerSample, _channelCount, _sampleRate, (AudioPCMConfig.SpeakerConfig)_channelMask);
+ if (pcm.BlockAlign != _blockAlign)
+ throw new Exception("WAVE has strange BlockAlign");
+ }
+ else if (ckID == fccData)
+ {
+ foundData = true;
+
+ _dataOffset = pos;
+ if (!_IO.CanSeek || _IO.Length <= maxFileSize)
+ {
+ if (ckSize == 0 || ckSize >= 0x7fffffff)
+ _dataLen = -1;
+ else
+ _dataLen = (long)ckSize;
+ }
+ else
+ {
+ _largeFile = true;
+ _dataLen = _IO.Length - pos;
+ }
+ }
+
+ if ((foundFormat & foundData) || _largeFile)
+ break;
+ if (_IO.CanSeek)
+ _IO.Seek(ckEnd, SeekOrigin.Begin);
+ else
+ _br.ReadBytes((int)(ckEnd - pos));
+ pos = ckEnd;
+ } while (true);
+
+ if ((foundFormat & foundData) == false || pcm == null)
+ throw new Exception("Format or data chunk not found.");
+ if (pcm.ChannelCount <= 0)
+ throw new Exception("Channel count is invalid.");
+ if (pcm.SampleRate <= 0)
+ throw new Exception("Sample rate is invalid.");
+ if ((pcm.BitsPerSample <= 0) || (pcm.BitsPerSample > 32))
+ throw new Exception("Bits per sample is invalid.");
+ if (pos != _dataOffset)
+ Position = 0;
+ }
+
+ public int Read(AudioBuffer buff, int maxLength)
+ {
+ buff.Prepare(this, maxLength);
+
+ byte[] bytes = buff.Bytes;
+ int byteCount = (int)buff.ByteLength;
+ int pos = 0;
+
+ while (pos < byteCount)
+ {
+ int len = _IO.Read(bytes, pos, byteCount - pos);
+ if (len <= 0)
+ {
+ if ((pos % PCM.BlockAlign) != 0 || _sampleLen >= 0)
+ throw new Exception("Incomplete file read.");
+ buff.Length = pos / PCM.BlockAlign;
+ _samplePos += buff.Length;
+ _sampleLen = _samplePos;
+ return buff.Length;
+ }
+ pos += len;
+ }
+ _samplePos += buff.Length;
+ return buff.Length;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/WAV/AudioEncoder.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/WAV/AudioEncoder.cs
new file mode 100644
index 000000000..3f32eb0a9
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/WAV/AudioEncoder.cs
@@ -0,0 +1,180 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace CUETools.Codecs.WAV
+{
+ public class AudioEncoder : IAudioDest
+ {
+ private Stream _IO;
+ private BinaryWriter _bw;
+ private long _sampleLen;
+ private string _path;
+ private long hdrLen = 0;
+ private bool _headersWritten = false;
+ private long _finalSampleCount = -1;
+ private List _chunks = null;
+ private List _chunkFCCs = null;
+
+ public long Position
+ {
+ get
+ {
+ return _sampleLen;
+ }
+ }
+
+ public long FinalSampleCount
+ {
+ set { _finalSampleCount = value; }
+ }
+
+ private EncoderSettings m_settings;
+ public IAudioEncoderSettings Settings => m_settings;
+
+ public string Path { get { return _path; } }
+
+ public AudioEncoder(EncoderSettings settings, string path, Stream IO = null)
+ {
+ m_settings = settings;
+ _path = path;
+ _IO = IO ?? new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
+ _bw = new BinaryWriter(_IO);
+ }
+
+ public void WriteChunk(uint fcc, byte[] data)
+ {
+ if (_sampleLen > 0)
+ throw new Exception("data already written, no chunks allowed");
+ if (_chunks == null)
+ {
+ _chunks = new List();
+ _chunkFCCs = new List();
+ }
+ _chunkFCCs.Add(fcc);
+ _chunks.Add(data);
+ hdrLen += 8 + data.Length + (data.Length & 1);
+ }
+
+ private void WriteHeaders()
+ {
+ const uint fccRIFF = 0x46464952;
+ const uint fccWAVE = 0x45564157;
+ const uint fccFormat = 0x20746D66;
+ const uint fccData = 0x61746164;
+
+ bool wavex = (Settings.PCM.BitsPerSample != 16 && Settings.PCM.BitsPerSample != 24) || Settings.PCM.ChannelMask != AudioPCMConfig.GetDefaultChannelMask(Settings.PCM.ChannelCount);
+
+ hdrLen += 36 + (wavex ? 24 : 0) + 8;
+
+ uint dataLen = (uint)(_finalSampleCount * Settings.PCM.BlockAlign);
+ uint dataLenPadded = dataLen + (dataLen & 1);
+
+ _bw.Write(fccRIFF);
+ if (_finalSampleCount <= 0)
+ _bw.Write((uint)0xffffffff);
+ else
+ _bw.Write((uint)(dataLenPadded + hdrLen - 8));
+ _bw.Write(fccWAVE);
+ _bw.Write(fccFormat);
+ if (wavex)
+ {
+ _bw.Write((uint)40);
+ _bw.Write((ushort)0xfffe); // WAVEX follows
+ }
+ else
+ {
+ _bw.Write((uint)16);
+ _bw.Write((ushort)1); // PCM
+ }
+ _bw.Write((ushort)Settings.PCM.ChannelCount);
+ _bw.Write((uint)Settings.PCM.SampleRate);
+ _bw.Write((uint)(Settings.PCM.SampleRate * Settings.PCM.BlockAlign));
+ _bw.Write((ushort)Settings.PCM.BlockAlign);
+ _bw.Write((ushort)((Settings.PCM.BitsPerSample + 7) / 8 * 8));
+ if (wavex)
+ {
+ _bw.Write((ushort)22); // length of WAVEX structure
+ _bw.Write((ushort)Settings.PCM.BitsPerSample);
+ _bw.Write((uint)Settings.PCM.ChannelMask);
+ _bw.Write((ushort)1); // PCM Guid
+ _bw.Write((ushort)0);
+ _bw.Write((ushort)0);
+ _bw.Write((ushort)0x10);
+ _bw.Write((byte)0x80);
+ _bw.Write((byte)0x00);
+ _bw.Write((byte)0x00);
+ _bw.Write((byte)0xaa);
+ _bw.Write((byte)0x00);
+ _bw.Write((byte)0x38);
+ _bw.Write((byte)0x9b);
+ _bw.Write((byte)0x71);
+ }
+ if (_chunks != null)
+ for (int i = 0; i < _chunks.Count; i++)
+ {
+ _bw.Write(_chunkFCCs[i]);
+ _bw.Write((uint)_chunks[i].Length);
+ _bw.Write(_chunks[i]);
+ if ((_chunks[i].Length & 1) != 0)
+ _bw.Write((byte)0);
+ }
+
+ _bw.Write(fccData);
+ if (_finalSampleCount <= 0)
+ _bw.Write((uint)0xffffffff);
+ else
+ _bw.Write(dataLen);
+
+ _headersWritten = true;
+ }
+
+ public void Close()
+ {
+ if (_finalSampleCount <= 0 && _IO.CanSeek)
+ {
+ long dataLen = _sampleLen * Settings.PCM.BlockAlign;
+ long dataLenPadded = dataLen + (dataLen & 1);
+ if (dataLenPadded + hdrLen - 8 < 0xffffffff)
+ {
+ if ((dataLen & 1) == 1)
+ _bw.Write((byte)0);
+
+ _bw.Seek(4, SeekOrigin.Begin);
+ _bw.Write((uint)(dataLenPadded + hdrLen - 8));
+
+ _bw.Seek((int)hdrLen - 4, SeekOrigin.Begin);
+ _bw.Write((uint)dataLen);
+ }
+ }
+
+ _bw.Close();
+
+ _bw = null;
+ _IO = null;
+
+ if (_finalSampleCount > 0 && _sampleLen != _finalSampleCount)
+ throw new Exception("Samples written differs from the expected sample count.");
+ }
+
+ public void Delete()
+ {
+ _bw.Close();
+ _bw = null;
+ _IO = null;
+ if (_path != "")
+ File.Delete(_path);
+ }
+
+ public void Write(AudioBuffer buff)
+ {
+ if (buff.Length == 0)
+ return;
+ buff.Prepare(this);
+ if (!_headersWritten)
+ WriteHeaders();
+ _IO.Write(buff.Bytes, 0, buff.ByteLength);
+ _sampleLen += buff.Length;
+ }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/WAV/DecoderSettings.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/WAV/DecoderSettings.cs
new file mode 100644
index 000000000..1163647bb
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/WAV/DecoderSettings.cs
@@ -0,0 +1,36 @@
+using Newtonsoft.Json;
+using System;
+using System.ComponentModel;
+
+namespace CUETools.Codecs.WAV
+{
+ [JsonObject(MemberSerialization.OptIn)]
+ public class DecoderSettings : IAudioDecoderSettings
+ {
+ #region IAudioDecoderSettings implementation
+ [Browsable(false)]
+ public string Extension => "wav";
+
+ [Browsable(false)]
+ public string Name => "cuetools";
+
+ [Browsable(false)]
+ public Type DecoderType => typeof(AudioDecoder);
+
+ [Browsable(false)]
+ public int Priority => 2;
+
+ public IAudioDecoderSettings Clone()
+ {
+ return MemberwiseClone() as IAudioDecoderSettings;
+ }
+ #endregion
+
+ public DecoderSettings()
+ {
+ this.Init();
+ }
+
+ public bool IgnoreChunkSizes { get; set; }
+ }
+}
diff --git a/Aaru.Compression/cuetools.net/CUETools.Codecs/WAV/EncoderSettings.cs b/Aaru.Compression/cuetools.net/CUETools.Codecs/WAV/EncoderSettings.cs
new file mode 100644
index 000000000..2a55a0fb2
--- /dev/null
+++ b/Aaru.Compression/cuetools.net/CUETools.Codecs/WAV/EncoderSettings.cs
@@ -0,0 +1,64 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Text;
+
+namespace CUETools.Codecs.WAV
+{
+ [JsonObject(MemberSerialization.OptIn)]
+ public class EncoderSettings : IAudioEncoderSettings
+ {
+ #region IAudioEncoderSettings implementation
+ [Browsable(false)]
+ public string Extension => "wav";
+
+ [Browsable(false)]
+ public string Name => "cuetools";
+
+ [Browsable(false)]
+ public Type EncoderType => typeof(WAV.AudioEncoder);
+
+ [Browsable(false)]
+ public bool Lossless => true;
+
+ [Browsable(false)]
+ public int Priority => 10;
+
+ [Browsable(false)]
+ public string SupportedModes => "";
+
+ [Browsable(false)]
+ public string DefaultMode => "";
+
+ [Browsable(false)]
+ [DefaultValue("")]
+ public string EncoderMode { get; set; }
+
+ [Browsable(false)]
+ public AudioPCMConfig PCM { get; set; }
+
+ [Browsable(false)]
+ public int BlockSize { get; set; }
+
+ [Browsable(false)]
+ [DefaultValue(4096)]
+ public int Padding { get; set; }
+
+ public IAudioEncoderSettings Clone()
+ {
+ return MemberwiseClone() as IAudioEncoderSettings;
+ }
+ #endregion
+
+ public EncoderSettings()
+ {
+ this.Init();
+ }
+
+ public EncoderSettings(AudioPCMConfig pcm)
+ {
+ this.Init(pcm);
+ }
+ }
+}