optimizations

This commit is contained in:
chudov
2009-12-24 16:14:41 +00:00
parent 65ef219a4e
commit 7b9609d0d0
3 changed files with 328 additions and 347 deletions

View File

@@ -31,6 +31,7 @@ namespace CUETools.Codecs.ALAC
public const int MAX_RICE_PARAM = 14; public const int MAX_RICE_PARAM = 14;
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 MAX_LPC_WINDOWS = 4;
public const uint UINT32_MAX = 0xffffffff; public const uint UINT32_MAX = 0xffffffff;
@@ -49,6 +50,10 @@ namespace CUETools.Codecs.ALAC
return (WindowFunction)(Enum.Parse(typeof(WindowFunction), name, true)); return (WindowFunction)(Enum.Parse(typeof(WindowFunction), name, true));
} }
public static WindowMethod LookupWindowMethod(string name)
{
return (WindowMethod)(Enum.Parse(typeof(WindowMethod), name, true));
}
} }
unsafe class RiceContext unsafe class RiceContext
@@ -80,6 +85,7 @@ namespace CUETools.Codecs.ALAC
{ {
rc = new RiceContext(); rc = new RiceContext();
coefs = new int[lpc.MAX_LPC_ORDER]; coefs = new int[lpc.MAX_LPC_ORDER];
coefs_adapted = new int[lpc.MAX_LPC_ORDER];
} }
public int order; public int order;
public int* residual; public int* residual;
@@ -90,6 +96,7 @@ namespace CUETools.Codecs.ALAC
public int cbits; public int cbits;
public int shift; public int shift;
public int[] coefs; public int[] coefs;
public int[] coefs_adapted;
public int window; public int window;
}; };
@@ -98,8 +105,8 @@ namespace CUETools.Codecs.ALAC
public ALACSubframeInfo() public ALACSubframeInfo()
{ {
best = new ALACSubframe(); best = new ALACSubframe();
lpc_ctx = new LpcContext[lpc.MAX_LPC_WINDOWS]; lpc_ctx = new LpcContext[Alac.MAX_LPC_WINDOWS];
for (int i = 0; i < lpc.MAX_LPC_WINDOWS; i++) for (int i = 0; i < Alac.MAX_LPC_WINDOWS; i++)
lpc_ctx[i] = new LpcContext(); lpc_ctx[i] = new LpcContext();
} }
@@ -108,7 +115,8 @@ namespace CUETools.Codecs.ALAC
samples = s; samples = s;
best.residual = r; best.residual = r;
best.size = AudioSamples.UINT32_MAX; best.size = AudioSamples.UINT32_MAX;
for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++) best.order = 0;
for (int iWindow = 0; iWindow < Alac.MAX_LPC_WINDOWS; iWindow++)
lpc_ctx[iWindow].Reset(); lpc_ctx[iWindow].Reset();
done_fixed = 0; done_fixed = 0;
} }
@@ -181,26 +189,27 @@ namespace CUETools.Codecs.ALAC
public int interlacing_shift, interlacing_leftweight; public int interlacing_shift, interlacing_leftweight;
public ALACSubframeInfo[] subframes; public ALACSubframeInfo[] subframes;
public ALACSubframe current; public ALACSubframe current;
public double* window_buffer; public float* window_buffer;
} }
public enum OrderMethod public enum OrderMethod
{ {
Max = 0, Estimate = 0
Estimate = 1,
LogFast = 2,
LogSearch = 3,
EstSearch2 = 4,
Search = 5
} }
public enum StereoMethod public enum StereoMethod
{ {
Independent = 0, Independent = 0,
Estimate = 1, Estimate = 1,
Estimate2 = 2, Evaluate = 2,
Evaluate = 3, Search = 3,
Search = 4 }
public enum WindowMethod
{
Estimate = 0,
Evaluate = 1,
Search = 2
} }
public enum FrameType public enum FrameType
@@ -224,6 +233,7 @@ namespace CUETools.Codecs.ALAC
Tukey = 2, Tukey = 2,
Hann = 4, Hann = 4,
Flattop = 8, Flattop = 8,
Bartlett = 16,
TukFlat = 10 TukFlat = 10
} }
} }

View File

@@ -19,12 +19,15 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#define INTEROP
using System; using System;
using System.Text; using System.Text;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; #if INTEROP
//using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#endif
using CUETools.Codecs; using CUETools.Codecs;
namespace CUETools.Codecs.ALAC namespace CUETools.Codecs.ALAC
@@ -56,7 +59,7 @@ namespace CUETools.Codecs.ALAC
// this can be used to allocate memory for output // this can be used to allocate memory for output
int max_frame_size; int max_frame_size;
int initial_history, history_mult, k_modifier; int initial_history = 10, history_mult = 40, k_modifier = 14;
byte[] frame_buffer = null; byte[] frame_buffer = null;
@@ -64,7 +67,9 @@ namespace CUETools.Codecs.ALAC
long first_frame_offset = 0; long first_frame_offset = 0;
#if INTEROP
TimeSpan _userProcessorTime; TimeSpan _userProcessorTime;
#endif
// header bytes // header bytes
byte[] header; byte[] header;
@@ -73,10 +78,10 @@ namespace CUETools.Codecs.ALAC
int[] samplesBuffer; int[] samplesBuffer;
int[] verifyBuffer; int[] verifyBuffer;
int[] residualBuffer; int[] residualBuffer;
double[] windowBuffer; float[] windowBuffer;
int samplesInBuffer = 0; int samplesInBuffer = 0;
int _compressionLevel = 4; int _compressionLevel = 5;
int _blocksize = 0; int _blocksize = 0;
int _totalSize = 0; int _totalSize = 0;
int _windowsize = 0, _windowcount = 0; int _windowsize = 0, _windowcount = 0;
@@ -108,7 +113,7 @@ namespace CUETools.Codecs.ALAC
samplesBuffer = new int[Alac.MAX_BLOCKSIZE * (channels == 2 ? 5 : channels)]; samplesBuffer = new int[Alac.MAX_BLOCKSIZE * (channels == 2 ? 5 : channels)];
residualBuffer = new int[Alac.MAX_BLOCKSIZE * (channels == 2 ? 6 : channels + 1)]; residualBuffer = new int[Alac.MAX_BLOCKSIZE * (channels == 2 ? 6 : channels + 1)];
windowBuffer = new double[Alac.MAX_BLOCKSIZE * 2 * lpc.MAX_LPC_WINDOWS]; windowBuffer = new float[Alac.MAX_BLOCKSIZE * 2 * Alac.MAX_LPC_WINDOWS];
eparams.set_defaults(_compressionLevel); eparams.set_defaults(_compressionLevel);
eparams.padding_size = 8192; eparams.padding_size = 8192;
@@ -147,17 +152,19 @@ namespace CUETools.Codecs.ALAC
} }
set set
{ {
if (value < 0 || value > 11) if (value < 0 || value > 10)
throw new Exception("unsupported compression level"); throw new Exception("unsupported compression level");
_compressionLevel = value; _compressionLevel = value;
eparams.set_defaults(_compressionLevel); eparams.set_defaults(_compressionLevel);
} }
} }
//[DllImport("kernel32.dll")] #if INTEROP
//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 bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime);
//static extern IntPtr GetCurrentThread(); [DllImport("kernel32.dll")]
static extern IntPtr GetCurrentThread();
#endif
void chunk_start(BitWriter bitwriter) void chunk_start(BitWriter bitwriter)
{ {
@@ -201,9 +208,11 @@ namespace CUETools.Codecs.ALAC
inited = false; inited = false;
} }
//long fake, KernelStart, UserStart; #if INTEROP
//GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart); long fake, KernelStart, UserStart;
//_userProcessorTime = new TimeSpan(UserStart); GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart);
_userProcessorTime = new TimeSpan(UserStart);
#endif
} }
public void Close() public void Close()
@@ -256,6 +265,12 @@ namespace CUETools.Codecs.ALAC
set { eparams.stereo_method = value; } set { eparams.stereo_method = value; }
} }
public WindowMethod WindowMethod
{
get { return eparams.window_method; }
set { eparams.window_method = value; }
}
public WindowFunction WindowFunction public WindowFunction WindowFunction
{ {
get { return eparams.window_function; } get { return eparams.window_function; }
@@ -330,7 +345,7 @@ namespace CUETools.Codecs.ALAC
} }
set set
{ {
if (value > 100) if (value > 7)
throw new Exception("invalid MaxHistoryModifier " + value.ToString()); throw new Exception("invalid MaxHistoryModifier " + value.ToString());
eparams.max_modifier = value; eparams.max_modifier = value;
if (eparams.min_modifier > value) if (eparams.min_modifier > value)
@@ -338,6 +353,34 @@ namespace CUETools.Codecs.ALAC
} }
} }
public int HistoryMult
{
get
{
return history_mult;
}
set
{
if (value < 1 || value > 255)
throw new Exception("invalid history_mult");
history_mult = value;
}
}
public int InitialHistory
{
get
{
return initial_history;
}
set
{
if (value < 1 || value > 255)
throw new Exception("invalid initial_history");
initial_history = value;
}
}
public int EstimationDepth public int EstimationDepth
{ {
get get
@@ -352,9 +395,30 @@ namespace CUETools.Codecs.ALAC
} }
} }
public int AdaptivePasses
{
get
{
return eparams.adaptive_passes;
}
set
{
if (value >= lpc.MAX_LPC_PRECISIONS || value < 0)
throw new Exception("invalid adaptive_passes " + value.ToString());
eparams.adaptive_passes = value;
}
}
public TimeSpan UserProcessorTime public TimeSpan UserProcessorTime
{ {
get { return _userProcessorTime; } get
{
#if INTEROP
return _userProcessorTime;
#else
return TimeSpan(0);
#endif
}
} }
public int BitsPerSample public int BitsPerSample
@@ -385,15 +449,6 @@ namespace CUETools.Codecs.ALAC
samplesInBuffer += block; samplesInBuffer += block;
} }
unsafe static void channel_decorrelation(int* leftS, int* rightS, int *leftM, int *rightM, int blocksize)
{
for (int i = 0; i < blocksize; i++)
{
leftM[i] = (leftS[i] + rightS[i]) >> 1;
rightM[i] = leftS[i] - rightS[i];
}
}
unsafe static void channel_decorrelation(int* leftS, int* rightS, int* leftM, int* rightM, int blocksize, int leftweight, int shift) unsafe static void channel_decorrelation(int* leftS, int* rightS, int* leftM, int* rightM, int blocksize, int leftweight, int shift)
{ {
for (int i = 0; i < blocksize; i++) for (int i = 0; i < blocksize; i++)
@@ -408,9 +463,9 @@ namespace CUETools.Codecs.ALAC
return (val << (32 - bits)) >> (32 - bits); return (val << (32 - bits)) >> (32 - bits);
} }
private static short sign_only(int val) private static int sign_only(int val)
{ {
return (short)((val >> 31) + ((val - 1) >> 31) + 1); return (val >> 31) + ((val - 1) >> 31) + 1;
} }
unsafe static void alac_encode_residual_31(int* res, int* smp, int n) unsafe static void alac_encode_residual_31(int* res, int* smp, int n)
@@ -428,7 +483,7 @@ namespace CUETools.Codecs.ALAC
unsafe static void alac_encode_residual(int* res, int* smp, int n, int order, int* coefs, int shift, int bps) unsafe static void alac_encode_residual(int* res, int* smp, int n, int order, int* coefs, int shift, int bps)
{ {
int csum = 0; int csum = 0;
for (int i = order - 1; i >= 0; i--) for (int i = order - 1; i >= 0; i--)
csum += coefs[i]; csum += coefs[i];
@@ -444,24 +499,31 @@ namespace CUETools.Codecs.ALAC
for (int i = order + 1; i < n; i++) for (int i = order + 1; i < n; i++)
{ {
int sample = *(smp++); int sample = *(smp++);
int/*long*/ sum = (1 << (shift - 1)) -csum * sample; int/*long*/ sum = (1 << (shift - 1));
int sum2 = 0; for (int j = 0; j < order; j++)
for (int j = 0; j < order; j+= 2) sum += (smp[j] - sample) * coefs[j];
{ int resval = extend_sign32(smp[order] - sample - (int)(sum >> shift), bps);
sum += smp[j] * coefs[j];
sum2 += smp[j+1] * coefs[j+1];
}
int resval = extend_sign32(smp[order] - (int)((sum + sum2) >> shift) - sample, bps);
res[i] = resval; res[i] = resval;
int error_sign = sign_only(resval);
for (int j = 0; j < order && resval * error_sign > 0; j++) if (resval > 0)
{ {
int val = sample - smp[j]; for (int j = 0; j < order && resval > 0; j++)
int sign = error_sign * sign_only(val); {
coefs[j] -= sign; int val = smp[j] - sample;
csum -= sign; int sign = sign_only(val);
resval -= ((val * sign) >> shift) * (j + 1); coefs[j] += sign;
//error_sign = (error_sign + sign_only(resval)) / 2; resval -= (val * sign >> shift) * (j + 1);
}
}
else
{
for (int j = 0; j < order && resval < 0; j++)
{
int val = smp[j] - sample;
int sign = -sign_only(val);
coefs[j] += sign;
resval -= (val * sign >> shift) * (j + 1);
}
} }
} }
res[n] = 1; // Stop byte to help alac_entropy_coder; res[n] = 1; // Stop byte to help alac_entropy_coder;
@@ -507,7 +569,7 @@ namespace CUETools.Codecs.ALAC
modifier = eparams.min_modifier; modifier = eparams.min_modifier;
for (int i = eparams.min_modifier; i <= eparams.max_modifier; i++) for (int i = eparams.min_modifier; i <= eparams.max_modifier; i++)
{ {
int newsize = alac_entropy_coder(res, n, bps, i); int newsize = alac_entropy_estimate(res, n, bps, i);
if (size > newsize) if (size > newsize)
{ {
size = newsize; size = newsize;
@@ -523,15 +585,13 @@ namespace CUETools.Codecs.ALAC
int sign_modifier = 0; int sign_modifier = 0;
int rice_historymult = modifier * history_mult / 4; int rice_historymult = modifier * history_mult / 4;
int size = 0; int size = 0;
int* fin = res + n;
for (int i = 0; i < n; ) while (res < fin)
{ {
int k = BitReader.log2i((history >> 9) + 3); int k = BitReader.log2i((history >> 9) + 3);
int x = -2 * (*res) - 1; int x = *(res++);
x ^= (x >> 31); x = (x << 1) ^ (x >> 31);
res++;
i++;
size += encode_scalar(x - sign_modifier, Math.Min(k, k_modifier), bps); size += encode_scalar(x - sign_modifier, Math.Min(k, k_modifier), bps);
@@ -541,14 +601,13 @@ namespace CUETools.Codecs.ALAC
if (x > 0xFFFF) if (x > 0xFFFF)
history = 0xFFFF; history = 0xFFFF;
if (history < 128 && i < n) if (history < 128 && res < fin)
{ {
k = 7 - BitReader.log2i(history) + ((history + 16) >> 6); k = 7 - BitReader.log2i(history) + ((history + 16) >> 6);
int block_size = 0; int* res1 = res;
while (res[block_size] == 0) // we have a stop byte, so need not check if i + blocksize < n while (*res == 0) // we have a stop byte, so need not check if res < fin
block_size++; res++;
res += block_size; int block_size = (int)(res - res1);
i += block_size;
size += encode_scalar(block_size, Math.Min(k, k_modifier), 16); size += encode_scalar(block_size, Math.Min(k, k_modifier), 16);
//sign_modifier = (block_size <= 0xFFFF) ? 1 : 0; //never happens //sign_modifier = (block_size <= 0xFFFF) ? 1 : 0; //never happens
sign_modifier = 1; sign_modifier = 1;
@@ -558,20 +617,45 @@ namespace CUETools.Codecs.ALAC
return size; return size;
} }
/// <summary>
/// Crude estimation of entropy length
/// </summary>
/// <param name="res"></param>
/// <param name="n"></param>
/// <param name="bps"></param>
/// <param name="modifier"></param>
/// <returns></returns>
unsafe int alac_entropy_estimate(int* res, int n, int bps, int modifier)
{
int history = initial_history;
int rice_historymult = modifier * history_mult / 4;
int size = 0;
int* fin = res + n;
while (res < fin)
{
int x = *(res++);
x = (x << 1) ^ (x >> 31);
int k = BitReader.log2i((history >> 9) + 3);
k = k > k_modifier ? k_modifier : k;
size += (x >> k) > 8 ? 9 + bps : (x >> k) + k + 1;
history += x * rice_historymult - ((history * rice_historymult) >> 9);
}
return size;
}
unsafe void alac_entropy_coder(BitWriter bitwriter, int* res, int n, int bps, int modifier) unsafe void alac_entropy_coder(BitWriter bitwriter, int* res, int n, int bps, int modifier)
{ {
int history = initial_history; int history = initial_history;
int sign_modifier = 0; int sign_modifier = 0;
int rice_historymult = modifier * history_mult / 4; int rice_historymult = modifier * history_mult / 4;
int* fin = res + n;
for (int i = 0; i < n; ) while (res < fin)
{ {
int k = BitReader.log2i((history >> 9) + 3); int k = BitReader.log2i((history >> 9) + 3);
int x = -2 * (*res) - 1; int x = *(res++);
x ^= (x >> 31); x = (x << 1) ^ (x >> 31);
res++;
i++;
encode_scalar(bitwriter, x - sign_modifier, k, bps); encode_scalar(bitwriter, x - sign_modifier, k, bps);
@@ -581,14 +665,13 @@ namespace CUETools.Codecs.ALAC
if (x > 0xFFFF) if (x > 0xFFFF)
history = 0xFFFF; history = 0xFFFF;
if (history < 128 && i < n) if (history < 128 && res < fin)
{ {
k = 7 - BitReader.log2i(history) + ((history + 16) >> 6); k = 7 - BitReader.log2i(history) + ((history + 16) >> 6);
int block_size = 0; int* res1 = res;
while (res[block_size] == 0) // we have a stop byte, so need not check if i + blocksize < n while (*res == 0) // we have a stop byte, so need not check if res < fin
block_size++; res++;
res += block_size; int block_size = (int)(res - res1);
i += block_size;
encode_scalar(bitwriter, block_size, k, 16); encode_scalar(bitwriter, block_size, k, 16);
sign_modifier = (block_size <= 0xFFFF) ? 1 : 0; sign_modifier = (block_size <= 0xFFFF) ? 1 : 0;
history = 0; history = 0;
@@ -596,35 +679,30 @@ namespace CUETools.Codecs.ALAC
} }
} }
unsafe void encode_residual_lpc_sub(ALACFrame frame, double * lpcs, int iWindow, int order, int ch) unsafe void encode_residual_lpc_sub(ALACFrame frame, float* lpcs, int iWindow, int order, int ch)
{ {
// select LPC precision based on block size // check if we already calculated with this order, window and precision
uint lpc_precision = 15; if ((frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[eparams.adaptive_passes] & (1U << (order - 1))) == 0)
int i_precision = 0; {
//if (frame.blocksize <= 192) lpc_precision = 7U; frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[eparams.adaptive_passes] |= (1U << (order - 1));
//else if (frame.blocksize <= 384) lpc_precision = 8U;
//else if (frame.blocksize <= 576) lpc_precision = 9U;
//else if (frame.blocksize <= 1152) lpc_precision = 10U;
//else if (frame.blocksize <= 2304) lpc_precision = 11U;
//else if (frame.blocksize <= 4608) lpc_precision = 12U;
//else if (frame.blocksize <= 8192) lpc_precision = 13U;
//else if (frame.blocksize <= 16384) lpc_precision = 14U;
//for (int i_precision = eparams.lpc_min_precision_search; i_precision <= eparams.lpc_max_precision_search && lpc_precision + i_precision < 16; i_precision++) uint cbits = 15U;
// check if we already calculated with this order, window and precision
if ((frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[i_precision] & (1U << (order - 1))) == 0) frame.current.order = order;
frame.current.window = iWindow;
int bps = (int)bits_per_sample + channels - 1;
int* coefs = stackalloc int[lpc.MAX_LPC_ORDER];
//if (frame.subframes[ch].best.order == order && frame.subframes[ch].best.window == iWindow)
//{
// frame.current.shift = frame.subframes[ch].best.shift;
// for (int i = 0; i < frame.current.order; i++)
// frame.current.coefs[i] = frame.subframes[ch].best.coefs_adapted[i];
//}
//else
{ {
frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[i_precision] |= (1U << (order - 1));
uint cbits = lpc_precision + (uint)i_precision;
frame.current.order = order;
frame.current.window = iWindow;
int bps = (int)bits_per_sample + channels - 1;
int* coefs = stackalloc int[lpc.MAX_LPC_ORDER];
lpc.quantize_lpc_coefs(lpcs + (frame.current.order - 1) * lpc.MAX_LPC_ORDER, lpc.quantize_lpc_coefs(lpcs + (frame.current.order - 1) * lpc.MAX_LPC_ORDER,
frame.current.order, cbits, coefs, out frame.current.shift, 15, 1); frame.current.order, cbits, coefs, out frame.current.shift, 15, 1);
@@ -633,25 +711,41 @@ namespace CUETools.Codecs.ALAC
for (int i = 0; i < frame.current.order; i++) for (int i = 0; i < frame.current.order; i++)
frame.current.coefs[i] = coefs[i]; frame.current.coefs[i] = coefs[i];
}
for (int i = 0; i < frame.current.order; i++)
coefs[i] = frame.current.coefs[frame.current.order - 1 - i];
for (int i = frame.current.order; i < lpc.MAX_LPC_ORDER; i++)
coefs[i] = 0;
alac_encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize,
frame.current.order, coefs, frame.current.shift, bps);
for (int i = 0; i < frame.current.order; i++)
frame.current.coefs_adapted[i] = coefs[frame.current.order - 1 - i];
for (int adaptive_pass = 0; adaptive_pass < eparams.adaptive_passes; adaptive_pass++)
{
for (int i = 0; i < frame.current.order; i++) for (int i = 0; i < frame.current.order; i++)
coefs[i] = frame.current.coefs[frame.current.order - 1 - i]; frame.current.coefs[i] = frame.current.coefs_adapted[i];
coefs[frame.current.order] = 0;
alac_encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, alac_encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize,
frame.current.order, coefs, frame.current.shift, bps); frame.current.order, coefs, frame.current.shift, bps);
frame.current.size = (uint)(alac_entropy_coder(frame.current.residual, frame.blocksize, bps, out frame.current.ricemodifier) + 16 + 16 * order); for (int i = 0; i < frame.current.order; i++)
frame.current.coefs_adapted[i] = coefs[frame.current.order - 1 - i];
frame.ChooseBestSubframe(ch);
} }
frame.current.size = (uint)(alac_entropy_estimate(frame.current.residual, frame.blocksize, bps, eparams.max_modifier) + 16 + 16 * order);
frame.ChooseBestSubframe(ch);
}
} }
unsafe void encode_residual(ALACFrame frame, int ch, OrderMethod omethod, int pass) unsafe void encode_residual(ALACFrame frame, int ch, int pass, int best_window)
{ {
int* smp = frame.subframes[ch].samples; int* smp = frame.subframes[ch].samples;
int i, n = frame.blocksize; int i, n = frame.blocksize;
int best_window = frame.subframes[ch].best.window;
int bps = (int)bits_per_sample + channels - 1; int bps = (int)bits_per_sample + channels - 1;
// FIXED // FIXED
@@ -678,70 +772,22 @@ namespace CUETools.Codecs.ALAC
if (n < eparams.max_prediction_order) if (n < eparams.max_prediction_order)
return; return;
double* lpcs = stackalloc double[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER]; float* lpcs = stackalloc float[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;
for (int iWindow = 0; iWindow < _windowcount; iWindow++) for (int iWindow = 0; iWindow < _windowcount; iWindow++)
{ {
if (pass == 2 && iWindow != best_window) if (best_window != -1 && iWindow != best_window)
continue; continue;
LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow]; LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow];
lpc_ctx.GetReflection(max_order, smp, n, frame.window_buffer + iWindow * Alac.MAX_BLOCKSIZE * 2); lpc_ctx.GetReflection(max_order, smp, n, frame.window_buffer + iWindow * Alac.MAX_BLOCKSIZE * 2);
lpc_ctx.ComputeLPC(lpcs); lpc_ctx.ComputeLPC(lpcs);
lpc_ctx.SortOrdersAkaike(frame.blocksize, eparams.estimation_depth, max_order, 5.0, 1.0/18);
switch (omethod) for (i = 0; i < eparams.estimation_depth && i < max_order; i++)
{ encode_residual_lpc_sub(frame, lpcs, iWindow, lpc_ctx.best_orders[i], ch);
case OrderMethod.Max:
// always use maximum order
encode_residual_lpc_sub(frame, lpcs, iWindow, max_order, ch);
break;
case OrderMethod.Estimate:
// estimated orders
// Search at reflection coeff thresholds (where they cross 0.10)
{
int found = 0;
for (i = max_order; i >= min_order && found < eparams.estimation_depth; i--)
if (lpc_ctx.IsInterestingOrder(i))
{
encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
found++;
}
if (0 == found)
encode_residual_lpc_sub(frame, lpcs, iWindow, min_order, ch);
}
break;
case OrderMethod.EstSearch2:
// Search at reflection coeff thresholds (where they cross 0.10)
{
int found = 0;
for (i = min_order; i <= max_order && found < eparams.estimation_depth; i++)
if (lpc_ctx.IsInterestingOrder(i))
{
encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
found++;
}
if (0 == found)
encode_residual_lpc_sub(frame, lpcs, iWindow, min_order, ch);
}
break;
case OrderMethod.Search:
// brute-force optimal order search
for (i = max_order; i >= min_order; i--)
encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
break;
case OrderMethod.LogFast:
// Try max, est, 32,16,8,4,2,1
encode_residual_lpc_sub(frame, lpcs, iWindow, max_order, ch);
for (i = lpc.MAX_LPC_ORDER; i >= min_order; i >>= 1)
if (i < max_order)
encode_residual_lpc_sub(frame, lpcs, iWindow, i, ch);
break;
default:
throw new Exception("unknown ordermethod");
}
} }
} }
@@ -777,82 +823,58 @@ namespace CUETools.Codecs.ALAC
bitwriter.flush(); bitwriter.flush();
} }
unsafe void window_welch(double* window, int L) unsafe void encode_residual_pass1(ALACFrame frame, int ch, int best_window)
{
int N = L - 1;
double N2 = (double)N / 2.0;
for (int n = 0; n <= N; n++)
{
double k = 1 / N2 - 1.0 - Math.Min(n, N - n);
//double k = ((double)n - N2) / N2;
window[n] = 1.0 - k * k;
}
}
unsafe void window_rectangle(double* window, int L)
{
for (int n = 0; n < L; n++)
window[n] = 1.0;
}
unsafe void window_flattop(double* window, int L)
{
int N = L - 1;
for (int n = 0; n < L; n++)
window[n] = 1.0 - 1.93 * Math.Cos(2.0 * Math.PI * n / N) + 1.29 * Math.Cos(4.0 * Math.PI * n / N) - 0.388 * Math.Cos(6.0 * Math.PI * n / N) + 0.0322 * Math.Cos(8.0 * Math.PI * n / N);
}
unsafe void window_tukey(double* window, int L)
{
window_rectangle(window, L);
double p = 0.5;
int Np = (int)(p / 2.0 * L) - 1;
if (Np > 0)
{
for (int n = 0; n <= Np; n++)
{
window[n] = 0.5 - 0.5 * Math.Cos(Math.PI * n / Np);
window[L - Np - 1 + n] = 0.5 - 0.5 * Math.Cos(Math.PI * (n + Np) / Np);
}
}
}
unsafe void window_hann(double* window, int L)
{
int N = L - 1;
for (int n = 0; n < L; n++)
window[n] = 0.5 - 0.5 * Math.Cos(2.0 * Math.PI * n / N);
}
unsafe void encode_residual_pass1(ALACFrame frame, int ch)
{ {
int max_prediction_order = eparams.max_prediction_order; int max_prediction_order = eparams.max_prediction_order;
int estimation_depth = eparams.estimation_depth; int estimation_depth = eparams.estimation_depth;
int min_modifier = eparams.min_modifier; int min_modifier = eparams.min_modifier;
int adaptive_passes = eparams.adaptive_passes;
eparams.max_prediction_order = Math.Min(8,eparams.max_prediction_order); eparams.max_prediction_order = Math.Min(8,eparams.max_prediction_order);
eparams.estimation_depth = 1; eparams.estimation_depth = 1;
eparams.min_modifier = eparams.max_modifier; eparams.min_modifier = eparams.max_modifier;
encode_residual(frame, ch, OrderMethod.Estimate, 1); eparams.adaptive_passes = 0;
encode_residual(frame, ch, 1, best_window);
eparams.max_prediction_order = max_prediction_order; eparams.max_prediction_order = max_prediction_order;
eparams.estimation_depth = estimation_depth; eparams.estimation_depth = estimation_depth;
eparams.min_modifier = min_modifier; eparams.min_modifier = min_modifier;
eparams.adaptive_passes = adaptive_passes;
} }
unsafe void encode_residual_pass2(ALACFrame frame, int ch) unsafe void encode_residual_pass2(ALACFrame frame, int ch)
{ {
encode_residual(frame, ch, eparams.order_method, 2); encode_residual(frame, ch, 2, estimate_best_window(frame, ch));
} }
unsafe void encode_residual_onepass(ALACFrame frame, int ch) unsafe int estimate_best_window(ALACFrame frame, int ch)
{ {
if (_windowcount > 1) if (_windowcount == 1)
return 0;
switch (eparams.window_method)
{ {
encode_residual_pass1(frame, ch); case WindowMethod.Estimate:
encode_residual_pass2(frame, ch); {
int best_window = -1;
double best_error = 0;
int order = 2;
for (int i = 0; i < _windowcount; i++)
{
frame.subframes[ch].lpc_ctx[i].GetReflection(order, frame.subframes[ch].samples, frame.blocksize, frame.window_buffer + i * Alac.MAX_BLOCKSIZE * 2);
double err = frame.subframes[ch].lpc_ctx[i].prediction_error[order - 1] / frame.subframes[ch].lpc_ctx[i].autocorr_values[0];
if (best_window == -1 || best_error > err)
{
best_window = i;
best_error = err;
}
}
return best_window;
}
case WindowMethod.Evaluate:
encode_residual_pass1(frame, ch, -1);
return frame.subframes[ch].best.window;
case WindowMethod.Search:
return -1;
} }
else return -1;
encode_residual(frame, ch, eparams.order_method, 0);
} }
unsafe void estimate_frame(ALACFrame frame, bool do_midside) unsafe void estimate_frame(ALACFrame frame, bool do_midside)
@@ -861,18 +883,24 @@ namespace CUETools.Codecs.ALAC
switch (eparams.stereo_method) switch (eparams.stereo_method)
{ {
case StereoMethod.Evaluate: case StereoMethod.Estimate:
for (int ch = 0; ch < subframes; ch++) for (int ch = 0; ch < subframes; ch++)
{ {
int windowcount = _windowcount; LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[0];
_windowcount = 1; int stereo_order = Math.Min(8, eparams.max_prediction_order);
encode_residual_pass1(frame, ch); double alpha = 1.5; // 4.5 + eparams.max_prediction_order / 10.0;
_windowcount = windowcount; lpc_ctx.GetReflection(stereo_order, frame.subframes[ch].samples, frame.blocksize, frame.window_buffer);
lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, stereo_order, alpha, 0);
frame.subframes[ch].best.size = (uint)Math.Max(0, lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], alpha, 0));
} }
break; break;
case StereoMethod.Evaluate:
for (int ch = 0; ch < subframes; ch++)
encode_residual_pass1(frame, ch, 0);
break;
case StereoMethod.Search: case StereoMethod.Search:
for (int ch = 0; ch < subframes; ch++) for (int ch = 0; ch < subframes; ch++)
encode_residual_onepass(frame, ch); encode_residual_pass2(frame, ch);
break; break;
} }
} }
@@ -920,27 +948,30 @@ namespace CUETools.Codecs.ALAC
{ {
switch (eparams.stereo_method) switch (eparams.stereo_method)
{ {
case StereoMethod.Evaluate: case StereoMethod.Estimate:
for (int ch = 0; ch < channels; ch++) for (int ch = 0; ch < channels; ch++)
{ {
if (_windowcount > 1) frame.subframes[ch].best.size = AudioSamples.UINT32_MAX;
encode_residual_pass1(frame, ch);
encode_residual_pass2(frame, ch); encode_residual_pass2(frame, ch);
} }
break; break;
case StereoMethod.Evaluate:
for (int ch = 0; ch < channels; ch++)
encode_residual_pass2(frame, ch);
break;
case StereoMethod.Search: case StereoMethod.Search:
break; break;
} }
} }
unsafe delegate void window_function(double* window, int size); unsafe delegate void window_function(float* window, int size);
unsafe void calculate_window(double* window, window_function func, WindowFunction flag) unsafe void calculate_window(float * window, window_function func, WindowFunction flag)
{ {
if ((eparams.window_function & flag) == 0 || _windowcount == lpc.MAX_LPC_WINDOWS) if ((eparams.window_function & flag) == 0 || _windowcount == Alac.MAX_LPC_WINDOWS)
return; return;
int sz = _windowsize; int sz = _windowsize;
double* pos = window + _windowcount * Alac.MAX_BLOCKSIZE * 2; float* pos = window + _windowcount * Alac.MAX_BLOCKSIZE * 2;
do do
{ {
func(pos, sz); func(pos, sz);
@@ -955,7 +986,7 @@ namespace CUETools.Codecs.ALAC
unsafe int encode_frame(ref int size) unsafe int encode_frame(ref int size)
{ {
fixed (int* s = samplesBuffer, r = residualBuffer) fixed (int* s = samplesBuffer, r = residualBuffer)
fixed (double* window = windowBuffer) fixed (float * window = windowBuffer)
{ {
frame.InitSize(size); frame.InitSize(size);
@@ -963,10 +994,11 @@ namespace CUETools.Codecs.ALAC
{ {
_windowsize = frame.blocksize; _windowsize = frame.blocksize;
_windowcount = 0; _windowcount = 0;
calculate_window(window, window_welch, WindowFunction.Welch); calculate_window(window, lpc.window_welch, WindowFunction.Welch);
calculate_window(window, window_tukey, WindowFunction.Tukey); calculate_window(window, lpc.window_bartlett, WindowFunction.Bartlett);
calculate_window(window, window_hann, WindowFunction.Hann); calculate_window(window, lpc.window_tukey, WindowFunction.Tukey);
calculate_window(window, window_flattop, WindowFunction.Flattop); calculate_window(window, lpc.window_hann, WindowFunction.Hann);
calculate_window(window, lpc.window_flattop, WindowFunction.Flattop);
if (_windowcount == 0) if (_windowcount == 0)
throw new Exception("invalid windowfunction"); throw new Exception("invalid windowfunction");
} }
@@ -981,93 +1013,7 @@ namespace CUETools.Codecs.ALAC
frame.subframes[ch].Init(s + ch * Alac.MAX_BLOCKSIZE, r + ch * Alac.MAX_BLOCKSIZE); frame.subframes[ch].Init(s + ch * Alac.MAX_BLOCKSIZE, r + ch * Alac.MAX_BLOCKSIZE);
for (int ch = 0; ch < channels; ch++) for (int ch = 0; ch < channels; ch++)
encode_residual_onepass(frame, ch); encode_residual_pass2(frame, ch);
}
else if (eparams.stereo_method == StereoMethod.Estimate || eparams.stereo_method == StereoMethod.Estimate2)
{
int* sl = s;
int* sr = s + Alac.MAX_BLOCKSIZE;
int n = frame.blocksize;
ulong lsum = 0, rsum = 0, dsum = 0, s31 = 0, s1 = 0, s2 = 0, s3 = 0;
if (eparams.stereo_method == StereoMethod.Estimate)
for (int i = 2; i < n; i++)
{
int lt = sl[i] - 2 * sl[i - 1] + sl[i - 2];
int rt = sr[i] - 2 * sr[i - 1] + sr[i - 2];
int df = lt - rt;
lsum += (ulong)Math.Abs(lt);
rsum += (ulong)Math.Abs(rt);
dsum += (ulong)Math.Abs(df);
s1 += (ulong)Math.Abs(rt + (df >> 1));
s31 += (ulong)Math.Abs(rt + (df >> 31));
}
else
for (int i = 2; i < n; i++)
{
int lt = sl[i] - 2 * sl[i - 1] + sl[i - 2];
int rt = sr[i] - 2 * sr[i - 1] + sr[i - 2];
int df = lt - rt;
lsum += (ulong)Math.Abs(lt);
rsum += (ulong)Math.Abs(rt);
dsum += (ulong)Math.Abs(df);
s1 += (ulong)Math.Abs(rt + (df >> 1));
s2 += (ulong)Math.Abs(rt + (df >> 2));
s3 += (ulong)Math.Abs(rt + (df * 3 >> 2));
s31 += (ulong)Math.Abs(rt + (df >> 31));
}
frame.interlacing_leftweight = 0;
frame.interlacing_shift = 0;
ulong score = lsum + rsum;
if (lsum + dsum < score) //leftside
{
frame.interlacing_leftweight = 1;
frame.interlacing_shift = 0;
score = lsum + dsum;
}
if (s1 + dsum < score) // midside
{
frame.interlacing_leftweight = 1;
frame.interlacing_shift = 1;
score = s1 + dsum;
}
if (s31 + dsum < score) // rightside
{
frame.interlacing_leftweight = 1;
frame.interlacing_shift = 31;
score = s31 + dsum;
}
if (eparams.stereo_method == StereoMethod.Estimate2)
{
if (s2 + dsum < score) // close to rightside
{
frame.interlacing_leftweight = 1;
frame.interlacing_shift = 2;
score = s2 + dsum;
}
if (s3 + dsum < score) // close to leftside
{
frame.interlacing_leftweight = 3;
frame.interlacing_shift = 2;
score = s3 + dsum;
}
}
if (frame.interlacing_leftweight == 0)
{
frame.current.residual = r + channels * Alac.MAX_BLOCKSIZE;
for (int ch = 0; ch < channels; ch++)
frame.subframes[ch].Init(s + ch * Alac.MAX_BLOCKSIZE, r + ch * Alac.MAX_BLOCKSIZE);
}
else
{
frame.current.residual = r + 2 * channels * Alac.MAX_BLOCKSIZE;
channel_decorrelation(s, s + Alac.MAX_BLOCKSIZE, s + 2 * Alac.MAX_BLOCKSIZE, s + 3 * Alac.MAX_BLOCKSIZE, frame.blocksize,
frame.interlacing_leftweight, frame.interlacing_shift);
for (int ch = 0; ch < channels; ch++)
frame.subframes[ch].Init(s + (channels + ch) * Alac.MAX_BLOCKSIZE, r + (channels + ch) * Alac.MAX_BLOCKSIZE);
}
for (int ch = 0; ch < channels; ch++)
encode_residual_onepass(frame, ch);
} }
else else
{ {
@@ -1081,6 +1027,16 @@ namespace CUETools.Codecs.ALAC
frame.ChooseSubframes(); frame.ChooseSubframes();
encode_estimated_frame(frame); encode_estimated_frame(frame);
} }
for (int ch = 0; ch < channels; ch++)
{
if (eparams.min_modifier == eparams.max_modifier)
frame.subframes[ch].best.ricemodifier = eparams.max_modifier;
else
/*frame.subframes[ch].best.size = 16 + 16 * order + */
alac_entropy_coder(frame.subframes[ch].best.residual, frame.blocksize, bps, out frame.subframes[ch].best.ricemodifier);
}
uint fs = measure_frame_size(frame, false); uint fs = measure_frame_size(frame, false);
frame.type = ((int)fs > frame.blocksize * channels * bps) ? FrameType.Verbatim : FrameType.Compressed; frame.type = ((int)fs > frame.blocksize * channels * bps) ? FrameType.Verbatim : FrameType.Compressed;
BitWriter bitwriter = new BitWriter(frame_buffer, 0, max_frame_size); BitWriter bitwriter = new BitWriter(frame_buffer, 0, max_frame_size);
@@ -1563,10 +1519,6 @@ namespace CUETools.Codecs.ALAC
frame_buffer = new byte[max_frame_size]; frame_buffer = new byte[max_frame_size];
_sample_byte_size = new uint[sample_count / eparams.block_size + 1]; _sample_byte_size = new uint[sample_count / eparams.block_size + 1];
initial_history = 10;
history_mult = 40;
k_modifier = 14;
if (eparams.do_verify) if (eparams.do_verify)
{ {
verify = new ALACReader(channels, (int)bits_per_sample, history_mult, initial_history, k_modifier, eparams.block_size); verify = new ALACReader(channels, (int)bits_per_sample, history_mult, initial_history, k_modifier, eparams.block_size);
@@ -1610,6 +1562,8 @@ namespace CUETools.Codecs.ALAC
// 1 = mid-side encoding // 1 = mid-side encoding
public StereoMethod stereo_method; public StereoMethod stereo_method;
public WindowMethod window_method;
// block size in samples // block size in samples
// set by the user prior to calling encode_init // set by the user prior to calling encode_init
// if set to 0, a block size is chosen based on block_time_ms // if set to 0, a block size is chosen based on block_time_ms
@@ -1645,6 +1599,8 @@ namespace CUETools.Codecs.ALAC
// valid values are 1 to 32 // valid values are 1 to 32
public int estimation_depth; public int estimation_depth;
public int adaptive_passes;
public int min_modifier, max_modifier; public int min_modifier, max_modifier;
public WindowFunction window_function; public WindowFunction window_function;
@@ -1665,6 +1621,7 @@ namespace CUETools.Codecs.ALAC
window_function = WindowFunction.Flattop | WindowFunction.Tukey; window_function = WindowFunction.Flattop | WindowFunction.Tukey;
order_method = OrderMethod.Estimate; order_method = OrderMethod.Estimate;
stereo_method = StereoMethod.Evaluate; stereo_method = StereoMethod.Evaluate;
window_method = WindowMethod.Evaluate;
block_size = 0; block_size = 0;
block_time_ms = 105; block_time_ms = 105;
min_modifier = 4; min_modifier = 4;
@@ -1672,6 +1629,7 @@ namespace CUETools.Codecs.ALAC
min_prediction_order = 1; min_prediction_order = 1;
max_prediction_order = 12; max_prediction_order = 12;
estimation_depth = 1; estimation_depth = 1;
adaptive_passes = 0;
do_verify = false; do_verify = false;
do_seektable = false; do_seektable = false;
@@ -1679,41 +1637,54 @@ namespace CUETools.Codecs.ALAC
switch (lvl) switch (lvl)
{ {
case 0: case 0:
block_time_ms = 53;
stereo_method = StereoMethod.Independent; stereo_method = StereoMethod.Independent;
window_function = WindowFunction.Welch; window_function = WindowFunction.Hann;
max_prediction_order = 6; max_prediction_order = 6;
break; break;
case 1: case 1:
stereo_method = StereoMethod.Independent; stereo_method = StereoMethod.Independent;
window_function = WindowFunction.Welch; window_function = WindowFunction.Hann;
max_prediction_order = 8; max_prediction_order = 8;
break; break;
case 2: case 2:
stereo_method = StereoMethod.Estimate; stereo_method = StereoMethod.Estimate;
window_function = WindowFunction.Welch; window_function = WindowFunction.Hann;
max_prediction_order = 6; max_prediction_order = 6;
break; break;
case 3: case 3:
stereo_method = StereoMethod.Estimate; stereo_method = StereoMethod.Estimate;
window_function = WindowFunction.Welch; window_function = WindowFunction.Hann;
max_prediction_order = 8; max_prediction_order = 8;
break; break;
case 4: case 4:
stereo_method = StereoMethod.Estimate2; stereo_method = StereoMethod.Estimate;
window_function = WindowFunction.Welch; window_method = WindowMethod.Estimate;
max_prediction_order = 8;
break; break;
case 5: case 5:
stereo_method = StereoMethod.Estimate2; stereo_method = StereoMethod.Estimate;
window_method = WindowMethod.Estimate;
break; break;
case 6: case 6:
stereo_method = StereoMethod.Estimate;
break; break;
case 7: case 7:
estimation_depth = 3; stereo_method = StereoMethod.Estimate;
min_modifier = 3; adaptive_passes = 1;
min_modifier = 2;
break; break;
case 8: case 8:
estimation_depth = 5; adaptive_passes = 1;
min_modifier = 2;
break;
case 9:
adaptive_passes = 1;
max_prediction_order = 30;
min_modifier = 2;
break;
case 10:
estimation_depth = 2;
adaptive_passes = 2;
max_prediction_order = 30; max_prediction_order = 30;
min_modifier = 2; min_modifier = 2;
break; break;

View File

@@ -23,7 +23,7 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath> <OutputPath>..\bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>