2009-08-22 22:13:03 +00:00
|
|
|
/**
|
|
|
|
|
* CUETools.Flake: pure managed FLAC audio encoder
|
|
|
|
|
* Copyright (c) 2009 Gregory S. 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
|
|
|
|
|
*/
|
|
|
|
|
|
2009-08-17 03:39:53 +00:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Text;
|
2009-08-30 21:58:54 +00:00
|
|
|
using CUETools.Codecs;
|
2009-08-17 03:39:53 +00:00
|
|
|
|
|
|
|
|
namespace CUETools.Codecs.FLAKE
|
|
|
|
|
{
|
|
|
|
|
public class Flake
|
|
|
|
|
{
|
|
|
|
|
public const int MAX_BLOCKSIZE = 65535;
|
|
|
|
|
public const int MAX_RICE_PARAM = 14;
|
|
|
|
|
public const int MAX_PARTITION_ORDER = 8;
|
|
|
|
|
public const int MAX_PARTITIONS = 1 << MAX_PARTITION_ORDER;
|
2009-08-17 20:16:56 +00:00
|
|
|
|
2009-08-20 04:09:53 +00:00
|
|
|
public const int FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */
|
|
|
|
|
public const int FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = 64; /* bits */
|
|
|
|
|
public const int FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */
|
|
|
|
|
|
2009-08-17 20:16:56 +00:00
|
|
|
public static readonly int[] flac_samplerates = new int[16] {
|
|
|
|
|
0, 0, 0, 0,
|
|
|
|
|
8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000,
|
|
|
|
|
0, 0, 0, 0
|
|
|
|
|
};
|
|
|
|
|
public static readonly int[] flac_blocksizes = new int[15] { 0, 192, 576, 1152, 2304, 4608, 0, 0, 256, 512, 1024, 2048, 4096, 8192, 16384 };
|
|
|
|
|
public static readonly int[] flac_bitdepths = new int[8] { 0, 8, 12, 0, 16, 20, 24, 0 };
|
|
|
|
|
|
2009-08-17 03:39:53 +00:00
|
|
|
public static PredictionType LookupPredictionType(string name)
|
|
|
|
|
{
|
2009-08-20 04:09:53 +00:00
|
|
|
return (PredictionType)(Enum.Parse(typeof(PredictionType), name, true));
|
2009-08-17 03:39:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static StereoMethod LookupStereoMethod(string name)
|
|
|
|
|
{
|
2009-08-20 04:09:53 +00:00
|
|
|
return (StereoMethod)(Enum.Parse(typeof(StereoMethod), name, true));
|
2009-08-17 03:39:53 +00:00
|
|
|
}
|
|
|
|
|
|
2009-12-24 16:11:22 +00:00
|
|
|
public static WindowMethod LookupWindowMethod(string name)
|
|
|
|
|
{
|
|
|
|
|
return (WindowMethod)(Enum.Parse(typeof(WindowMethod), name, true));
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-17 03:39:53 +00:00
|
|
|
public static OrderMethod LookupOrderMethod(string name)
|
|
|
|
|
{
|
2009-08-20 04:09:53 +00:00
|
|
|
return (OrderMethod)(Enum.Parse(typeof(OrderMethod), name, true));
|
2009-08-17 03:39:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static WindowFunction LookupWindowFunction(string name)
|
|
|
|
|
{
|
2009-08-20 04:09:53 +00:00
|
|
|
return (WindowFunction)(Enum.Parse(typeof(WindowFunction), name, true));
|
2009-08-17 03:39:53 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-16 23:35:56 +00:00
|
|
|
unsafe public class RiceContext
|
2009-08-17 03:39:53 +00:00
|
|
|
{
|
2009-08-28 13:00:27 +00:00
|
|
|
public RiceContext()
|
|
|
|
|
{
|
|
|
|
|
rparams = new int[Flake.MAX_PARTITIONS];
|
|
|
|
|
esc_bps = new int[Flake.MAX_PARTITIONS];
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// partition order
|
|
|
|
|
/// </summary>
|
|
|
|
|
public int porder;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Rice parameters
|
|
|
|
|
/// </summary>
|
|
|
|
|
public int[] rparams;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// bps if using escape code
|
|
|
|
|
/// </summary>
|
|
|
|
|
public int[] esc_bps;
|
2009-08-17 03:39:53 +00:00
|
|
|
};
|
|
|
|
|
|
2009-10-16 23:35:56 +00:00
|
|
|
unsafe public class FlacSubframe
|
2009-08-17 03:39:53 +00:00
|
|
|
{
|
2009-08-28 13:00:27 +00:00
|
|
|
public FlacSubframe()
|
|
|
|
|
{
|
|
|
|
|
rc = new RiceContext();
|
|
|
|
|
coefs = new int[lpc.MAX_LPC_ORDER];
|
|
|
|
|
}
|
2009-08-17 03:39:53 +00:00
|
|
|
public SubframeType type;
|
|
|
|
|
public int order;
|
2009-08-20 04:09:53 +00:00
|
|
|
public int* residual;
|
|
|
|
|
public RiceContext rc;
|
|
|
|
|
public uint size;
|
|
|
|
|
|
2009-08-17 03:39:53 +00:00
|
|
|
public int cbits;
|
|
|
|
|
public int shift;
|
2009-08-28 13:00:27 +00:00
|
|
|
public int[] coefs;
|
2009-08-20 04:09:53 +00:00
|
|
|
public int window;
|
|
|
|
|
};
|
|
|
|
|
|
2009-10-16 23:35:56 +00:00
|
|
|
unsafe public class FlacSubframeInfo
|
2009-08-20 04:09:53 +00:00
|
|
|
{
|
2009-08-28 13:00:27 +00:00
|
|
|
public FlacSubframeInfo()
|
|
|
|
|
{
|
|
|
|
|
best = new FlacSubframe();
|
|
|
|
|
lpc_ctx = new LpcContext[lpc.MAX_LPC_WINDOWS];
|
|
|
|
|
for (int i = 0; i < lpc.MAX_LPC_WINDOWS; i++)
|
|
|
|
|
lpc_ctx[i] = new LpcContext();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Init(int* s, int* r, uint bps, uint w)
|
|
|
|
|
{
|
|
|
|
|
if (w > bps)
|
|
|
|
|
throw new Exception("internal error");
|
|
|
|
|
samples = s;
|
|
|
|
|
obits = bps - w;
|
|
|
|
|
wbits = w;
|
|
|
|
|
best.residual = r;
|
|
|
|
|
best.type = SubframeType.Verbatim;
|
2009-08-30 21:58:54 +00:00
|
|
|
best.size = AudioSamples.UINT32_MAX;
|
2009-08-28 13:00:27 +00:00
|
|
|
for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++)
|
|
|
|
|
lpc_ctx[iWindow].Reset();
|
|
|
|
|
done_fixed = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-20 04:09:53 +00:00
|
|
|
public FlacSubframe best;
|
|
|
|
|
public uint obits;
|
|
|
|
|
public uint wbits;
|
2009-08-17 03:39:53 +00:00
|
|
|
public int* samples;
|
|
|
|
|
public uint done_fixed;
|
2009-08-28 13:00:27 +00:00
|
|
|
public LpcContext[] lpc_ctx;
|
2009-08-17 03:39:53 +00:00
|
|
|
};
|
|
|
|
|
|
2009-10-16 23:35:56 +00:00
|
|
|
unsafe public class FlacFrame
|
2009-08-17 03:39:53 +00:00
|
|
|
{
|
2009-08-28 13:00:27 +00:00
|
|
|
public FlacFrame(int subframes_count)
|
|
|
|
|
{
|
|
|
|
|
subframes = new FlacSubframeInfo[subframes_count];
|
|
|
|
|
for (int ch = 0; ch < subframes_count; ch++)
|
|
|
|
|
subframes[ch] = new FlacSubframeInfo();
|
|
|
|
|
current = new FlacSubframe();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void InitSize(int bs, bool vbs)
|
|
|
|
|
{
|
2009-10-16 23:35:56 +00:00
|
|
|
blocksize = bs;
|
2009-08-28 13:00:27 +00:00
|
|
|
int i = 15;
|
|
|
|
|
if (!vbs)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < 15; i++)
|
|
|
|
|
{
|
|
|
|
|
if (bs == Flake.flac_blocksizes[i])
|
|
|
|
|
{
|
|
|
|
|
bs_code0 = i;
|
|
|
|
|
bs_code1 = -1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (i == 15)
|
|
|
|
|
{
|
|
|
|
|
if (blocksize <= 256)
|
|
|
|
|
{
|
|
|
|
|
bs_code0 = 6;
|
|
|
|
|
bs_code1 = blocksize - 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
bs_code0 = 7;
|
|
|
|
|
bs_code1 = blocksize - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ChooseBestSubframe(int ch)
|
|
|
|
|
{
|
|
|
|
|
if (current.size >= subframes[ch].best.size)
|
|
|
|
|
return;
|
|
|
|
|
FlacSubframe tmp = subframes[ch].best;
|
|
|
|
|
subframes[ch].best = current;
|
|
|
|
|
current = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SwapSubframes(int ch1, int ch2)
|
|
|
|
|
{
|
|
|
|
|
FlacSubframeInfo tmp = subframes[ch1];
|
|
|
|
|
subframes[ch1] = subframes[ch2];
|
|
|
|
|
subframes[ch2] = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Swap subframes according to channel mode.
|
|
|
|
|
/// It is assumed that we have 4 subframes,
|
|
|
|
|
/// 0 is right, 1 is left, 2 is middle, 3 is difference
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void ChooseSubframes()
|
|
|
|
|
{
|
|
|
|
|
switch (ch_mode)
|
|
|
|
|
{
|
|
|
|
|
case ChannelMode.MidSide:
|
|
|
|
|
SwapSubframes(0, 2);
|
|
|
|
|
SwapSubframes(1, 3);
|
|
|
|
|
break;
|
|
|
|
|
case ChannelMode.RightSide:
|
|
|
|
|
SwapSubframes(0, 3);
|
|
|
|
|
break;
|
|
|
|
|
case ChannelMode.LeftSide:
|
|
|
|
|
SwapSubframes(1, 3);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-17 03:39:53 +00:00
|
|
|
public int blocksize;
|
|
|
|
|
public int bs_code0, bs_code1;
|
|
|
|
|
public ChannelMode ch_mode;
|
2009-08-28 13:00:27 +00:00
|
|
|
//public int ch_order0, ch_order1;
|
2009-08-17 03:39:53 +00:00
|
|
|
public byte crc8;
|
2009-08-28 13:00:27 +00:00
|
|
|
public FlacSubframeInfo[] subframes;
|
2009-12-24 16:11:22 +00:00
|
|
|
public int frame_number;
|
2009-08-17 03:39:53 +00:00
|
|
|
public FlacSubframe current;
|
2009-10-16 23:35:56 +00:00
|
|
|
public float* window_buffer;
|
2009-12-24 16:11:22 +00:00
|
|
|
|
|
|
|
|
public BitWriter writer = null;
|
|
|
|
|
public int writer_offset = 0;
|
2009-08-17 03:39:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum OrderMethod
|
|
|
|
|
{
|
2009-10-16 23:35:56 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Select orders based on Akaike's criteria
|
|
|
|
|
/// </summary>
|
|
|
|
|
Akaike = 0
|
2009-08-17 03:39:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Type of linear prediction
|
|
|
|
|
/// </summary>
|
|
|
|
|
public enum PredictionType
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
2009-08-28 13:00:27 +00:00
|
|
|
/// Verbatim
|
2009-08-17 03:39:53 +00:00
|
|
|
/// </summary>
|
|
|
|
|
None = 0,
|
|
|
|
|
/// <summary>
|
2009-08-28 13:00:27 +00:00
|
|
|
/// Fixed prediction only
|
2009-08-17 03:39:53 +00:00
|
|
|
/// </summary>
|
|
|
|
|
Fixed = 1,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Levinson-Durbin recursion
|
|
|
|
|
/// </summary>
|
|
|
|
|
Levinson = 2,
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Exhaustive search
|
|
|
|
|
/// </summary>
|
2009-08-28 13:00:27 +00:00
|
|
|
Search = 3
|
2009-08-17 03:39:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum StereoMethod
|
|
|
|
|
{
|
|
|
|
|
Independent = 0,
|
|
|
|
|
Estimate = 1,
|
2009-08-20 04:09:53 +00:00
|
|
|
Evaluate = 2,
|
|
|
|
|
Search = 3
|
2009-08-17 03:39:53 +00:00
|
|
|
}
|
|
|
|
|
|
2009-12-24 16:11:22 +00:00
|
|
|
public enum WindowMethod
|
|
|
|
|
{
|
|
|
|
|
Estimate = 0,
|
|
|
|
|
Evaluate = 1,
|
|
|
|
|
Search = 2
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-17 03:39:53 +00:00
|
|
|
public enum SubframeType
|
|
|
|
|
{
|
|
|
|
|
Constant = 0,
|
|
|
|
|
Verbatim = 1,
|
|
|
|
|
Fixed = 8,
|
|
|
|
|
LPC = 32
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public enum ChannelMode
|
|
|
|
|
{
|
|
|
|
|
NotStereo = 0,
|
|
|
|
|
LeftRight = 1,
|
|
|
|
|
LeftSide = 8,
|
|
|
|
|
RightSide = 9,
|
|
|
|
|
MidSide = 10
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum WindowFunction
|
|
|
|
|
{
|
|
|
|
|
Welch = 1,
|
|
|
|
|
Tukey = 2,
|
|
|
|
|
Hann = 4,
|
2009-08-20 04:09:53 +00:00
|
|
|
Flattop = 8,
|
2009-10-16 23:35:56 +00:00
|
|
|
Bartlett = 16,
|
|
|
|
|
TukeyFlattop = 10
|
2009-08-17 03:39:53 +00:00
|
|
|
}
|
|
|
|
|
|
2009-08-17 20:16:56 +00:00
|
|
|
public struct SeekPoint
|
|
|
|
|
{
|
|
|
|
|
public ulong number;
|
|
|
|
|
public ulong offset;
|
|
|
|
|
public uint framesize;
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-17 03:39:53 +00:00
|
|
|
public enum MetadataType
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// <A HREF="../format.html#metadata_block_streaminfo">STREAMINFO</A> block
|
|
|
|
|
/// </summary>
|
2009-08-28 13:00:27 +00:00
|
|
|
StreamInfo = 0,
|
2009-08-17 03:39:53 +00:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// <A HREF="../format.html#metadata_block_padding">PADDING</A> block
|
|
|
|
|
/// </summary>
|
2009-08-28 13:00:27 +00:00
|
|
|
Padding = 1,
|
2009-08-17 03:39:53 +00:00
|
|
|
|
2009-08-17 20:16:56 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// <A HREF="../format.html#metadata_block_application">APPLICATION</A> block
|
|
|
|
|
/// </summary>
|
2009-08-28 13:00:27 +00:00
|
|
|
Application = 2,
|
2009-08-17 03:39:53 +00:00
|
|
|
|
2009-08-17 20:16:56 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// <A HREF="../format.html#metadata_block_seektable">SEEKTABLE</A> block
|
|
|
|
|
/// </summary>
|
2009-08-28 13:00:27 +00:00
|
|
|
Seektable = 3,
|
2009-08-17 03:39:53 +00:00
|
|
|
|
2009-08-17 20:16:56 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// <A HREF="../format.html#metadata_block_vorbis_comment">VORBISCOMMENT</A> block (a.k.a. FLAC tags)
|
|
|
|
|
/// </summary>
|
2009-08-28 13:00:27 +00:00
|
|
|
VorbisComment = 4,
|
2009-08-17 03:39:53 +00:00
|
|
|
|
2009-08-17 20:16:56 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// <A HREF="../format.html#metadata_block_cuesheet">CUESHEET</A> block
|
|
|
|
|
/// </summary>
|
2009-08-28 13:00:27 +00:00
|
|
|
CUESheet = 5,
|
2009-08-17 03:39:53 +00:00
|
|
|
|
2009-08-17 20:16:56 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// <A HREF="../format.html#metadata_block_picture">PICTURE</A> block
|
|
|
|
|
/// </summary>
|
2009-08-28 13:00:27 +00:00
|
|
|
Picture = 6,
|
2009-08-17 03:39:53 +00:00
|
|
|
|
2009-08-17 20:16:56 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// marker to denote beginning of undefined type range; this number will increase as new metadata types are added
|
|
|
|
|
/// </summary>
|
2009-08-28 13:00:27 +00:00
|
|
|
Undefined = 7
|
2009-08-17 03:39:53 +00:00
|
|
|
};
|
|
|
|
|
}
|