Files
cuetools.net/CUETools.Codecs.Flake/AudioEncoder.cs

2403 lines
91 KiB
C#
Raw Normal View History

/**
* CUETools.Flake: pure managed FLAC audio encoder
Bump copyright year to 2020 The copyright year was last time updated in 2018. There is some cleanup involved in this commit and the next copyright year update is supposed to be simpler (i.e. substitute "-2020"). - Substitute occurrences of "-2018" with "-2020" using: git grep -I -l -e '-2018' -- ':(exclude)*.bak' | xargs \ sed -b -i -e 's/-2018/-2020/g' - Update special cases: CUEPlayer git grep -I -l -e 'Grigory Chudov 2010' -- | xargs \ sed -b -i -e 's/Grigory Chudov 2010/2010-2020 Grigory Chudov/g' CUERipper git grep -I -l -e '2008-2009' -- | xargs \ sed -b -i -e 's/2008-2009/2008-2020/g' CUETools, CUETools.FLACCL.cmd git grep -I -l -e '2008-2010' -- ':(exclude)*FlaCuda*' | xargs \ sed -b -i -e 's/2008-2010/2008-2020/g' git grep -I -l -e '2010-2013' -- | xargs \ sed -b -i -e 's/2010-2013/2010-2020/g' CUETools.ChaptersToCue git grep -I -l -e 'Grigory Chudov 2017' -- | xargs \ sed -b -i -e 's/Grigory Chudov 2017/2017-2020 Grigory Chudov/g' CUETools.CTDB.EACPlugin git grep -I -l -e 'Grigory Chudov 2012' -- | xargs \ sed -b -i -e 's/Grigory Chudov 2012/2012-2020 Grigory Chudov/g' git grep -I -l -e '2011-12' -- | xargs \ sed -b -i -e 's/2011-12/2011-2020/g' CUETools.Codecs.FLACCL git grep -I -l -e '2009-2010' -- ':(exclude)*FlaCuda*' | xargs \ sed -b -i -e 's/2009-2010/2009-2020/g' CUETools.eac3ui (BluTools) git grep -I -l -e '© 2018' -- | xargs \ sed -b -i -e 's/© 2018/© 2018-2020 Grigory Chudov/g' CUETools.Flake git grep -I -l -e ' 2009-2014 Gr' -- | xargs \ sed -b -i -e 's/ 2009-2014 Gr/ 2009-2020 Gr/g' CUETools.Processor git grep -I -l -e ' 2008-2013 Gr' -- | xargs \ sed -b -i -e 's/ 2008-2013 Gr/ 2008-2020 Gr/g' CUETools.Ripper.Console git grep -I -l -e ' 2008-10 Gr' -- | xargs \ sed -b -i -e 's/ 2008-10 Gr/ 2008-2020 Gr/g' CUETools.Ripper.Console, CUETools.Ripper.SCSI git grep -I -l -e ' 2008-13 Gr' -- | xargs \ sed -b -i -e 's/ 2008-13 Gr/ 2008-2020 Gr/g' Single year entries: 2008, 2009, 2010, 2011, 2017, 2018 git grep -I -l -e ' 2008 Gr' -- | xargs \ sed -b -i -e 's/ 2008 Gr/ 2008-2020 Gr/g' git grep -I -l -e ' 2009 Gr' -- ':(exclude)*FlaCuda*' | xargs \ sed -b -i -e 's/ 2009 Gr/ 2009-2020 Gr/g' git grep -I -l -e ' 2010 Gr' -- | xargs \ sed -b -i -e 's/ 2010 Gr/ 2010-2020 Gr/g' git grep -I -l -e ' 2011 Gr' -- | xargs \ sed -b -i -e 's/ 2011 Gr/ 2011-2020 Gr/g' git grep -I -l -e ' 2017 Gr' -- | xargs \ sed -b -i -e 's/ 2017 Gr/ 2017-2020 Gr/g' git grep -I -l -e ' 2018 Gr' -- | xargs \ sed -b -i -e 's/ 2018 Gr/ 2018-2020 Gr/g' Fix typo in copyright year of CUETools.Codecs.WMA/AudioDecoder.cs: Copyright (c) 20139 Grigory Chudov git grep -I -lw -e '20139' -- | xargs \ sed -b -i -e 's/20139/2013-2020/g'
2020-01-30 18:13:46 +01:00
* Copyright (c) 2009-2020 Grigory Chudov
* Based on Flake encoder, http://flake-enc.sourceforge.net/
* Copyright (c) 2006-2009 Justin Ruggles
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define NOINTEROP
#define VARIANT1
using System;
using System.ComponentModel;
using System.Text;
using System.IO;
using System.Collections.Generic;
using System.Security.Cryptography;
#if INTEROP
using System.Runtime.InteropServices;
#endif
using CUETools.Codecs;
using Newtonsoft.Json;
namespace CUETools.Codecs.Flake
{
public class AudioEncoder : IAudioDest
{
Stream _IO = null;
string _path;
long _position;
// number of audio channels
// set by user prior to calling flake_encode_init
// valid values are 1 to 8
int channels, ch_code;
// audio sample rate in Hz
// set by user prior to calling flake_encode_init
int sr_code0, sr_code1;
// sample size in bits
// set by user prior to calling flake_encode_init
// only 16-bit is currently supported
int bps_code;
// total stream samples
// set by user prior to calling flake_encode_init
// if 0, stream length is unknown
int sample_count = -1;
FlakeEncodeParams eparams;
// maximum frame size in bytes
// set by flake_encode_init
// this can be used to allocate memory for output
int max_frame_size;
byte[] frame_buffer = null;
int frame_count = 0;
long first_frame_offset = 0;
#if INTEROP
TimeSpan _userProcessorTime;
#endif
// header bytes
// allocated by flake_encode_init and freed by flake_encode_close
byte[] header;
int[] samplesBuffer;
int[] verifyBuffer;
int[] residualBuffer;
float[] windowBuffer;
double[] windowScale;
LpcWindowSection[, ,] windowSections;
WindowFunction[] windowType;
int samplesInBuffer = 0;
int m_blockSize = 0;
int _totalSize = 0;
int _windowsize = 0, _windowcount = 0;
Crc8 crc8;
MD5 md5;
FlacFrame frame;
AudioDecoder verify;
SeekPoint[] seek_table;
int seek_table_offset = -1;
bool inited = false;
public AudioEncoder(EncoderSettings settings, string path, Stream IO = null)
{
m_settings = settings.Clone() as EncoderSettings;
m_settings.Validate();
//if (Settings.PCM.BitsPerSample != 16)
// throw new Exception("Bits per sample must be 16.");
//if (Settings.PCM.ChannelCount != 2)
// throw new Exception("ChannelCount must be 2.");
channels = Settings.PCM.ChannelCount;
// flake_validate_params
_path = path;
_IO = IO;
samplesBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * (channels == 2 ? 4 : channels)];
residualBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * (channels == 2 ? 10 : channels + 1)];
windowBuffer = new float[FlakeConstants.MAX_BLOCKSIZE * 2 * lpc.MAX_LPC_WINDOWS];
windowScale = new double[lpc.MAX_LPC_WINDOWS];
windowType = new WindowFunction[lpc.MAX_LPC_WINDOWS];
windowSections = new LpcWindowSection[12, lpc.MAX_LPC_WINDOWS, lpc.MAX_LPC_SECTIONS];
eparams.flake_set_defaults(m_settings);
crc8 = new Crc8();
frame = new FlacFrame(channels * 2);
}
public int TotalSize
{
get
{
return _totalSize;
}
}
EncoderSettings m_settings;
public IAudioEncoderSettings Settings => m_settings;
#if INTEROP
[DllImport("kernel32.dll")]
static extern bool GetThreadTimes(IntPtr hThread, out long lpCreationTime, out long lpExitTime, out long lpKernelTime, out long lpUserTime);
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentThread();
#endif
void DoClose()
{
if (inited)
{
while (samplesInBuffer > 0)
{
m_blockSize = samplesInBuffer;
output_frame();
}
if (_IO.CanSeek)
{
if (sample_count <= 0 && _position != 0)
{
BitWriter bitwriter = new BitWriter(header, 0, 4);
bitwriter.writebits(32, (int)_position);
bitwriter.flush();
_IO.Position = 22;
_IO.Write(header, 0, 4);
}
if (md5 != null)
{
md5.TransformFinalBlock(frame_buffer, 0, 0);
_IO.Position = 26;
_IO.Write(md5.Hash, 0, md5.Hash.Length);
}
if (seek_table != null)
{
_IO.Position = seek_table_offset;
int len = write_seekpoints(header, 0, 0);
_IO.Write(header, 4, len - 4);
}
}
_IO.Close();
inited = false;
}
#if INTEROP
long fake, KernelStart, UserStart;
GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart);
_userProcessorTime = new TimeSpan(UserStart);
#endif
}
public void Close()
{
DoClose();
if (sample_count > 0 && _position != sample_count)
throw new Exception(Properties.Resources.ExceptionSampleCount);
}
public void Delete()
{
if (inited)
{
_IO.Close();
inited = false;
}
if (_path != "")
File.Delete(_path);
}
public long Position
{
get
{
return _position;
}
}
public long FinalSampleCount
{
set { sample_count = (int)value; }
}
public OrderMethod OrderMethod
{
get { return eparams.order_method; }
set { eparams.order_method = value; }
}
public int DevelopmentMode
{
get { return eparams.development_mode; }
set { eparams.development_mode = value; }
}
public bool DoSeekTable
{
get { return eparams.do_seektable; }
set { eparams.do_seektable = value; }
}
public int VBRMode
{
get { return eparams.variable_block_size; }
set { eparams.variable_block_size = value; }
}
public TimeSpan UserProcessorTime
{
get
{
#if INTEROP
return _userProcessorTime;
#else
return new TimeSpan(0);
#endif
}
}
unsafe int get_wasted_bits(int* signal, int samples)
{
int i, shift;
int x = 0;
for (i = 0; i < samples && 0 == (x & 1); i++)
x |= signal[i];
if (x == 0)
{
shift = 0;
}
else
{
for (shift = 0; 0 == (x & 1); shift++)
x >>= 1;
}
if (shift > 0)
{
for (i = 0; i < samples; i++)
signal[i] >>= shift;
}
return shift;
}
/// <summary>
/// Copy channel-interleaved input samples into separate subframes
/// </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])
{
if (channels == 2)
{
if (m_settings.StereoMethod == StereoMethod.Independent)
AudioSamples.Deinterlace(fsamples + samplesInBuffer, fsamples + FlakeConstants.MAX_BLOCKSIZE + samplesInBuffer, src, block);
else
{
int* left = fsamples + samplesInBuffer;
int* right = left + FlakeConstants.MAX_BLOCKSIZE;
int* leftM = right + FlakeConstants.MAX_BLOCKSIZE;
int* rightM = leftM + FlakeConstants.MAX_BLOCKSIZE;
for (int i = 0; i < block; i++)
{
int l = src[2 * i];
int r = src[2 * i + 1];
left[i] = l;
right[i] = r;
leftM[i] = (l + r) >> 1;
rightM[i] = l - r;
}
}
}
else
for (int ch = 0; ch < channels; ch++)
{
int* psamples = fsamples + ch * FlakeConstants.MAX_BLOCKSIZE + samplesInBuffer;
for (int i = 0; i < block; i++)
psamples[i] = src[i * channels + ch];
}
}
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 void encode_residual_verbatim(int* res, int* smp, uint n)
{
AudioSamples.MemCpy(res, smp, (int) n);
}
unsafe static ulong encode_residual_fixed_partition(int* res, int* smp, int* end, int order, int* last_errors)
{
ulong sum = 0UL;
switch (order)
{
case 0:
{
while (smp < end)
{
int error = *(res++) = *(smp++);
sum += (uint)((error << 1) ^ (error >> 31));
}
break;
}
case 1:
{
int last_error_0 = last_errors[0];
while (smp < end)
{
int error, save;
error = *(smp++); save = error;
error -= last_error_0; *(res++) = error; last_error_0 = save;
sum += (uint)((error << 1) ^ (error >> 31));
}
last_errors[0] = last_error_0;
break;
}
case 2:
{
int last_error_0 = last_errors[0], last_error_1 = last_errors[1];
while (smp < end)
{
int error, save;
error = *(smp++); save = error;
error -= last_error_0; last_error_0 = save; save = error;
error -= last_error_1; *(res++) = error; last_error_1 = save;
sum += (uint)((error << 1) ^ (error >> 31));
}
last_errors[0] = last_error_0; last_errors[1] = last_error_1; ;
break;
}
case 3:
{
int last_error_0 = last_errors[0], last_error_1 = last_errors[1], last_error_2 = last_errors[2];
while (smp < end)
{
int error, save;
error = *(smp++); save = error;
error -= last_error_0; last_error_0 = save; save = error;
error -= last_error_1; last_error_1 = save; save = error;
error -= last_error_2; *(res++) = error; last_error_2 = save;
sum += (uint)((error << 1) ^ (error >> 31));
}
last_errors[0] = last_error_0; last_errors[1] = last_error_1; last_errors[2] = last_error_2;
break;
}
case 4:
{
int last_error_0 = last_errors[0], last_error_1 = last_errors[1], last_error_2 = last_errors[2], last_error_3 = last_errors[3];
while (smp < end)
{
int error, save;
error = *(smp++); save = error;
error -= last_error_0; last_error_0 = save; save = error;
error -= last_error_1; last_error_1 = save; save = error;
error -= last_error_2; last_error_2 = save; save = error;
error -= last_error_3; *(res++) = error; last_error_3 = save;
sum += (uint)((error << 1) ^ (error >> 31));
}
last_errors[0] = last_error_0; last_errors[1] = last_error_1; last_errors[2] = last_error_2; last_errors[3] = last_error_3;
break;
}
default:
throw new ArgumentOutOfRangeException();
}
return sum;
}
unsafe static void encode_residual_fixed(int* res, int* smp, int n, int order, ulong* sums, int pmax)
{
int* last_errors = stackalloc int[4];
int* end = smp + n;
int* seg_end = smp + (n >> pmax);
if (order > 4)
throw new ArgumentOutOfRangeException();
for (int i = 0; i < order; i++)
{
int* next_errors = stackalloc int[4];
next_errors[0] = *(res++) = *(smp++);
for (int j = 0; j < i; j++)
next_errors[j + 1] = next_errors[j] - last_errors[j];
for (int j = 0; j <= i; j++)
last_errors[j] = next_errors[j];
}
while (smp < end)
{
*(sums++) = encode_residual_fixed_partition(res, smp, seg_end, order, last_errors);
res += seg_end - smp;
smp = seg_end;
seg_end += n >> pmax;
}
}
#if XXX
unsafe static int encode_residual_fixed_estimate_best_order(int* res, int* smp, int n, int order)
{
int next_error_0, next_error_1, next_error_2, next_error_3, next_error_4;
int last_error_0, last_error_1, last_error_2, last_error_3;
int* end = smp + n;
ulong total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0;
if (order == 0)
{
AudioSamples.MemCpy(res, smp, n);
return 0;
}
next_error_0 = *(res++) = *(smp++);
last_error_0 = next_error_0;
if (order == 1)
{
while (smp < end)
{
next_error_0 = *(smp++);
next_error_1 = next_error_0 - last_error_0;
last_error_0 = next_error_0;
total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31));
total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31));
*(res++) = (int)next_error_1;
}
if ((total_error_0 < total_error_1))
return 0;
return 1;
}
next_error_0 = *(res++) = *(smp++);
next_error_1 = next_error_0 - last_error_0;
last_error_0 = next_error_0;
last_error_1 = next_error_1;
if (order == 2)
{
while (smp < end)
{
next_error_0 = *(smp++);
next_error_1 = next_error_0 - last_error_0;
next_error_2 = next_error_1 - last_error_1;
last_error_0 = next_error_0;
last_error_1 = next_error_1;
total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31));
total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31));
total_error_2 += (ulong)((next_error_2 << 1) ^ (next_error_2 >> 31));
*(res++) = (int)next_error_2;
}
if ((total_error_0 < total_error_1) & (total_error_0 < total_error_2))
return 0;
else if ((total_error_1 < total_error_2))
return 1;
return 2;
}
next_error_0 = *(res++) = *(smp++);
next_error_1 = next_error_0 - last_error_0;
next_error_2 = next_error_1 - last_error_1;
last_error_0 = next_error_0;
last_error_1 = next_error_1;
last_error_2 = next_error_2;
if (order == 3)
{
while (smp < end)
{
next_error_0 = *(smp++);
next_error_1 = next_error_0 - last_error_0;
next_error_2 = next_error_1 - last_error_1;
next_error_3 = next_error_2 - last_error_2;
last_error_0 = next_error_0;
last_error_1 = next_error_1;
last_error_2 = next_error_2;
total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31));
total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31));
total_error_2 += (ulong)((next_error_2 << 1) ^ (next_error_2 >> 31));
total_error_3 += (ulong)((next_error_3 << 1) ^ (next_error_3 >> 31));
*(res++) = (int)next_error_3;
}
if ((total_error_0 < total_error_1) & (total_error_0 < total_error_2) & (total_error_0 < total_error_3))
return 0;
else if ((total_error_1 < total_error_2) & (total_error_1 < total_error_3))
return 1;
else if ((total_error_2 < total_error_3))
return 2;
return 3;
}
next_error_0 = *(res++) = *(smp++);
next_error_1 = next_error_0 - last_error_0;
next_error_2 = next_error_1 - last_error_1;
next_error_3 = next_error_2 - last_error_2;
last_error_0 = next_error_0;
last_error_1 = next_error_1;
last_error_2 = next_error_2;
last_error_3 = next_error_3;
if (order == 4)
{
while (smp < end)
{
next_error_0 = *(smp++);
next_error_1 = next_error_0 - last_error_0;
next_error_2 = next_error_1 - last_error_1;
next_error_3 = next_error_2 - last_error_2;
next_error_4 = next_error_3 - last_error_3;
last_error_0 = next_error_0;
last_error_1 = next_error_1;
last_error_2 = next_error_2;
last_error_3 = next_error_3;
total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31));
total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31));
total_error_2 += (ulong)((next_error_2 << 1) ^ (next_error_2 >> 31));
total_error_3 += (ulong)((next_error_3 << 1) ^ (next_error_3 >> 31));
total_error_4 += (ulong)((next_error_4 << 1) ^ (next_error_4 >> 31));
*(res++) = (int)next_error_4;
}
if ((total_error_0 < total_error_1) & (total_error_0 < total_error_2) & (total_error_0 < total_error_3) & (total_error_0 < total_error_4))
return 0;
else if ((total_error_1 < total_error_2) & (total_error_1 < total_error_3) & (total_error_1 < total_error_4))
return 1;
else if ((total_error_2 < total_error_3) & (total_error_2 < total_error_4))
return 2;
else if (total_error_3 < total_error_4)
return 3;
return 4;
}
throw new ArgumentOutOfRangeException();
}
#endif
static unsafe uint calc_optimal_rice_params(int porder, int* parm, ulong* sums, uint n, uint pred_order, ref int method)
{
uint part = (1U << porder);
uint cnt = (n >> porder) - pred_order;
int maxK = method > 0 ? 30 : FlakeConstants.MAX_RICE_PARAM;
int k = cnt > 0 ? Math.Min(maxK, BitReader.log2i(sums[0] / cnt)) : 0;
int realMaxK0 = k;
ulong all_bits = cnt * ((uint)k + 1U) + (sums[0] >> k);
parm[0] = k;
cnt = (n >> porder);
int logcnt = BitReader.log2i(cnt);
if (cnt == 1 << logcnt)
{
for (uint i = 1; i < part; i++)
{
ulong s = sums[i];
ulong u = s >> logcnt;
k = u >> maxK != 0 ? maxK : BitReader.log2i((uint)u);
realMaxK0 = Math.Max(realMaxK0, k);
all_bits += ((uint)k << logcnt) + (s >> k);
parm[i] = k;
}
}
else
{
for (uint i = 1; i < part; i++)
{
ulong s = sums[i];
ulong u = s / cnt;
k = u >> maxK != 0 ? maxK : BitReader.log2i((uint)u);
realMaxK0 = Math.Max(realMaxK0, k);
all_bits += cnt * (uint)k + (s >> k);
parm[i] = k;
}
}
all_bits += cnt * (part - 1U);
method = realMaxK0 > FlakeConstants.MAX_RICE_PARAM ? 1 : 0;
return (uint)all_bits + ((4U + (uint)method) * part);
}
static unsafe void calc_lower_sums(int pmin, int pmax, ulong* sums)
{
for (int i = pmax - 1; i >= pmin; i--)
{
for (int j = 0; j < (1 << i); j++)
{
sums[i * FlakeConstants.MAX_PARTITIONS + j] =
sums[(i + 1) * FlakeConstants.MAX_PARTITIONS + 2 * j] +
sums[(i + 1) * FlakeConstants.MAX_PARTITIONS + 2 * j + 1];
}
}
}
static unsafe uint calc_rice_params_sums(RiceContext rc, int pmin, int pmax, ulong* sums, uint n, uint pred_order, int bps)
{
int* parm = stackalloc int[(pmax + 1) * FlakeConstants.MAX_PARTITIONS];
//uint* bits = stackalloc uint[FlakeConstants.MAX_PARTITION_ORDER];
//assert(pmin >= 0 && pmin <= FlakeConstants.MAX_PARTITION_ORDER);
//assert(pmax >= 0 && pmax <= FlakeConstants.MAX_PARTITION_ORDER);
//assert(pmin <= pmax);
// sums for lower levels
calc_lower_sums(pmin, pmax, sums);
uint opt_bits = AudioSamples.UINT32_MAX;
int opt_porder = pmin;
int opt_method = 0;
for (int i = pmin; i <= pmax; i++)
{
int method = bps > 16 ? 1 : 0;
uint bits = calc_optimal_rice_params(i, parm + i * FlakeConstants.MAX_PARTITIONS, sums + i * FlakeConstants.MAX_PARTITIONS, n, pred_order, ref method);
if (bits <= opt_bits)
{
opt_bits = bits;
opt_porder = i;
opt_method = method;
}
}
rc.porder = opt_porder;
rc.coding_method = opt_method;
fixed (int* rparms = rc.rparams)
AudioSamples.MemCpy(rparms, parm + opt_porder * FlakeConstants.MAX_PARTITIONS, (1 << opt_porder));
return opt_bits;
}
static int get_max_p_order(int max_porder, int n, int order)
{
int porder = Math.Min(max_porder, BitReader.log2i(n ^ (n - 1)));
if (order > 0)
porder = Math.Min(porder, BitReader.log2i(n / order));
return porder;
}
// private static int[,] best_x = new int[14,8193];
private static int[][] good_x = new int[][] {
new int[] {}, // 0
new int[] { // 1
0x03,0x01,0x00,0x02
},
new int[] {// 2
0x01,0x07,0x06,0x02, 0x03,0x04,0x00,0x05
},
new int[] { // 3
0x0b,0x0f,0x0e,0x0d, 0x03,0x01,0x05,0x02
},
new int[] { //4
0x17,0x09,0x03,0x0a, 0x06,0x1d,0x1f,0x05, 0x1c,0x0d,0x07,0x0c,
},
new int[] { // 5
0x2b,0x3d,0x37,0x07, 0x11,0x15,0x36,0x3f,
},
new int[] { // 6
0x6b,0x15,0x7e,0x31, 0x07,0x1a,0x29,0x26, 0x5d,0x23,0x6f,0x19, 0x56,0x75
},
new int[] { // 7
0xdb,0xef,0xb5,0x47, 0xee,0x63,0x0b,0xfd, 0x31,0xbe,0xed,0x33, 0xff,0xfb,0xd6,0xbb
},
new int[] { // 8
0x1bb,0x1c7,0x069,0x087, 0x1fd,0x16e,0x095,0x1de, 0x066,0x071,0x055,0x09a,
},
new int[] { // 9
0x36b,0x3bd,0x097,0x0c3, 0x0e3,0x0b1,0x107,0x2de, 0x3ef,0x2fb,0x3d5,0x139
},
new int[] { // 10
//0x0e3,0x199,0x383,0x307, 0x1e3,0x01f,0x269,0x0f1, 0x266,0x03f,0x2cd,0x1c3, 0x19a,0x387,0x339,0x259,
0x6eb,0x187,0x77d,0x271, 0x195,0x259,0x5ae,0x169,
},
new int[] { // 11
0xddb,0xf77,0xb6d,0x587, 0x2c3,0x03b,0xef5,0x1e3, 0xdbe,
},
new int[] { // 12
0x1aeb,0x0587,0x0a71,0x1dbd, 0x0559,0x0aa5,0x0a2e,0x0d43, 0x05aa,0x00f3,0x0696,0x03c6,
},
new int[] { // 13
0x35d7,0x2f6f,0x0aa3,0x1569, 0x150f,0x3d79,0x0dc3,0x309f/*?*/,
},
new int[] { // 14
0x75d7,0x5f7b,0x6a8f,0x29a3,
},
new int[] { // 15
0xddd7,0xaaaf,0x55c3,0xf77b,
},
new int[] { // 16
0x1baeb,0x1efaf,0x1d5bf,0x1cff3,
},
new int[] { // 17
0x36dd7,0x3bb7b,0x3df6f,0x2d547,
},
new int[] { // 18
0x75dd7,0x6f77b,0x7aaaf,0x5ddd3,
},
new int[] { // 19
0xdddd7,0xf777b,0xd5547,0xb6ddb,
},
new int[] { // 20
0x1baeeb,0x1efbaf,0x1aaabf,0x17bbeb,
},
new int[] { // 21
0x376dd7,0x3ddf7b,0x2d550f,0x0aaaa3,
},
new int[] { // 22
0x6eddd7,0x77777b,0x5dcd4f,0x5d76f9,
},
new int[] { // 23
0xdeddd7,0xb5b6eb,0x55552b,0x2aaac3,
},
new int[] { // 24
0x1dddbb7,0x1b76eeb,0x17bbf5f,0x1eeaa9f,
},
new int[] { // 25
},
new int[] { // 26
},
new int[] { // 27
},
new int[] { // 28
},
new int[] { // 29
},
new int[] { // 30
},
};
unsafe void postprocess_coefs(FlacFrame frame, FlacSubframe sf, int ch)
{
if (eparams.development_mode < 0)
return;
if (sf.type != SubframeType.LPC || sf.order > 30)
return;
int orig_window = sf.window;
int orig_order = sf.order;
int orig_shift = sf.shift;
int orig_cbits = sf.cbits;
uint orig_size = sf.size;
var orig_coefs = stackalloc int[orig_order];
for (int i = 0; i < orig_order; i++) orig_coefs[i] = sf.coefs[i];
int orig_xx = -1;
int orig_seq = 0;
int maxxx = Math.Min(good_x[orig_order].Length, eparams.development_mode);
var pmax = get_max_p_order(m_settings.MaxPartitionOrder, frame.blocksize, orig_order);
var pmin = Math.Min(m_settings.MinPartitionOrder, pmax);
ulong* sums = stackalloc ulong[(pmax + 1) * FlakeConstants.MAX_PARTITIONS];
while (true)
{
var best_coefs = stackalloc int[orig_order];
int best_shift = orig_shift;
int best_cbits = orig_cbits;
uint best_size = orig_size;
int best_xx = -1;
for (int xx = -1; xx < maxxx; xx++)
{
int x = xx;
if (xx < 0)
{
if (orig_xx < 0 || maxxx < 1/*3*/)// || (orig_xx >> orig_order) != 0)
continue;
x = orig_xx;
orig_seq++;
}
else
{
orig_seq = 0;
if (orig_order < good_x.Length && good_x[orig_order] != null)
x = good_x[orig_order][xx];
}
frame.current.type = SubframeType.LPC;
frame.current.order = orig_order;
frame.current.window = orig_window;
frame.current.shift = orig_shift;
frame.current.cbits = orig_cbits;
if (((x >> orig_order) & 1) != 0)
{
frame.current.shift--;
frame.current.cbits--;
if (frame.current.shift < 0 || frame.current.cbits < 2)
continue;
}
ulong csum = 0;
int qmax = (1 << (frame.current.cbits - 1)) - 1;
for (int i = 0; i < frame.current.order; i++)
{
int shift = (x >> orig_order) & 1;
int increment = (x == 1 << orig_order) ? 0 : (((x >> i) & 1) << 1) - 1;
frame.current.coefs[i] = (orig_coefs[i] + (increment << orig_seq)) >> shift;
if (frame.current.coefs[i] < -(qmax + 1)) frame.current.coefs[i] = -(qmax + 1);
if (frame.current.coefs[i] > qmax) frame.current.coefs[i] = qmax;
csum += (ulong)Math.Abs(frame.current.coefs[i]);
}
fixed (int* coefs = frame.current.coefs)
{
if ((csum << frame.subframes[ch].obits) >= 1UL << 32)
lpc.encode_residual_long(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax);
else
lpc.encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax);
}
var cur_size = calc_rice_params_sums(frame.current.rc, pmin, pmax, sums, (uint)frame.blocksize, (uint)frame.current.order, Settings.PCM.BitsPerSample);
frame.current.size = (uint)(frame.current.order * frame.subframes[ch].obits + 4 + 5 + frame.current.order * frame.current.cbits + 6 + (int)cur_size);
if (frame.current.size < best_size)
{
//var dif = best_size - frame.current.size;
for (int i = 0; i < frame.current.order; i++) best_coefs[i] = frame.current.coefs[i];
best_shift = frame.current.shift;
best_cbits = frame.current.cbits;
best_size = frame.current.size;
best_xx = x;
frame.ChooseBestSubframe(ch);
//if (dif > orig_order * 5)
// break;
}
if (xx < 0 && best_size < orig_size)
break;
}
if (best_size < orig_size)
{
//if (best_xx >= 0) best_x[order, best_xx]++;
//if (orig_size != 0x7FFFFFFF)
// System.Console.Write(string.Format(" {0}[{1:x}]", orig_size - best_size, best_xx));
for (int i = 0; i < orig_order; i++) orig_coefs[i] = best_coefs[i];
orig_shift = best_shift;
orig_cbits = best_cbits;
orig_size = best_size;
orig_xx = best_xx;
}
else
{
break;
}
}
//if (orig_size != 0x7FFFFFFF)
// System.Console.WriteLine();
//if (frame_count % 0x400 == 0)
//{
// for (int o = 0; o < best_x.GetLength(0); o++)
// {
// //for (int x = 0; x <= (1 << o); x++)
// // if (best_x[o, x] != 0)
// // System.Console.WriteLine(string.Format("{0:x2}\t{1:x4}\t{2}", o, x, best_x[o, x]));
// var s = new List<KeyValuePair<int, int>>();
// for (int x = 0; x < (1 << o); x++)
// if (best_x[o, x] != 0)
// s.Add(new KeyValuePair<int, int>(x, best_x[o, x]));
// s.Sort((x, y) => y.Value.CompareTo(x.Value));
// foreach (var x in s)
// System.Console.WriteLine(string.Format("{0:x2}\t{1:x4}\t{2}", o, x.Key, x.Value));
// int i = 0;
// foreach (var x in s)
// {
// System.Console.Write(string.Format(o <= 8 ? "0x{0:x2}," : "0x{0:x3},", x.Key));
// if ((++i) % 16 == 0)
// System.Console.WriteLine();
// }
// System.Console.WriteLine();
// }
//}
}
public static void SetCoefs(int order, int[] coefs)
{
good_x[order] = new int[coefs.Length];
for (int i = 0; i < coefs.Length; i++)
good_x[order][i] = coefs[i];
}
unsafe void encode_residual_lpc_sub(FlacFrame frame, float* lpcs, int iWindow, int order, int ch)
{
// select LPC precision based on block size
uint lpc_precision;
if (frame.blocksize <= 192) lpc_precision = 7U;
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;
else lpc_precision = 15;
for (int i_precision = m_settings.MinPrecisionSearch; i_precision <= m_settings.MaxPrecisionSearch && lpc_precision + i_precision < 16; i_precision++)
// 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.subframes[ch].lpc_ctx[iWindow].done_lpcs[i_precision] |= (1U << (order - 1));
uint cbits = lpc_precision + (uint)i_precision;
frame.current.type = SubframeType.LPC;
frame.current.order = order;
frame.current.window = iWindow;
frame.current.cbits = (int)cbits;
int pmax = get_max_p_order(m_settings.MaxPartitionOrder, frame.blocksize, frame.current.order);
int pmin = Math.Min(m_settings.MinPartitionOrder, pmax);
ulong* sums = stackalloc ulong[(pmax + 1) * FlakeConstants.MAX_PARTITIONS];
ulong csum = 0;
fixed (int* coefs = frame.current.coefs)
{
lpc.quantize_lpc_coefs(lpcs + (frame.current.order - 1) * lpc.MAX_LPC_ORDER,
frame.current.order, cbits, coefs, out frame.current.shift, 15, 0);
if (frame.current.shift < 0 || frame.current.shift > 15)
throw new Exception("negative shift");
for (int i = frame.current.order; i > 0; i--)
csum += (ulong)Math.Abs(coefs[i - 1]);
if ((csum << frame.subframes[ch].obits) >= 1UL << 32)
lpc.encode_residual_long(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax);
else
lpc.encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax);
}
uint best_size = calc_rice_params_sums(frame.current.rc, pmin, pmax, sums, (uint)frame.blocksize, (uint)frame.current.order, Settings.PCM.BitsPerSample);
frame.current.size = (uint)(frame.current.order * frame.subframes[ch].obits + 4 + 5 + frame.current.order * (int)cbits + 6 + (int)best_size);
frame.ChooseBestSubframe(ch);
//if (frame.current.size >= frame.subframes[ch].best.size)
// postprocess_coefs(frame, frame.current, ch);
//else
//{
// frame.ChooseBestSubframe(ch);
// postprocess_coefs(frame, frame.subframes[ch].best, ch);
//}
}
}
unsafe void encode_residual_fixed_sub(FlacFrame frame, int order, int ch)
{
if ((frame.subframes[ch].done_fixed & (1U << order)) != 0)
return; // already calculated;
frame.current.order = order;
frame.current.type = SubframeType.Fixed;
#if XXX
int best_order = order;
if (frame.subframes[ch].done_fixed == 0)
{
best_order = encode_residual_fixed_estimate_best_order(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order);
if (best_order != order)
{
//frame.subframes[ch].done_fixed |= (1U << order);
order = best_order;
frame.current.order = order;
encode_residual_fixed(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order);
}
}
else
#endif
int pmax = get_max_p_order(m_settings.MaxPartitionOrder, frame.blocksize, frame.current.order);
int pmin = Math.Min(m_settings.MinPartitionOrder, pmax);
ulong* sums = stackalloc ulong[(pmax + 1) * FlakeConstants.MAX_PARTITIONS];
encode_residual_fixed(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax);
frame.current.size = (uint)(frame.current.order * frame.subframes[ch].obits) + 6
+ calc_rice_params_sums(frame.current.rc, pmin, pmax, sums, (uint)frame.blocksize, (uint)frame.current.order, Settings.PCM.BitsPerSample);
frame.subframes[ch].done_fixed |= (1U << order);
frame.ChooseBestSubframe(ch);
}
unsafe void fixed_compute_best_predictor(int* data, uint data_len, ulong* errors)//, float* residual_bits_per_sample)
{
long last_error_0 = data[-1];
long last_error_1 = data[-1] - data[-2];
long last_error_2 = last_error_1 - (data[-2] - data[-3]);
long last_error_3 = last_error_2 - (data[-2] - 2 * data[-3] + data[-4]);
ulong total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0;
#if VARIANT1
long error, save;
int* finish = data + data_len;
while (data < finish)
{
error = *(data++); total_error_0 += (ulong)((error << 1) ^ (error >> 63)); save = error;
error -= last_error_0; total_error_1 += (ulong)((error << 1) ^ (error >> 63)); last_error_0 = save; save = error;
error -= last_error_1; total_error_2 += (ulong)((error << 1) ^ (error >> 63)); last_error_1 = save; save = error;
error -= last_error_2; total_error_3 += (ulong)((error << 1) ^ (error >> 63)); last_error_2 = save; save = error;
error -= last_error_3; total_error_4 += (ulong)((error << 1) ^ (error >> 63)); last_error_3 = save;
}
#else
int* finish = data + data_len;
while (data < finish)
{
long next_error_0 = *(data++);
long next_error_1 = next_error_0 - last_error_0;
long next_error_2 = next_error_1 - last_error_1;
long next_error_3 = next_error_2 - last_error_2;
long next_error_4 = next_error_3 - last_error_3;
last_error_0 = next_error_0;
last_error_1 = next_error_1;
last_error_2 = next_error_2;
last_error_3 = next_error_3;
total_error_0 += (ulong)((last_error_0 << 1) ^ (last_error_0 >> 63));
total_error_1 += (ulong)((last_error_1 << 1) ^ (last_error_1 >> 63));
total_error_2 += (ulong)((last_error_2 << 1) ^ (last_error_2 >> 63));
total_error_3 += (ulong)((last_error_3 << 1) ^ (last_error_3 >> 63));
total_error_4 += (ulong)((next_error_4 << 1) ^ (next_error_4 >> 63));
}
#endif
errors[0] = total_error_0;
errors[1] = total_error_1;
errors[2] = total_error_2;
errors[3] = total_error_3;
errors[4] = total_error_4;
//residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0);
//residual_bits_per_sample[1] = (float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0);
//residual_bits_per_sample[2] = (float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0);
//residual_bits_per_sample[3] = (float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0);
//residual_bits_per_sample[4] = (float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0);
}
unsafe int fixed_compute_best_predictor_order(ulong* error)
{
int order;
if ((error[0] < error[1]) & (error[0] < error[2]) & (error[0] < error[3]) & (error[0] < error[4]))
order = 0;
else if ((error[1] < error[2]) & (error[1] < error[3]) & (error[1] < error[4]))
order = 1;
else if ((error[2] < error[3]) & (error[2] < error[4]))
order = 2;
else if (error[3] < error[4])
order = 3;
else
order = 4;
return order;
}
unsafe void encode_residual(FlacFrame frame, int ch, PredictionType predict, OrderMethod omethod, int pass, int windows_mask)
{
int* smp = frame.subframes[ch].samples;
int i, n = frame.blocksize;
// save best.window, because we can overwrite it later with fixed frame
// CONSTANT
for (i = 1; i < n; i++)
{
if (smp[i] != smp[0]) break;
}
if (i == n)
{
frame.subframes[ch].best.type = SubframeType.Constant;
frame.subframes[ch].best.residual[0] = smp[0];
frame.subframes[ch].best.size = (uint)frame.subframes[ch].obits;
return;
}
// VERBATIM
frame.current.type = SubframeType.Verbatim;
frame.current.size = (uint)(frame.subframes[ch].obits * frame.blocksize);
frame.ChooseBestSubframe(ch);
if (n < 5 || predict == PredictionType.None)
return;
// LPC
if (n > m_settings.MaxLPCOrder &&
(predict == PredictionType.Levinson ||
predict == PredictionType.Search)
//predict == PredictionType.Search ||
//(pass == 2 && frame.subframes[ch].best.type == SubframeType.LPC))
)
{
float* lpcs = stackalloc float[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER];
int min_order = m_settings.MinLPCOrder;
int max_order = m_settings.MaxLPCOrder;
for (int iWindow = 0; iWindow < _windowcount; iWindow++)
{
if ((windows_mask & (1 << iWindow)) == 0)
continue;
LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow];
fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, iWindow, 0])
lpc_ctx.GetReflection(
frame.subframes[ch].sf, max_order, frame.blocksize,
smp, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2, sections);
lpc_ctx.ComputeLPC(lpcs);
//int frameSize = n;
//float* F = stackalloc float[frameSize];
//float* B = stackalloc float[frameSize];
//float* PE = stackalloc float[max_order + 1];
//float* arp = stackalloc float[max_order];
//float* rc = stackalloc float[max_order];
//for (int j = 0; j < frameSize; j++)
// F[j] = B[j] = smp[j];
//for (int K = 1; K <= max_order; K++)
//{
// // BURG:
// float denominator = 0.0f;
// //float denominator = F[K - 1] * F[K - 1] + B[frameSize - K] * B[frameSize - K];
// for (int j = 0; j < frameSize - K; j++)
// denominator += F[j + K] * F[j + K] + B[j] * B[j];
// denominator /= 2;
// // Estimate error
// PE[K - 1] = denominator / (frameSize - K);
// float reflectionCoeff = 0.0f;
// for (int j = 0; j < frameSize - K; j++)
// reflectionCoeff += F[j + K] * B[j];
// reflectionCoeff /= denominator;
// rc[K - 1] = arp[K - 1] = reflectionCoeff;
// // Levinson-Durbin
// for (int j = 0; j < (K - 1) >> 1; j++)
// {
// float arptmp = arp[j];
// arp[j] -= reflectionCoeff * arp[K - 2 - j];
// arp[K - 2 - j] -= reflectionCoeff * arptmp;
// }
// if (((K - 1) & 1) != 0)
// arp[(K - 1) >> 1] -= reflectionCoeff * arp[(K - 1) >> 1];
// for (int j = 0; j < frameSize - K; j++)
// {
// float f = F[j + K];
// float b = B[j];
// F[j + K] = f - reflectionCoeff * b;
// B[j] = b - reflectionCoeff * f;
// }
// for (int j = 0; j < K; j++)
// lpcs[(K - 1) * lpc.MAX_LPC_ORDER + j] = (float)arp[j];
//}
switch (omethod)
{
case OrderMethod.Akaike:
//lpc_ctx.SortOrdersAkaike(frame.blocksize, m_settings.EstimationDepth, max_order, 7.1, 0.0);
lpc_ctx.SortOrdersAkaike(frame.blocksize, m_settings.EstimationDepth, min_order, max_order, 4.5, 0);
break;
default:
throw new Exception("unknown order method");
}
for (i = 0; i < m_settings.EstimationDepth && i < max_order; i++)
encode_residual_lpc_sub(frame, lpcs, iWindow, lpc_ctx.best_orders[i], ch);
}
postprocess_coefs(frame, frame.subframes[ch].best, ch);
}
// FIXED
if (predict == PredictionType.Fixed ||
(predict == PredictionType.Search && pass != 1) ||
//predict == PredictionType.Search ||
//(pass == 2 && frame.subframes[ch].best.type == SubframeType.Fixed) ||
(n > m_settings.MaxFixedOrder && n <= m_settings.MaxLPCOrder))
{
int max_fixed_order = Math.Min(m_settings.MaxFixedOrder, 4);
int min_fixed_order = Math.Min(m_settings.MinFixedOrder, max_fixed_order);
if (min_fixed_order == 0 && max_fixed_order == 4)
{
fixed (ulong* fixed_errors = frame.subframes[ch].best_fixed)
{
if ((frame.subframes[ch].done_fixed & (1U << 5)) == 0)
{
fixed_compute_best_predictor(smp + 4, (uint)n - 4, fixed_errors);
frame.subframes[ch].done_fixed |= (1U << 5);
}
i = fixed_compute_best_predictor_order(fixed_errors);
encode_residual_fixed_sub(frame, i, ch);
}
}
else
{
for (i = max_fixed_order; i >= min_fixed_order; i--)
encode_residual_fixed_sub(frame, i, ch);
}
}
}
unsafe void output_frame_header(FlacFrame frame, BitWriter bitwriter)
{
bitwriter.writebits(15, 0x7FFC);
bitwriter.writebits(1, eparams.variable_block_size > 0 ? 1 : 0);
bitwriter.writebits(4, frame.bs_code0);
bitwriter.writebits(4, sr_code0);
if (frame.ch_mode == ChannelMode.NotStereo)
bitwriter.writebits(4, ch_code);
else
bitwriter.writebits(4, (int) frame.ch_mode);
bitwriter.writebits(3, bps_code);
bitwriter.writebits(1, 0);
bitwriter.write_utf8(frame_count);
// custom block size
if (frame.bs_code1 >= 0)
{
if (frame.bs_code1 < 256)
bitwriter.writebits(8, frame.bs_code1);
else
bitwriter.writebits(16, frame.bs_code1);
}
// custom sample rate
if (sr_code1 > 0)
{
if (sr_code1 < 256)
bitwriter.writebits(8, sr_code1);
else
bitwriter.writebits(16, sr_code1);
}
// CRC-8 of frame header
bitwriter.flush();
byte crc = crc8.ComputeChecksum(frame_buffer, 0, bitwriter.Length);
bitwriter.writebits(8, crc);
}
unsafe void output_residual(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub)
{
// rice-encoded block
bitwriter.writebits(2, sub.best.rc.coding_method);
// partition order
int porder = sub.best.rc.porder;
int psize = frame.blocksize >> porder;
//assert(porder >= 0);
bitwriter.writebits(4, porder);
int res_cnt = psize - sub.best.order;
int rice_len = 4 + sub.best.rc.coding_method;
// residual
int j = sub.best.order;
fixed (byte* fixbuf = &frame_buffer[0])
for (int p = 0; p < (1 << porder); p++)
{
int k = sub.best.rc.rparams[p];
bitwriter.writebits(rice_len, k);
if (p == 1) res_cnt = psize;
int cnt = Math.Min(res_cnt, frame.blocksize - j);
bitwriter.write_rice_block_signed(fixbuf, k, sub.best.residual + j, cnt);
j += cnt;
}
}
unsafe void
output_subframe_constant(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub)
{
bitwriter.writebits_signed(sub.obits, sub.best.residual[0]);
}
unsafe void
output_subframe_verbatim(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub)
{
int n = frame.blocksize;
for (int i = 0; i < n; 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.
}
unsafe void
output_subframe_fixed(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub)
{
// warm-up samples
for (int i = 0; i < sub.best.order; i++)
bitwriter.writebits_signed(sub.obits, sub.best.residual[i]);
// residual
output_residual(frame, bitwriter, sub);
}
unsafe void
output_subframe_lpc(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub)
{
// warm-up samples
for (int i = 0; i < sub.best.order; i++)
bitwriter.writebits_signed(sub.obits, sub.best.residual[i]);
// LPC coefficients
int cbits = 1;
for (int i = 0; i < sub.best.order; i++)
while (cbits < 16 && sub.best.coefs[i] != (sub.best.coefs[i] << (32 - cbits)) >> (32 - cbits))
cbits++;
bitwriter.writebits(4, cbits - 1);
bitwriter.writebits_signed(5, sub.best.shift);
for (int i = 0; i < sub.best.order; i++)
bitwriter.writebits_signed(cbits, sub.best.coefs[i]);
// residual
output_residual(frame, bitwriter, sub);
}
unsafe void output_subframes(FlacFrame frame, BitWriter bitwriter)
{
for (int ch = 0; ch < channels; ch++)
{
FlacSubframeInfo sub = frame.subframes[ch];
// subframe header
int type_code = (int) sub.best.type;
if (sub.best.type == SubframeType.Fixed)
type_code |= sub.best.order;
if (sub.best.type == SubframeType.LPC)
type_code |= sub.best.order - 1;
bitwriter.writebits(1, 0);
bitwriter.writebits(6, type_code);
bitwriter.writebits(1, sub.wbits != 0 ? 1 : 0);
if (sub.wbits > 0)
bitwriter.writebits((int)sub.wbits, 1);
// subframe
switch (sub.best.type)
{
case SubframeType.Constant:
output_subframe_constant(frame, bitwriter, sub);
break;
case SubframeType.Verbatim:
output_subframe_verbatim(frame, bitwriter, sub);
break;
case SubframeType.Fixed:
output_subframe_fixed(frame, bitwriter, sub);
break;
case SubframeType.LPC:
output_subframe_lpc(frame, bitwriter, sub);
break;
}
}
}
void output_frame_footer(BitWriter bitwriter)
{
bitwriter.flush();
ushort crc = bitwriter.get_crc16();
bitwriter.writebits(16, crc);
bitwriter.flush();
}
unsafe void encode_residual_pass1(FlacFrame frame, int ch, int windows_mask)
{
int max_prediction_order = m_settings.MaxLPCOrder;
//int max_fixed_order = m_settings.MaxFixedOrder;
//int min_fixed_order = m_settings.MinFixedOrder;
int lpc_min_precision_search = m_settings.MinPrecisionSearch;
int lpc_max_precision_search = m_settings.MaxPrecisionSearch;
int max_partition_order = m_settings.MaxPartitionOrder;
int estimation_depth = m_settings.EstimationDepth;
var development_mode = eparams.development_mode;
//m_settings.MinFixedOrder = 2;
//m_settings.MaxFixedOrder = 2;
m_settings.MinPrecisionSearch = m_settings.MaxPrecisionSearch;
m_settings.MaxLPCOrder = Math.Min(m_settings.MaxLPCOrder, Math.Max(m_settings.MinLPCOrder, 8));
m_settings.EstimationDepth = 1;
eparams.development_mode = Math.Min(eparams.development_mode, -1);
encode_residual(frame, ch, m_settings.PredictionType, OrderMethod.Akaike, 1, windows_mask);
//m_settings.MinFixedOrder = min_fixed_order;
//m_settings.MaxFixedOrder = max_fixed_order;
m_settings.MaxLPCOrder = max_prediction_order;
m_settings.MinPrecisionSearch = lpc_min_precision_search;
m_settings.MaxPrecisionSearch = lpc_max_precision_search;
m_settings.MaxPartitionOrder = max_partition_order;
m_settings.EstimationDepth = estimation_depth;
eparams.development_mode = development_mode;
}
unsafe void encode_residual_pass2(FlacFrame frame, int ch)
{
encode_residual(frame, ch, m_settings.PredictionType, eparams.order_method, 2, estimate_best_windows(frame, ch));
}
unsafe int estimate_best_windows_akaike(FlacFrame frame, int ch, int count, bool onePerType)
{
int* windows_present = stackalloc int[_windowcount];
for (int i = 0; i < _windowcount; i++)
windows_present[i] = 0;
if (onePerType)
{
for (int i = 0; i < _windowcount; i++)
for (int j = 0; j < _windowcount; j++)
if (windowType[j] == windowType[i])
windows_present[j]++;
}
float* err = stackalloc float[lpc.MAX_LPC_ORDER];
for (int i = 0; i < _windowcount; i++)
{
LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[i];
if (onePerType && windows_present[i] <= count)
{
err[i] = 0;
continue;
}
int estimate_order = 4;
fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, i, 0])
lpc_ctx.GetReflection(
frame.subframes[ch].sf, estimate_order, frame.blocksize,
frame.subframes[ch].samples, frame.window_buffer + i * FlakeConstants.MAX_BLOCKSIZE * 2, sections);
lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, estimate_order, 4.5, 0.0);
//err[i] = (float)(lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0));
//err[i] = (float)((frame.blocksize * lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1] / windowScale[i]) + lpc_ctx.best_orders[0] * 4.5);
//err[i] = (float)((frame.blocksize * lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1] / windowScale[i]) + lpc_ctx.best_orders[0] * frame.subframes[ch].obits);
// realistic
//err[i] = (float)(frame.blocksize * Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1]) / Math.Log(2) / 2.5
//- windowScale[i] / 2 + lpc_ctx.best_orders[0] * frame.subframes[ch].obits / 2);
//err[i] = (float)(frame.blocksize * Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1]) / Math.Log(2) / 2.5
//- frame.blocksize * Math.Log(lpc_ctx.autocorr_values[0]) / 2.1
//+ Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5 / 2.5 / Math.Log(2));
// Akaike
//err[i] = (float)(frame.blocksize * (Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1])) + Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5);
//err[i] = (float)(lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0) - frame.blocksize * (frame.subframes[ch].obits + Math.Log(windowScale[i] / frame.blocksize) / 2));
// tested good
err[i] = (float)(lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0) - frame.blocksize * Math.Log(lpc_ctx.autocorr_values[0]) / 2);
}
int* best_windows = stackalloc int[lpc.MAX_LPC_ORDER];
for (int i = 0; i < _windowcount; i++)
best_windows[i] = i;
for (int i = 0; i < _windowcount; i++)
{
for (int j = i + 1; j < _windowcount; j++)
{
if (err[best_windows[i]] > err[best_windows[j]])
{
int tmp = best_windows[j];
best_windows[j] = best_windows[i];
best_windows[i] = tmp;
}
}
}
int window_mask = 0;
if (onePerType)
{
for (int i = 0; i < _windowcount; i++)
windows_present[i] = count;
for (int i = 0; i < _windowcount; i++)
{
int w = best_windows[i];
if (windows_present[w] > 0)
{
for (int j = 0; j < _windowcount; j++)
if (windowType[j] == windowType[w])
windows_present[j]--;
window_mask |= 1 << w;
}
}
}
else
{
for (int i = 0; i < _windowcount && i < count; i++)
window_mask |= 1 << best_windows[i];
}
return window_mask;
}
unsafe int estimate_best_windows(FlacFrame frame, int ch)
{
if (_windowcount == 1 || m_settings.PredictionType == PredictionType.Fixed)
return 1;
switch (m_settings.WindowMethod)
{
case WindowMethod.Estimate:
return estimate_best_windows_akaike(frame, ch, 1, false);
case WindowMethod.Estimate2:
return estimate_best_windows_akaike(frame, ch, 2, false);
case WindowMethod.Estimate3:
return estimate_best_windows_akaike(frame, ch, 3, false);
case WindowMethod.EstimateN:
return estimate_best_windows_akaike(frame, ch, 1, true);
case WindowMethod.Evaluate2:
encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 2, false));
return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0;
case WindowMethod.Evaluate3:
encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 3, false));
return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0;
case WindowMethod.EvaluateN:
encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 1, true));
#if XXX
if (frame.subframes[ch].best.type == SubframeType.LPC && frame.subframes[ch].best.order <= 4)
{
LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[frame.subframes[ch].best.window];
double err = lpc_ctx.prediction_error[frame.subframes[ch].best.order - 1] / lpc_ctx.autocorr_values[0];
double est = frame.blocksize * (frame.subframes[ch].obits * (1 - err));
double est1 = frame.blocksize * (frame.subframes[ch].obits * (err));
if (est < 0 || est1 < 0) return -1;
}
#endif
return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0;
case WindowMethod.Evaluate2N:
encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 2, true));
return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0;
case WindowMethod.Evaluate3N:
encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 3, true));
return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0;
case WindowMethod.Evaluate:
encode_residual_pass1(frame, ch, -1);
return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0;
case WindowMethod.Search:
return -1;
}
return -1;
}
unsafe void estimate_frame(FlacFrame frame, bool do_midside)
{
int subframes = do_midside ? channels * 2 : channels;
switch (m_settings.StereoMethod)
{
case StereoMethod.Estimate:
for (int ch = 0; ch < subframes; ch++)
{
LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[0];
int estimate_order = 4;
int iWindow = 0;
fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, iWindow, 0])
lpc_ctx.GetReflection(
frame.subframes[ch].sf, estimate_order, frame.blocksize,
frame.subframes[ch].samples, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2, sections);
lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, estimate_order, 4.5, 0.0);
frame.subframes[ch].best.size
= (uint)Math.Max(0, frame.blocksize * (Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1])) + Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5
//= (uint)Math.Max(0, lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0)
//* 2.0 / Math.Log(windowScale[0] / frame.blocksize)
+ 7.1 * frame.subframes[ch].obits * m_settings.MaxLPCOrder);
}
break;
case StereoMethod.EstimateFixed:
for (int ch = 0; ch < subframes; ch++)
{
fixed (ulong* fixed_errors = frame.subframes[ch].best_fixed)
{
if ((frame.subframes[ch].done_fixed & (1U << 5)) == 0)
{
fixed_compute_best_predictor(frame.subframes[ch].samples + 4, (uint)frame.blocksize - 4, fixed_errors);
frame.subframes[ch].done_fixed |= (1U << 5);
}
int best_order = fixed_compute_best_predictor_order(fixed_errors);
//residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0);
frame.subframes[ch].best.size = (uint)fixed_errors[best_order];
}
}
break;
case StereoMethod.EstimateX:
for (int ch = 0; ch < subframes; ch++)
{
for (int iWindow = 0; iWindow < _windowcount; iWindow++)
{
LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow];
int estimate_order = 4;
fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, iWindow, 0])
lpc_ctx.GetReflection(
frame.subframes[ch].sf, estimate_order, frame.blocksize,
frame.subframes[ch].samples, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2, sections);
lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, estimate_order, 4.5, 0.0);
uint estimate
= (uint)Math.Max(0, frame.blocksize * (Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1])) + Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5
//= (uint)Math.Max(0, lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0)
//* 2.0 / Math.Log(windowScale[0] / frame.blocksize)
+ 7.1 * frame.subframes[ch].obits * m_settings.MaxLPCOrder);
if (iWindow == 0 || frame.subframes[ch].best.size > estimate)
frame.subframes[ch].best.size = estimate;
}
}
break;
case StereoMethod.Evaluate:
for (int ch = 0; ch < subframes; ch++)
encode_residual_pass1(frame, ch, 1);
break;
case StereoMethod.EvaluateX:
for (int ch = 0; ch < subframes; ch++)
encode_residual_pass1(frame, ch,
estimate_best_windows_akaike(frame, ch, 1, false));
break;
case StereoMethod.Search:
for (int ch = 0; ch < subframes; ch++)
encode_residual_pass2(frame, ch);
break;
}
}
unsafe uint measure_frame_size(FlacFrame frame, bool do_midside)
{
// crude estimation of header/footer size
uint total = (uint)(32 + ((BitReader.log2i(frame_count) + 4) / 5) * 8 + (eparams.variable_block_size != 0 ? 16 : 0) + 16);
if (do_midside)
{
uint bitsBest = AudioSamples.UINT32_MAX;
ChannelMode modeBest = ChannelMode.LeftRight;
if (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;
}
if (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;
}
if (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;
}
if (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;
}
frame.ch_mode = modeBest;
return total + bitsBest;
}
for (int ch = 0; ch < channels; ch++)
total += frame.subframes[ch].best.size;
return total;
}
unsafe void encode_estimated_frame(FlacFrame frame)
{
switch (m_settings.StereoMethod)
{
case StereoMethod.Estimate:
case StereoMethod.EstimateX:
case StereoMethod.EstimateFixed:
for (int ch = 0; ch < channels; ch++)
{
frame.subframes[ch].best.size = AudioSamples.UINT32_MAX;
encode_residual_pass2(frame, ch);
}
break;
case StereoMethod.Evaluate:
case StereoMethod.EvaluateX:
for (int ch = 0; ch < channels; ch++)
encode_residual_pass2(frame, ch);
break;
case StereoMethod.Search:
break;
}
}
unsafe delegate void window_function(float* window, int size);
unsafe void calculate_window(float* window, window_function func, WindowFunction flag)
{
if ((m_settings.WindowFunctions & flag) == 0 || _windowcount == lpc.MAX_LPC_WINDOWS)
return;
int sz = _windowsize;
float* pos1 = window + _windowcount * FlakeConstants.MAX_BLOCKSIZE * 2;
float* pos = pos1;
int nSeg = 0;
do
{
windowSections[nSeg, _windowcount, 0].setData(0, sz);
for (int j = 1; j < lpc.MAX_LPC_SECTIONS; j++)
windowSections[nSeg, _windowcount, j].setZero(sz, sz);
fixed (LpcWindowSection* sections = &windowSections[nSeg, _windowcount, 0])
func(pos, sz);
if ((sz & 1) != 0)
break;
nSeg++;
pos += sz;
sz >>= 1;
} while (sz >= 32);
double scale = 0.0;
for (int i = 0; i < _windowsize; i++)
scale += pos1[i] * pos1[i];
windowScale[_windowcount] = scale;
windowType[_windowcount] = flag;
_windowcount++;
}
class PunchoutTukeyVariant
{
public PunchoutTukeyVariant(
WindowFunction _type,
int _parts, double _overlap, double _p)
{
parts = _parts;
type = _type;
overlap = _overlap;
p = _p;
}
public WindowFunction type;
public int parts;
public double overlap;
public double p;
};
unsafe int encode_frame(out int size)
{
fixed (int* s = samplesBuffer, r = residualBuffer)
fixed (float* window = windowBuffer)
{
frame.InitSize(m_blockSize, eparams.variable_block_size != 0);
if (frame.blocksize != _windowsize && frame.blocksize > 4 && m_settings.PredictionType != PredictionType.Fixed)
{
_windowsize = frame.blocksize;
_windowcount = 0;
calculate_window(window, lpc.window_welch, WindowFunction.Welch);
calculate_window(window, lpc.window_flattop, WindowFunction.Flattop);
calculate_window(window, lpc.window_hann, WindowFunction.Hann);
calculate_window(window, lpc.window_bartlett, WindowFunction.Bartlett);
var tukeys = new PunchoutTukeyVariant[]
{
new PunchoutTukeyVariant(WindowFunction.Tukey4, 4, 0, 0.03),
new PunchoutTukeyVariant(WindowFunction.Tukey4A, 4, 0, 0.03),
new PunchoutTukeyVariant(WindowFunction.Tukey4B, 4, 0, 0.03),
new PunchoutTukeyVariant(WindowFunction.Tukey4X, 4, m_settings.TukeyOverlap, m_settings.TukeyP),
new PunchoutTukeyVariant(WindowFunction.Tukey3, 3, 1.0/3, 0.03),
new PunchoutTukeyVariant(WindowFunction.Tukey3A, 3, 1.0/3, 0.03),
new PunchoutTukeyVariant(WindowFunction.Tukey3B, 3, 1.0/3, 0.03),
new PunchoutTukeyVariant(WindowFunction.Tukey3X, 3, m_settings.TukeyOverlap, m_settings.TukeyP),
new PunchoutTukeyVariant(WindowFunction.Tukey2, 2, 0.25, 0.03),
new PunchoutTukeyVariant(WindowFunction.Tukey2A, 2, 0.25, 0.03),
new PunchoutTukeyVariant(WindowFunction.Tukey2B, 2, 0.25, 0.03),
new PunchoutTukeyVariant(WindowFunction.Tukey2X, 2, m_settings.TukeyOverlap, m_settings.TukeyP),
new PunchoutTukeyVariant(WindowFunction.Tukey, 1, 0.0, 0.03),
new PunchoutTukeyVariant(WindowFunction.Tukey1A, 1, 0.0, 0.03),
new PunchoutTukeyVariant(WindowFunction.Tukey1B, 1, 0.0, 0.03),
new PunchoutTukeyVariant(WindowFunction.Tukey1X, 1, m_settings.TukeyOverlap, m_settings.TukeyP),
};
foreach (var tukey in tukeys)
{
if (tukey.parts == 0 || (m_settings.WindowFunctions & tukey.type) == 0) continue;
if (tukey.parts == 1)
{
calculate_window(window, (w, wsz) =>
{
lpc.window_tukey(w, wsz, tukey.p);
}, tukey.type);
continue;
}
double overlap = tukey.overlap;
double overlap_units = overlap / (1.0 - overlap);
for (int m = 0; m < tukey.parts; m++)
calculate_window(window, (w, wsz) =>
{
lpc.window_punchout_tukey(w, wsz, tukey.p, tukey.p,
m / (tukey.parts + overlap_units),
(m + 1 + overlap_units) / (tukey.parts + overlap_units));
}, tukey.type);
}
if (_windowcount == 0)
throw new Exception("invalid windowfunction");
int nSeg = 0;
int sz = _windowsize;
float* window_segment = window;
do
{
fixed (LpcWindowSection* sections = &windowSections[nSeg, 0, 0])
LpcWindowSection.Detect(_windowcount, window_segment, FlakeConstants.MAX_BLOCKSIZE * 2, sz, Settings.PCM.BitsPerSample, sections);
if ((sz & 1) != 0)
break;
window_segment += sz;
nSeg++;
sz >>= 1;
} while (sz >= 32);
#if NONONO
using (TextWriter tx = File.CreateText(@"H:\ubuntu\flac\w.txt"))
{
#if !NONONO
int totaltotal = 0;
for (int i = 0; i < _windowcount; i++)
{
int total = 0;
for (int sec = 0; sec < lpc.MAX_LPC_SECTIONS; sec++)
if (windowSections[0, i, sec].m_type != LpcWindowSection.SectionType.Zero || windowSections[0, i, sec].m_start != windowSections[0, i, sec].m_end)
{
tx.WriteLine("{0}\t{1}\t{2}\t{3}", windowSections[0, i, sec].m_start, windowSections[0, i, sec].m_end, windowSections[0, i, sec].m_type, windowSections[0, i, sec].m_id);
if (windowSections[0, i, sec].m_type != LpcWindowSection.SectionType.One)
total += windowSections[0, i, sec].m_end - windowSections[0, i, sec].m_start;
}
totaltotal += total;
tx.WriteLine("{0} total window data", total);
}
tx.WriteLine("{0} grand total window data", totaltotal);
#endif
for (int x = 0; x < frame.blocksize; x++)
{
tx.Write("{0}", x);
for (int i = 0; i < _windowcount; i++)
tx.Write("\t{0}", window[i * FlakeConstants.MAX_BLOCKSIZE * 2 + x]);
tx.WriteLine();
}
}
#endif
}
if (channels != 2 || frame.blocksize <= 32 || m_settings.StereoMethod == StereoMethod.Independent)
{
frame.window_buffer = window;
frame.nSeg = 0;
frame.current.residual = r + channels * FlakeConstants.MAX_BLOCKSIZE;
frame.ch_mode = channels != 2 ? ChannelMode.NotStereo : ChannelMode.LeftRight;
for (int ch = 0; ch < channels; ch++)
frame.subframes[ch].Init(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE,
Settings.PCM.BitsPerSample, get_wasted_bits(s + ch * FlakeConstants.MAX_BLOCKSIZE, frame.blocksize));
for (int ch = 0; ch < channels; ch++)
encode_residual_pass2(frame, ch);
}
else
{
//channel_decorrelation(s, s + FlakeConstants.MAX_BLOCKSIZE, s + 2 * FlakeConstants.MAX_BLOCKSIZE, s + 3 * FlakeConstants.MAX_BLOCKSIZE, frame.blocksize);
frame.window_buffer = window;
frame.nSeg = 0;
frame.current.residual = r + 4 * FlakeConstants.MAX_BLOCKSIZE;
for (int ch = 0; ch < 4; ch++)
frame.subframes[ch].Init(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE,
Settings.PCM.BitsPerSample + (ch == 3 ? 1 : 0), get_wasted_bits(s + ch * FlakeConstants.MAX_BLOCKSIZE, frame.blocksize));
//for (int ch = 0; ch < 4; ch++)
// for (int iWindow = 0; iWindow < _windowcount; iWindow++)
// frame.subframes[ch].lpc_ctx[iWindow].GetReflection(32, frame.subframes[ch].samples, frame.blocksize, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2);
estimate_frame(frame, true);
uint fs = measure_frame_size(frame, true);
if (0 != eparams.variable_block_size)
{
FlacFrame frame2 = new FlacFrame(channels * 2);
FlacFrame frame3 = new FlacFrame(channels * 2);
int tumbler = 1;
while ((frame.blocksize & 1) == 0 && frame.blocksize >= 1024)
{
frame2.InitSize(frame.blocksize / 2, true);
frame2.window_buffer = frame.window_buffer + frame.blocksize;
frame2.nSeg = frame.nSeg + 1;
frame2.current.residual = r + tumbler * 5 * FlakeConstants.MAX_BLOCKSIZE;
for (int ch = 0; ch < 4; ch++)
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);
estimate_frame(frame2, true);
//measure_frame_size(frame2, true);
//frame2.ChooseSubframes();
//encode_estimated_frame(frame2);
//uint fs2 = measure_frame_size(frame2, false);
uint fs2 = measure_frame_size(frame2, true);
uint fs3 = fs2;
if (eparams.variable_block_size == 2 || eparams.variable_block_size == 4)
{
frame3.InitSize(frame2.blocksize, true);
frame3.window_buffer = frame2.window_buffer;
frame3.nSeg = frame2.nSeg;
frame3.current.residual = frame2.current.residual + 5 * frame2.blocksize;
for (int ch = 0; ch < 4; ch++)
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);
estimate_frame(frame3, true);
fs3 = measure_frame_size(frame3, true);
}
if (fs2 + fs3 > fs)
break;
FlacFrame tmp = frame;
frame = frame2;
frame2 = tmp;
fs = fs2;
if (eparams.variable_block_size <= 2)
break;
tumbler = 1 - tumbler;
}
}
frame.ChooseSubframes();
encode_estimated_frame(frame);
}
BitWriter bitwriter = new BitWriter(frame_buffer, 0, max_frame_size);
output_frame_header(frame, bitwriter);
output_subframes(frame, bitwriter);
output_frame_footer(bitwriter);
if (bitwriter.Length >= max_frame_size)
throw new Exception("buffer overflow");
if (frame_buffer != null)
{
if (eparams.variable_block_size > 0)
frame_count += frame.blocksize;
else
frame_count++;
}
size = frame.blocksize;
return bitwriter.Length;
}
}
unsafe int output_frame()
{
if (verify != null)
{
fixed (int* s = verifyBuffer, r = samplesBuffer)
for (int ch = 0; ch < channels; ch++)
AudioSamples.MemCpy(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE, m_blockSize);
}
int fs, bs;
//if (0 != eparams.variable_block_size && 0 == (m_blockSize & 7) && m_blockSize >= 128)
// fs = encode_frame_vbs();
//else
fs = encode_frame(out bs);
if (seek_table != null && _IO.CanSeek)
{
for (int sp = 0; sp < seek_table.Length; sp++)
{
if (seek_table[sp].framesize != 0)
continue;
if (seek_table[sp].number > _position + bs)
break;
if (seek_table[sp].number >= _position)
{
seek_table[sp].number = _position;
seek_table[sp].offset = _IO.Position - first_frame_offset;
seek_table[sp].framesize = bs;
}
}
}
_position += bs;
_IO.Write(frame_buffer, 0, fs);
_totalSize += fs;
if (verify != null)
try
{
int decoded = verify.DecodeFrame(frame_buffer, 0, fs);
if (decoded != fs || verify.Remaining != bs)
throw new Exception(Properties.Resources.ExceptionValidationFailed);
fixed (int* s = verifyBuffer, r = verify.Samples)
{
for (int ch = 0; ch < channels; ch++)
if (AudioSamples.MemCmp(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE, bs))
throw new Exception(Properties.Resources.ExceptionValidationFailed);
}
}
catch (Exception ex)
{
//if (channels == 2)
//{
// var sw = new WAVWriter(string.Format("verify_{0}.wav", this.frame_count), new WAVWriterSettings(this.Settings.PCM));
// sw.FinalSampleCount = this.frame.blocksize;
// var ab = new AudioBuffer(this.Settings.PCM, this.frame.blocksize);
// ab.Prepare(this.frame.blocksize);
// fixed (int* abs = ab.Samples, s = verifyBuffer)
// AudioSamples.Interlace(abs, s, s + FlakeConstants.MAX_BLOCKSIZE, this.frame.blocksize);
// sw.Write(ab);
// sw.Close();
//} else
throw ex;
}
if (bs < m_blockSize)
{
for (int ch = 0; ch < (channels == 2 ? 4 : channels); ch++)
Buffer.BlockCopy(samplesBuffer, (bs + ch * FlakeConstants.MAX_BLOCKSIZE) * sizeof(int), samplesBuffer, ch * FlakeConstants.MAX_BLOCKSIZE * sizeof(int), (m_blockSize - bs) * sizeof(int));
//fixed (int* s = samplesBuffer)
// for (int ch = 0; ch < channels; ch++)
// AudioSamples.MemCpy(s + ch * FlakeConstants.MAX_BLOCKSIZE, s + bs + ch * FlakeConstants.MAX_BLOCKSIZE, m_blockSize - bs);
}
samplesInBuffer -= bs;
return bs;
}
public void Write(AudioBuffer buff)
{
if (!inited)
{
if (_IO == null)
_IO = new FileStream(_path, FileMode.Create, FileAccess.Write, FileShare.Read, 0x20000);
inited = true;
int header_size = flake_encode_init();
_IO.Write(header, 0, header_size);
if (_IO.CanSeek)
first_frame_offset = _IO.Position;
}
buff.Prepare(this);
int pos = 0;
while (pos < buff.Length)
{
int block = Math.Min(buff.Length - pos, m_blockSize - samplesInBuffer);
copy_samples(buff.Samples, pos, block);
pos += block;
while (samplesInBuffer >= m_blockSize)
output_frame();
}
if (md5 != null)
md5.TransformBlock(buff.Bytes, 0, buff.ByteLength, null, 0);
}
public string Path { get { return _path; } }
public static string Vendor
{
get
{
var version = typeof(AudioEncoder).Assembly.GetName().Version;
return vendor_string ?? "CUETools " + version.Major + "." + version.Minor + "." + version.Build;
}
set
{
vendor_string = value;
}
}
static string vendor_string = null;
int select_blocksize(int samplerate, int time_ms)
{
int blocksize = FlakeConstants.flac_blocksizes[1];
int target = (samplerate * time_ms) / 1000;
if (eparams.variable_block_size > 0)
{
blocksize = 1024;
while (target >= blocksize)
blocksize <<= 1;
return blocksize >> 1;
}
for (int i = 8; i < FlakeConstants.flac_blocksizes.Length - 1; i++)
if (target >= FlakeConstants.flac_blocksizes[i] && FlakeConstants.flac_blocksizes[i] > blocksize)
{
blocksize = FlakeConstants.flac_blocksizes[i];
}
return blocksize;
}
void write_streaminfo(byte[] header, int pos, int last)
{
Array.Clear(header, pos, 38);
BitWriter bitwriter = new BitWriter(header, pos, 38);
// metadata header
bitwriter.writebits(1, last);
bitwriter.writebits(7, (int)MetadataType.StreamInfo);
bitwriter.writebits(24, 34);
if (eparams.variable_block_size > 0)
bitwriter.writebits(16, 0);
else
bitwriter.writebits(16, m_blockSize);
bitwriter.writebits(16, m_blockSize);
bitwriter.writebits(24, 0);
bitwriter.writebits(24, max_frame_size);
bitwriter.writebits(20, Settings.PCM.SampleRate);
bitwriter.writebits(3, channels - 1);
bitwriter.writebits(5, Settings.PCM.BitsPerSample - 1);
// total samples
if (sample_count > 0)
{
bitwriter.writebits(4, 0);
bitwriter.writebits(32, sample_count);
}
else
{
bitwriter.writebits(4, 0);
bitwriter.writebits(32, 0);
}
bitwriter.flush();
}
/**
* Write vorbis comment metadata block to byte array.
* Just writes the vendor string for now.
*/
int write_vorbis_comment(byte[] comment, int pos, int len, int last)
{
BitWriter bitwriter = new BitWriter(comment, pos, len);
Encoding enc = new UTF8Encoding();
byte[] str = enc.GetBytes(Vendor);
// metadata header
bitwriter.writebits(1, last);
bitwriter.writebits(7, (int)MetadataType.VorbisComment);
int tagsLen = 0;
if (m_settings.Tags != null)
foreach (var t in m_settings.Tags)
tagsLen += 4 + enc.GetByteCount(t);
bitwriter.writebits(24, 8 + str.Length + tagsLen);
for (int i = 0; i < 4; i++)
bitwriter.writebits(8, (str.Length >> (i * 8)) & 0xff);
bitwriter.write(str);
int nTags = m_settings.Tags != null ? m_settings.Tags.Length : 0;
for (int i = 0; i < 4; i++)
bitwriter.writebits(8, (nTags >> (i * 8)) & 0xff);
if (m_settings.Tags != null)
foreach (var tag in m_settings.Tags)
{
str = enc.GetBytes(tag);
for (int i = 0; i < 4; i++)
bitwriter.writebits(8, (str.Length >> (i * 8)) & 0xff);
bitwriter.write(str);
}
bitwriter.flush();
return bitwriter.Length;
}
int write_seekpoints(byte[] header, int pos, int last)
{
seek_table_offset = pos + 4;
BitWriter bitwriter = new BitWriter(header, pos, 4 + 18 * seek_table.Length);
// metadata header
bitwriter.writebits(1, last);
bitwriter.writebits(7, (int)MetadataType.Seektable);
bitwriter.writebits(24, 18 * seek_table.Length);
for (int i = 0; i < seek_table.Length; i++)
{
bitwriter.writebits(FlakeConstants.FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN, (ulong)seek_table[i].number);
bitwriter.writebits(FlakeConstants.FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN, (ulong)seek_table[i].offset);
bitwriter.writebits(FlakeConstants.FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN, seek_table[i].framesize);
}
bitwriter.flush();
return 4 + 18 * seek_table.Length;
}
/**
* Write padding metadata block to byte array.
*/
int
write_padding(byte[] padding, int pos, int last, int padlen)
{
BitWriter bitwriter = new BitWriter(padding, pos, 4);
// metadata header
bitwriter.writebits(1, last);
bitwriter.writebits(7, (int)MetadataType.Padding);
bitwriter.writebits(24, padlen);
bitwriter.flush();
return padlen + 4;
}
int write_headers()
{
int header_size = 0;
int last = 0;
// stream marker
header[0] = 0x66;
header[1] = 0x4C;
header[2] = 0x61;
header[3] = 0x43;
header_size += 4;
// streaminfo
write_streaminfo(header, header_size, last);
header_size += 38;
// seek table
if (_IO.CanSeek && seek_table != null)
header_size += write_seekpoints(header, header_size, last);
// vorbis comments
if (m_settings.Padding == 0) last = 1;
header_size += write_vorbis_comment(header, header_size, header.Length - header_size, last);
// padding
if (m_settings.Padding > 0)
{
last = 1;
header_size += write_padding(header, header_size, last, m_settings.Padding);
}
return header_size;
}
int flake_encode_init()
{
int i, header_len;
//if(flake_validate_params(s) < 0)
ch_code = channels - 1;
// find samplerate in table
for (i = 1; i < 12; i++)
{
if (Settings.PCM.SampleRate == FlakeConstants.flac_samplerates[i])
{
sr_code0 = i;
break;
}
}
// if not in table, samplerate is non-standard
if (i == 12)
throw new Exception("non-standard samplerate");
for (i = 1; i < 8; i++)
{
if (Settings.PCM.BitsPerSample == FlakeConstants.flac_bitdepths[i])
{
bps_code = i;
break;
}
}
if (i == 8)
throw new Exception("non-standard bps");
m_blockSize = m_settings.BlockSize != 0 ? m_settings.BlockSize :
select_blocksize(Settings.PCM.SampleRate, eparams.block_time_ms);
// set maximum encoded frame size (if larger, re-encodes in verbatim mode)
if (channels == 2)
max_frame_size = 16 + ((m_blockSize * (Settings.PCM.BitsPerSample + Settings.PCM.BitsPerSample + 1) + 7) >> 3);
else
max_frame_size = 16 + ((m_blockSize * channels * Settings.PCM.BitsPerSample + 7) >> 3);
if (_IO.CanSeek && eparams.do_seektable && sample_count > 0)
{
int seek_points_distance = Settings.PCM.SampleRate * 10;
int num_seek_points = 1 + sample_count / seek_points_distance; // 1 seek point per 10 seconds
if (sample_count % seek_points_distance == 0)
num_seek_points--;
seek_table = new SeekPoint[num_seek_points];
for (int sp = 0; sp < num_seek_points; sp++)
{
seek_table[sp].framesize = 0;
seek_table[sp].offset = 0;
seek_table[sp].number = sp * seek_points_distance;
}
}
// output header bytes
int tagsLen = 0;
Encoding enc = new UTF8Encoding();
if (m_settings.Tags != null)
foreach (var t in m_settings.Tags)
tagsLen += 4 + enc.GetByteCount(t);
header = new byte[m_settings.Padding + 1024 + (seek_table == null ? 0 : seek_table.Length * 18) + tagsLen];
header_len = write_headers();
// initialize CRC & MD5
if (_IO.CanSeek && m_settings.DoMD5)
md5 = new MD5CryptoServiceProvider();
if (m_settings.DoVerify)
{
verify = new AudioDecoder(Settings.PCM);
verifyBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * channels];
}
frame_buffer = new byte[max_frame_size];
return header_len;
}
}
struct FlakeEncodeParams
{
// prediction order selection method
// set by user prior to calling flake_encode_init
// if set to less than 0, it is chosen based on compression.
// valid values are 0 to 5
// 0 = use maximum order only
// 1 = use estimation
// 2 = 2-level
// 3 = 4-level
// 4 = 8-level
// 5 = full search
// 6 = log search
public OrderMethod order_method;
// block time in milliseconds
// set by the user prior to calling flake_encode_init
// used to calculate block_size based on sample rate
// can also be changed by user before encoding a frame
public int block_time_ms;
// whether to use variable block sizes
// set by user prior to calling flake_encode_init
// 0 = fixed block size
// 1 = variable block size
public int variable_block_size;
public bool do_seektable;
public int development_mode;
public int flake_set_defaults(EncoderSettings settings)
{
order_method = OrderMethod.Akaike;
block_time_ms = 105;
variable_block_size = 0;
do_seektable = true;
development_mode = -1;
if (settings.GetEncoderModeIndex() == 11)
variable_block_size = 4;
return 0;
}
}
}