mirror of
https://github.com/claunia/cuetools.net.git
synced 2025-12-16 18:14:25 +00:00
optimizations
This commit is contained in:
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user