mirror of
https://github.com/claunia/cuetools.net.git
synced 2025-12-16 18:14:25 +00:00
seektable support, optimizations
This commit is contained in:
@@ -38,6 +38,17 @@ namespace CUETools.Codecs.FLAKE
|
||||
writebits(bits, (uint)val);
|
||||
}
|
||||
|
||||
public void writebits64(int bits, ulong val)
|
||||
{
|
||||
if (bits > 32)
|
||||
{
|
||||
writebits(bits - 32, (uint)(val >> 32));
|
||||
val &= 0xffffffffL;
|
||||
bits = 32;
|
||||
}
|
||||
writebits(bits, (uint)val);
|
||||
}
|
||||
|
||||
public void writebits(int bits, uint val)
|
||||
{
|
||||
//assert(bits == 32 || val < (1U << bits));
|
||||
@@ -100,12 +111,26 @@ namespace CUETools.Codecs.FLAKE
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int v, q;
|
||||
|
||||
if (k < 0) return;
|
||||
|
||||
// convert signed to unsigned
|
||||
v = -2 * val - 1;
|
||||
v ^= (v >> 31);
|
||||
|
||||
@@ -11,6 +11,10 @@ namespace CUETools.Codecs.FLAKE
|
||||
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, 0, 0, 0,
|
||||
8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000,
|
||||
@@ -39,59 +43,22 @@ namespace CUETools.Codecs.FLAKE
|
||||
|
||||
public static PredictionType LookupPredictionType(string name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "fixed": return PredictionType.Fixed;
|
||||
case "levinson": return PredictionType.Levinson;
|
||||
case "search" : return PredictionType.Search;
|
||||
}
|
||||
return (PredictionType)Int32.Parse(name);
|
||||
return (PredictionType)(Enum.Parse(typeof(PredictionType), name, true));
|
||||
}
|
||||
|
||||
public static StereoMethod LookupStereoMethod(string name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "independent": return StereoMethod.Independent;
|
||||
case "estimate": return StereoMethod.Estimate;
|
||||
case "estimate2": return StereoMethod.Estimate2;
|
||||
case "estimate3": return StereoMethod.Estimate3;
|
||||
case "estimate4": return StereoMethod.Estimate4;
|
||||
case "estimate5": return StereoMethod.Estimate5;
|
||||
case "search": return StereoMethod.Search;
|
||||
}
|
||||
return (StereoMethod)Int32.Parse(name);
|
||||
return (StereoMethod)(Enum.Parse(typeof(StereoMethod), name, true));
|
||||
}
|
||||
|
||||
public static OrderMethod LookupOrderMethod(string name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "estimate": return OrderMethod.Estimate;
|
||||
case "logfast": return OrderMethod.LogFast;
|
||||
case "logsearch": return OrderMethod.LogSearch;
|
||||
case "estsearch": return OrderMethod.EstSearch;
|
||||
case "search": return OrderMethod.Search;
|
||||
}
|
||||
return (OrderMethod)Int32.Parse(name);
|
||||
return (OrderMethod)(Enum.Parse(typeof(OrderMethod), name, true));
|
||||
}
|
||||
|
||||
public static WindowFunction LookupWindowFunction(string name)
|
||||
{
|
||||
string[] parts = name.Split(',');
|
||||
WindowFunction res = (WindowFunction)0;
|
||||
foreach (string part in parts)
|
||||
{
|
||||
switch (part)
|
||||
{
|
||||
case "welch": res |= WindowFunction.Welch; break;
|
||||
case "tukey": res |= WindowFunction.Tukey; break;
|
||||
case "hann": res |= WindowFunction.Hann; break;
|
||||
case "flattop": res |= WindowFunction.Flattop; break;
|
||||
default: res |= (WindowFunction)Int32.Parse(name); break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
return (WindowFunction)(Enum.Parse(typeof(WindowFunction), name, true));
|
||||
}
|
||||
|
||||
unsafe public static bool memcmp(int* res, int* smp, int n)
|
||||
@@ -124,6 +91,14 @@ namespace CUETools.Codecs.FLAKE
|
||||
*(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 struct RiceContext
|
||||
@@ -137,20 +112,28 @@ namespace CUETools.Codecs.FLAKE
|
||||
{
|
||||
public SubframeType type;
|
||||
public int order;
|
||||
public uint obits;
|
||||
public uint wbits;
|
||||
public int cbits;
|
||||
public int shift;
|
||||
public fixed int coefs[lpc.MAX_LPC_ORDER];
|
||||
public int* samples;
|
||||
public int* residual;
|
||||
public RiceContext rc;
|
||||
public uint size;
|
||||
public fixed uint done_lpcs[lpc.MAX_LPC_WINDOWS];
|
||||
public uint done_fixed;
|
||||
|
||||
public int cbits;
|
||||
public int shift;
|
||||
public fixed int coefs[lpc.MAX_LPC_ORDER];
|
||||
public int window;
|
||||
};
|
||||
|
||||
unsafe struct FlacSubframeInfo
|
||||
{
|
||||
public FlacSubframe best;
|
||||
public uint obits;
|
||||
public uint wbits;
|
||||
public int* samples;
|
||||
public fixed uint done_lpcs[lpc.MAX_LPC_WINDOWS * 2];
|
||||
public uint done_fixed;
|
||||
public fixed double lpcs_reff[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_WINDOWS];
|
||||
public fixed int lpcs_order[lpc.MAX_LPC_WINDOWS];
|
||||
};
|
||||
|
||||
unsafe struct FlacFrame
|
||||
{
|
||||
public int blocksize;
|
||||
@@ -158,7 +141,7 @@ namespace CUETools.Codecs.FLAKE
|
||||
public ChannelMode ch_mode;
|
||||
public int ch_order0, ch_order1;
|
||||
public byte crc8;
|
||||
public FlacSubframe* subframes;
|
||||
public FlacSubframeInfo* subframes;
|
||||
public uint frame_count;
|
||||
public FlacSubframe current;
|
||||
}
|
||||
@@ -170,6 +153,7 @@ namespace CUETools.Codecs.FLAKE
|
||||
LogFast = 2,
|
||||
LogSearch = 3,
|
||||
EstSearch = 4,
|
||||
Estimate8 = 6,
|
||||
Search = 5
|
||||
}
|
||||
|
||||
@@ -204,11 +188,8 @@ namespace CUETools.Codecs.FLAKE
|
||||
{
|
||||
Independent = 0,
|
||||
Estimate = 1,
|
||||
Estimate2 = 2,
|
||||
Estimate3 = 3,
|
||||
Estimate4 = 4,
|
||||
Estimate5 = 5,
|
||||
Search = 9
|
||||
Evaluate = 2,
|
||||
Search = 3
|
||||
}
|
||||
|
||||
public enum SubframeType
|
||||
@@ -233,7 +214,8 @@ namespace CUETools.Codecs.FLAKE
|
||||
Welch = 1,
|
||||
Tukey = 2,
|
||||
Hann = 4,
|
||||
Flattop = 8
|
||||
Flattop = 8,
|
||||
TukeyFlattop = 10
|
||||
}
|
||||
|
||||
public struct SeekPoint
|
||||
|
||||
@@ -118,13 +118,28 @@ namespace CUETools.Codecs.FLAKE
|
||||
{
|
||||
if (value > _sampleCount)
|
||||
throw new Exception("seeking past end of stream");
|
||||
if (value < Position)
|
||||
throw new Exception("backwards seeking not yet supported");
|
||||
if (seek_table != null)
|
||||
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;
|
||||
_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;
|
||||
@@ -313,14 +328,14 @@ namespace CUETools.Codecs.FLAKE
|
||||
unsafe void decode_subframe_constant(BitReader bitreader, FlacFrame* frame, int ch)
|
||||
{
|
||||
int obits = (int)frame->subframes[ch].obits;
|
||||
frame->subframes[ch].residual[0] = bitreader.readbits_signed(obits);
|
||||
frame->subframes[ch].best.residual[0] = bitreader.readbits_signed(obits);
|
||||
}
|
||||
|
||||
unsafe void decode_subframe_verbatim(BitReader bitreader, FlacFrame* frame, int ch)
|
||||
{
|
||||
int obits = (int)frame->subframes[ch].obits;
|
||||
for (int i = 0; i < frame->blocksize; i++)
|
||||
frame->subframes[ch].residual[i] = bitreader.readbits_signed(obits);
|
||||
frame->subframes[ch].best.residual[i] = bitreader.readbits_signed(obits);
|
||||
}
|
||||
|
||||
unsafe void decode_residual(BitReader bitreader, FlacFrame* frame, int ch)
|
||||
@@ -330,25 +345,25 @@ namespace CUETools.Codecs.FLAKE
|
||||
if (coding_method != 0 && coding_method != 1) // if 1, then parameter length == 5 bits instead of 4
|
||||
throw new Exception("unsupported residual coding");
|
||||
// partition order
|
||||
frame->subframes[ch].rc.porder = (int)bitreader.readbits(4);
|
||||
if (frame->subframes[ch].rc.porder > 8)
|
||||
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].rc.porder;
|
||||
int res_cnt = psize - frame->subframes[ch].order;
|
||||
int psize = frame->blocksize >> frame->subframes[ch].best.rc.porder;
|
||||
int res_cnt = psize - frame->subframes[ch].best.order;
|
||||
|
||||
int rice_len = 4 + (int)coding_method;
|
||||
// residual
|
||||
int j = frame->subframes[ch].order;
|
||||
int* r = frame->subframes[ch].residual + j;
|
||||
for (int p = 0; p < (1 << frame->subframes[ch].rc.porder); p++)
|
||||
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].rc.rparams[p] = (int)bitreader.readbits(rice_len);
|
||||
int k = frame->subframes[ch].best.rc.rparams[p] = (int)bitreader.readbits(rice_len);
|
||||
if (k == (1 << rice_len) - 1)
|
||||
{
|
||||
k = frame->subframes[ch].rc.esc_bps[p] = (int)bitreader.readbits(5);
|
||||
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);
|
||||
}
|
||||
@@ -365,8 +380,8 @@ namespace CUETools.Codecs.FLAKE
|
||||
{
|
||||
// warm-up samples
|
||||
int obits = (int)frame->subframes[ch].obits;
|
||||
for (int i = 0; i < frame->subframes[ch].order; i++)
|
||||
frame->subframes[ch].residual[i] = bitreader.readbits_signed(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);
|
||||
@@ -376,14 +391,14 @@ namespace CUETools.Codecs.FLAKE
|
||||
{
|
||||
// warm-up samples
|
||||
int obits = (int)frame->subframes[ch].obits;
|
||||
for (int i = 0; i < frame->subframes[ch].order; i++)
|
||||
frame->subframes[ch].residual[i] = bitreader.readbits_signed(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].cbits = (int)bitreader.readbits(4) + 1; // lpc_precision
|
||||
frame->subframes[ch].shift = bitreader.readbits_signed(5);
|
||||
for (int i = 0; i < frame->subframes[ch].order; i++)
|
||||
frame->subframes[ch].coefs[i] = bitreader.readbits_signed(frame->subframes[ch].cbits);
|
||||
frame->subframes[ch].best.cbits = (int)bitreader.readbits(4) + 1; // lpc_precision
|
||||
frame->subframes[ch].best.shift = bitreader.readbits_signed(5);
|
||||
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);
|
||||
@@ -411,25 +426,25 @@ namespace CUETools.Codecs.FLAKE
|
||||
case ChannelMode.RightSide: frame->subframes[ch].obits += 1 - (uint)ch; break;
|
||||
}
|
||||
|
||||
frame->subframes[ch].type = (SubframeType)type_code;
|
||||
frame->subframes[ch].order = 0;
|
||||
frame->subframes[ch].best.type = (SubframeType)type_code;
|
||||
frame->subframes[ch].best.order = 0;
|
||||
|
||||
if ((type_code & (uint)SubframeType.LPC) != 0)
|
||||
{
|
||||
frame->subframes[ch].order = (type_code - (int)SubframeType.LPC) + 1;
|
||||
frame->subframes[ch].type = SubframeType.LPC;
|
||||
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].order = (type_code - (int)SubframeType.Fixed);
|
||||
frame->subframes[ch].type = SubframeType.Fixed;
|
||||
frame->subframes[ch].best.order = (type_code - (int)SubframeType.Fixed);
|
||||
frame->subframes[ch].best.type = SubframeType.Fixed;
|
||||
}
|
||||
|
||||
frame->subframes[ch].residual = r + ch * Flake.MAX_BLOCKSIZE;
|
||||
frame->subframes[ch].best.residual = r + ch * Flake.MAX_BLOCKSIZE;
|
||||
frame->subframes[ch].samples = s + ch * Flake.MAX_BLOCKSIZE;
|
||||
|
||||
// subframe
|
||||
switch (frame->subframes[ch].type)
|
||||
switch (frame->subframes[ch].best.type)
|
||||
{
|
||||
case SubframeType.Constant:
|
||||
decode_subframe_constant(bitreader, frame, ch);
|
||||
@@ -451,14 +466,14 @@ namespace CUETools.Codecs.FLAKE
|
||||
|
||||
unsafe void restore_samples_fixed(FlacFrame* frame, int ch)
|
||||
{
|
||||
FlacSubframe* sub = frame->subframes + ch;
|
||||
FlacSubframeInfo* sub = frame->subframes + ch;
|
||||
|
||||
Flake.memcpy(sub->samples, sub->residual, sub->order);
|
||||
int* data = sub->samples + sub->order;
|
||||
int* residual = sub->residual + sub->order;
|
||||
int data_len = frame->blocksize - sub->order;
|
||||
Flake.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->order)
|
||||
switch (sub->best.order)
|
||||
{
|
||||
case 0:
|
||||
Flake.memcpy(data, residual, data_len);
|
||||
@@ -497,27 +512,27 @@ namespace CUETools.Codecs.FLAKE
|
||||
|
||||
unsafe void restore_samples_lpc(FlacFrame* frame, int ch)
|
||||
{
|
||||
FlacSubframe* sub = frame->subframes + ch;
|
||||
FlacSubframeInfo* sub = frame->subframes + ch;
|
||||
ulong csum = 0;
|
||||
for (int i = sub->order; i > 0; i--)
|
||||
csum += (ulong)Math.Abs(sub->coefs[i - 1]);
|
||||
for (int i = sub->best.order; i > 0; i--)
|
||||
csum += (ulong)Math.Abs(sub->best.coefs[i - 1]);
|
||||
if ((csum << (int)sub->obits) >= 1UL << 32)
|
||||
lpc.decode_residual_long(sub->residual, sub->samples, frame->blocksize, sub->order, sub->coefs, sub->shift);
|
||||
lpc.decode_residual_long(sub->best.residual, sub->samples, frame->blocksize, sub->best.order, sub->best.coefs, sub->best.shift);
|
||||
else
|
||||
lpc.decode_residual(sub->residual, sub->samples, frame->blocksize, sub->order, sub->coefs, sub->shift);
|
||||
lpc.decode_residual(sub->best.residual, sub->samples, frame->blocksize, sub->best.order, sub->best.coefs, sub->best.shift);
|
||||
}
|
||||
|
||||
unsafe void restore_samples(FlacFrame* frame)
|
||||
{
|
||||
for (int ch = 0; ch < channels; ch++)
|
||||
{
|
||||
switch (frame->subframes[ch].type)
|
||||
switch (frame->subframes[ch].best.type)
|
||||
{
|
||||
case SubframeType.Constant:
|
||||
Flake.memset(frame->subframes[ch].samples, frame->subframes[ch].residual[0], frame->blocksize);
|
||||
Flake.memset(frame->subframes[ch].samples, frame->subframes[ch].best.residual[0], frame->blocksize);
|
||||
break;
|
||||
case SubframeType.Verbatim:
|
||||
Flake.memcpy(frame->subframes[ch].samples, frame->subframes[ch].residual, frame->blocksize);
|
||||
Flake.memcpy(frame->subframes[ch].samples, frame->subframes[ch].best.residual, frame->blocksize);
|
||||
break;
|
||||
case SubframeType.Fixed:
|
||||
restore_samples_fixed(frame, ch);
|
||||
@@ -574,12 +589,12 @@ namespace CUETools.Codecs.FLAKE
|
||||
{
|
||||
BitReader bitreader = new BitReader(buf, pos, len);
|
||||
FlacFrame frame;
|
||||
FlacSubframe* subframes = stackalloc FlacSubframe[channels];
|
||||
FlacSubframeInfo* subframes = stackalloc FlacSubframeInfo[channels];
|
||||
frame.subframes = subframes;
|
||||
decode_frame_header(bitreader, &frame);
|
||||
decode_subframes(bitreader, &frame);
|
||||
bitreader.flush();
|
||||
ushort crc = crc16.ComputeChecksum(bitreader.Buffer, pos, bitreader.Position - pos);
|
||||
ushort crc = crc16.ComputeChecksum(bitreader.Buffer + pos, bitreader.Position - pos);
|
||||
if (crc != bitreader.readbits(16))
|
||||
throw new Exception("frame crc mismatch");
|
||||
restore_samples(&frame);
|
||||
@@ -691,17 +706,13 @@ namespace CUETools.Codecs.FLAKE
|
||||
}
|
||||
else if (type == MetadataType.FLAC__METADATA_TYPE_SEEKTABLE)
|
||||
{
|
||||
const int FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */
|
||||
const int FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = 64; /* bits */
|
||||
const int FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */
|
||||
|
||||
int num_entries = len / 18;
|
||||
seek_table = new SeekPoint[num_entries];
|
||||
for (int e = 0; e < num_entries; e++)
|
||||
{
|
||||
seek_table[e].number = bitreader.readbits64(FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN);
|
||||
seek_table[e].offset = bitreader.readbits64(FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN);
|
||||
seek_table[e].framesize = bitreader.readbits24(FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN);
|
||||
seek_table[e].number = bitreader.readbits64(Flake.FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN);
|
||||
seek_table[e].offset = bitreader.readbits64(Flake.FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN);
|
||||
seek_table[e].framesize = bitreader.readbits24(Flake.FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN);
|
||||
}
|
||||
}
|
||||
if (_framesBufferLength < 4 + len)
|
||||
|
||||
@@ -48,6 +48,8 @@ namespace CUETools.Codecs.FLAKE
|
||||
|
||||
int frame_count = 0;
|
||||
|
||||
long first_frame_offset = 0;
|
||||
|
||||
TimeSpan _userProcessorTime;
|
||||
|
||||
// header bytes
|
||||
@@ -71,6 +73,9 @@ namespace CUETools.Codecs.FLAKE
|
||||
|
||||
FlakeReader verify;
|
||||
|
||||
SeekPoint[] seek_table;
|
||||
int seek_table_offset = -1;
|
||||
|
||||
bool inited = false;
|
||||
|
||||
public FlakeWriter(string path, int bitsPerSample, int channelCount, int sampleRate, Stream IO)
|
||||
@@ -89,20 +94,15 @@ namespace CUETools.Codecs.FLAKE
|
||||
_path = path;
|
||||
_IO = IO;
|
||||
|
||||
//verify = new FlakeReader(channels, bits_per_sample);
|
||||
|
||||
samplesBuffer = new int[Flake.MAX_BLOCKSIZE * (channels == 2 ? 4 : channels)];
|
||||
residualBuffer = new int[Flake.MAX_BLOCKSIZE * (channels == 2 ? 4 : channels)];
|
||||
windowBuffer = new double[Flake.MAX_BLOCKSIZE * lpc.MAX_LPC_WINDOWS];
|
||||
if (verify != null)
|
||||
verifyBuffer = new int[Flake.MAX_BLOCKSIZE * channels];
|
||||
|
||||
eparams.flake_set_defaults(_compressionLevel);
|
||||
eparams.padding_size = 8192;
|
||||
|
||||
crc8 = new Crc8();
|
||||
crc16 = new Crc16();
|
||||
md5 = new MD5CryptoServiceProvider();
|
||||
}
|
||||
|
||||
public int TotalSize
|
||||
@@ -144,6 +144,8 @@ namespace CUETools.Codecs.FLAKE
|
||||
static extern IntPtr GetCurrentThread();
|
||||
|
||||
void DoClose()
|
||||
{
|
||||
if (inited)
|
||||
{
|
||||
if (samplesInBuffer > 0)
|
||||
{
|
||||
@@ -151,17 +153,29 @@ namespace CUETools.Codecs.FLAKE
|
||||
output_frame();
|
||||
}
|
||||
|
||||
long fake, KernelStart, UserStart;
|
||||
GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart);
|
||||
_userProcessorTime = new TimeSpan(UserStart);
|
||||
|
||||
md5.TransformFinalBlock(frame_buffer, 0, 0);
|
||||
if (_IO.CanSeek)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
long fake, KernelStart, UserStart;
|
||||
GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart);
|
||||
_userProcessorTime = new TimeSpan(UserStart);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
@@ -174,6 +188,7 @@ namespace CUETools.Codecs.FLAKE
|
||||
public void Delete()
|
||||
{
|
||||
DoClose();
|
||||
if (_path != "")
|
||||
File.Delete(_path);
|
||||
}
|
||||
|
||||
@@ -193,6 +208,7 @@ namespace CUETools.Codecs.FLAKE
|
||||
public long BlockSize
|
||||
{
|
||||
set { _blocksize = (int)value; }
|
||||
get { return _blocksize == 0 ? eparams.block_size : _blocksize; }
|
||||
}
|
||||
|
||||
public OrderMethod OrderMethod
|
||||
@@ -213,31 +229,54 @@ namespace CUETools.Codecs.FLAKE
|
||||
set { eparams.stereo_method = value; }
|
||||
}
|
||||
|
||||
public int MaxPrecisionSearch
|
||||
{
|
||||
get { return eparams.lpc_precision_search; }
|
||||
set
|
||||
{
|
||||
if (value < 0 || value > 1)
|
||||
throw new Exception("unsupported MaxPrecisionSearch value");
|
||||
eparams.lpc_precision_search = value;
|
||||
}
|
||||
}
|
||||
|
||||
public WindowFunction WindowFunction
|
||||
{
|
||||
get { return eparams.window_function; }
|
||||
set { eparams.window_function = value; }
|
||||
}
|
||||
|
||||
public bool DoMD5
|
||||
{
|
||||
get { return eparams.do_md5; }
|
||||
set { eparams.do_md5 = value; }
|
||||
}
|
||||
|
||||
public bool DoVerify
|
||||
{
|
||||
get { return eparams.do_verify; }
|
||||
set { eparams.do_verify = value; }
|
||||
}
|
||||
|
||||
public bool DoSeekTable
|
||||
{
|
||||
get { return eparams.do_seektable; }
|
||||
set { eparams.do_seektable = value; }
|
||||
}
|
||||
|
||||
public int MinPredictionOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
return eparams.prediction_type == PredictionType.Fixed ?
|
||||
eparams.min_fixed_order : eparams.min_prediction_order;
|
||||
return PredictionType == PredictionType.Fixed ?
|
||||
MinFixedOrder : MinLPCOrder;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (eparams.prediction_type == PredictionType.Fixed)
|
||||
{
|
||||
if (value < 0 || value > eparams.max_fixed_order)
|
||||
throw new Exception("invalid order");
|
||||
eparams.min_fixed_order = value;
|
||||
return;
|
||||
}
|
||||
if (value < 0 || value > eparams.max_prediction_order)
|
||||
throw new Exception("invalid order");
|
||||
eparams.min_prediction_order = value;
|
||||
if (PredictionType == PredictionType.Fixed)
|
||||
MinFixedOrder = value;
|
||||
else
|
||||
MinLPCOrder = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,21 +284,82 @@ namespace CUETools.Codecs.FLAKE
|
||||
{
|
||||
get
|
||||
{
|
||||
return eparams.prediction_type == PredictionType.Fixed ?
|
||||
eparams.max_fixed_order : eparams.max_prediction_order;
|
||||
return PredictionType == PredictionType.Fixed ?
|
||||
MaxFixedOrder : MaxLPCOrder;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (eparams.prediction_type == PredictionType.Fixed)
|
||||
if (PredictionType == PredictionType.Fixed)
|
||||
MaxFixedOrder = value;
|
||||
else
|
||||
MaxLPCOrder = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int MinLPCOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
return eparams.min_prediction_order;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < 1 || value > eparams.max_prediction_order)
|
||||
throw new Exception("invalid MinLPCOrder " + value.ToString());
|
||||
eparams.min_prediction_order = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int MaxLPCOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
return eparams.max_prediction_order;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value > 32 || value < eparams.min_prediction_order)
|
||||
throw new Exception("invalid MaxLPCOrder " + value.ToString());
|
||||
eparams.max_prediction_order = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int MinFixedOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
return eparams.min_fixed_order;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < 0 || value > eparams.max_fixed_order)
|
||||
throw new Exception("invalid MinFixedOrder " + value.ToString());
|
||||
eparams.min_fixed_order = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int MaxFixedOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
return eparams.max_fixed_order;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value > 4 || value < eparams.min_fixed_order)
|
||||
throw new Exception("invalid order");
|
||||
throw new Exception("invalid MaxFixedOrder " + value.ToString());
|
||||
eparams.max_fixed_order = value;
|
||||
return;
|
||||
}
|
||||
if (value > 32 || value < eparams.min_prediction_order)
|
||||
throw new Exception("invalid order");
|
||||
eparams.max_prediction_order = value;
|
||||
}
|
||||
|
||||
public int MinPartitionOrder
|
||||
{
|
||||
get { return eparams.min_partition_order; }
|
||||
set
|
||||
{
|
||||
if (value < 0 || value > eparams.max_partition_order)
|
||||
throw new Exception("invalid MinPartitionOrder " + value.ToString());
|
||||
eparams.min_partition_order = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +369,7 @@ namespace CUETools.Codecs.FLAKE
|
||||
set
|
||||
{
|
||||
if (value > 8 || value < eparams.min_partition_order)
|
||||
throw new Exception("invalid order");
|
||||
throw new Exception("invalid MaxPartitionOrder " + value.ToString());
|
||||
eparams.max_partition_order = value;
|
||||
}
|
||||
}
|
||||
@@ -351,17 +451,20 @@ namespace CUETools.Codecs.FLAKE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy channel-interleaved input samples into separate subframes
|
||||
*/
|
||||
/// Copy channel-interleaved input samples into separate subframes
|
||||
unsafe void copy_samples(int[,] samples, int pos, int block)
|
||||
{
|
||||
fixed (int* fsamples = samplesBuffer)
|
||||
fixed (int* fsamples = samplesBuffer, src = &samples[pos, 0])
|
||||
{
|
||||
if (channels == 2)
|
||||
Flake.deinterlace(fsamples + samplesInBuffer, fsamples + Flake.MAX_BLOCKSIZE + samplesInBuffer, src, block);
|
||||
else
|
||||
for (int ch = 0; ch < channels; ch++)
|
||||
{
|
||||
int* psamples = fsamples + ch * Flake.MAX_BLOCKSIZE + samplesInBuffer;
|
||||
for (int i = 0; i < block; i++)
|
||||
psamples[i] = samples[pos + i, ch];
|
||||
psamples[i] = src[i * channels + ch];
|
||||
}
|
||||
}
|
||||
samplesInBuffer += block;
|
||||
}
|
||||
@@ -407,9 +510,9 @@ namespace CUETools.Codecs.FLAKE
|
||||
return k_opt;
|
||||
}
|
||||
|
||||
unsafe uint calc_decorr_score(FlacFrame* frame, int ch, FlacSubframe* sub)
|
||||
unsafe uint calc_decorr_score(FlacFrame* frame, int ch)
|
||||
{
|
||||
int* s = sub->samples;
|
||||
int* s = frame->subframes[ch].samples;
|
||||
int n = frame->blocksize;
|
||||
ulong sum = 0;
|
||||
for (int i = 2; i < n; i++)
|
||||
@@ -425,14 +528,16 @@ namespace CUETools.Codecs.FLAKE
|
||||
if (w > bps)
|
||||
throw new Exception("internal error");
|
||||
frame->subframes[ch].samples = s;
|
||||
frame->subframes[ch].residual = r;
|
||||
frame->subframes[ch].obits = bps - (uint)w;
|
||||
frame->subframes[ch].wbits = (uint)w;
|
||||
frame->subframes[ch].type = SubframeType.Verbatim;
|
||||
frame->subframes[ch].size = UINT32_MAX;
|
||||
for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++)
|
||||
frame->subframes[ch].best.residual = r;
|
||||
frame->subframes[ch].best.type = SubframeType.Verbatim;
|
||||
frame->subframes[ch].best.size = UINT32_MAX;
|
||||
for (int iWindow = 0; iWindow < 2 * lpc.MAX_LPC_WINDOWS; iWindow++)
|
||||
frame->subframes[ch].done_lpcs[iWindow] = 0;
|
||||
frame->subframes[ch].done_fixed = 0;
|
||||
for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++)
|
||||
frame->subframes[ch].lpcs_order[iWindow] = 0;
|
||||
}
|
||||
|
||||
unsafe static void channel_decorrelation(int* leftS, int* rightS, int *leftM, int *rightM, int blocksize)
|
||||
@@ -613,38 +718,46 @@ namespace CUETools.Codecs.FLAKE
|
||||
|
||||
unsafe void choose_best_subframe(FlacFrame* frame, int ch)
|
||||
{
|
||||
if (frame->current.size < frame->subframes[ch].size)
|
||||
if (frame->current.size < frame->subframes[ch].best.size)
|
||||
{
|
||||
FlacSubframe tmp = frame->subframes[ch];
|
||||
frame->subframes[ch] = frame->current;
|
||||
for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++)
|
||||
frame->subframes[ch].done_lpcs[iWindow] = tmp.done_lpcs[iWindow];
|
||||
frame->subframes[ch].done_fixed = tmp.done_fixed;
|
||||
FlacSubframe tmp = frame->subframes[ch].best;
|
||||
frame->subframes[ch].best = frame->current;
|
||||
frame->current = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe void encode_residual_lpc_sub(FlacFrame* frame, double * lpcs, int iWindow, int order, int ch)
|
||||
{
|
||||
if ((frame->subframes[ch].done_lpcs[iWindow] & (1U << (order - 1))) != 0)
|
||||
return; // already calculated;
|
||||
for (uint i_precision = 0; i_precision <= eparams.lpc_precision_search && lpc_precision + i_precision < 16; i_precision++)
|
||||
// check if we already calculated with this order, window and precision
|
||||
if ((frame->subframes[ch].done_lpcs[iWindow + i_precision * lpc.MAX_LPC_WINDOWS] & (1U << (order - 1))) == 0)
|
||||
{
|
||||
frame->subframes[ch].done_lpcs[iWindow + i_precision * lpc.MAX_LPC_WINDOWS] |= (1U << (order - 1));
|
||||
|
||||
frame->subframes[ch].done_lpcs[iWindow] |= (1U << (order - 1));
|
||||
uint cbits = lpc_precision + i_precision;
|
||||
|
||||
frame->current.type = SubframeType.LPC;
|
||||
frame->current.order = order;
|
||||
frame->current.window = iWindow;
|
||||
|
||||
lpc.quantize_lpc_coefs(lpcs + (frame->current.order - 1) * lpc.MAX_LPC_ORDER,
|
||||
frame->current.order, lpc_precision, frame->current.coefs, out frame->current.shift);
|
||||
frame->current.order, cbits, frame->current.coefs, out frame->current.shift);
|
||||
|
||||
lpc.encode_residual(frame->current.residual, frame->current.samples, frame->blocksize, frame->current.order, frame->current.coefs, frame->current.shift);
|
||||
ulong csum = 0;
|
||||
for (int i = frame->current.order; i > 0; i--)
|
||||
csum += (ulong)Math.Abs(frame->current.coefs[i - 1]);
|
||||
|
||||
if ((csum << (int)frame->subframes[ch].obits) >= 1UL << 32)
|
||||
lpc.encode_residual_long(frame->current.residual, frame->subframes[ch].samples, frame->blocksize, frame->current.order, frame->current.coefs, frame->current.shift);
|
||||
else
|
||||
lpc.encode_residual(frame->current.residual, frame->subframes[ch].samples, frame->blocksize, frame->current.order, frame->current.coefs, frame->current.shift);
|
||||
|
||||
frame->current.size = calc_rice_params_lpc(&frame->current.rc, eparams.min_partition_order, eparams.max_partition_order,
|
||||
frame->current.residual, frame->blocksize, frame->current.order, frame->current.obits, lpc_precision);
|
||||
frame->current.residual, frame->blocksize, frame->current.order, frame->subframes[ch].obits, cbits);
|
||||
|
||||
choose_best_subframe(frame, ch);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe void encode_residual_fixed_sub(FlacFrame* frame, int order, int ch)
|
||||
{
|
||||
@@ -654,10 +767,10 @@ namespace CUETools.Codecs.FLAKE
|
||||
frame->current.order = order;
|
||||
frame->current.type = SubframeType.Fixed;
|
||||
|
||||
encode_residual_fixed(frame->current.residual, frame->current.samples, frame->blocksize, frame->current.order);
|
||||
encode_residual_fixed(frame->current.residual, frame->subframes[ch].samples, frame->blocksize, frame->current.order);
|
||||
|
||||
frame->current.size = calc_rice_params_fixed(&frame->current.rc, eparams.min_partition_order, eparams.max_partition_order,
|
||||
frame->current.residual, frame->blocksize, frame->current.order, frame->current.obits);
|
||||
frame->current.residual, frame->blocksize, frame->current.order, frame->subframes[ch].obits);
|
||||
|
||||
frame->subframes[ch].done_fixed |= (1U << order);
|
||||
|
||||
@@ -666,14 +779,8 @@ namespace CUETools.Codecs.FLAKE
|
||||
|
||||
unsafe void encode_residual(FlacFrame* frame, int ch, PredictionType predict, OrderMethod omethod)
|
||||
{
|
||||
int i;
|
||||
FlacSubframe* sub = frame->subframes + ch;
|
||||
int* smp = sub->samples;
|
||||
int n = frame->blocksize;
|
||||
|
||||
frame->current.samples = sub->samples;
|
||||
frame->current.obits = sub->obits;
|
||||
frame->current.wbits = sub->wbits;
|
||||
int* smp = frame->subframes[ch].samples;
|
||||
int i, n = frame->blocksize;
|
||||
|
||||
// CONSTANT
|
||||
for (i = 1; i < n; i++)
|
||||
@@ -682,15 +789,15 @@ namespace CUETools.Codecs.FLAKE
|
||||
}
|
||||
if (i == n)
|
||||
{
|
||||
sub->type = SubframeType.Constant;
|
||||
sub->residual[0] = smp[0];
|
||||
sub->size = sub->obits;
|
||||
frame->subframes[ch].best.type = SubframeType.Constant;
|
||||
frame->subframes[ch].best.residual[0] = smp[0];
|
||||
frame->subframes[ch].best.size = frame->subframes[ch].obits;
|
||||
return;
|
||||
}
|
||||
|
||||
// VERBATIM
|
||||
frame->current.type = SubframeType.Verbatim;
|
||||
frame->current.size = frame->current.obits * (uint)frame->blocksize;
|
||||
frame->current.size = frame->subframes[ch].obits * (uint)frame->blocksize;
|
||||
choose_best_subframe(frame, ch);
|
||||
|
||||
if (n < 5 || predict == PredictionType.None)
|
||||
@@ -699,7 +806,7 @@ namespace CUETools.Codecs.FLAKE
|
||||
// FIXED
|
||||
if (predict == PredictionType.Fixed ||
|
||||
predict == PredictionType.Search ||
|
||||
(predict == PredictionType.Estimated && sub->type == SubframeType.Fixed) ||
|
||||
(predict == PredictionType.Estimated && frame->subframes[ch].best.type == SubframeType.Fixed) ||
|
||||
n <= eparams.max_prediction_order)
|
||||
{
|
||||
int max_fixed_order = Math.Min(eparams.max_fixed_order, 4);
|
||||
@@ -713,20 +820,51 @@ namespace CUETools.Codecs.FLAKE
|
||||
if (n > eparams.max_prediction_order &&
|
||||
(predict == PredictionType.Levinson ||
|
||||
predict == PredictionType.Search ||
|
||||
(predict == PredictionType.Estimated && sub->type == SubframeType.LPC)))
|
||||
(predict == PredictionType.Estimated && frame->subframes[ch].best.type == SubframeType.LPC)))
|
||||
{
|
||||
double* lpcs = stackalloc double[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER];
|
||||
//double* lpcs = stackalloc double[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER];
|
||||
int min_order = eparams.min_prediction_order;
|
||||
int max_order = eparams.max_prediction_order;
|
||||
|
||||
fixed (double* window = windowBuffer)
|
||||
for (int iWindow = 0; iWindow < _windowcount; iWindow++)
|
||||
{
|
||||
if (predict == PredictionType.Estimated && sub->window != iWindow)
|
||||
if (predict == PredictionType.Estimated && frame->subframes[ch].best.window != iWindow)
|
||||
continue;
|
||||
|
||||
int est_order = (int)lpc.calc_coefs(smp, (uint)n, (uint)max_order, omethod, lpcs,
|
||||
double* reff = frame->subframes[ch].lpcs_reff + iWindow * lpc.MAX_LPC_ORDER;
|
||||
if (frame->subframes[ch].lpcs_order[iWindow] != max_order)
|
||||
{
|
||||
double* autoc = stackalloc double[lpc.MAX_LPC_ORDER + 1];
|
||||
lpc.compute_autocorr(smp, (uint)n, (uint)max_order + 1, autoc,
|
||||
window + iWindow * _windowsize);
|
||||
lpc.compute_schur_reflection(autoc, (uint)max_order, reff);
|
||||
frame->subframes[ch].lpcs_order[iWindow] = max_order;
|
||||
}
|
||||
|
||||
int est_order = 1;
|
||||
int est_order2 = 1;
|
||||
if (omethod == OrderMethod.Estimate || omethod == OrderMethod.Estimate8 || omethod == OrderMethod.EstSearch)
|
||||
{
|
||||
// Estimate optimal order using reflection coefficients
|
||||
for (int r = max_order - 1; r >= 0; r--)
|
||||
if (Math.Abs(reff[r]) > 0.1)
|
||||
{
|
||||
est_order = r + 1;
|
||||
break;
|
||||
}
|
||||
for (int r = Math.Min(max_order, 2 * eparams.max_fixed_order) - 1; r >= 0; r--)
|
||||
if (Math.Abs(reff[r]) > 0.1)
|
||||
{
|
||||
est_order2 = r + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
est_order = max_order;
|
||||
|
||||
double* lpcs = stackalloc double[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER];
|
||||
lpc.compute_lpc_coefs(null, (uint)est_order, reff, lpcs);
|
||||
|
||||
switch (omethod)
|
||||
{
|
||||
@@ -738,21 +876,31 @@ namespace CUETools.Codecs.FLAKE
|
||||
// estimated order
|
||||
encode_residual_lpc_sub(frame, lpcs, iWindow, est_order, ch);
|
||||
break;
|
||||
case OrderMethod.Search:
|
||||
// brute-force optimal order search
|
||||
for (i = max_order; i > 0; i--)
|
||||
encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
|
||||
case OrderMethod.Estimate8:
|
||||
// estimated order
|
||||
encode_residual_lpc_sub(frame, lpcs, iWindow, est_order2, ch);
|
||||
break;
|
||||
//case OrderMethod.EstSearch:
|
||||
// brute-force search starting from estimate
|
||||
//encode_residual_lpc_sub(frame, lpcs, iWindow, est_order, ch);
|
||||
//encode_residual_lpc_sub(frame, lpcs, iWindow, est_order2, ch);
|
||||
//break;
|
||||
case OrderMethod.EstSearch:
|
||||
// brute-force search starting from estimate
|
||||
for (i = est_order; i > 0; i--)
|
||||
for (i = est_order; i >= min_order; i--)
|
||||
if (i == est_order || Math.Abs(reff[i - 1]) > 0.10)
|
||||
encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
|
||||
break;
|
||||
case OrderMethod.Search:
|
||||
// brute-force optimal order search
|
||||
for (i = max_order; i >= min_order; i--)
|
||||
encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
|
||||
break;
|
||||
case OrderMethod.LogFast:
|
||||
// Try max, est, 32,16,8,4,2,1
|
||||
encode_residual_lpc_sub(frame, lpcs, iWindow, max_order, ch);
|
||||
//encode_residual_lpc_sub(frame, lpcs, est_order, ch);
|
||||
for (i = lpc.MAX_LPC_ORDER; i > 0; i >>= 1)
|
||||
for (i = lpc.MAX_LPC_ORDER; i >= min_order; i >>= 1)
|
||||
if (i < max_order)
|
||||
encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
|
||||
break;
|
||||
@@ -760,16 +908,16 @@ namespace CUETools.Codecs.FLAKE
|
||||
// do LogFast first
|
||||
encode_residual_lpc_sub(frame, lpcs, iWindow, max_order, ch);
|
||||
//encode_residual_lpc_sub(frame, lpcs, est_order, ch);
|
||||
for (i = lpc.MAX_LPC_ORDER; i > 0; i >>= 1)
|
||||
for (i = lpc.MAX_LPC_ORDER; i >= min_order; i >>= 1)
|
||||
if (i < max_order)
|
||||
encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
|
||||
// if found a good order, try to search around it
|
||||
if (frame->subframes[ch].type == SubframeType.LPC)
|
||||
if (frame->subframes[ch].best.type == SubframeType.LPC)
|
||||
{
|
||||
// log search (written by Michael Niedermayer for FFmpeg)
|
||||
for (int step = lpc.MAX_LPC_ORDER; step > 0; step >>= 1)
|
||||
{
|
||||
int last = frame->subframes[ch].order;
|
||||
int last = frame->subframes[ch].best.order;
|
||||
if (step <= (last + 1) / 2)
|
||||
for (i = last - step; i <= last + step; i += step)
|
||||
if (i >= min_order && i <= max_order)
|
||||
@@ -821,40 +969,42 @@ namespace CUETools.Codecs.FLAKE
|
||||
bitwriter.writebits(8, crc);
|
||||
}
|
||||
|
||||
unsafe void output_residual(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub)
|
||||
unsafe void output_residual(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* sub)
|
||||
{
|
||||
// rice-encoded block
|
||||
bitwriter.writebits(2, 0);
|
||||
|
||||
// partition order
|
||||
int porder = sub->rc.porder;
|
||||
int porder = sub->best.rc.porder;
|
||||
int psize = frame->blocksize >> porder;
|
||||
//assert(porder >= 0);
|
||||
bitwriter.writebits(4, porder);
|
||||
int res_cnt = psize - sub->order;
|
||||
int res_cnt = psize - sub->best.order;
|
||||
|
||||
// residual
|
||||
int j = sub->order;
|
||||
int j = sub->best.order;
|
||||
for (int p = 0; p < (1 << porder); p++)
|
||||
{
|
||||
int k = sub->rc.rparams[p];
|
||||
int k = sub->best.rc.rparams[p];
|
||||
bitwriter.writebits(4, k);
|
||||
if (p == 1) res_cnt = psize;
|
||||
if (k == 0)
|
||||
for (int i = 0; i < res_cnt && j < frame->blocksize; i++, j++)
|
||||
{
|
||||
bitwriter.write_rice_signed(k, sub->residual[j]);
|
||||
}
|
||||
bitwriter.write_unary_signed(sub->best.residual[j]);
|
||||
else
|
||||
for (int i = 0; i < res_cnt && j < frame->blocksize; i++, j++)
|
||||
bitwriter.write_rice_signed(k, sub->best.residual[j]);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe void
|
||||
output_subframe_constant(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub)
|
||||
output_subframe_constant(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* sub)
|
||||
{
|
||||
bitwriter.writebits_signed(sub->obits, sub->residual[0]);
|
||||
bitwriter.writebits_signed(sub->obits, sub->best.residual[0]);
|
||||
}
|
||||
|
||||
unsafe void
|
||||
output_subframe_verbatim(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub)
|
||||
output_subframe_verbatim(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* sub)
|
||||
{
|
||||
int n = frame->blocksize;
|
||||
for (int i = 0; i < n; i++)
|
||||
@@ -863,29 +1013,32 @@ namespace CUETools.Codecs.FLAKE
|
||||
}
|
||||
|
||||
unsafe void
|
||||
output_subframe_fixed(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub)
|
||||
output_subframe_fixed(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* sub)
|
||||
{
|
||||
// warm-up samples
|
||||
for (int i = 0; i < sub->order; i++)
|
||||
bitwriter.writebits_signed(sub->obits, sub->residual[i]);
|
||||
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, FlacSubframe* sub)
|
||||
output_subframe_lpc(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* sub)
|
||||
{
|
||||
// warm-up samples
|
||||
for (int i = 0; i < sub->order; i++)
|
||||
bitwriter.writebits_signed(sub->obits, sub->residual[i]);
|
||||
for (int i = 0; i < sub->best.order; i++)
|
||||
bitwriter.writebits_signed(sub->obits, sub->best.residual[i]);
|
||||
|
||||
// LPC coefficients
|
||||
uint cbits = lpc_precision;
|
||||
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->shift);
|
||||
for (int i = 0; i < sub->order; i++)
|
||||
bitwriter.writebits_signed(cbits, sub->coefs[i]);
|
||||
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);
|
||||
@@ -895,13 +1048,13 @@ namespace CUETools.Codecs.FLAKE
|
||||
{
|
||||
for (int ch = 0; ch < channels; ch++)
|
||||
{
|
||||
FlacSubframe* sub = frame->subframes + ch;
|
||||
FlacSubframeInfo* sub = frame->subframes + ch;
|
||||
// subframe header
|
||||
int type_code = (int) sub->type;
|
||||
if (sub->type == SubframeType.Fixed)
|
||||
type_code |= sub->order;
|
||||
if (sub->type == SubframeType.LPC)
|
||||
type_code |= sub->order - 1;
|
||||
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);
|
||||
@@ -909,7 +1062,7 @@ namespace CUETools.Codecs.FLAKE
|
||||
bitwriter.writebits((int)sub->wbits, 1);
|
||||
|
||||
// subframe
|
||||
switch (sub->type)
|
||||
switch (sub->best.type)
|
||||
{
|
||||
case SubframeType.Constant:
|
||||
output_subframe_constant(frame, bitwriter, sub);
|
||||
@@ -986,7 +1139,7 @@ namespace CUETools.Codecs.FLAKE
|
||||
unsafe int encode_frame()
|
||||
{
|
||||
FlacFrame* frame = stackalloc FlacFrame[1];
|
||||
FlacSubframe* sf = stackalloc FlacSubframe[channels * 3];
|
||||
FlacSubframeInfo* sf = stackalloc FlacSubframeInfo[channels * 3];
|
||||
|
||||
fixed (int* s = samplesBuffer, r = residualBuffer)
|
||||
{
|
||||
@@ -1044,65 +1197,31 @@ namespace CUETools.Codecs.FLAKE
|
||||
|
||||
if (eparams.stereo_method == StereoMethod.Estimate)
|
||||
{
|
||||
frameM->subframes[0].size = (uint)frame->blocksize * 32 + calc_decorr_score(frameM, 0, frameM->subframes + 0);
|
||||
frameM->subframes[1].size = (uint)frame->blocksize * 32 + calc_decorr_score(frameM, 1, frameM->subframes + 1);
|
||||
frameS->subframes[0].size = (uint)frame->blocksize * 32 + calc_decorr_score(frameS, 0, frameS->subframes + 0);
|
||||
frameS->subframes[1].size = (uint)frame->blocksize * 32 + calc_decorr_score(frameS, 1, frameS->subframes + 1);
|
||||
frameM->subframes[0].best.size = (uint)frame->blocksize * 32 + calc_decorr_score(frameM, 0);
|
||||
frameM->subframes[1].best.size = (uint)frame->blocksize * 32 + calc_decorr_score(frameM, 1);
|
||||
frameS->subframes[0].best.size = (uint)frame->blocksize * 32 + calc_decorr_score(frameS, 0);
|
||||
frameS->subframes[1].best.size = (uint)frame->blocksize * 32 + calc_decorr_score(frameS, 1);
|
||||
}
|
||||
else if (eparams.stereo_method == StereoMethod.Estimate2)
|
||||
{
|
||||
int max_prediction_order = eparams.max_prediction_order;
|
||||
int max_window = _windowcount;
|
||||
eparams.max_prediction_order /= 2;
|
||||
_windowcount = 1;
|
||||
encode_residual(frameM, 0, PredictionType.Levinson, OrderMethod.Estimate);
|
||||
encode_residual(frameM, 1, PredictionType.Levinson, OrderMethod.Estimate);
|
||||
encode_residual(frameS, 0, PredictionType.Levinson, OrderMethod.Estimate);
|
||||
encode_residual(frameS, 1, PredictionType.Levinson, OrderMethod.Estimate);
|
||||
_windowcount = max_window;
|
||||
eparams.max_prediction_order = max_prediction_order;
|
||||
}
|
||||
else if (eparams.stereo_method == StereoMethod.Estimate3)
|
||||
{
|
||||
int max_fixed_order = eparams.max_fixed_order;
|
||||
eparams.max_fixed_order = 2;
|
||||
encode_residual(frameM, 0, PredictionType.Fixed, OrderMethod.Estimate);
|
||||
encode_residual(frameM, 1, PredictionType.Fixed, OrderMethod.Estimate);
|
||||
encode_residual(frameS, 0, PredictionType.Fixed, OrderMethod.Estimate);
|
||||
encode_residual(frameS, 1, PredictionType.Fixed, OrderMethod.Estimate);
|
||||
eparams.max_fixed_order = max_fixed_order;
|
||||
}
|
||||
else if (eparams.stereo_method == StereoMethod.Estimate4)
|
||||
else if (eparams.stereo_method == StereoMethod.Evaluate)
|
||||
{
|
||||
int max_prediction_order = eparams.max_prediction_order;
|
||||
int max_fixed_order = eparams.max_fixed_order;
|
||||
int min_fixed_order = eparams.min_fixed_order;
|
||||
int lpc_precision_search = eparams.lpc_precision_search;
|
||||
OrderMethod omethod = OrderMethod.Estimate8;
|
||||
eparams.min_fixed_order = 2;
|
||||
eparams.max_fixed_order = 2;
|
||||
eparams.max_prediction_order = Math.Min(eparams.max_prediction_order, 8);
|
||||
encode_residual(frameM, 0, eparams.prediction_type, OrderMethod.Estimate);
|
||||
encode_residual(frameM, 1, eparams.prediction_type, OrderMethod.Estimate);
|
||||
encode_residual(frameS, 0, eparams.prediction_type, OrderMethod.Estimate);
|
||||
encode_residual(frameS, 1, eparams.prediction_type, OrderMethod.Estimate);
|
||||
eparams.min_fixed_order = min_fixed_order;
|
||||
eparams.max_fixed_order = max_fixed_order;
|
||||
eparams.max_prediction_order = max_prediction_order;
|
||||
}
|
||||
else if (eparams.stereo_method == StereoMethod.Estimate5)
|
||||
{
|
||||
int max_prediction_order = eparams.max_prediction_order;
|
||||
int max_fixed_order = eparams.max_fixed_order;
|
||||
int min_fixed_order = eparams.min_fixed_order;
|
||||
eparams.min_fixed_order = 2;
|
||||
eparams.max_fixed_order = 2;
|
||||
eparams.max_prediction_order = Math.Min(eparams.max_prediction_order, 12);
|
||||
encode_residual(frameM, 0, eparams.prediction_type, OrderMethod.Estimate);
|
||||
encode_residual(frameM, 1, eparams.prediction_type, OrderMethod.Estimate);
|
||||
encode_residual(frameS, 0, eparams.prediction_type, OrderMethod.Estimate);
|
||||
encode_residual(frameS, 1, eparams.prediction_type, OrderMethod.Estimate);
|
||||
eparams.lpc_precision_search = 0;
|
||||
if (eparams.max_prediction_order > 12)
|
||||
eparams.max_prediction_order = 8;
|
||||
encode_residual(frameM, 0, eparams.prediction_type, omethod);
|
||||
encode_residual(frameM, 1, eparams.prediction_type, omethod);
|
||||
encode_residual(frameS, 0, eparams.prediction_type, omethod);
|
||||
encode_residual(frameS, 1, eparams.prediction_type, omethod);
|
||||
eparams.min_fixed_order = min_fixed_order;
|
||||
eparams.max_fixed_order = max_fixed_order;
|
||||
eparams.max_prediction_order = max_prediction_order;
|
||||
eparams.lpc_precision_search = lpc_precision_search;
|
||||
}
|
||||
else // StereoMethod.Search
|
||||
{
|
||||
@@ -1113,37 +1232,37 @@ namespace CUETools.Codecs.FLAKE
|
||||
alreadyEncoded = true;
|
||||
}
|
||||
|
||||
if (bitsBest > frameM->subframes[0].size + frameM->subframes[1].size)
|
||||
if (bitsBest > frameM->subframes[0].best.size + frameM->subframes[1].best.size)
|
||||
{
|
||||
bitsBest = frameM->subframes[0].size + frameM->subframes[1].size;
|
||||
bitsBest = frameM->subframes[0].best.size + frameM->subframes[1].best.size;
|
||||
modeBest = ChannelMode.MidSide;
|
||||
frame->subframes[0] = frameM->subframes[0];
|
||||
frame->subframes[1] = frameM->subframes[1];
|
||||
}
|
||||
if (bitsBest > frameM->subframes[1].size + frameS->subframes[1].size)
|
||||
if (bitsBest > frameM->subframes[1].best.size + frameS->subframes[1].best.size)
|
||||
{
|
||||
bitsBest = frameM->subframes[1].size + frameS->subframes[1].size;
|
||||
bitsBest = frameM->subframes[1].best.size + frameS->subframes[1].best.size;
|
||||
modeBest = ChannelMode.RightSide;
|
||||
frame->subframes[0] = frameM->subframes[1];
|
||||
frame->subframes[1] = frameS->subframes[1];
|
||||
}
|
||||
if (bitsBest > frameM->subframes[1].size + frameS->subframes[0].size)
|
||||
if (bitsBest > frameM->subframes[1].best.size + frameS->subframes[0].best.size)
|
||||
{
|
||||
bitsBest = frameM->subframes[1].size + frameS->subframes[0].size;
|
||||
bitsBest = frameM->subframes[1].best.size + frameS->subframes[0].best.size;
|
||||
modeBest = ChannelMode.LeftSide;
|
||||
frame->subframes[0] = frameS->subframes[0];
|
||||
frame->subframes[1] = frameM->subframes[1];
|
||||
}
|
||||
if (bitsBest > frameS->subframes[0].size + frameS->subframes[1].size)
|
||||
if (bitsBest > frameS->subframes[0].best.size + frameS->subframes[1].best.size)
|
||||
{
|
||||
bitsBest = frameS->subframes[0].size + frameS->subframes[1].size;
|
||||
bitsBest = frameS->subframes[0].best.size + frameS->subframes[1].best.size;
|
||||
modeBest = ChannelMode.LeftRight;
|
||||
frame->subframes[0] = frameS->subframes[0];
|
||||
frame->subframes[1] = frameS->subframes[1];
|
||||
}
|
||||
frame->ch_mode = modeBest;
|
||||
frame->current.residual = current_residual + 2 * frame->blocksize;
|
||||
if (eparams.stereo_method == StereoMethod.Estimate4 || eparams.stereo_method == StereoMethod.Estimate5)
|
||||
if (eparams.stereo_method == StereoMethod.Evaluate)
|
||||
{
|
||||
encode_residual(frame, 0, PredictionType.Estimated, eparams.order_method);
|
||||
encode_residual(frame, 1, PredictionType.Estimated, eparams.order_method);
|
||||
@@ -1187,6 +1306,23 @@ namespace CUETools.Codecs.FLAKE
|
||||
else
|
||||
fs = encode_frame();
|
||||
|
||||
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 > (ulong)_position + (ulong)eparams.block_size)
|
||||
break;
|
||||
if (seek_table[sp].number >= (ulong)_position)
|
||||
{
|
||||
seek_table[sp].number = (ulong)_position;
|
||||
seek_table[sp].offset = (ulong)(_IO.Position - first_frame_offset);
|
||||
seek_table[sp].framesize = (uint)eparams.block_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_position += eparams.block_size;
|
||||
_IO.Write(frame_buffer, 0, fs);
|
||||
_totalSize += fs;
|
||||
@@ -1209,10 +1345,12 @@ namespace CUETools.Codecs.FLAKE
|
||||
{
|
||||
if (!inited)
|
||||
{
|
||||
int header_size = flake_encode_init();
|
||||
if (_IO == null)
|
||||
_IO = new FileStream(_path, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||
int header_size = flake_encode_init();
|
||||
_IO.Write(header, 0, header_size);
|
||||
if (_IO.CanSeek)
|
||||
first_frame_offset = _IO.Position;
|
||||
inited = true;
|
||||
}
|
||||
|
||||
@@ -1223,8 +1361,11 @@ namespace CUETools.Codecs.FLAKE
|
||||
|
||||
copy_samples(buff, pos, block);
|
||||
|
||||
if (md5 != null)
|
||||
{
|
||||
AudioSamples.FLACSamplesToBytes(buff, pos, frame_buffer, 0, block, channels, (int)bits_per_sample);
|
||||
md5.TransformBlock(frame_buffer, 0, block * channels * ((int)bits_per_sample >> 3), null, 0);
|
||||
}
|
||||
|
||||
if (samplesInBuffer < eparams.block_size)
|
||||
return;
|
||||
@@ -1316,6 +1457,26 @@ namespace CUETools.Codecs.FLAKE
|
||||
return vendor_len + 12;
|
||||
}
|
||||
|
||||
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.FLAC__METADATA_TYPE_SEEKTABLE);
|
||||
bitwriter.writebits(24, 18 * seek_table.Length);
|
||||
for (int i = 0; i < seek_table.Length; i++)
|
||||
{
|
||||
bitwriter.writebits64(Flake.FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN, seek_table[i].number);
|
||||
bitwriter.writebits64(Flake.FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN, seek_table[i].offset);
|
||||
bitwriter.writebits(Flake.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.
|
||||
*/
|
||||
@@ -1348,6 +1509,10 @@ namespace CUETools.Codecs.FLAKE
|
||||
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 comment
|
||||
if (eparams.padding_size == 0) last = 1;
|
||||
header_size += write_vorbis_comment(header, header_size, last);
|
||||
@@ -1421,13 +1586,34 @@ namespace CUETools.Codecs.FLAKE
|
||||
else
|
||||
max_frame_size = 16 + ((eparams.block_size * channels * (int)bits_per_sample + 7) >> 3);
|
||||
|
||||
if (_IO.CanSeek && eparams.do_seektable)
|
||||
{
|
||||
int seek_points_distance = sample_rate * 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 = (ulong)(sp * seek_points_distance);
|
||||
}
|
||||
}
|
||||
|
||||
// output header bytes
|
||||
header = new byte[eparams.padding_size + 1024];
|
||||
header = new byte[eparams.padding_size + 1024 + (seek_table == null ? 0 : seek_table.Length * 18)];
|
||||
header_len = write_headers();
|
||||
|
||||
// initialize CRC & MD5
|
||||
//crc_init();
|
||||
//md5_init(&ctx->md5ctx);
|
||||
if (_IO.CanSeek && eparams.do_md5)
|
||||
md5 = new MD5CryptoServiceProvider();
|
||||
|
||||
if (eparams.do_verify)
|
||||
{
|
||||
verify = new FlakeReader(channels, bits_per_sample);
|
||||
verifyBuffer = new int[Flake.MAX_BLOCKSIZE * channels];
|
||||
}
|
||||
|
||||
frame_buffer = new byte[max_frame_size];
|
||||
|
||||
@@ -1536,9 +1722,17 @@ namespace CUETools.Codecs.FLAKE
|
||||
// 1 = variable block size
|
||||
public int variable_block_size;
|
||||
|
||||
// whether to try various lpc_precisions
|
||||
// 0 - use only one precision
|
||||
// 1 - try two precisions
|
||||
public int lpc_precision_search;
|
||||
|
||||
public WindowFunction window_function;
|
||||
|
||||
public bool do_md5;
|
||||
public bool do_verify;
|
||||
public bool do_seektable;
|
||||
|
||||
public int flake_set_defaults(int lvl)
|
||||
{
|
||||
compression = lvl;
|
||||
@@ -1562,6 +1756,10 @@ namespace CUETools.Codecs.FLAKE
|
||||
min_partition_order = 0;
|
||||
max_partition_order = 6;
|
||||
variable_block_size = 0;
|
||||
lpc_precision_search = 0;
|
||||
do_md5 = true;
|
||||
do_verify = true;
|
||||
do_seektable = true;
|
||||
|
||||
// differences from level 5
|
||||
switch (lvl)
|
||||
@@ -1605,28 +1803,28 @@ namespace CUETools.Codecs.FLAKE
|
||||
prediction_type = PredictionType.Levinson;
|
||||
break;
|
||||
case 6:
|
||||
stereo_method = StereoMethod.Estimate4;
|
||||
stereo_method = StereoMethod.Evaluate;
|
||||
break;
|
||||
case 7:
|
||||
order_method = OrderMethod.LogSearch;
|
||||
stereo_method = StereoMethod.Estimate4;
|
||||
stereo_method = StereoMethod.Evaluate;
|
||||
break;
|
||||
case 8:
|
||||
order_method = OrderMethod.Search;
|
||||
stereo_method = StereoMethod.Estimate4;
|
||||
stereo_method = StereoMethod.Evaluate;
|
||||
break;
|
||||
case 9:
|
||||
stereo_method = StereoMethod.Estimate4;
|
||||
stereo_method = StereoMethod.Evaluate;
|
||||
max_prediction_order = 32;
|
||||
break;
|
||||
case 10:
|
||||
order_method = OrderMethod.LogFast;
|
||||
stereo_method = StereoMethod.Estimate5;
|
||||
stereo_method = StereoMethod.Evaluate;
|
||||
max_prediction_order = 32;
|
||||
break;
|
||||
case 11:
|
||||
order_method = OrderMethod.LogSearch;
|
||||
stereo_method = StereoMethod.Estimate5;
|
||||
stereo_method = StereoMethod.Evaluate;
|
||||
max_prediction_order = 32;
|
||||
max_partition_order = 8;
|
||||
break;
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace CUETools.Codecs.FLAKE
|
||||
* Calculates autocorrelation data from audio samples
|
||||
* A Welch window function is applied before calculation.
|
||||
*/
|
||||
static unsafe void
|
||||
static public unsafe void
|
||||
compute_autocorr(/*const*/ int* data, uint len, uint lag, double* autoc, double* window)
|
||||
{
|
||||
double* data1 = stackalloc double[(int)len + 16];
|
||||
@@ -83,7 +83,7 @@ namespace CUETools.Codecs.FLAKE
|
||||
* Levinson-Durbin recursion.
|
||||
* Produces LPC coefficients from autocorrelation data.
|
||||
*/
|
||||
static unsafe void
|
||||
public static unsafe void
|
||||
compute_lpc_coefs(/*const*/ double* autoc, uint max_order, double* reff,
|
||||
double* lpc/*[][MAX_LPC_ORDER]*/)
|
||||
{
|
||||
@@ -139,19 +139,12 @@ namespace CUETools.Codecs.FLAKE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute LPC coefs for Flake.OrderMethod._EST
|
||||
* Faster LPC coeff computation by first calculating the reflection coefficients
|
||||
* using Schur recursion. That allows for estimating the optimal order before
|
||||
* running Levinson recursion.
|
||||
*/
|
||||
static unsafe uint
|
||||
compute_lpc_coefs_est(/*const*/ double* autoc, uint max_order,
|
||||
double* lpc/*[][MAX_LPC_ORDER]*/)
|
||||
public static unsafe void
|
||||
compute_schur_reflection(/*const*/ double* autoc, uint max_order,
|
||||
double* reff/*[][MAX_LPC_ORDER]*/)
|
||||
{
|
||||
double* gen0 = stackalloc double[MAX_LPC_ORDER];
|
||||
double* gen1 = stackalloc double[MAX_LPC_ORDER];
|
||||
double* reff = stackalloc double[MAX_LPC_ORDER];
|
||||
|
||||
// Schur recursion
|
||||
for (uint i = 0; i < max_order; i++)
|
||||
@@ -170,6 +163,22 @@ namespace CUETools.Codecs.FLAKE
|
||||
reff[i] = -gen1[0] / error;
|
||||
error += gen1[0] * reff[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute LPC coefs for Flake.OrderMethod._EST
|
||||
* Faster LPC coeff computation by first calculating the reflection coefficients
|
||||
* using Schur recursion. That allows for estimating the optimal order before
|
||||
* running Levinson recursion.
|
||||
*/
|
||||
public static unsafe uint
|
||||
compute_lpc_coefs_est(/*const*/ double* autoc, uint max_order,
|
||||
double* lpc/*[][MAX_LPC_ORDER]*/)
|
||||
{
|
||||
double* reff = stackalloc double[MAX_LPC_ORDER];
|
||||
|
||||
// Schur recursion
|
||||
compute_schur_reflection(autoc, max_order, reff);
|
||||
|
||||
// Estimate optimal order using reflection coefficients
|
||||
uint order_est = 1;
|
||||
@@ -396,6 +405,132 @@ namespace CUETools.Codecs.FLAKE
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe void
|
||||
encode_residual_long(int* res, int* smp, int n, int order,
|
||||
int* coefs, int shift)
|
||||
{
|
||||
for (int i = 0; i < order; i++)
|
||||
res[i] = smp[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++);
|
||||
*(r++) = *s - (int)(pred >> shift);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
for (int i = n - order; i > 0; i--)
|
||||
{
|
||||
long pred = c1 * (long)*(s++);
|
||||
pred += c0 * (long)*(s++);
|
||||
*(r++) = *(s--) - (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++);
|
||||
*(r++) = *s - (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++);
|
||||
*(r++) = *s - (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++);
|
||||
*(r++) = *s - (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++);
|
||||
*(r++) = *s - (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++);
|
||||
*(r++) = *s - (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++);
|
||||
*(r++) = *s - (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++);
|
||||
*(r++) = *s - (int)(pred >> shift);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe void
|
||||
decode_residual(int* res, int* smp, int n, int order,
|
||||
int* coefs, int shift)
|
||||
|
||||
Reference in New Issue
Block a user