seektable support, optimizations

This commit is contained in:
chudov
2009-08-20 04:09:53 +00:00
parent 4ca116c1c5
commit f61382e43b
5 changed files with 681 additions and 330 deletions

View File

@@ -38,6 +38,17 @@ namespace CUETools.Codecs.FLAKE
writebits(bits, (uint)val); 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) public void writebits(int bits, uint val)
{ {
//assert(bits == 32 || val < (1U << bits)); //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) public void write_rice_signed(int k, int val)
{ {
int v, q; int v, q;
if (k < 0) return;
// convert signed to unsigned // convert signed to unsigned
v = -2 * val - 1; v = -2 * val - 1;
v ^= (v >> 31); v ^= (v >> 31);

View File

@@ -11,6 +11,10 @@ namespace CUETools.Codecs.FLAKE
public const int MAX_PARTITION_ORDER = 8; public const int MAX_PARTITION_ORDER = 8;
public const int MAX_PARTITIONS = 1 << MAX_PARTITION_ORDER; 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] { public static readonly int[] flac_samplerates = new int[16] {
0, 0, 0, 0, 0, 0, 0, 0,
8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000,
@@ -39,59 +43,22 @@ namespace CUETools.Codecs.FLAKE
public static PredictionType LookupPredictionType(string name) public static PredictionType LookupPredictionType(string name)
{ {
switch (name) return (PredictionType)(Enum.Parse(typeof(PredictionType), name, true));
{
case "fixed": return PredictionType.Fixed;
case "levinson": return PredictionType.Levinson;
case "search" : return PredictionType.Search;
}
return (PredictionType)Int32.Parse(name);
} }
public static StereoMethod LookupStereoMethod(string name) public static StereoMethod LookupStereoMethod(string name)
{ {
switch (name) return (StereoMethod)(Enum.Parse(typeof(StereoMethod), name, true));
{
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);
} }
public static OrderMethod LookupOrderMethod(string name) public static OrderMethod LookupOrderMethod(string name)
{ {
switch (name) return (OrderMethod)(Enum.Parse(typeof(OrderMethod), name, true));
{
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);
} }
public static WindowFunction LookupWindowFunction(string name) public static WindowFunction LookupWindowFunction(string name)
{ {
string[] parts = name.Split(','); return (WindowFunction)(Enum.Parse(typeof(WindowFunction), name, true));
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;
} }
unsafe public static bool memcmp(int* res, int* smp, int n) unsafe public static bool memcmp(int* res, int* smp, int n)
@@ -124,6 +91,14 @@ namespace CUETools.Codecs.FLAKE
*(res++) = *(src2++); *(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 unsafe struct RiceContext
@@ -137,20 +112,28 @@ namespace CUETools.Codecs.FLAKE
{ {
public SubframeType type; public SubframeType type;
public int order; 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 int* residual;
public RiceContext rc; public RiceContext rc;
public uint size; 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; 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 unsafe struct FlacFrame
{ {
public int blocksize; public int blocksize;
@@ -158,7 +141,7 @@ namespace CUETools.Codecs.FLAKE
public ChannelMode ch_mode; public ChannelMode ch_mode;
public int ch_order0, ch_order1; public int ch_order0, ch_order1;
public byte crc8; public byte crc8;
public FlacSubframe* subframes; public FlacSubframeInfo* subframes;
public uint frame_count; public uint frame_count;
public FlacSubframe current; public FlacSubframe current;
} }
@@ -170,6 +153,7 @@ namespace CUETools.Codecs.FLAKE
LogFast = 2, LogFast = 2,
LogSearch = 3, LogSearch = 3,
EstSearch = 4, EstSearch = 4,
Estimate8 = 6,
Search = 5 Search = 5
} }
@@ -204,11 +188,8 @@ namespace CUETools.Codecs.FLAKE
{ {
Independent = 0, Independent = 0,
Estimate = 1, Estimate = 1,
Estimate2 = 2, Evaluate = 2,
Estimate3 = 3, Search = 3
Estimate4 = 4,
Estimate5 = 5,
Search = 9
} }
public enum SubframeType public enum SubframeType
@@ -233,7 +214,8 @@ namespace CUETools.Codecs.FLAKE
Welch = 1, Welch = 1,
Tukey = 2, Tukey = 2,
Hann = 4, Hann = 4,
Flattop = 8 Flattop = 8,
TukeyFlattop = 10
} }
public struct SeekPoint public struct SeekPoint

View File

@@ -118,13 +118,28 @@ namespace CUETools.Codecs.FLAKE
{ {
if (value > _sampleCount) if (value > _sampleCount)
throw new Exception("seeking past end of stream"); throw new Exception("seeking past end of stream");
if (value < Position) if (value < Position || value > _sampleOffset)
throw new Exception("backwards seeking not yet supported");
if (seek_table != null)
{ {
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) while (value > _sampleOffset)
{ {
_samplesInBuffer = 0; _samplesInBuffer = 0;
@@ -313,14 +328,14 @@ namespace CUETools.Codecs.FLAKE
unsafe void decode_subframe_constant(BitReader bitreader, FlacFrame* frame, int ch) unsafe void decode_subframe_constant(BitReader bitreader, FlacFrame* frame, int ch)
{ {
int obits = (int)frame->subframes[ch].obits; 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) unsafe void decode_subframe_verbatim(BitReader bitreader, FlacFrame* frame, int ch)
{ {
int obits = (int)frame->subframes[ch].obits; int obits = (int)frame->subframes[ch].obits;
for (int i = 0; i < frame->blocksize; i++) 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) 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 if (coding_method != 0 && coding_method != 1) // if 1, then parameter length == 5 bits instead of 4
throw new Exception("unsupported residual coding"); throw new Exception("unsupported residual coding");
// partition order // partition order
frame->subframes[ch].rc.porder = (int)bitreader.readbits(4); frame->subframes[ch].best.rc.porder = (int)bitreader.readbits(4);
if (frame->subframes[ch].rc.porder > 8) if (frame->subframes[ch].best.rc.porder > 8)
throw new Exception("invalid partition order"); throw new Exception("invalid partition order");
int psize = frame->blocksize >> frame->subframes[ch].rc.porder; int psize = frame->blocksize >> frame->subframes[ch].best.rc.porder;
int res_cnt = psize - frame->subframes[ch].order; int res_cnt = psize - frame->subframes[ch].best.order;
int rice_len = 4 + (int)coding_method; int rice_len = 4 + (int)coding_method;
// residual // residual
int j = frame->subframes[ch].order; int j = frame->subframes[ch].best.order;
int* r = frame->subframes[ch].residual + j; int* r = frame->subframes[ch].best.residual + j;
for (int p = 0; p < (1 << frame->subframes[ch].rc.porder); p++) for (int p = 0; p < (1 << frame->subframes[ch].best.rc.porder); p++)
{ {
if (p == 1) res_cnt = psize; if (p == 1) res_cnt = psize;
int n = Math.Min(res_cnt, frame->blocksize - j); 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) 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--) for (int i = n; i > 0; i--)
*(r++) = bitreader.readbits_signed((int)k); *(r++) = bitreader.readbits_signed((int)k);
} }
@@ -365,8 +380,8 @@ namespace CUETools.Codecs.FLAKE
{ {
// warm-up samples // warm-up samples
int obits = (int)frame->subframes[ch].obits; int obits = (int)frame->subframes[ch].obits;
for (int i = 0; i < frame->subframes[ch].order; i++) for (int i = 0; i < frame->subframes[ch].best.order; i++)
frame->subframes[ch].residual[i] = bitreader.readbits_signed(obits); frame->subframes[ch].best.residual[i] = bitreader.readbits_signed(obits);
// residual // residual
decode_residual(bitreader, frame, ch); decode_residual(bitreader, frame, ch);
@@ -376,14 +391,14 @@ namespace CUETools.Codecs.FLAKE
{ {
// warm-up samples // warm-up samples
int obits = (int)frame->subframes[ch].obits; int obits = (int)frame->subframes[ch].obits;
for (int i = 0; i < frame->subframes[ch].order; i++) for (int i = 0; i < frame->subframes[ch].best.order; i++)
frame->subframes[ch].residual[i] = bitreader.readbits_signed(obits); frame->subframes[ch].best.residual[i] = bitreader.readbits_signed(obits);
// LPC coefficients // LPC coefficients
frame->subframes[ch].cbits = (int)bitreader.readbits(4) + 1; // lpc_precision frame->subframes[ch].best.cbits = (int)bitreader.readbits(4) + 1; // lpc_precision
frame->subframes[ch].shift = bitreader.readbits_signed(5); frame->subframes[ch].best.shift = bitreader.readbits_signed(5);
for (int i = 0; i < frame->subframes[ch].order; i++) for (int i = 0; i < frame->subframes[ch].best.order; i++)
frame->subframes[ch].coefs[i] = bitreader.readbits_signed(frame->subframes[ch].cbits); frame->subframes[ch].best.coefs[i] = bitreader.readbits_signed(frame->subframes[ch].best.cbits);
// residual // residual
decode_residual(bitreader, frame, ch); decode_residual(bitreader, frame, ch);
@@ -411,25 +426,25 @@ namespace CUETools.Codecs.FLAKE
case ChannelMode.RightSide: frame->subframes[ch].obits += 1 - (uint)ch; break; case ChannelMode.RightSide: frame->subframes[ch].obits += 1 - (uint)ch; break;
} }
frame->subframes[ch].type = (SubframeType)type_code; frame->subframes[ch].best.type = (SubframeType)type_code;
frame->subframes[ch].order = 0; frame->subframes[ch].best.order = 0;
if ((type_code & (uint)SubframeType.LPC) != 0) if ((type_code & (uint)SubframeType.LPC) != 0)
{ {
frame->subframes[ch].order = (type_code - (int)SubframeType.LPC) + 1; frame->subframes[ch].best.order = (type_code - (int)SubframeType.LPC) + 1;
frame->subframes[ch].type = SubframeType.LPC; frame->subframes[ch].best.type = SubframeType.LPC;
} }
else if ((type_code & (uint)SubframeType.Fixed) != 0) else if ((type_code & (uint)SubframeType.Fixed) != 0)
{ {
frame->subframes[ch].order = (type_code - (int)SubframeType.Fixed); frame->subframes[ch].best.order = (type_code - (int)SubframeType.Fixed);
frame->subframes[ch].type = 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; frame->subframes[ch].samples = s + ch * Flake.MAX_BLOCKSIZE;
// subframe // subframe
switch (frame->subframes[ch].type) switch (frame->subframes[ch].best.type)
{ {
case SubframeType.Constant: case SubframeType.Constant:
decode_subframe_constant(bitreader, frame, ch); decode_subframe_constant(bitreader, frame, ch);
@@ -451,14 +466,14 @@ namespace CUETools.Codecs.FLAKE
unsafe void restore_samples_fixed(FlacFrame* frame, int ch) 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); Flake.memcpy(sub->samples, sub->best.residual, sub->best.order);
int* data = sub->samples + sub->order; int* data = sub->samples + sub->best.order;
int* residual = sub->residual + sub->order; int* residual = sub->best.residual + sub->best.order;
int data_len = frame->blocksize - sub->order; int data_len = frame->blocksize - sub->best.order;
int s0, s1, s2; int s0, s1, s2;
switch (sub->order) switch (sub->best.order)
{ {
case 0: case 0:
Flake.memcpy(data, residual, data_len); Flake.memcpy(data, residual, data_len);
@@ -497,27 +512,27 @@ namespace CUETools.Codecs.FLAKE
unsafe void restore_samples_lpc(FlacFrame* frame, int ch) unsafe void restore_samples_lpc(FlacFrame* frame, int ch)
{ {
FlacSubframe* sub = frame->subframes + ch; FlacSubframeInfo* sub = frame->subframes + ch;
ulong csum = 0; ulong csum = 0;
for (int i = sub->order; i > 0; i--) for (int i = sub->best.order; i > 0; i--)
csum += (ulong)Math.Abs(sub->coefs[i - 1]); csum += (ulong)Math.Abs(sub->best.coefs[i - 1]);
if ((csum << (int)sub->obits) >= 1UL << 32) 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 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) unsafe void restore_samples(FlacFrame* frame)
{ {
for (int ch = 0; ch < channels; ch++) for (int ch = 0; ch < channels; ch++)
{ {
switch (frame->subframes[ch].type) switch (frame->subframes[ch].best.type)
{ {
case SubframeType.Constant: 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; break;
case SubframeType.Verbatim: 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; break;
case SubframeType.Fixed: case SubframeType.Fixed:
restore_samples_fixed(frame, ch); restore_samples_fixed(frame, ch);
@@ -574,12 +589,12 @@ namespace CUETools.Codecs.FLAKE
{ {
BitReader bitreader = new BitReader(buf, pos, len); BitReader bitreader = new BitReader(buf, pos, len);
FlacFrame frame; FlacFrame frame;
FlacSubframe* subframes = stackalloc FlacSubframe[channels]; FlacSubframeInfo* subframes = stackalloc FlacSubframeInfo[channels];
frame.subframes = subframes; frame.subframes = subframes;
decode_frame_header(bitreader, &frame); decode_frame_header(bitreader, &frame);
decode_subframes(bitreader, &frame); decode_subframes(bitreader, &frame);
bitreader.flush(); 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)) if (crc != bitreader.readbits(16))
throw new Exception("frame crc mismatch"); throw new Exception("frame crc mismatch");
restore_samples(&frame); restore_samples(&frame);
@@ -691,17 +706,13 @@ namespace CUETools.Codecs.FLAKE
} }
else if (type == MetadataType.FLAC__METADATA_TYPE_SEEKTABLE) 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; int num_entries = len / 18;
seek_table = new SeekPoint[num_entries]; seek_table = new SeekPoint[num_entries];
for (int e = 0; e < num_entries; e++) for (int e = 0; e < num_entries; e++)
{ {
seek_table[e].number = bitreader.readbits64(FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN); seek_table[e].number = bitreader.readbits64(Flake.FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN);
seek_table[e].offset = bitreader.readbits64(FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN); seek_table[e].offset = bitreader.readbits64(Flake.FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN);
seek_table[e].framesize = bitreader.readbits24(FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN); seek_table[e].framesize = bitreader.readbits24(Flake.FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN);
} }
} }
if (_framesBufferLength < 4 + len) if (_framesBufferLength < 4 + len)

View File

@@ -48,6 +48,8 @@ namespace CUETools.Codecs.FLAKE
int frame_count = 0; int frame_count = 0;
long first_frame_offset = 0;
TimeSpan _userProcessorTime; TimeSpan _userProcessorTime;
// header bytes // header bytes
@@ -71,6 +73,9 @@ namespace CUETools.Codecs.FLAKE
FlakeReader verify; FlakeReader verify;
SeekPoint[] seek_table;
int seek_table_offset = -1;
bool inited = false; bool inited = false;
public FlakeWriter(string path, int bitsPerSample, int channelCount, int sampleRate, Stream IO) public FlakeWriter(string path, int bitsPerSample, int channelCount, int sampleRate, Stream IO)
@@ -89,20 +94,15 @@ namespace CUETools.Codecs.FLAKE
_path = path; _path = path;
_IO = IO; _IO = IO;
//verify = new FlakeReader(channels, bits_per_sample);
samplesBuffer = new int[Flake.MAX_BLOCKSIZE * (channels == 2 ? 4 : channels)]; samplesBuffer = new int[Flake.MAX_BLOCKSIZE * (channels == 2 ? 4 : channels)];
residualBuffer = 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]; 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.flake_set_defaults(_compressionLevel);
eparams.padding_size = 8192; eparams.padding_size = 8192;
crc8 = new Crc8(); crc8 = new Crc8();
crc16 = new Crc16(); crc16 = new Crc16();
md5 = new MD5CryptoServiceProvider();
} }
public int TotalSize public int TotalSize
@@ -144,6 +144,8 @@ namespace CUETools.Codecs.FLAKE
static extern IntPtr GetCurrentThread(); static extern IntPtr GetCurrentThread();
void DoClose() void DoClose()
{
if (inited)
{ {
if (samplesInBuffer > 0) if (samplesInBuffer > 0)
{ {
@@ -151,17 +153,29 @@ namespace CUETools.Codecs.FLAKE
output_frame(); 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 (_IO.CanSeek)
{ {
if (md5 != null)
{
md5.TransformFinalBlock(frame_buffer, 0, 0);
_IO.Position = 26; _IO.Position = 26;
_IO.Write(md5.Hash, 0, md5.Hash.Length); _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(); _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() public void Close()
@@ -174,6 +188,7 @@ namespace CUETools.Codecs.FLAKE
public void Delete() public void Delete()
{ {
DoClose(); DoClose();
if (_path != "")
File.Delete(_path); File.Delete(_path);
} }
@@ -193,6 +208,7 @@ namespace CUETools.Codecs.FLAKE
public long BlockSize public long BlockSize
{ {
set { _blocksize = (int)value; } set { _blocksize = (int)value; }
get { return _blocksize == 0 ? eparams.block_size : _blocksize; }
} }
public OrderMethod OrderMethod public OrderMethod OrderMethod
@@ -213,31 +229,54 @@ namespace CUETools.Codecs.FLAKE
set { eparams.stereo_method = value; } 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 public WindowFunction WindowFunction
{ {
get { return eparams.window_function; } get { return eparams.window_function; }
set { eparams.window_function = value; } 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 public int MinPredictionOrder
{ {
get get
{ {
return eparams.prediction_type == PredictionType.Fixed ? return PredictionType == PredictionType.Fixed ?
eparams.min_fixed_order : eparams.min_prediction_order; MinFixedOrder : MinLPCOrder;
} }
set set
{ {
if (eparams.prediction_type == PredictionType.Fixed) if (PredictionType == PredictionType.Fixed)
{ MinFixedOrder = value;
if (value < 0 || value > eparams.max_fixed_order) else
throw new Exception("invalid order"); MinLPCOrder = value;
eparams.min_fixed_order = value;
return;
}
if (value < 0 || value > eparams.max_prediction_order)
throw new Exception("invalid order");
eparams.min_prediction_order = value;
} }
} }
@@ -245,21 +284,82 @@ namespace CUETools.Codecs.FLAKE
{ {
get get
{ {
return eparams.prediction_type == PredictionType.Fixed ? return PredictionType == PredictionType.Fixed ?
eparams.max_fixed_order : eparams.max_prediction_order; MaxFixedOrder : MaxLPCOrder;
} }
set 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) 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; 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 set
{ {
if (value > 8 || value < eparams.min_partition_order) 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; 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) 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++) for (int ch = 0; ch < channels; ch++)
{ {
int* psamples = fsamples + ch * Flake.MAX_BLOCKSIZE + samplesInBuffer; int* psamples = fsamples + ch * Flake.MAX_BLOCKSIZE + samplesInBuffer;
for (int i = 0; i < block; i++) for (int i = 0; i < block; i++)
psamples[i] = samples[pos + i, ch]; psamples[i] = src[i * channels + ch];
}
} }
samplesInBuffer += block; samplesInBuffer += block;
} }
@@ -407,9 +510,9 @@ namespace CUETools.Codecs.FLAKE
return k_opt; 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; int n = frame->blocksize;
ulong sum = 0; ulong sum = 0;
for (int i = 2; i < n; i++) for (int i = 2; i < n; i++)
@@ -425,14 +528,16 @@ namespace CUETools.Codecs.FLAKE
if (w > bps) if (w > bps)
throw new Exception("internal error"); throw new Exception("internal error");
frame->subframes[ch].samples = s; frame->subframes[ch].samples = s;
frame->subframes[ch].residual = r;
frame->subframes[ch].obits = bps - (uint)w; frame->subframes[ch].obits = bps - (uint)w;
frame->subframes[ch].wbits = (uint)w; frame->subframes[ch].wbits = (uint)w;
frame->subframes[ch].type = SubframeType.Verbatim; frame->subframes[ch].best.residual = r;
frame->subframes[ch].size = UINT32_MAX; frame->subframes[ch].best.type = SubframeType.Verbatim;
for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++) 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_lpcs[iWindow] = 0;
frame->subframes[ch].done_fixed = 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) 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) 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]; FlacSubframe tmp = frame->subframes[ch].best;
frame->subframes[ch] = frame->current; frame->subframes[ch].best = 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;
frame->current = tmp; frame->current = tmp;
} }
} }
unsafe void encode_residual_lpc_sub(FlacFrame* frame, double* lpcs, int iWindow, int order, int ch) 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) for (uint i_precision = 0; i_precision <= eparams.lpc_precision_search && lpc_precision + i_precision < 16; i_precision++)
return; // already calculated; // 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.type = SubframeType.LPC;
frame->current.order = order; frame->current.order = order;
frame->current.window = iWindow; frame->current.window = iWindow;
lpc.quantize_lpc_coefs(lpcs + (frame->current.order - 1) * lpc.MAX_LPC_ORDER, 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.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); choose_best_subframe(frame, ch);
} }
}
unsafe void encode_residual_fixed_sub(FlacFrame* frame, int order, int 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.order = order;
frame->current.type = SubframeType.Fixed; 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.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); 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) unsafe void encode_residual(FlacFrame* frame, int ch, PredictionType predict, OrderMethod omethod)
{ {
int i; int* smp = frame->subframes[ch].samples;
FlacSubframe* sub = frame->subframes + ch; int i, n = frame->blocksize;
int* smp = sub->samples;
int n = frame->blocksize;
frame->current.samples = sub->samples;
frame->current.obits = sub->obits;
frame->current.wbits = sub->wbits;
// CONSTANT // CONSTANT
for (i = 1; i < n; i++) for (i = 1; i < n; i++)
@@ -682,15 +789,15 @@ namespace CUETools.Codecs.FLAKE
} }
if (i == n) if (i == n)
{ {
sub->type = SubframeType.Constant; frame->subframes[ch].best.type = SubframeType.Constant;
sub->residual[0] = smp[0]; frame->subframes[ch].best.residual[0] = smp[0];
sub->size = sub->obits; frame->subframes[ch].best.size = frame->subframes[ch].obits;
return; return;
} }
// VERBATIM // VERBATIM
frame->current.type = SubframeType.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); choose_best_subframe(frame, ch);
if (n < 5 || predict == PredictionType.None) if (n < 5 || predict == PredictionType.None)
@@ -699,7 +806,7 @@ namespace CUETools.Codecs.FLAKE
// FIXED // FIXED
if (predict == PredictionType.Fixed || if (predict == PredictionType.Fixed ||
predict == PredictionType.Search || 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) n <= eparams.max_prediction_order)
{ {
int max_fixed_order = Math.Min(eparams.max_fixed_order, 4); 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 && if (n > eparams.max_prediction_order &&
(predict == PredictionType.Levinson || (predict == PredictionType.Levinson ||
predict == PredictionType.Search || 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 min_order = eparams.min_prediction_order;
int max_order = eparams.max_prediction_order; int max_order = eparams.max_prediction_order;
fixed (double* window = windowBuffer) fixed (double* window = windowBuffer)
for (int iWindow = 0; iWindow < _windowcount; iWindow++) 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; 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); 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) switch (omethod)
{ {
@@ -738,21 +876,31 @@ namespace CUETools.Codecs.FLAKE
// estimated order // estimated order
encode_residual_lpc_sub(frame, lpcs, iWindow, est_order, ch); encode_residual_lpc_sub(frame, lpcs, iWindow, est_order, ch);
break; break;
case OrderMethod.Search: case OrderMethod.Estimate8:
// brute-force optimal order search // estimated order
for (i = max_order; i > 0; i--) encode_residual_lpc_sub(frame, lpcs, iWindow, est_order2, ch);
encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
break; 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: case OrderMethod.EstSearch:
// brute-force search starting from estimate // 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); encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
break; break;
case OrderMethod.LogFast: case OrderMethod.LogFast:
// Try max, est, 32,16,8,4,2,1 // 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, iWindow, max_order, ch);
//encode_residual_lpc_sub(frame, lpcs, est_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) if (i < max_order)
encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch); encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
break; break;
@@ -760,16 +908,16 @@ namespace CUETools.Codecs.FLAKE
// do LogFast first // do LogFast first
encode_residual_lpc_sub(frame, lpcs, iWindow, max_order, ch); encode_residual_lpc_sub(frame, lpcs, iWindow, max_order, ch);
//encode_residual_lpc_sub(frame, lpcs, est_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) if (i < max_order)
encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch); encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
// if found a good order, try to search around it // 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) // log search (written by Michael Niedermayer for FFmpeg)
for (int step = lpc.MAX_LPC_ORDER; step > 0; step >>= 1) 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) if (step <= (last + 1) / 2)
for (i = last - step; i <= last + step; i += step) for (i = last - step; i <= last + step; i += step)
if (i >= min_order && i <= max_order) if (i >= min_order && i <= max_order)
@@ -821,40 +969,42 @@ namespace CUETools.Codecs.FLAKE
bitwriter.writebits(8, crc); 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 // rice-encoded block
bitwriter.writebits(2, 0); bitwriter.writebits(2, 0);
// partition order // partition order
int porder = sub->rc.porder; int porder = sub->best.rc.porder;
int psize = frame->blocksize >> porder; int psize = frame->blocksize >> porder;
//assert(porder >= 0); //assert(porder >= 0);
bitwriter.writebits(4, porder); bitwriter.writebits(4, porder);
int res_cnt = psize - sub->order; int res_cnt = psize - sub->best.order;
// residual // residual
int j = sub->order; int j = sub->best.order;
for (int p = 0; p < (1 << porder); p++) 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); bitwriter.writebits(4, k);
if (p == 1) res_cnt = psize; if (p == 1) res_cnt = psize;
if (k == 0)
for (int i = 0; i < res_cnt && j < frame->blocksize; i++, j++) for (int i = 0; i < res_cnt && j < frame->blocksize; i++, j++)
{ bitwriter.write_unary_signed(sub->best.residual[j]);
bitwriter.write_rice_signed(k, sub->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 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 unsafe void
output_subframe_verbatim(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub) output_subframe_verbatim(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* sub)
{ {
int n = frame->blocksize; int n = frame->blocksize;
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
@@ -863,29 +1013,32 @@ namespace CUETools.Codecs.FLAKE
} }
unsafe void unsafe void
output_subframe_fixed(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub) output_subframe_fixed(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* sub)
{ {
// warm-up samples // warm-up samples
for (int i = 0; i < sub->order; i++) for (int i = 0; i < sub->best.order; i++)
bitwriter.writebits_signed(sub->obits, sub->residual[i]); bitwriter.writebits_signed(sub->obits, sub->best.residual[i]);
// residual // residual
output_residual(frame, bitwriter, sub); output_residual(frame, bitwriter, sub);
} }
unsafe void unsafe void
output_subframe_lpc(FlacFrame* frame, BitWriter bitwriter, FlacSubframe* sub) output_subframe_lpc(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* sub)
{ {
// warm-up samples // warm-up samples
for (int i = 0; i < sub->order; i++) for (int i = 0; i < sub->best.order; i++)
bitwriter.writebits_signed(sub->obits, sub->residual[i]); bitwriter.writebits_signed(sub->obits, sub->best.residual[i]);
// LPC coefficients // 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(4, cbits - 1);
bitwriter.writebits_signed(5, sub->shift); bitwriter.writebits_signed(5, sub->best.shift);
for (int i = 0; i < sub->order; i++) for (int i = 0; i < sub->best.order; i++)
bitwriter.writebits_signed(cbits, sub->coefs[i]); bitwriter.writebits_signed(cbits, sub->best.coefs[i]);
// residual // residual
output_residual(frame, bitwriter, sub); output_residual(frame, bitwriter, sub);
@@ -895,13 +1048,13 @@ namespace CUETools.Codecs.FLAKE
{ {
for (int ch = 0; ch < channels; ch++) for (int ch = 0; ch < channels; ch++)
{ {
FlacSubframe* sub = frame->subframes + ch; FlacSubframeInfo* sub = frame->subframes + ch;
// subframe header // subframe header
int type_code = (int) sub->type; int type_code = (int) sub->best.type;
if (sub->type == SubframeType.Fixed) if (sub->best.type == SubframeType.Fixed)
type_code |= sub->order; type_code |= sub->best.order;
if (sub->type == SubframeType.LPC) if (sub->best.type == SubframeType.LPC)
type_code |= sub->order - 1; type_code |= sub->best.order - 1;
bitwriter.writebits(1, 0); bitwriter.writebits(1, 0);
bitwriter.writebits(6, type_code); bitwriter.writebits(6, type_code);
bitwriter.writebits(1, sub->wbits != 0 ? 1 : 0); bitwriter.writebits(1, sub->wbits != 0 ? 1 : 0);
@@ -909,7 +1062,7 @@ namespace CUETools.Codecs.FLAKE
bitwriter.writebits((int)sub->wbits, 1); bitwriter.writebits((int)sub->wbits, 1);
// subframe // subframe
switch (sub->type) switch (sub->best.type)
{ {
case SubframeType.Constant: case SubframeType.Constant:
output_subframe_constant(frame, bitwriter, sub); output_subframe_constant(frame, bitwriter, sub);
@@ -986,7 +1139,7 @@ namespace CUETools.Codecs.FLAKE
unsafe int encode_frame() unsafe int encode_frame()
{ {
FlacFrame* frame = stackalloc FlacFrame[1]; FlacFrame* frame = stackalloc FlacFrame[1];
FlacSubframe* sf = stackalloc FlacSubframe[channels * 3]; FlacSubframeInfo* sf = stackalloc FlacSubframeInfo[channels * 3];
fixed (int* s = samplesBuffer, r = residualBuffer) fixed (int* s = samplesBuffer, r = residualBuffer)
{ {
@@ -1044,65 +1197,31 @@ namespace CUETools.Codecs.FLAKE
if (eparams.stereo_method == StereoMethod.Estimate) if (eparams.stereo_method == StereoMethod.Estimate)
{ {
frameM->subframes[0].size = (uint)frame->blocksize * 32 + calc_decorr_score(frameM, 0, frameM->subframes + 0); frameM->subframes[0].best.size = (uint)frame->blocksize * 32 + calc_decorr_score(frameM, 0);
frameM->subframes[1].size = (uint)frame->blocksize * 32 + calc_decorr_score(frameM, 1, frameM->subframes + 1); frameM->subframes[1].best.size = (uint)frame->blocksize * 32 + calc_decorr_score(frameM, 1);
frameS->subframes[0].size = (uint)frame->blocksize * 32 + calc_decorr_score(frameS, 0, frameS->subframes + 0); frameS->subframes[0].best.size = (uint)frame->blocksize * 32 + calc_decorr_score(frameS, 0);
frameS->subframes[1].size = (uint)frame->blocksize * 32 + calc_decorr_score(frameS, 1, frameS->subframes + 1); frameS->subframes[1].best.size = (uint)frame->blocksize * 32 + calc_decorr_score(frameS, 1);
} }
else if (eparams.stereo_method == StereoMethod.Estimate2) else if (eparams.stereo_method == StereoMethod.Evaluate)
{
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)
{ {
int max_prediction_order = eparams.max_prediction_order; int max_prediction_order = eparams.max_prediction_order;
int max_fixed_order = eparams.max_fixed_order; int max_fixed_order = eparams.max_fixed_order;
int min_fixed_order = eparams.min_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.min_fixed_order = 2;
eparams.max_fixed_order = 2; eparams.max_fixed_order = 2;
eparams.max_prediction_order = Math.Min(eparams.max_prediction_order, 8); eparams.lpc_precision_search = 0;
encode_residual(frameM, 0, eparams.prediction_type, OrderMethod.Estimate); if (eparams.max_prediction_order > 12)
encode_residual(frameM, 1, eparams.prediction_type, OrderMethod.Estimate); eparams.max_prediction_order = 8;
encode_residual(frameS, 0, eparams.prediction_type, OrderMethod.Estimate); encode_residual(frameM, 0, eparams.prediction_type, omethod);
encode_residual(frameS, 1, eparams.prediction_type, OrderMethod.Estimate); encode_residual(frameM, 1, eparams.prediction_type, omethod);
eparams.min_fixed_order = min_fixed_order; encode_residual(frameS, 0, eparams.prediction_type, omethod);
eparams.max_fixed_order = max_fixed_order; encode_residual(frameS, 1, eparams.prediction_type, omethod);
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.min_fixed_order = min_fixed_order; eparams.min_fixed_order = min_fixed_order;
eparams.max_fixed_order = max_fixed_order; eparams.max_fixed_order = max_fixed_order;
eparams.max_prediction_order = max_prediction_order; eparams.max_prediction_order = max_prediction_order;
eparams.lpc_precision_search = lpc_precision_search;
} }
else // StereoMethod.Search else // StereoMethod.Search
{ {
@@ -1113,37 +1232,37 @@ namespace CUETools.Codecs.FLAKE
alreadyEncoded = true; 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; modeBest = ChannelMode.MidSide;
frame->subframes[0] = frameM->subframes[0]; frame->subframes[0] = frameM->subframes[0];
frame->subframes[1] = frameM->subframes[1]; 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; modeBest = ChannelMode.RightSide;
frame->subframes[0] = frameM->subframes[1]; frame->subframes[0] = frameM->subframes[1];
frame->subframes[1] = frameS->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; modeBest = ChannelMode.LeftSide;
frame->subframes[0] = frameS->subframes[0]; frame->subframes[0] = frameS->subframes[0];
frame->subframes[1] = frameM->subframes[1]; 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; modeBest = ChannelMode.LeftRight;
frame->subframes[0] = frameS->subframes[0]; frame->subframes[0] = frameS->subframes[0];
frame->subframes[1] = frameS->subframes[1]; frame->subframes[1] = frameS->subframes[1];
} }
frame->ch_mode = modeBest; frame->ch_mode = modeBest;
frame->current.residual = current_residual + 2 * frame->blocksize; 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, 0, PredictionType.Estimated, eparams.order_method);
encode_residual(frame, 1, PredictionType.Estimated, eparams.order_method); encode_residual(frame, 1, PredictionType.Estimated, eparams.order_method);
@@ -1187,6 +1306,23 @@ namespace CUETools.Codecs.FLAKE
else else
fs = encode_frame(); 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; _position += eparams.block_size;
_IO.Write(frame_buffer, 0, fs); _IO.Write(frame_buffer, 0, fs);
_totalSize += fs; _totalSize += fs;
@@ -1209,10 +1345,12 @@ namespace CUETools.Codecs.FLAKE
{ {
if (!inited) if (!inited)
{ {
int header_size = flake_encode_init();
if (_IO == null) if (_IO == null)
_IO = new FileStream(_path, FileMode.Create, FileAccess.Write, FileShare.Read); _IO = new FileStream(_path, FileMode.Create, FileAccess.Write, FileShare.Read);
int header_size = flake_encode_init();
_IO.Write(header, 0, header_size); _IO.Write(header, 0, header_size);
if (_IO.CanSeek)
first_frame_offset = _IO.Position;
inited = true; inited = true;
} }
@@ -1223,8 +1361,11 @@ namespace CUETools.Codecs.FLAKE
copy_samples(buff, pos, block); copy_samples(buff, pos, block);
if (md5 != null)
{
AudioSamples.FLACSamplesToBytes(buff, pos, frame_buffer, 0, block, channels, (int)bits_per_sample); 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); md5.TransformBlock(frame_buffer, 0, block * channels * ((int)bits_per_sample >> 3), null, 0);
}
if (samplesInBuffer < eparams.block_size) if (samplesInBuffer < eparams.block_size)
return; return;
@@ -1316,6 +1457,26 @@ namespace CUETools.Codecs.FLAKE
return vendor_len + 12; 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. * Write padding metadata block to byte array.
*/ */
@@ -1348,6 +1509,10 @@ namespace CUETools.Codecs.FLAKE
write_streaminfo(header, header_size, last); write_streaminfo(header, header_size, last);
header_size += 38; header_size += 38;
// seek table
if (_IO.CanSeek && seek_table != null)
header_size += write_seekpoints(header, header_size, last);
// vorbis comment // vorbis comment
if (eparams.padding_size == 0) last = 1; if (eparams.padding_size == 0) last = 1;
header_size += write_vorbis_comment(header, header_size, last); header_size += write_vorbis_comment(header, header_size, last);
@@ -1421,13 +1586,34 @@ namespace CUETools.Codecs.FLAKE
else else
max_frame_size = 16 + ((eparams.block_size * channels * (int)bits_per_sample + 7) >> 3); 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 // 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(); header_len = write_headers();
// initialize CRC & MD5 // initialize CRC & MD5
//crc_init(); if (_IO.CanSeek && eparams.do_md5)
//md5_init(&ctx->md5ctx); 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]; frame_buffer = new byte[max_frame_size];
@@ -1536,9 +1722,17 @@ namespace CUETools.Codecs.FLAKE
// 1 = variable block size // 1 = variable block size
public int 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 WindowFunction window_function;
public bool do_md5;
public bool do_verify;
public bool do_seektable;
public int flake_set_defaults(int lvl) public int flake_set_defaults(int lvl)
{ {
compression = lvl; compression = lvl;
@@ -1562,6 +1756,10 @@ namespace CUETools.Codecs.FLAKE
min_partition_order = 0; min_partition_order = 0;
max_partition_order = 6; max_partition_order = 6;
variable_block_size = 0; variable_block_size = 0;
lpc_precision_search = 0;
do_md5 = true;
do_verify = true;
do_seektable = true;
// differences from level 5 // differences from level 5
switch (lvl) switch (lvl)
@@ -1605,28 +1803,28 @@ namespace CUETools.Codecs.FLAKE
prediction_type = PredictionType.Levinson; prediction_type = PredictionType.Levinson;
break; break;
case 6: case 6:
stereo_method = StereoMethod.Estimate4; stereo_method = StereoMethod.Evaluate;
break; break;
case 7: case 7:
order_method = OrderMethod.LogSearch; order_method = OrderMethod.LogSearch;
stereo_method = StereoMethod.Estimate4; stereo_method = StereoMethod.Evaluate;
break; break;
case 8: case 8:
order_method = OrderMethod.Search; order_method = OrderMethod.Search;
stereo_method = StereoMethod.Estimate4; stereo_method = StereoMethod.Evaluate;
break; break;
case 9: case 9:
stereo_method = StereoMethod.Estimate4; stereo_method = StereoMethod.Evaluate;
max_prediction_order = 32; max_prediction_order = 32;
break; break;
case 10: case 10:
order_method = OrderMethod.LogFast; order_method = OrderMethod.LogFast;
stereo_method = StereoMethod.Estimate5; stereo_method = StereoMethod.Evaluate;
max_prediction_order = 32; max_prediction_order = 32;
break; break;
case 11: case 11:
order_method = OrderMethod.LogSearch; order_method = OrderMethod.LogSearch;
stereo_method = StereoMethod.Estimate5; stereo_method = StereoMethod.Evaluate;
max_prediction_order = 32; max_prediction_order = 32;
max_partition_order = 8; max_partition_order = 8;
break; break;

View File

@@ -28,7 +28,7 @@ namespace CUETools.Codecs.FLAKE
* Calculates autocorrelation data from audio samples * Calculates autocorrelation data from audio samples
* A Welch window function is applied before calculation. * 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) compute_autocorr(/*const*/ int* data, uint len, uint lag, double* autoc, double* window)
{ {
double* data1 = stackalloc double[(int)len + 16]; double* data1 = stackalloc double[(int)len + 16];
@@ -83,7 +83,7 @@ namespace CUETools.Codecs.FLAKE
* Levinson-Durbin recursion. * Levinson-Durbin recursion.
* Produces LPC coefficients from autocorrelation data. * Produces LPC coefficients from autocorrelation data.
*/ */
static unsafe void public static unsafe void
compute_lpc_coefs(/*const*/ double* autoc, uint max_order, double* reff, compute_lpc_coefs(/*const*/ double* autoc, uint max_order, double* reff,
double* lpc/*[][MAX_LPC_ORDER]*/) double* lpc/*[][MAX_LPC_ORDER]*/)
{ {
@@ -139,19 +139,12 @@ namespace CUETools.Codecs.FLAKE
} }
} }
/** public static unsafe void
* Compute LPC coefs for Flake.OrderMethod._EST compute_schur_reflection(/*const*/ double* autoc, uint max_order,
* Faster LPC coeff computation by first calculating the reflection coefficients double* reff/*[][MAX_LPC_ORDER]*/)
* 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]*/)
{ {
double* gen0 = stackalloc double[MAX_LPC_ORDER]; double* gen0 = stackalloc double[MAX_LPC_ORDER];
double* gen1 = stackalloc double[MAX_LPC_ORDER]; double* gen1 = stackalloc double[MAX_LPC_ORDER];
double* reff = stackalloc double[MAX_LPC_ORDER];
// Schur recursion // Schur recursion
for (uint i = 0; i < max_order; i++) for (uint i = 0; i < max_order; i++)
@@ -170,6 +163,22 @@ namespace CUETools.Codecs.FLAKE
reff[i] = -gen1[0] / error; reff[i] = -gen1[0] / error;
error += gen1[0] * reff[i]; 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 // Estimate optimal order using reflection coefficients
uint order_est = 1; 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 public static unsafe void
decode_residual(int* res, int* smp, int n, int order, decode_residual(int* res, int* smp, int n, int order,
int* coefs, int shift) int* coefs, int shift)