further Flake optimizations

This commit is contained in:
chudov
2009-08-28 13:00:27 +00:00
parent 108c9c328c
commit 2b7312e261
8 changed files with 621 additions and 437 deletions

View File

@@ -32,7 +32,7 @@ namespace CUETools.Codecs.FLAKE
int _bitaccumulator; int _bitaccumulator;
uint cache; uint cache;
static readonly byte[] byte_to_unary_table = new byte[] public static readonly byte[] byte_to_unary_table = new byte[]
{ {
8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,

View File

@@ -110,6 +110,32 @@ namespace CUETools.Codecs.FLAKE
} }
} }
/// <summary>
/// Assumes there's enough space, buffer != null and bits is in range 1..31
/// </summary>
/// <param name="bits"></param>
/// <param name="val"></param>
unsafe void writebits_fast(int bits, uint val, ref byte * buf)
{
if (bits < bit_left)
{
bit_buf = (bit_buf << bits) | val;
bit_left -= bits;
}
else
{
uint bb = (bit_buf << bit_left) | (val >> (bits - bit_left));
bit_left += (32 - bits);
*(buf++) = (byte)(bb >> 24);
*(buf++) = (byte)(bb >> 16);
*(buf++) = (byte)(bb >> 8);
*(buf++) = (byte)(bb);
bit_buf = val;
}
}
public void write_utf8(int val) public void write_utf8(int val)
{ {
write_utf8((uint)val); write_utf8((uint)val);
@@ -150,23 +176,78 @@ namespace CUETools.Codecs.FLAKE
public void write_rice_signed(int k, int val) public void write_rice_signed(int k, int val)
{ {
int v, q;
// convert signed to unsigned // convert signed to unsigned
v = -2 * val - 1; int v = -2 * val - 1;
v ^= (v >> 31); v ^= (v >> 31);
// write quotient in unary // write quotient in unary
q = (v >> k) + 1; int q = (v >> k) + 1;
while (q > 31) while (q + k > 31)
{ {
writebits(31, 0); int b = Math.Min(q + k - 31, 31);
q -= 31; writebits(b, 0);
q -= b;
} }
writebits(q, 1);
// write write remainder in binary using 'k' bits // write remainder in binary using 'k' bits
writebits(k, v & ((1 << k) - 1)); writebits(k + q, (v & ((1 << k) - 1)) | (1 << k));
}
public unsafe void write_rice_block_signed(int k, int* residual, int count)
{
fixed (byte* fixbuf = &buffer[buf_ptr])
{
byte* buf = fixbuf;
for (int i = count; i > 0; i--)
{
int v = -2 * *(residual++) - 1;
v ^= (v >> 31);
// write quotient in unary
int q = (v >> k) + 1;
int bits = k + q;
while (bits > 31)
{
int b = Math.Min(bits - 31, 31);
if (b < bit_left)
{
bit_buf = (bit_buf << b);
bit_left -= b;
}
else
{
uint bb = bit_buf << bit_left;
bit_buf = 0;
bit_left += (32 - b);
*(buf++) = (byte)(bb >> 24);
*(buf++) = (byte)(bb >> 16);
*(buf++) = (byte)(bb >> 8);
*(buf++) = (byte)(bb);
}
bits -= b;
}
// write remainder in binary using 'k' bits
//writebits_fast(k + q, (uint)((v & ((1 << k) - 1)) | (1 << k)), ref buf);
uint val = (uint)((v & ((1 << k) - 1)) | (1 << k));
if (bits < bit_left)
{
bit_buf = (bit_buf << bits) | val;
bit_left -= bits;
}
else
{
uint bb = (bit_buf << bit_left) | (val >> (bits - bit_left));
bit_buf = val;
bit_left += (32 - bits);
*(buf++) = (byte)(bb >> 24);
*(buf++) = (byte)(bb >> 16);
*(buf++) = (byte)(bb >> 8);
*(buf++) = (byte)(bb);
}
}
buf_ptr += (int)(buf - fixbuf);
}
} }
public void flush() public void flush()

View File

@@ -32,6 +32,8 @@ 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 uint UINT32_MAX = 0xffffffff;
public const int FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */ 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_STREAM_OFFSET_LEN = 64; /* bits */
public const int FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */ public const int FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */
@@ -50,15 +52,16 @@ namespace CUETools.Codecs.FLAKE
} }
public static int log2i(uint v) public static int log2i(uint v)
{ {
int i; //int i;
int n = 0; int n = 0;
if (0 != (v & 0xffff0000)) { v >>= 16; n += 16; } if (0 != (v & 0xffff0000)) { v >>= 16; n += 16; }
if (0 != (v & 0xff00)) { v >>= 8; n += 8; } if (0 != (v & 0xff00)) { v >>= 8; n += 8; }
for (i = 2; i < 256; i <<= 1) if (0 != v) return n + 7 - BitReader.byte_to_unary_table[v];
{ //for (i = 2; i < 256; i <<= 1)
if (v >= i) n++; //{
else break; // if (v >= i) n++;
} // else break;
//}
return n; return n;
} }
@@ -122,15 +125,36 @@ namespace CUETools.Codecs.FLAKE
} }
} }
unsafe struct RiceContext unsafe class RiceContext
{ {
public int porder; /* partition order */ public RiceContext()
public fixed int rparams[Flake.MAX_PARTITIONS]; /* Rice parameters */ {
public fixed int esc_bps[Flake.MAX_PARTITIONS]; /* bps if using escape code */ rparams = new int[Flake.MAX_PARTITIONS];
esc_bps = new int[Flake.MAX_PARTITIONS];
}
/// <summary>
/// partition order
/// </summary>
public int porder;
/// <summary>
/// Rice parameters
/// </summary>
public int[] rparams;
/// <summary>
/// bps if using escape code
/// </summary>
public int[] esc_bps;
}; };
unsafe struct FlacSubframe unsafe class FlacSubframe
{ {
public FlacSubframe()
{
rc = new RiceContext();
coefs = new int[lpc.MAX_LPC_ORDER];
}
public SubframeType type; public SubframeType type;
public int order; public int order;
public int* residual; public int* residual;
@@ -139,31 +163,129 @@ namespace CUETools.Codecs.FLAKE
public int cbits; public int cbits;
public int shift; public int shift;
public fixed int coefs[lpc.MAX_LPC_ORDER]; public int[] coefs;
public int window; public int window;
}; };
unsafe struct FlacSubframeInfo unsafe class FlacSubframeInfo
{ {
public FlacSubframeInfo()
{
best = new FlacSubframe();
lpc_ctx = new LpcContext[lpc.MAX_LPC_WINDOWS];
for (int i = 0; i < lpc.MAX_LPC_WINDOWS; i++)
lpc_ctx[i] = new LpcContext();
}
public void Init(int* s, int* r, uint bps, uint w)
{
if (w > bps)
throw new Exception("internal error");
samples = s;
obits = bps - w;
wbits = w;
best.residual = r;
best.type = SubframeType.Verbatim;
best.size = Flake.UINT32_MAX;
for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++)
lpc_ctx[iWindow].Reset();
done_fixed = 0;
}
public FlacSubframe best; public FlacSubframe best;
public uint obits; public uint obits;
public uint wbits; public uint wbits;
public int* samples; public int* samples;
public fixed uint done_lpcs[lpc.MAX_LPC_WINDOWS * lpc.MAX_LPC_PRECISIONS];
public uint done_fixed; public uint done_fixed;
public fixed double reflection_coeffs[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_WINDOWS]; public LpcContext[] lpc_ctx;
public fixed double autocorr_values[(lpc.MAX_LPC_ORDER + 1) * lpc.MAX_LPC_WINDOWS];
public fixed int autocorr_orders[lpc.MAX_LPC_WINDOWS];
}; };
unsafe struct FlacFrame unsafe class FlacFrame
{ {
public FlacFrame(int subframes_count)
{
subframes = new FlacSubframeInfo[subframes_count];
for (int ch = 0; ch < subframes_count; ch++)
subframes[ch] = new FlacSubframeInfo();
current = new FlacSubframe();
}
public void InitSize(int bs, bool vbs)
{
int i = 15;
if (!vbs)
{
for (i = 0; i < 15; i++)
{
if (bs == Flake.flac_blocksizes[i])
{
blocksize = Flake.flac_blocksizes[i];
bs_code0 = i;
bs_code1 = -1;
break;
}
}
}
if (i == 15)
{
blocksize = bs;
if (blocksize <= 256)
{
bs_code0 = 6;
bs_code1 = blocksize - 1;
}
else
{
bs_code0 = 7;
bs_code1 = blocksize - 1;
}
}
}
public void ChooseBestSubframe(int ch)
{
if (current.size >= subframes[ch].best.size)
return;
FlacSubframe tmp = subframes[ch].best;
subframes[ch].best = current;
current = tmp;
}
public void SwapSubframes(int ch1, int ch2)
{
FlacSubframeInfo tmp = subframes[ch1];
subframes[ch1] = subframes[ch2];
subframes[ch2] = tmp;
}
/// <summary>
/// Swap subframes according to channel mode.
/// It is assumed that we have 4 subframes,
/// 0 is right, 1 is left, 2 is middle, 3 is difference
/// </summary>
public void ChooseSubframes()
{
switch (ch_mode)
{
case ChannelMode.MidSide:
SwapSubframes(0, 2);
SwapSubframes(1, 3);
break;
case ChannelMode.RightSide:
SwapSubframes(0, 3);
break;
case ChannelMode.LeftSide:
SwapSubframes(1, 3);
break;
}
}
public int blocksize; public int blocksize;
public int bs_code0, bs_code1; public int bs_code0, bs_code1;
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 FlacSubframeInfo* subframes; public FlacSubframeInfo[] subframes;
public uint frame_count; public uint frame_count;
public FlacSubframe current; public FlacSubframe current;
public double* window_buffer; public double* window_buffer;
@@ -185,11 +307,11 @@ namespace CUETools.Codecs.FLAKE
public enum PredictionType public enum PredictionType
{ {
/// <summary> /// <summary>
/// verbatim /// Verbatim
/// </summary> /// </summary>
None = 0, None = 0,
/// <summary> /// <summary>
/// Fixed only /// Fixed prediction only
/// </summary> /// </summary>
Fixed = 1, Fixed = 1,
/// <summary> /// <summary>
@@ -199,11 +321,7 @@ namespace CUETools.Codecs.FLAKE
/// <summary> /// <summary>
/// Exhaustive search /// Exhaustive search
/// </summary> /// </summary>
Search = 3, Search = 3
/// <summary>
/// Internal; Use prediction type from previous estimation
/// </summary>
Estimated = 4
} }
public enum StereoMethod public enum StereoMethod
@@ -249,45 +367,44 @@ namespace CUETools.Codecs.FLAKE
public enum MetadataType public enum MetadataType
{ {
/// <summary> /// <summary>
/// <A HREF="../format.html#metadata_block_streaminfo">STREAMINFO</A> block /// <A HREF="../format.html#metadata_block_streaminfo">STREAMINFO</A> block
/// </summary> /// </summary>
FLAC__METADATA_TYPE_STREAMINFO = 0, StreamInfo = 0,
/// <summary> /// <summary>
/// <A HREF="../format.html#metadata_block_padding">PADDING</A> block /// <A HREF="../format.html#metadata_block_padding">PADDING</A> block
/// </summary> /// </summary>
FLAC__METADATA_TYPE_PADDING = 1, Padding = 1,
/// <summary> /// <summary>
/// <A HREF="../format.html#metadata_block_application">APPLICATION</A> block /// <A HREF="../format.html#metadata_block_application">APPLICATION</A> block
/// </summary> /// </summary>
FLAC__METADATA_TYPE_APPLICATION = 2, Application = 2,
/// <summary> /// <summary>
/// <A HREF="../format.html#metadata_block_seektable">SEEKTABLE</A> block /// <A HREF="../format.html#metadata_block_seektable">SEEKTABLE</A> block
/// </summary> /// </summary>
FLAC__METADATA_TYPE_SEEKTABLE = 3, Seektable = 3,
/// <summary> /// <summary>
/// <A HREF="../format.html#metadata_block_vorbis_comment">VORBISCOMMENT</A> block (a.k.a. FLAC tags) /// <A HREF="../format.html#metadata_block_vorbis_comment">VORBISCOMMENT</A> block (a.k.a. FLAC tags)
/// </summary> /// </summary>
FLAC__METADATA_TYPE_VORBIS_COMMENT = 4, VorbisComment = 4,
/// <summary> /// <summary>
/// <A HREF="../format.html#metadata_block_cuesheet">CUESHEET</A> block /// <A HREF="../format.html#metadata_block_cuesheet">CUESHEET</A> block
/// </summary> /// </summary>
FLAC__METADATA_TYPE_CUESHEET = 5, CUESheet = 5,
/// <summary> /// <summary>
/// <A HREF="../format.html#metadata_block_picture">PICTURE</A> block /// <A HREF="../format.html#metadata_block_picture">PICTURE</A> block
/// </summary> /// </summary>
FLAC__METADATA_TYPE_PICTURE = 6, Picture = 6,
/// <summary> /// <summary>
/// marker to denote beginning of undefined type range; this number will increase as new metadata types are added /// marker to denote beginning of undefined type range; this number will increase as new metadata types are added
/// </summary> /// </summary>
FLAC__METADATA_TYPE_UNDEFINED = 7 Undefined = 7
}; };
} }

View File

@@ -39,6 +39,7 @@ namespace CUETools.Codecs.FLAKE
Crc8 crc8; Crc8 crc8;
Crc16 crc16; Crc16 crc16;
FlacFrame frame;
int channels; int channels;
uint bits_per_sample; uint bits_per_sample;
int sample_rate = 44100; int sample_rate = 44100;
@@ -74,6 +75,8 @@ namespace CUETools.Codecs.FLAKE
_framesBuffer = new byte[0x20000]; _framesBuffer = new byte[0x20000];
decode_metadata(); decode_metadata();
frame = new FlacFrame(channels);
//max_frame_size = 16 + ((Flake.MAX_BLOCKSIZE * (int)(bits_per_sample * channels + 1) + 7) >> 3); //max_frame_size = 16 + ((Flake.MAX_BLOCKSIZE * (int)(bits_per_sample * channels + 1) + 7) >> 3);
if (((int)max_frame_size * (int)bits_per_sample * channels * 2 >> 3) > _framesBuffer.Length) if (((int)max_frame_size * (int)bits_per_sample * channels * 2 >> 3) > _framesBuffer.Length)
{ {
@@ -101,6 +104,7 @@ namespace CUETools.Codecs.FLAKE
bits_per_sample = _bits_per_sample; bits_per_sample = _bits_per_sample;
samplesBuffer = new int[Flake.MAX_BLOCKSIZE * channels]; samplesBuffer = new int[Flake.MAX_BLOCKSIZE * channels];
residualBuffer = new int[Flake.MAX_BLOCKSIZE * channels]; residualBuffer = new int[Flake.MAX_BLOCKSIZE * channels];
frame = new FlacFrame(channels);
} }
public void Close() public void Close()
@@ -288,37 +292,37 @@ namespace CUETools.Codecs.FLAKE
} }
} }
unsafe void decode_frame_header(BitReader bitreader, FlacFrame* frame) unsafe void decode_frame_header(BitReader bitreader, FlacFrame frame)
{ {
int header_start = bitreader.Position; int header_start = bitreader.Position;
if (bitreader.readbits(15) != 0x7FFC) if (bitreader.readbits(15) != 0x7FFC)
throw new Exception("invalid frame"); throw new Exception("invalid frame");
uint vbs = bitreader.readbit(); uint vbs = bitreader.readbit();
frame->bs_code0 = (int) bitreader.readbits(4); frame.bs_code0 = (int) bitreader.readbits(4);
uint sr_code0 = bitreader.readbits(4); uint sr_code0 = bitreader.readbits(4);
frame->ch_mode = (ChannelMode)bitreader.readbits(4); frame.ch_mode = (ChannelMode)bitreader.readbits(4);
uint bps_code = bitreader.readbits(3); uint bps_code = bitreader.readbits(3);
if (Flake.flac_bitdepths[bps_code] != bits_per_sample) if (Flake.flac_bitdepths[bps_code] != bits_per_sample)
throw new Exception("unsupported bps coding"); throw new Exception("unsupported bps coding");
uint t1 = bitreader.readbit(); // == 0????? uint t1 = bitreader.readbit(); // == 0?????
if (t1 != 0) if (t1 != 0)
throw new Exception("unsupported frame coding"); throw new Exception("unsupported frame coding");
frame->frame_count = bitreader.read_utf8(); frame.frame_count = bitreader.read_utf8();
// custom block size // custom block size
if (frame->bs_code0 == 6) if (frame.bs_code0 == 6)
{ {
frame->bs_code1 = (int)bitreader.readbits(8); frame.bs_code1 = (int)bitreader.readbits(8);
frame->blocksize = frame->bs_code1 + 1; frame.blocksize = frame.bs_code1 + 1;
} }
else if (frame->bs_code0 == 7) else if (frame.bs_code0 == 7)
{ {
frame->bs_code1 = (int)bitreader.readbits(16); frame.bs_code1 = (int)bitreader.readbits(16);
frame->blocksize = frame->bs_code1 + 1; frame.blocksize = frame.bs_code1 + 1;
} }
else else
frame->blocksize = Flake.flac_blocksizes[frame->bs_code0]; frame.blocksize = Flake.flac_blocksizes[frame.bs_code0];
// custom sample rate // custom sample rate
if (sr_code0 < 4 || sr_code0 > 11) if (sr_code0 < 4 || sr_code0 > 11)
@@ -329,62 +333,62 @@ namespace CUETools.Codecs.FLAKE
throw new Exception("invalid sample rate mode"); throw new Exception("invalid sample rate mode");
} }
int frame_channels = (int)frame->ch_mode + 1; int frame_channels = (int)frame.ch_mode + 1;
if (frame_channels > 11) if (frame_channels > 11)
throw new Exception("invalid channel mode"); throw new Exception("invalid channel mode");
if (frame_channels == 2 || frame_channels > 8) // Mid/Left/Right Side Stereo if (frame_channels == 2 || frame_channels > 8) // Mid/Left/Right Side Stereo
frame_channels = 2; frame_channels = 2;
else else
frame->ch_mode = ChannelMode.NotStereo; frame.ch_mode = ChannelMode.NotStereo;
if (frame_channels != channels) if (frame_channels != channels)
throw new Exception("invalid channel mode"); throw new Exception("invalid channel mode");
// CRC-8 of frame header // CRC-8 of frame header
byte crc = crc8.ComputeChecksum(bitreader.Buffer, header_start, bitreader.Position - header_start); byte crc = crc8.ComputeChecksum(bitreader.Buffer, header_start, bitreader.Position - header_start);
frame->crc8 = (byte)bitreader.readbits(8); frame.crc8 = (byte)bitreader.readbits(8);
if (frame->crc8 != crc) if (frame.crc8 != crc)
throw new Exception("header crc mismatch"); throw new Exception("header crc mismatch");
} }
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].best.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].best.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)
{ {
// rice-encoded block // rice-encoded block
uint coding_method = bitreader.readbits(2); // ????? == 0 uint coding_method = bitreader.readbits(2); // ????? == 0
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].best.rc.porder = (int)bitreader.readbits(4); frame.subframes[ch].best.rc.porder = (int)bitreader.readbits(4);
if (frame->subframes[ch].best.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].best.rc.porder; int psize = frame.blocksize >> frame.subframes[ch].best.rc.porder;
int res_cnt = psize - frame->subframes[ch].best.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].best.order; int j = frame.subframes[ch].best.order;
int* r = frame->subframes[ch].best.residual + j; int* r = frame.subframes[ch].best.residual + j;
for (int p = 0; p < (1 << frame->subframes[ch].best.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].best.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].best.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);
} }
@@ -397,37 +401,37 @@ namespace CUETools.Codecs.FLAKE
} }
} }
unsafe void decode_subframe_fixed(BitReader bitreader, FlacFrame* frame, int ch) unsafe void decode_subframe_fixed(BitReader bitreader, FlacFrame frame, int ch)
{ {
// 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].best.order; i++) for (int i = 0; i < frame.subframes[ch].best.order; i++)
frame->subframes[ch].best.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);
} }
unsafe void decode_subframe_lpc(BitReader bitreader, FlacFrame* frame, int ch) unsafe void decode_subframe_lpc(BitReader bitreader, FlacFrame frame, int ch)
{ {
// 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].best.order; i++) for (int i = 0; i < frame.subframes[ch].best.order; i++)
frame->subframes[ch].best.residual[i] = bitreader.readbits_signed(obits); frame.subframes[ch].best.residual[i] = bitreader.readbits_signed(obits);
// LPC coefficients // LPC coefficients
frame->subframes[ch].best.cbits = (int)bitreader.readbits(4) + 1; // lpc_precision frame.subframes[ch].best.cbits = (int)bitreader.readbits(4) + 1; // lpc_precision
frame->subframes[ch].best.shift = bitreader.readbits_signed(5); frame.subframes[ch].best.shift = bitreader.readbits_signed(5);
if (frame->subframes[ch].best.shift < 0) if (frame.subframes[ch].best.shift < 0)
throw new Exception("negative shift"); throw new Exception("negative shift");
for (int i = 0; i < frame->subframes[ch].best.order; i++) 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); 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);
} }
unsafe void decode_subframes(BitReader bitreader, FlacFrame* frame) unsafe void decode_subframes(BitReader bitreader, FlacFrame frame)
{ {
fixed (int *r = residualBuffer, s = samplesBuffer) fixed (int *r = residualBuffer, s = samplesBuffer)
for (int ch = 0; ch < channels; ch++) for (int ch = 0; ch < channels; ch++)
@@ -437,37 +441,37 @@ namespace CUETools.Codecs.FLAKE
if (t1 != 0) if (t1 != 0)
throw new Exception("unsupported subframe coding"); throw new Exception("unsupported subframe coding");
int type_code = (int)bitreader.readbits(6); int type_code = (int)bitreader.readbits(6);
frame->subframes[ch].wbits = bitreader.readbit(); frame.subframes[ch].wbits = bitreader.readbit();
if (frame->subframes[ch].wbits != 0) if (frame.subframes[ch].wbits != 0)
frame->subframes[ch].wbits += bitreader.read_unary(); frame.subframes[ch].wbits += bitreader.read_unary();
frame->subframes[ch].obits = bits_per_sample - frame->subframes[ch].wbits; frame.subframes[ch].obits = bits_per_sample - frame.subframes[ch].wbits;
switch (frame->ch_mode) switch (frame.ch_mode)
{ {
case ChannelMode.MidSide: frame->subframes[ch].obits += (uint)ch; break; case ChannelMode.MidSide: frame.subframes[ch].obits += (uint)ch; break;
case ChannelMode.LeftSide: frame->subframes[ch].obits += (uint)ch; break; case ChannelMode.LeftSide: frame.subframes[ch].obits += (uint)ch; break;
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].best.type = (SubframeType)type_code; frame.subframes[ch].best.type = (SubframeType)type_code;
frame->subframes[ch].best.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].best.order = (type_code - (int)SubframeType.LPC) + 1; frame.subframes[ch].best.order = (type_code - (int)SubframeType.LPC) + 1;
frame->subframes[ch].best.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].best.order = (type_code - (int)SubframeType.Fixed); frame.subframes[ch].best.order = (type_code - (int)SubframeType.Fixed);
frame->subframes[ch].best.type = SubframeType.Fixed; frame.subframes[ch].best.type = SubframeType.Fixed;
} }
frame->subframes[ch].best.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].best.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);
@@ -487,16 +491,16 @@ namespace CUETools.Codecs.FLAKE
} }
} }
unsafe void restore_samples_fixed(FlacFrame* frame, int ch) unsafe void restore_samples_fixed(FlacFrame frame, int ch)
{ {
FlacSubframeInfo* sub = frame->subframes + ch; FlacSubframeInfo sub = frame.subframes[ch];
Flake.memcpy(sub->samples, sub->best.residual, sub->best.order); Flake.memcpy(sub.samples, sub.best.residual, sub.best.order);
int* data = sub->samples + sub->best.order; int* data = sub.samples + sub.best.order;
int* residual = sub->best.residual + sub->best.order; int* residual = sub.best.residual + sub.best.order;
int data_len = frame->blocksize - sub->best.order; int data_len = frame.blocksize - sub.best.order;
int s0, s1, s2; int s0, s1, s2;
switch (sub->best.order) switch (sub.best.order)
{ {
case 0: case 0:
Flake.memcpy(data, residual, data_len); Flake.memcpy(data, residual, data_len);
@@ -533,29 +537,32 @@ namespace CUETools.Codecs.FLAKE
} }
} }
unsafe void restore_samples_lpc(FlacFrame* frame, int ch) unsafe void restore_samples_lpc(FlacFrame frame, int ch)
{ {
FlacSubframeInfo* sub = frame->subframes + ch; FlacSubframeInfo sub = frame.subframes[ch];
ulong csum = 0; ulong csum = 0;
for (int i = sub->best.order; i > 0; i--) fixed (int* coefs = sub.best.coefs)
csum += (ulong)Math.Abs(sub->best.coefs[i - 1]); {
if ((csum << (int)sub->obits) >= 1UL << 32) for (int i = sub.best.order; i > 0; i--)
lpc.decode_residual_long(sub->best.residual, sub->samples, frame->blocksize, sub->best.order, sub->best.coefs, sub->best.shift); csum += (ulong)Math.Abs(coefs[i - 1]);
else if ((csum << (int)sub.obits) >= 1UL << 32)
lpc.decode_residual(sub->best.residual, sub->samples, frame->blocksize, sub->best.order, sub->best.coefs, sub->best.shift); lpc.decode_residual_long(sub.best.residual, sub.samples, frame.blocksize, sub.best.order, coefs, sub.best.shift);
else
lpc.decode_residual(sub.best.residual, sub.samples, frame.blocksize, sub.best.order, coefs, sub.best.shift);
}
} }
unsafe void restore_samples(FlacFrame* frame) unsafe void restore_samples(FlacFrame frame)
{ {
for (int ch = 0; ch < channels; ch++) for (int ch = 0; ch < channels; ch++)
{ {
switch (frame->subframes[ch].best.type) switch (frame.subframes[ch].best.type)
{ {
case SubframeType.Constant: case SubframeType.Constant:
Flake.memset(frame->subframes[ch].samples, frame->subframes[ch].best.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].best.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);
@@ -564,24 +571,24 @@ namespace CUETools.Codecs.FLAKE
restore_samples_lpc(frame, ch); restore_samples_lpc(frame, ch);
break; break;
} }
if (frame->subframes[ch].wbits != 0) if (frame.subframes[ch].wbits != 0)
{ {
int* s = frame->subframes[ch].samples; int* s = frame.subframes[ch].samples;
int x = (int) frame->subframes[ch].wbits; int x = (int) frame.subframes[ch].wbits;
for (int i = frame->blocksize; i > 0; i--) for (int i = frame.blocksize; i > 0; i--)
*(s++) <<= x; *(s++) <<= x;
} }
} }
if (frame->ch_mode != ChannelMode.NotStereo) if (frame.ch_mode != ChannelMode.NotStereo)
{ {
int* l = frame->subframes[0].samples; int* l = frame.subframes[0].samples;
int* r = frame->subframes[1].samples; int* r = frame.subframes[1].samples;
switch (frame->ch_mode) switch (frame.ch_mode)
{ {
case ChannelMode.LeftRight: case ChannelMode.LeftRight:
break; break;
case ChannelMode.MidSide: case ChannelMode.MidSide:
for (int i = frame->blocksize; i > 0; i--) for (int i = frame.blocksize; i > 0; i--)
{ {
int mid = *l; int mid = *l;
int side = *r; int side = *r;
@@ -592,14 +599,14 @@ namespace CUETools.Codecs.FLAKE
} }
break; break;
case ChannelMode.LeftSide: case ChannelMode.LeftSide:
for (int i = frame->blocksize; i > 0; i--) for (int i = frame.blocksize; i > 0; i--)
{ {
int _l = *(l++), _r = *r; int _l = *(l++), _r = *r;
*(r++) = _l - _r; *(r++) = _l - _r;
} }
break; break;
case ChannelMode.RightSide: case ChannelMode.RightSide:
for (int i = frame->blocksize; i > 0; i--) for (int i = frame.blocksize; i > 0; i--)
*(l++) += *(r++); *(l++) += *(r++);
break; break;
} }
@@ -611,16 +618,13 @@ namespace CUETools.Codecs.FLAKE
fixed (byte* buf = buffer) fixed (byte* buf = buffer)
{ {
BitReader bitreader = new BitReader(buf, pos, len); BitReader bitreader = new BitReader(buf, pos, len);
FlacFrame frame; decode_frame_header(bitreader, frame);
FlacSubframeInfo* subframes = stackalloc FlacSubframeInfo[channels]; decode_subframes(bitreader, frame);
frame.subframes = subframes;
decode_frame_header(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);
_samplesInBuffer = (uint)frame.blocksize; _samplesInBuffer = (uint)frame.blocksize;
return bitreader.Position - pos; return bitreader.Position - pos;
} }
@@ -705,7 +709,7 @@ namespace CUETools.Codecs.FLAKE
MetadataType type = (MetadataType)bitreader.readbits(7); MetadataType type = (MetadataType)bitreader.readbits(7);
int len = (int)bitreader.readbits(24); int len = (int)bitreader.readbits(24);
if (type == MetadataType.FLAC__METADATA_TYPE_STREAMINFO) if (type == MetadataType.StreamInfo)
{ {
const int FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */ const int FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */
const int FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */ const int FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */
@@ -727,7 +731,7 @@ namespace CUETools.Codecs.FLAKE
_sampleCount = bitreader.readbits64(FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN); _sampleCount = bitreader.readbits64(FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN);
bitreader.skipbits(FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN); bitreader.skipbits(FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN);
} }
else if (type == MetadataType.FLAC__METADATA_TYPE_SEEKTABLE) else if (type == MetadataType.Seektable)
{ {
int num_entries = len / 18; int num_entries = len / 18;
seek_table = new SeekPoint[num_entries]; seek_table = new SeekPoint[num_entries];

View File

@@ -25,7 +25,7 @@ using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Security.Cryptography; using System.Security.Cryptography;
//using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using CUETools.Codecs; using CUETools.Codecs;
namespace CUETools.Codecs.FLAKE namespace CUETools.Codecs.FLAKE
@@ -81,7 +81,7 @@ namespace CUETools.Codecs.FLAKE
double[] windowBuffer; double[] windowBuffer;
int samplesInBuffer = 0; int samplesInBuffer = 0;
int _compressionLevel = 5; int _compressionLevel = 7;
int _blocksize = 0; int _blocksize = 0;
int _totalSize = 0; int _totalSize = 0;
int _windowsize = 0, _windowcount = 0; int _windowsize = 0, _windowcount = 0;
@@ -90,6 +90,7 @@ namespace CUETools.Codecs.FLAKE
Crc16 crc16; Crc16 crc16;
MD5 md5; MD5 md5;
FlacFrame frame;
FlakeReader verify; FlakeReader verify;
SeekPoint[] seek_table; SeekPoint[] seek_table;
@@ -122,6 +123,7 @@ namespace CUETools.Codecs.FLAKE
crc8 = new Crc8(); crc8 = new Crc8();
crc16 = new Crc16(); crc16 = new Crc16();
frame = new FlacFrame(channels * 2);
} }
public int TotalSize public int TotalSize
@@ -159,10 +161,10 @@ namespace CUETools.Codecs.FLAKE
} }
} }
//[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]
//static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime); static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime);
//[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]
//static extern IntPtr GetCurrentThread(); static extern IntPtr GetCurrentThread();
void DoClose() void DoClose()
{ {
@@ -194,9 +196,9 @@ namespace CUETools.Codecs.FLAKE
inited = false; inited = false;
} }
//long fake, KernelStart, UserStart; long fake, KernelStart, UserStart;
//GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart); GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart);
//_userProcessorTime = new TimeSpan(UserStart); _userProcessorTime = new TimeSpan(UserStart);
} }
public void Close() public void Close()
@@ -468,43 +470,13 @@ namespace CUETools.Codecs.FLAKE
return (uint)shift; return (uint)shift;
} }
unsafe void init_frame(FlacFrame * frame, int bs) /// <summary>
{
//if (channels == 2)
//max_frame_size =
int i = 15;
if (eparams.variable_block_size == 0)
{
for (i = 0; i < 15; i++)
{
if (bs == Flake.flac_blocksizes[i])
{
frame->blocksize = Flake.flac_blocksizes[i];
frame->bs_code0 = i;
frame->bs_code1 = -1;
break;
}
}
}
if (i == 15)
{
frame->blocksize = bs;
if (frame->blocksize <= 256)
{
frame->bs_code0 = 6;
frame->bs_code1 = frame->blocksize - 1;
}
else
{
frame->bs_code0 = 7;
frame->bs_code1 = frame->blocksize - 1;
}
}
}
/// 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) /// </summary>
/// <param name="samples"></param>
/// <param name="pos"></param>
/// <param name="block"></param>
unsafe void copy_samples(int[,] samples, int pos, int block)
{ {
fixed (int* fsamples = samplesBuffer, src = &samples[pos, 0]) fixed (int* fsamples = samplesBuffer, src = &samples[pos, 0])
{ {
@@ -562,10 +534,10 @@ namespace CUETools.Codecs.FLAKE
return k_opt; return k_opt;
} }
unsafe uint calc_decorr_score(FlacFrame* frame, int ch) unsafe uint calc_decorr_score(FlacFrame frame, int ch)
{ {
int* s = frame->subframes[ch].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++)
sum += (ulong)Math.Abs(s[i] - 2 * s[i - 1] + s[i - 2]); sum += (ulong)Math.Abs(s[i] - 2 * s[i - 1] + s[i - 2]);
@@ -574,23 +546,6 @@ namespace CUETools.Codecs.FLAKE
return nbits; return nbits;
} }
unsafe void initialize_subframe(FlacFrame* frame, int ch, int *s, int * r, uint bps, uint w)
{
if (w > bps)
throw new Exception("internal error");
frame->subframes[ch].samples = s;
frame->subframes[ch].obits = bps - w;
frame->subframes[ch].wbits = w;
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 < lpc.MAX_LPC_PRECISIONS * 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].autocorr_orders[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)
{ {
for (int i = 0; i < blocksize; i++) for (int i = 0; i < blocksize; i++)
@@ -658,22 +613,20 @@ namespace CUETools.Codecs.FLAKE
} }
} }
public const uint UINT32_MAX = 0xffffffff; static unsafe uint calc_optimal_rice_params(ref RiceContext rc, int porder, uint* sums, uint n, uint pred_order)
static unsafe uint calc_optimal_rice_params(RiceContext* rc, int porder, uint* sums, uint n, uint pred_order)
{ {
uint part = (1U << porder); uint part = (1U << porder);
uint all_bits = 0; uint all_bits = 0;
rc->rparams[0] = find_optimal_rice_param(sums[0], (n >> porder) - pred_order, out all_bits); rc.rparams[0] = find_optimal_rice_param(sums[0], (n >> porder) - pred_order, out all_bits);
uint cnt = (n >> porder); uint cnt = (n >> porder);
for (uint i = 1; i < part; i++) for (uint i = 1; i < part; i++)
{ {
uint nbits; uint nbits;
rc->rparams[i] = find_optimal_rice_param(sums[i], cnt, out nbits); rc.rparams[i] = find_optimal_rice_param(sums[i], cnt, out nbits);
all_bits += nbits; all_bits += nbits;
} }
all_bits += (4 * part); all_bits += (4 * part);
rc->porder = porder; rc.porder = porder;
return all_bits; return all_bits;
} }
@@ -708,9 +661,9 @@ namespace CUETools.Codecs.FLAKE
} }
} }
static unsafe uint calc_rice_params(RiceContext* rc, int pmin, int pmax, int* data, uint n, uint pred_order) static unsafe uint calc_rice_params(ref RiceContext rc, int pmin, int pmax, int* data, uint n, uint pred_order)
{ {
RiceContext tmp_rc; RiceContext tmp_rc = new RiceContext(), tmp_rc2;
uint* udata = stackalloc uint[(int)n]; uint* udata = stackalloc uint[(int)n];
uint* sums = stackalloc uint[(pmax + 1) * Flake.MAX_PARTITIONS]; uint* sums = stackalloc uint[(pmax + 1) * Flake.MAX_PARTITIONS];
//uint* bits = stackalloc uint[Flake.MAX_PARTITION_ORDER]; //uint* bits = stackalloc uint[Flake.MAX_PARTITION_ORDER];
@@ -725,15 +678,17 @@ namespace CUETools.Codecs.FLAKE
calc_sums(pmin, pmax, udata, n, pred_order, sums); calc_sums(pmin, pmax, udata, n, pred_order, sums);
int opt_porder = pmin; int opt_porder = pmin;
uint opt_bits = UINT32_MAX; uint opt_bits = Flake.UINT32_MAX;
for (int i = pmin; i <= pmax; i++) for (int i = pmin; i <= pmax; i++)
{ {
uint bits = calc_optimal_rice_params(&tmp_rc, i, sums + i * Flake.MAX_PARTITIONS, n, pred_order); uint bits = calc_optimal_rice_params(ref tmp_rc, i, sums + i * Flake.MAX_PARTITIONS, n, pred_order);
if (bits <= opt_bits) if (bits <= opt_bits)
{ {
opt_porder = i; opt_porder = i;
opt_bits = bits; opt_bits = bits;
*rc = tmp_rc; tmp_rc2 = rc;
rc = tmp_rc;
tmp_rc = tmp_rc2;
} }
} }
@@ -748,128 +703,102 @@ namespace CUETools.Codecs.FLAKE
return porder; return porder;
} }
static unsafe uint calc_rice_params_fixed(RiceContext* rc, int pmin, int pmax, static unsafe uint calc_rice_params_fixed(ref RiceContext rc, int pmin, int pmax,
int* data, int n, int pred_order, uint bps) int* data, int n, int pred_order, uint bps)
{ {
pmin = get_max_p_order(pmin, n, pred_order); pmin = get_max_p_order(pmin, n, pred_order);
pmax = get_max_p_order(pmax, n, pred_order); pmax = get_max_p_order(pmax, n, pred_order);
uint bits = (uint)pred_order * bps + 6; uint bits = (uint)pred_order * bps + 6;
bits += calc_rice_params(rc, pmin, pmax, data, (uint)n, (uint)pred_order); bits += calc_rice_params(ref rc, pmin, pmax, data, (uint)n, (uint)pred_order);
return bits; return bits;
} }
static unsafe uint calc_rice_params_lpc(RiceContext* rc, int pmin, int pmax, static unsafe uint calc_rice_params_lpc(ref RiceContext rc, int pmin, int pmax,
int* data, int n, int pred_order, uint bps, uint precision) int* data, int n, int pred_order, uint bps, uint precision)
{ {
pmin = get_max_p_order(pmin, n, pred_order); pmin = get_max_p_order(pmin, n, pred_order);
pmax = get_max_p_order(pmax, n, pred_order); pmax = get_max_p_order(pmax, n, pred_order);
uint bits = (uint)pred_order * bps + 4 + 5 + (uint)pred_order * precision + 6; uint bits = (uint)pred_order * bps + 4 + 5 + (uint)pred_order * precision + 6;
bits += calc_rice_params(rc, pmin, pmax, data, (uint)n, (uint)pred_order); bits += calc_rice_params(ref rc, pmin, pmax, data, (uint)n, (uint)pred_order);
return bits; return bits;
} }
unsafe void choose_best_subframe(FlacFrame* frame, int ch) unsafe void encode_residual_lpc_sub(FlacFrame frame, double * lpcs, int iWindow, int order, int ch)
{
if (frame->current.size < frame->subframes[ch].best.size)
{
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)
{ {
// select LPC precision based on block size // select LPC precision based on block size
uint lpc_precision; uint lpc_precision;
if (frame->blocksize <= 192) lpc_precision = 7U; if (frame.blocksize <= 192) lpc_precision = 7U;
else if (frame->blocksize <= 384) lpc_precision = 8U; else if (frame.blocksize <= 384) lpc_precision = 8U;
else if (frame->blocksize <= 576) lpc_precision = 9U; else if (frame.blocksize <= 576) lpc_precision = 9U;
else if (frame->blocksize <= 1152) lpc_precision = 10U; else if (frame.blocksize <= 1152) lpc_precision = 10U;
else if (frame->blocksize <= 2304) lpc_precision = 11U; else if (frame.blocksize <= 2304) lpc_precision = 11U;
else if (frame->blocksize <= 4608) lpc_precision = 12U; else if (frame.blocksize <= 4608) lpc_precision = 12U;
else if (frame->blocksize <= 8192) lpc_precision = 13U; else if (frame.blocksize <= 8192) lpc_precision = 13U;
else if (frame->blocksize <= 16384) lpc_precision = 14U; else if (frame.blocksize <= 16384) lpc_precision = 14U;
else lpc_precision = 15; else lpc_precision = 15;
for (int i_precision = eparams.lpc_min_precision_search; i_precision <= eparams.lpc_max_precision_search && lpc_precision + i_precision < 16; i_precision++) for (int i_precision = eparams.lpc_min_precision_search; i_precision <= eparams.lpc_max_precision_search && lpc_precision + i_precision < 16; i_precision++)
// check if we already calculated with this order, window and precision // 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) if ((frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[i_precision] & (1U << (order - 1))) == 0)
{ {
frame->subframes[ch].done_lpcs[iWindow + i_precision * lpc.MAX_LPC_WINDOWS] |= (1U << (order - 1)); frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[i_precision] |= (1U << (order - 1));
uint cbits = lpc_precision + (uint)i_precision; uint cbits = lpc_precision + (uint)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, fixed (int* coefs = frame.current.coefs)
frame->current.order, cbits, frame->current.coefs, out frame->current.shift); {
lpc.quantize_lpc_coefs(lpcs + (frame.current.order - 1) * lpc.MAX_LPC_ORDER,
frame.current.order, cbits, coefs, out frame.current.shift);
if (frame->current.shift < 0 || frame->current.shift > 15) if (frame.current.shift < 0 || frame.current.shift > 15)
throw new Exception("negative shift"); throw new Exception("negative shift");
ulong csum = 0; ulong csum = 0;
for (int i = frame->current.order; i > 0; i--) for (int i = frame.current.order; i > 0; i--)
csum += (ulong)Math.Abs(frame->current.coefs[i - 1]); csum += (ulong)Math.Abs(coefs[i - 1]);
if ((csum << (int)frame->subframes[ch].obits) >= 1UL << 32) 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); lpc.encode_residual_long(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift);
else else
lpc.encode_residual(frame->current.residual, frame->subframes[ch].samples, frame->blocksize, frame->current.order, frame->current.coefs, frame->current.shift); lpc.encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, 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->subframes[ch].obits, cbits);
choose_best_subframe(frame, ch); frame.current.size = calc_rice_params_lpc(ref frame.current.rc, eparams.min_partition_order, eparams.max_partition_order,
frame.current.residual, frame.blocksize, frame.current.order, frame.subframes[ch].obits, cbits);
frame.ChooseBestSubframe(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)
{ {
if ((frame->subframes[ch].done_fixed & (1U << order)) != 0) if ((frame.subframes[ch].done_fixed & (1U << order)) != 0)
return; // already calculated; return; // already calculated;
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->subframes[ch].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(ref frame.current.rc, eparams.min_partition_order, eparams.max_partition_order,
frame->current.residual, frame->blocksize, frame->current.order, frame->subframes[ch].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);
choose_best_subframe(frame, ch); frame.ChooseBestSubframe(ch);
} }
unsafe static bool is_interesting_order(double* reff, int order, int max_order) unsafe void encode_residual(FlacFrame frame, int ch, PredictionType predict, OrderMethod omethod, int pass)
{ {
return (order > 4 && Math.Abs(reff[order - 1]) >= 0.10 && (order == max_order || Math.Abs(reff[order]) < 0.10)) || int* smp = frame.subframes[ch].samples;
(order < 6 && order < max_order - 1 && reff[order + 1] * reff[order + 1] + reff[order] * reff[order] < 0.1); int i, n = frame.blocksize;
}
unsafe double* get_reflection_coeffs(FlacFrame* frame, int ch, int order, int iWindow)
{
double* reff = frame->subframes[ch].reflection_coeffs + iWindow * lpc.MAX_LPC_ORDER;
if (frame->subframes[ch].autocorr_orders[iWindow] > order)
return reff;
double* autoc = frame->subframes[ch].autocorr_values + iWindow * (lpc.MAX_LPC_ORDER + 1);
lpc.compute_autocorr(frame->subframes[ch].samples, (uint)frame->blocksize,
(uint)frame->subframes[ch].autocorr_orders[iWindow],
(uint)order, autoc, frame->window_buffer + iWindow * Flake.MAX_BLOCKSIZE * 2);
lpc.compute_schur_reflection(autoc, (uint)order, reff);
frame->subframes[ch].autocorr_orders[iWindow] = order + 1;
return reff;
}
unsafe void encode_residual(FlacFrame* frame, int ch, PredictionType predict, OrderMethod omethod, int pass)
{
int* smp = frame->subframes[ch].samples;
int i, n = frame->blocksize;
// save best.window, because we can overwrite it later with fixed frame // save best.window, because we can overwrite it later with fixed frame
int best_window = frame->subframes[ch].best.type == SubframeType.LPC ? frame->subframes[ch].best.window : -1; int best_window = frame.subframes[ch].best.type == SubframeType.LPC ? frame.subframes[ch].best.window : -1;
// CONSTANT // CONSTANT
for (i = 1; i < n; i++) for (i = 1; i < n; i++)
@@ -878,16 +807,16 @@ namespace CUETools.Codecs.FLAKE
} }
if (i == n) if (i == n)
{ {
frame->subframes[ch].best.type = SubframeType.Constant; frame.subframes[ch].best.type = SubframeType.Constant;
frame->subframes[ch].best.residual[0] = smp[0]; frame.subframes[ch].best.residual[0] = smp[0];
frame->subframes[ch].best.size = frame->subframes[ch].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->subframes[ch].obits * (uint)frame->blocksize; frame.current.size = frame.subframes[ch].obits * (uint)frame.blocksize;
choose_best_subframe(frame, ch); frame.ChooseBestSubframe(ch);
if (n < 5 || predict == PredictionType.None) if (n < 5 || predict == PredictionType.None)
return; return;
@@ -896,7 +825,7 @@ namespace CUETools.Codecs.FLAKE
if (predict == PredictionType.Fixed || if (predict == PredictionType.Fixed ||
(predict == PredictionType.Search && pass != 1) || (predict == PredictionType.Search && pass != 1) ||
//predict == PredictionType.Search || //predict == PredictionType.Search ||
//(pass == 2 && frame->subframes[ch].best.type == SubframeType.Fixed) || //(pass == 2 && 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);
@@ -911,10 +840,10 @@ namespace CUETools.Codecs.FLAKE
(predict == PredictionType.Levinson || (predict == PredictionType.Levinson ||
predict == PredictionType.Search) predict == PredictionType.Search)
//predict == PredictionType.Search || //predict == PredictionType.Search ||
//(pass == 2 && frame->subframes[ch].best.type == SubframeType.LPC)) //(pass == 2 && 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;
@@ -923,9 +852,10 @@ namespace CUETools.Codecs.FLAKE
if (pass == 2 && iWindow != best_window) if (pass == 2 && iWindow != best_window)
continue; continue;
double* reff = get_reflection_coeffs(frame, ch, max_order, iWindow); LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow];
double* lpcs = stackalloc double[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER];
lpc.compute_lpc_coefs(null, (uint)max_order, reff, lpcs); lpc_ctx.GetReflection(max_order, smp, n, frame.window_buffer + iWindow * Flake.MAX_BLOCKSIZE * 2);
lpc_ctx.ComputeLPC(lpcs);
switch (omethod) switch (omethod)
{ {
@@ -939,7 +869,7 @@ namespace CUETools.Codecs.FLAKE
{ {
int found = 0; int found = 0;
for (i = max_order; i >= min_order && found < eparams.estimation_depth; i--) for (i = max_order; i >= min_order && found < eparams.estimation_depth; i--)
if (is_interesting_order(reff, i, max_order)) if (lpc_ctx.IsInterestingOrder(i))
{ {
encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch); encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
found++; found++;
@@ -953,7 +883,7 @@ namespace CUETools.Codecs.FLAKE
{ {
int found = 0; int found = 0;
for (i = min_order; i <= max_order && found < eparams.estimation_depth; i++) for (i = min_order; i <= max_order && found < eparams.estimation_depth; i++)
if (is_interesting_order(reff, i, max_order)) if (lpc_ctx.IsInterestingOrder(i))
{ {
encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch); encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
found++; found++;
@@ -981,12 +911,12 @@ namespace CUETools.Codecs.FLAKE
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].best.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].best.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)
@@ -1001,27 +931,27 @@ namespace CUETools.Codecs.FLAKE
} }
} }
unsafe void output_frame_header(FlacFrame* frame, BitWriter bitwriter) unsafe void output_frame_header(FlacFrame frame, BitWriter bitwriter)
{ {
bitwriter.writebits(15, 0x7FFC); bitwriter.writebits(15, 0x7FFC);
bitwriter.writebits(1, eparams.variable_block_size > 0 ? 1 : 0); bitwriter.writebits(1, eparams.variable_block_size > 0 ? 1 : 0);
bitwriter.writebits(4, frame->bs_code0); bitwriter.writebits(4, frame.bs_code0);
bitwriter.writebits(4, sr_code0); bitwriter.writebits(4, sr_code0);
if (frame->ch_mode == ChannelMode.NotStereo) if (frame.ch_mode == ChannelMode.NotStereo)
bitwriter.writebits(4, ch_code); bitwriter.writebits(4, ch_code);
else else
bitwriter.writebits(4, (int) frame->ch_mode); bitwriter.writebits(4, (int) frame.ch_mode);
bitwriter.writebits(3, bps_code); bitwriter.writebits(3, bps_code);
bitwriter.writebits(1, 0); bitwriter.writebits(1, 0);
bitwriter.write_utf8(frame_count); bitwriter.write_utf8(frame_count);
// custom block size // custom block size
if (frame->bs_code1 >= 0) if (frame.bs_code1 >= 0)
{ {
if (frame->bs_code1 < 256) if (frame.bs_code1 < 256)
bitwriter.writebits(8, frame->bs_code1); bitwriter.writebits(8, frame.bs_code1);
else else
bitwriter.writebits(16, frame->bs_code1); bitwriter.writebits(16, frame.bs_code1);
} }
// custom sample rate // custom sample rate
@@ -1039,100 +969,97 @@ namespace CUETools.Codecs.FLAKE
bitwriter.writebits(8, crc); bitwriter.writebits(8, crc);
} }
unsafe void output_residual(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* 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->best.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->best.order; int res_cnt = psize - sub.best.order;
// residual // residual
int j = sub->best.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->best.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) int cnt = Math.Min(res_cnt, frame.blocksize - j);
for (int i = 0; i < res_cnt && j < frame->blocksize; i++, j++) bitwriter.write_rice_block_signed(k, sub.best.residual + j, cnt);
bitwriter.write_unary_signed(sub->best.residual[j]); j += cnt;
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, FlacSubframeInfo* sub) output_subframe_constant(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub)
{ {
bitwriter.writebits_signed(sub->obits, sub->best.residual[0]); bitwriter.writebits_signed(sub.obits, sub.best.residual[0]);
} }
unsafe void unsafe void
output_subframe_verbatim(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* 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++)
bitwriter.writebits_signed(sub->obits, sub->samples[i]); bitwriter.writebits_signed(sub.obits, sub.samples[i]);
// Don't use residual here, because we don't copy samples to residual for verbatim frames. // Don't use residual here, because we don't copy samples to residual for verbatim frames.
} }
unsafe void unsafe void
output_subframe_fixed(FlacFrame* frame, BitWriter bitwriter, FlacSubframeInfo* sub) output_subframe_fixed(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub)
{ {
// warm-up samples // warm-up samples
for (int i = 0; i < sub->best.order; i++) for (int i = 0; i < sub.best.order; i++)
bitwriter.writebits_signed(sub->obits, sub->best.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, FlacSubframeInfo* sub) output_subframe_lpc(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub)
{ {
// warm-up samples // warm-up samples
for (int i = 0; i < sub->best.order; i++) for (int i = 0; i < sub.best.order; i++)
bitwriter.writebits_signed(sub->obits, sub->best.residual[i]); bitwriter.writebits_signed(sub.obits, sub.best.residual[i]);
// LPC coefficients // LPC coefficients
int cbits = 1; int cbits = 1;
for (int i = 0; i < sub->best.order; i++) for (int i = 0; i < sub.best.order; i++)
while (cbits < 16 && sub->best.coefs[i] != (sub->best.coefs[i] << (32 - cbits)) >> (32 - cbits)) while (cbits < 16 && sub.best.coefs[i] != (sub.best.coefs[i] << (32 - cbits)) >> (32 - cbits))
cbits++; cbits++;
bitwriter.writebits(4, cbits - 1); bitwriter.writebits(4, cbits - 1);
bitwriter.writebits_signed(5, sub->best.shift); bitwriter.writebits_signed(5, sub.best.shift);
for (int i = 0; i < sub->best.order; i++) for (int i = 0; i < sub.best.order; i++)
bitwriter.writebits_signed(cbits, sub->best.coefs[i]); bitwriter.writebits_signed(cbits, sub.best.coefs[i]);
// residual // residual
output_residual(frame, bitwriter, sub); output_residual(frame, bitwriter, sub);
} }
unsafe void output_subframes(FlacFrame* frame, BitWriter bitwriter) unsafe void output_subframes(FlacFrame frame, BitWriter bitwriter)
{ {
for (int ch = 0; ch < channels; ch++) for (int ch = 0; ch < channels; ch++)
{ {
FlacSubframeInfo* sub = frame->subframes + ch; FlacSubframeInfo sub = frame.subframes[ch];
// subframe header // subframe header
int type_code = (int) sub->best.type; int type_code = (int) sub.best.type;
if (sub->best.type == SubframeType.Fixed) if (sub.best.type == SubframeType.Fixed)
type_code |= sub->best.order; type_code |= sub.best.order;
if (sub->best.type == SubframeType.LPC) if (sub.best.type == SubframeType.LPC)
type_code |= sub->best.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);
if (sub->wbits > 0) if (sub.wbits > 0)
bitwriter.writebits((int)sub->wbits, 1); bitwriter.writebits((int)sub.wbits, 1);
// subframe // subframe
switch (sub->best.type) switch (sub.best.type)
{ {
case SubframeType.Constant: case SubframeType.Constant:
output_subframe_constant(frame, bitwriter, sub); output_subframe_constant(frame, bitwriter, sub);
@@ -1206,7 +1133,7 @@ namespace CUETools.Codecs.FLAKE
window[n] = 0.5 - 0.5 * Math.Cos(2.0 * Math.PI * n / N); window[n] = 0.5 - 0.5 * Math.Cos(2.0 * Math.PI * n / N);
} }
unsafe void encode_residual_pass1(FlacFrame* frame, int ch) unsafe void encode_residual_pass1(FlacFrame frame, int ch)
{ {
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;
@@ -1230,12 +1157,12 @@ namespace CUETools.Codecs.FLAKE
eparams.estimation_depth = estimation_depth; eparams.estimation_depth = estimation_depth;
} }
unsafe void encode_residual_pass2(FlacFrame* frame, int ch) unsafe void encode_residual_pass2(FlacFrame frame, int ch)
{ {
encode_residual(frame, ch, eparams.prediction_type, eparams.order_method, 2); encode_residual(frame, ch, eparams.prediction_type, eparams.order_method, 2);
} }
unsafe void encode_residual_onepass(FlacFrame* frame, int ch) unsafe void encode_residual_onepass(FlacFrame frame, int ch)
{ {
if (_windowcount > 1) if (_windowcount > 1)
{ {
@@ -1245,7 +1172,7 @@ namespace CUETools.Codecs.FLAKE
encode_residual(frame, ch, eparams.prediction_type, eparams.order_method, 0); encode_residual(frame, ch, eparams.prediction_type, eparams.order_method, 0);
} }
unsafe void estimate_frame(FlacFrame* frame, bool do_midside) unsafe void estimate_frame(FlacFrame frame, bool do_midside)
{ {
int subframes = do_midside ? channels * 2 : channels; int subframes = do_midside ? channels * 2 : channels;
@@ -1253,7 +1180,7 @@ namespace CUETools.Codecs.FLAKE
{ {
case StereoMethod.Estimate: case StereoMethod.Estimate:
for (int ch = 0; ch < subframes; ch++) for (int ch = 0; ch < subframes; ch++)
frame->subframes[ch].best.size = (uint)frame->blocksize * 32 + calc_decorr_score(frame, ch); frame.subframes[ch].best.size = (uint)frame.blocksize * 32 + calc_decorr_score(frame, ch);
break; break;
case StereoMethod.Evaluate: case StereoMethod.Evaluate:
for (int ch = 0; ch < subframes; ch++) for (int ch = 0; ch < subframes; ch++)
@@ -1271,67 +1198,53 @@ namespace CUETools.Codecs.FLAKE
} }
} }
unsafe uint measure_frame_size(FlacFrame* frame, bool do_midside) unsafe uint measure_frame_size(FlacFrame frame, bool do_midside)
{ {
uint total = 48 + 16; // crude estimation of header/footer size; // crude estimation of header/footer size
uint total = (uint)(32 + ((Flake.log2i(frame_count) + 4) / 5) * 8 + (eparams.variable_block_size != 0 ? 16 : 0) + 16);
if (do_midside) if (do_midside)
{ {
uint bitsBest = UINT32_MAX; uint bitsBest = Flake.UINT32_MAX;
ChannelMode modeBest = ChannelMode.LeftRight; ChannelMode modeBest = ChannelMode.LeftRight;
if (bitsBest > frame->subframes[2].best.size + frame->subframes[3].best.size) if (bitsBest > frame.subframes[2].best.size + frame.subframes[3].best.size)
{ {
bitsBest = frame->subframes[2].best.size + frame->subframes[3].best.size; bitsBest = frame.subframes[2].best.size + frame.subframes[3].best.size;
modeBest = ChannelMode.MidSide; modeBest = ChannelMode.MidSide;
} }
if (bitsBest > frame->subframes[3].best.size + frame->subframes[1].best.size) if (bitsBest > frame.subframes[3].best.size + frame.subframes[1].best.size)
{ {
bitsBest = frame->subframes[3].best.size + frame->subframes[1].best.size; bitsBest = frame.subframes[3].best.size + frame.subframes[1].best.size;
modeBest = ChannelMode.RightSide; modeBest = ChannelMode.RightSide;
} }
if (bitsBest > frame->subframes[3].best.size + frame->subframes[0].best.size) if (bitsBest > frame.subframes[3].best.size + frame.subframes[0].best.size)
{ {
bitsBest = frame->subframes[3].best.size + frame->subframes[0].best.size; bitsBest = frame.subframes[3].best.size + frame.subframes[0].best.size;
modeBest = ChannelMode.LeftSide; modeBest = ChannelMode.LeftSide;
} }
if (bitsBest > frame->subframes[0].best.size + frame->subframes[1].best.size) if (bitsBest > frame.subframes[0].best.size + frame.subframes[1].best.size)
{ {
bitsBest = frame->subframes[0].best.size + frame->subframes[1].best.size; bitsBest = frame.subframes[0].best.size + frame.subframes[1].best.size;
modeBest = ChannelMode.LeftRight; modeBest = ChannelMode.LeftRight;
} }
frame->ch_mode = modeBest; frame.ch_mode = modeBest;
return total + bitsBest; return total + bitsBest;
} }
for (int ch = 0; ch < channels; ch++) for (int ch = 0; ch < channels; ch++)
total += frame->subframes[ch].best.size; total += frame.subframes[ch].best.size;
return total; return total;
} }
unsafe void encode_estimated_frame(FlacFrame* frame, bool do_midside) unsafe void encode_estimated_frame(FlacFrame frame)
{ {
if (do_midside)
switch (frame->ch_mode)
{
case ChannelMode.MidSide:
frame->subframes[0] = frame->subframes[2];
frame->subframes[1] = frame->subframes[3];
break;
case ChannelMode.RightSide:
frame->subframes[0] = frame->subframes[3];
break;
case ChannelMode.LeftSide:
frame->subframes[1] = frame->subframes[3];
break;
}
switch (eparams.stereo_method) switch (eparams.stereo_method)
{ {
case StereoMethod.Estimate: case StereoMethod.Estimate:
for (int ch = 0; ch < channels; ch++) for (int ch = 0; ch < channels; ch++)
{ {
frame->subframes[ch].best.size = UINT32_MAX; frame.subframes[ch].best.size = Flake.UINT32_MAX;
encode_residual_onepass(frame, ch); encode_residual_onepass(frame, ch);
} }
break; break;
@@ -1369,16 +1282,11 @@ namespace CUETools.Codecs.FLAKE
unsafe int encode_frame(out int size) unsafe int encode_frame(out int size)
{ {
FlacFrame frame;
FlacFrame frame2, frame3;
FlacSubframeInfo* sf = stackalloc FlacSubframeInfo[channels * 6];
fixed (int* s = samplesBuffer, r = residualBuffer) fixed (int* s = samplesBuffer, r = residualBuffer)
fixed (double* window = windowBuffer) fixed (double* window = windowBuffer)
{ {
frame.subframes = sf; frame.InitSize(eparams.block_size, eparams.variable_block_size != 0);
init_frame(&frame, eparams.block_size);
if (frame.blocksize != _windowsize && frame.blocksize > 4) if (frame.blocksize != _windowsize && frame.blocksize > 4)
{ {
_windowsize = frame.blocksize; _windowsize = frame.blocksize;
@@ -1397,11 +1305,11 @@ namespace CUETools.Codecs.FLAKE
frame.current.residual = r + channels * Flake.MAX_BLOCKSIZE; frame.current.residual = r + channels * Flake.MAX_BLOCKSIZE;
frame.ch_mode = channels != 2 ? ChannelMode.NotStereo : ChannelMode.LeftRight; frame.ch_mode = channels != 2 ? ChannelMode.NotStereo : ChannelMode.LeftRight;
for (int ch = 0; ch < channels; ch++) for (int ch = 0; ch < channels; ch++)
initialize_subframe(&frame, ch, s + ch * Flake.MAX_BLOCKSIZE, r + ch * Flake.MAX_BLOCKSIZE, frame.subframes[ch].Init(s + ch * Flake.MAX_BLOCKSIZE, r + ch * Flake.MAX_BLOCKSIZE,
bits_per_sample, get_wasted_bits(s + ch * Flake.MAX_BLOCKSIZE, frame.blocksize)); bits_per_sample, get_wasted_bits(s + ch * Flake.MAX_BLOCKSIZE, frame.blocksize));
for (int ch = 0; ch < channels; ch++) for (int ch = 0; ch < channels; ch++)
encode_residual_onepass(&frame, ch); encode_residual_onepass(frame, ch);
} }
else else
{ {
@@ -1409,41 +1317,43 @@ namespace CUETools.Codecs.FLAKE
frame.window_buffer = window; frame.window_buffer = window;
frame.current.residual = r + 4 * Flake.MAX_BLOCKSIZE; frame.current.residual = r + 4 * Flake.MAX_BLOCKSIZE;
for (int ch = 0; ch < 4; ch++) for (int ch = 0; ch < 4; ch++)
initialize_subframe(&frame, ch, s + ch * Flake.MAX_BLOCKSIZE, r + ch * Flake.MAX_BLOCKSIZE, frame.subframes[ch].Init(s + ch * Flake.MAX_BLOCKSIZE, r + ch * Flake.MAX_BLOCKSIZE,
bits_per_sample + (ch == 3 ? 1U : 0U), get_wasted_bits(s + ch * Flake.MAX_BLOCKSIZE, frame.blocksize)); bits_per_sample + (ch == 3 ? 1U : 0U), get_wasted_bits(s + ch * Flake.MAX_BLOCKSIZE, frame.blocksize));
estimate_frame(&frame, true); estimate_frame(frame, true);
uint fs = measure_frame_size(&frame, true); uint fs = measure_frame_size(frame, true);
if (0 != eparams.variable_block_size) if (0 != eparams.variable_block_size)
{ {
FlacFrame frame2 = new FlacFrame(channels * 2);
FlacFrame frame3 = new FlacFrame(channels * 2);
int tumbler = 1; int tumbler = 1;
while ((frame.blocksize & 1) == 0 && frame.blocksize >= 1024) while ((frame.blocksize & 1) == 0 && frame.blocksize >= 1024)
{ {
init_frame(&frame2, frame.blocksize / 2); frame2.InitSize(frame.blocksize / 2, true);
frame2.window_buffer = frame.window_buffer + frame.blocksize; frame2.window_buffer = frame.window_buffer + frame.blocksize;
frame2.current.residual = r + tumbler * 5 * Flake.MAX_BLOCKSIZE; frame2.current.residual = r + tumbler * 5 * Flake.MAX_BLOCKSIZE;
frame2.subframes = sf + tumbler * channels * 2;
for (int ch = 0; ch < 4; ch++) for (int ch = 0; ch < 4; ch++)
initialize_subframe(&frame2, ch, frame.subframes[ch].samples, frame2.current.residual + (ch + 1) * frame2.blocksize, frame2.subframes[ch].Init(frame.subframes[ch].samples, frame2.current.residual + (ch + 1) * frame2.blocksize,
frame.subframes[ch].obits + frame.subframes[ch].wbits, frame.subframes[ch].wbits); frame.subframes[ch].obits + frame.subframes[ch].wbits, frame.subframes[ch].wbits);
estimate_frame(&frame2, true); estimate_frame(frame2, true);
uint fs2 = measure_frame_size(&frame2, true); uint fs2 = measure_frame_size(frame2, true);
uint fs3 = fs2; uint fs3 = fs2;
if (eparams.variable_block_size == 2 || eparams.variable_block_size == 4) if (eparams.variable_block_size == 2 || eparams.variable_block_size == 4)
{ {
init_frame(&frame3, frame2.blocksize); frame3.InitSize(frame2.blocksize, true);
frame3.window_buffer = frame2.window_buffer; frame3.window_buffer = frame2.window_buffer;
frame3.current.residual = frame2.current.residual + 5 * frame2.blocksize; frame3.current.residual = frame2.current.residual + 5 * frame2.blocksize;
frame3.subframes = sf + channels * 4;
for (int ch = 0; ch < 4; ch++) for (int ch = 0; ch < 4; ch++)
initialize_subframe(&frame3, ch, frame2.subframes[ch].samples + frame2.blocksize, frame3.current.residual + (ch + 1) * frame3.blocksize, frame3.subframes[ch].Init(frame2.subframes[ch].samples + frame2.blocksize, frame3.current.residual + (ch + 1) * frame3.blocksize,
frame.subframes[ch].obits + frame.subframes[ch].wbits, frame.subframes[ch].wbits); frame.subframes[ch].obits + frame.subframes[ch].wbits, frame.subframes[ch].wbits);
estimate_frame(&frame3, true); estimate_frame(frame3, true);
fs3 = measure_frame_size(&frame3, true); fs3 = measure_frame_size(frame3, true);
} }
if (fs2 + fs3 > fs) if (fs2 + fs3 > fs)
break; break;
FlacFrame tmp = frame;
frame = frame2; frame = frame2;
frame2 = tmp;
fs = fs2; fs = fs2;
if (eparams.variable_block_size <= 2) if (eparams.variable_block_size <= 2)
break; break;
@@ -1451,13 +1361,14 @@ namespace CUETools.Codecs.FLAKE
} }
} }
encode_estimated_frame(&frame, true); frame.ChooseSubframes();
encode_estimated_frame(frame);
} }
BitWriter bitwriter = new BitWriter(frame_buffer, 0, max_frame_size); BitWriter bitwriter = new BitWriter(frame_buffer, 0, max_frame_size);
output_frame_header(&frame, bitwriter); output_frame_header(frame, bitwriter);
output_subframes(&frame, bitwriter); output_subframes(frame, bitwriter);
output_frame_footer(bitwriter); output_frame_footer(bitwriter);
if (frame_buffer != null) if (frame_buffer != null)
@@ -1598,7 +1509,7 @@ namespace CUETools.Codecs.FLAKE
// metadata header // metadata header
bitwriter.writebits(1, last); bitwriter.writebits(1, last);
bitwriter.writebits(7, (int)MetadataType.FLAC__METADATA_TYPE_STREAMINFO); bitwriter.writebits(7, (int)MetadataType.StreamInfo);
bitwriter.writebits(24, 34); bitwriter.writebits(24, 34);
if (eparams.variable_block_size > 0) if (eparams.variable_block_size > 0)
@@ -1639,7 +1550,7 @@ namespace CUETools.Codecs.FLAKE
// metadata header // metadata header
bitwriter.writebits(1, last); bitwriter.writebits(1, last);
bitwriter.writebits(7, (int)MetadataType.FLAC__METADATA_TYPE_VORBIS_COMMENT); bitwriter.writebits(7, (int)MetadataType.VorbisComment);
bitwriter.writebits(24, vendor_len + 8); bitwriter.writebits(24, vendor_len + 8);
comment[pos + 4] = (byte)(vendor_len & 0xFF); comment[pos + 4] = (byte)(vendor_len & 0xFF);
@@ -1662,7 +1573,7 @@ namespace CUETools.Codecs.FLAKE
// metadata header // metadata header
bitwriter.writebits(1, last); bitwriter.writebits(1, last);
bitwriter.writebits(7, (int)MetadataType.FLAC__METADATA_TYPE_SEEKTABLE); bitwriter.writebits(7, (int)MetadataType.Seektable);
bitwriter.writebits(24, 18 * seek_table.Length); bitwriter.writebits(24, 18 * seek_table.Length);
for (int i = 0; i < seek_table.Length; i++) for (int i = 0; i < seek_table.Length; i++)
{ {
@@ -1684,7 +1595,7 @@ namespace CUETools.Codecs.FLAKE
// metadata header // metadata header
bitwriter.writebits(1, last); bitwriter.writebits(1, last);
bitwriter.writebits(7, (int)MetadataType.FLAC__METADATA_TYPE_PADDING); bitwriter.writebits(7, (int)MetadataType.Padding);
bitwriter.writebits(24, padlen); bitwriter.writebits(24, padlen);
return padlen + 4; return padlen + 4;
@@ -1947,7 +1858,7 @@ namespace CUETools.Codecs.FLAKE
block_time_ms = 105; block_time_ms = 105;
prediction_type = PredictionType.Search; prediction_type = PredictionType.Search;
min_prediction_order = 1; min_prediction_order = 1;
max_prediction_order = 8; max_prediction_order = 12;
estimation_depth = 1; estimation_depth = 1;
min_fixed_order = 2; min_fixed_order = 2;
max_fixed_order = 2; max_fixed_order = 2;
@@ -1960,49 +1871,46 @@ namespace CUETools.Codecs.FLAKE
do_verify = false; do_verify = false;
do_seektable = true; do_seektable = true;
// differences from level 5 // differences from level 7
switch (lvl) switch (lvl)
{ {
case 0: case 0:
block_time_ms = 27; block_time_ms = 53;
prediction_type = PredictionType.Fixed; prediction_type = PredictionType.Fixed;
stereo_method = StereoMethod.Independent;
max_partition_order = 4; max_partition_order = 4;
break; break;
case 1: case 1:
prediction_type = PredictionType.Levinson; prediction_type = PredictionType.Levinson;
stereo_method = StereoMethod.Independent; stereo_method = StereoMethod.Independent;
window_function = WindowFunction.Welch; window_function = WindowFunction.Welch;
max_prediction_order = 8;
max_partition_order = 4; max_partition_order = 4;
break; break;
case 2: case 2:
stereo_method = StereoMethod.Independent; stereo_method = StereoMethod.Independent;
window_function = WindowFunction.Welch; window_function = WindowFunction.Welch;
max_prediction_order = 12;
max_partition_order = 4; max_partition_order = 4;
break; break;
case 3: case 3:
stereo_method = StereoMethod.Estimate; stereo_method = StereoMethod.Estimate;
window_function = WindowFunction.Welch; window_function = WindowFunction.Welch;
max_prediction_order = 8;
break; break;
case 4: case 4:
stereo_method = StereoMethod.Estimate; stereo_method = StereoMethod.Estimate;
window_function = WindowFunction.Welch; window_function = WindowFunction.Welch;
max_prediction_order = 12;
break; break;
case 5: case 5:
window_function = WindowFunction.Welch; window_function = WindowFunction.Welch;
max_prediction_order = 12;
break; break;
case 6: case 6:
stereo_method = StereoMethod.Estimate; stereo_method = StereoMethod.Estimate;
max_prediction_order = 12;
break; break;
case 7: case 7:
max_prediction_order = 12;
break; break;
case 8: case 8:
estimation_depth = 3; estimation_depth = 3;
max_prediction_order = 12;
min_fixed_order = 0; min_fixed_order = 0;
max_fixed_order = 4; max_fixed_order = 4;
lpc_max_precision_search = 2; lpc_max_precision_search = 2;

View File

@@ -804,4 +804,78 @@ namespace CUETools.Codecs.FLAKE
} }
} }
} }
/// <summary>
/// Context for LPC coefficients calculation and order estimation
/// </summary>
unsafe class LpcContext
{
public LpcContext()
{
reflection_coeffs = new double[lpc.MAX_LPC_ORDER];
autocorr_values = new double[lpc.MAX_LPC_ORDER + 1];
done_lpcs = new uint[lpc.MAX_LPC_PRECISIONS];
}
/// <summary>
/// Reset to initial (blank) state
/// </summary>
public void Reset()
{
autocorr_order = 0;
for (int iPrecision = 0; iPrecision < lpc.MAX_LPC_PRECISIONS; iPrecision++)
done_lpcs[iPrecision] = 0;
}
/// <summary>
/// Calculate autocorrelation data and reflection coefficients.
/// Can be used to incrementaly compute coefficients for higher orders,
/// because it caches them.
/// </summary>
/// <param name="order">Maximum order</param>
/// <param name="samples">Samples pointer</param>
/// <param name="blocksize">Block size</param>
/// <param name="window">Window function</param>
public void GetReflection(int order, int* samples, int blocksize, double* window)
{
if (autocorr_order > order)
return;
fixed (double* reff = reflection_coeffs, autoc = autocorr_values)
{
lpc.compute_autocorr(samples, (uint)blocksize, (uint)autocorr_order, (uint)order, autoc, window);
lpc.compute_schur_reflection(autoc, (uint)order, reff);
autocorr_order = order + 1;
}
}
/// <summary>
/// Produces LPC coefficients from autocorrelation data.
/// </summary>
/// <param name="lpcs">LPC coefficients buffer (for all orders)</param>
public void ComputeLPC(double* lpcs)
{
fixed (double* reff = reflection_coeffs)
lpc.compute_lpc_coefs(null, (uint)autocorr_order - 1, reff, lpcs);
}
/// <summary>
/// Checks if an order is 'interesting'.
/// Lower orders are likely to be useful if next two reflection coefficients are small,
/// Higher orders are likely to be useful if reflection coefficient for that order is greater than 0.1 and the next one is not.
/// </summary>
/// <param name="order"></param>
/// <returns></returns>
public bool IsInterestingOrder(int order)
{
return (order > 4 && Math.Abs(reflection_coeffs[order - 1]) >= 0.10 && (order == autocorr_order - 1 || Math.Abs(reflection_coeffs[order]) < 0.10)) ||
(order < 6 && order < autocorr_order - 2 && reflection_coeffs[order + 1] * reflection_coeffs[order + 1] + reflection_coeffs[order] * reflection_coeffs[order] < 0.1);
}
double[] autocorr_values;
double[] reflection_coeffs;
int autocorr_order;
public uint[] done_lpcs;
}
} }

View File

@@ -15,7 +15,7 @@ namespace CUETools.FlakeExe
Console.WriteLine(); Console.WriteLine();
Console.WriteLine("Options:"); Console.WriteLine("Options:");
Console.WriteLine(); Console.WriteLine();
Console.WriteLine(" -0 .. -11 Compression level, default 5."); Console.WriteLine(" -0 .. -11 Compression level, default 7.");
Console.WriteLine(" -o <file> Output filename, or \"-\" for stdout, or nul."); Console.WriteLine(" -o <file> Output filename, or \"-\" for stdout, or nul.");
Console.WriteLine(" -p # Padding bytes."); Console.WriteLine(" -p # Padding bytes.");
Console.WriteLine(" -q --quiet Quiet mode."); Console.WriteLine(" -q --quiet Quiet mode.");

View File

@@ -911,7 +911,7 @@ namespace CUETools.Processor
encoders = new CUEToolsUDCList(); encoders = new CUEToolsUDCList();
#if !MONO #if !MONO
encoders.Add(new CUEToolsUDC("libFLAC", "flac", true, "0 1 2 3 4 5 6 7 8", "5", "FLACWriter")); encoders.Add(new CUEToolsUDC("libFLAC", "flac", true, "0 1 2 3 4 5 6 7 8", "5", "FLACWriter"));
encoders.Add(new CUEToolsUDC("libFlake", "flac", true, "0 1 2 3 4 5 6 7 8 9 10 11 12", "5", "FlakeWriter")); encoders.Add(new CUEToolsUDC("libFlake", "flac", true, "0 1 2 3 4 5 6 7 8 9 10 11", "7", "FlakeWriter"));
encoders.Add(new CUEToolsUDC("libwavpack", "wv", true, "fast normal high high+", "normal", "WavPackWriter")); encoders.Add(new CUEToolsUDC("libwavpack", "wv", true, "fast normal high high+", "normal", "WavPackWriter"));
encoders.Add(new CUEToolsUDC("MAC_SDK", "ape", true, "fast normal high extra insane", "high", "APEWriter")); encoders.Add(new CUEToolsUDC("MAC_SDK", "ape", true, "fast normal high extra insane", "high", "APEWriter"));
encoders.Add(new CUEToolsUDC("ttalib", "tta", true, "", "", "TTAWriter")); encoders.Add(new CUEToolsUDC("ttalib", "tta", true, "", "", "TTAWriter"));