mirror of
https://github.com/claunia/cuetools.net.git
synced 2025-12-16 18:14:25 +00:00
ALAC encoder
This commit is contained in:
229
CUETools.Codecs.ALAC/ALAC.cs
Normal file
229
CUETools.Codecs.ALAC/ALAC.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
/**
|
||||
* CUETools.Codecs.ALAC: pure managed ALAC audio encoder
|
||||
* Copyright (c) 2009 Gregory S. Chudov
|
||||
* Based on ffdshow ALAC audio encoder
|
||||
* Copyright (c) 2008 Jaikrishnan Menon, realityman@gmx.net
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace CUETools.Codecs.ALAC
|
||||
{
|
||||
public class Alac
|
||||
{
|
||||
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;
|
||||
|
||||
public const uint UINT32_MAX = 0xffffffff;
|
||||
|
||||
public static StereoMethod LookupStereoMethod(string name)
|
||||
{
|
||||
return (StereoMethod)(Enum.Parse(typeof(StereoMethod), name, true));
|
||||
}
|
||||
|
||||
public static OrderMethod LookupOrderMethod(string name)
|
||||
{
|
||||
return (OrderMethod)(Enum.Parse(typeof(OrderMethod), name, true));
|
||||
}
|
||||
|
||||
public static WindowFunction LookupWindowFunction(string name)
|
||||
{
|
||||
return (WindowFunction)(Enum.Parse(typeof(WindowFunction), name, true));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
unsafe class RiceContext
|
||||
{
|
||||
public RiceContext()
|
||||
{
|
||||
rparams = new int[Alac.MAX_PARTITIONS];
|
||||
esc_bps = new int[Alac.MAX_PARTITIONS];
|
||||
}
|
||||
/// <summary>
|
||||
/// partition order
|
||||
/// </summary>
|
||||
public int porder;
|
||||
|
||||
/// <summary>
|
||||
/// Rice parameters
|
||||
/// </summary>
|
||||
public int[] rparams;
|
||||
|
||||
/// <summary>
|
||||
/// bps if using escape code
|
||||
/// </summary>
|
||||
public int[] esc_bps;
|
||||
};
|
||||
|
||||
unsafe class ALACSubframe
|
||||
{
|
||||
public ALACSubframe()
|
||||
{
|
||||
rc = new RiceContext();
|
||||
coefs = new int[lpc.MAX_LPC_ORDER];
|
||||
}
|
||||
public int order;
|
||||
public int* residual;
|
||||
public RiceContext rc;
|
||||
public uint size;
|
||||
|
||||
public int ricemodifier;
|
||||
public int cbits;
|
||||
public int shift;
|
||||
public int[] coefs;
|
||||
public int window;
|
||||
};
|
||||
|
||||
unsafe class ALACSubframeInfo
|
||||
{
|
||||
public ALACSubframeInfo()
|
||||
{
|
||||
best = new ALACSubframe();
|
||||
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)
|
||||
{
|
||||
samples = s;
|
||||
best.residual = r;
|
||||
best.size = AudioSamples.UINT32_MAX;
|
||||
for (int iWindow = 0; iWindow < lpc.MAX_LPC_WINDOWS; iWindow++)
|
||||
lpc_ctx[iWindow].Reset();
|
||||
done_fixed = 0;
|
||||
}
|
||||
|
||||
public ALACSubframe best;
|
||||
public int* samples;
|
||||
public uint done_fixed;
|
||||
public LpcContext[] lpc_ctx;
|
||||
};
|
||||
|
||||
unsafe class ALACFrame
|
||||
{
|
||||
public ALACFrame(int subframes_count)
|
||||
{
|
||||
subframes = new ALACSubframeInfo[subframes_count];
|
||||
for (int ch = 0; ch < subframes_count; ch++)
|
||||
subframes[ch] = new ALACSubframeInfo();
|
||||
current = new ALACSubframe();
|
||||
}
|
||||
|
||||
public void InitSize(int bs)
|
||||
{
|
||||
blocksize = bs;
|
||||
type = FrameType.Verbatim;
|
||||
interlacing_shift = interlacing_leftweight = 0;
|
||||
}
|
||||
|
||||
public void ChooseBestSubframe(int ch)
|
||||
{
|
||||
if (current.size >= subframes[ch].best.size)
|
||||
return;
|
||||
ALACSubframe tmp = subframes[ch].best;
|
||||
subframes[ch].best = current;
|
||||
current = tmp;
|
||||
}
|
||||
|
||||
public void SwapSubframes(int ch1, int ch2)
|
||||
{
|
||||
ALACSubframeInfo 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()
|
||||
{
|
||||
if (interlacing_leftweight != 0)
|
||||
{
|
||||
SwapSubframes(1, 3);
|
||||
switch (interlacing_shift)
|
||||
{
|
||||
case 0: // leftside
|
||||
break;
|
||||
case 1: // midside
|
||||
SwapSubframes(0, 2);
|
||||
break;
|
||||
case 31: // rightside
|
||||
SwapSubframes(0, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FrameType type;
|
||||
public int blocksize;
|
||||
public int interlacing_shift, interlacing_leftweight;
|
||||
public ALACSubframeInfo[] subframes;
|
||||
public ALACSubframe current;
|
||||
public double* window_buffer;
|
||||
}
|
||||
|
||||
public enum OrderMethod
|
||||
{
|
||||
Max = 0,
|
||||
Estimate = 1,
|
||||
LogFast = 2,
|
||||
LogSearch = 3,
|
||||
EstSearch2 = 4,
|
||||
Search = 5
|
||||
}
|
||||
|
||||
public enum StereoMethod
|
||||
{
|
||||
Independent = 0,
|
||||
Estimate = 1,
|
||||
Estimate2 = 2,
|
||||
Evaluate = 3,
|
||||
Search = 4
|
||||
}
|
||||
|
||||
public enum FrameType
|
||||
{
|
||||
Verbatim = 0,
|
||||
Compressed = 1
|
||||
};
|
||||
|
||||
public enum ChannelMode
|
||||
{
|
||||
NotStereo = 0,
|
||||
LeftRight = 1,
|
||||
LeftSide = 8,
|
||||
RightSide = 9,
|
||||
MidSide = 10
|
||||
}
|
||||
|
||||
public enum WindowFunction
|
||||
{
|
||||
Welch = 1,
|
||||
Tukey = 2,
|
||||
Hann = 4,
|
||||
Flattop = 8,
|
||||
TukFlat = 10
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,24 @@ namespace CUETools.Codecs.ALAC
|
||||
calculate_length();
|
||||
}
|
||||
|
||||
public ALACReader(int channels, int bps, int rice_historymult, int rice_initialhistory, int rice_kmodifier, int blocksize)
|
||||
{
|
||||
_channelCount = channels;
|
||||
_bitsPerSample = bps;
|
||||
_sampleRate = 44100;
|
||||
|
||||
setinfo_max_samples_per_frame = (uint)blocksize;
|
||||
setinfo_rice_historymult = (byte)rice_historymult;
|
||||
setinfo_rice_initialhistory = (byte)rice_initialhistory;
|
||||
setinfo_rice_kmodifier = (byte)rice_kmodifier;
|
||||
|
||||
_predicterror_buffer_a = new int[setinfo_max_samples_per_frame];
|
||||
_predicterror_buffer_b = new int[setinfo_max_samples_per_frame];
|
||||
_outputsamples_buffer_a = new int[setinfo_max_samples_per_frame];
|
||||
_outputsamples_buffer_b = new int[setinfo_max_samples_per_frame];
|
||||
_framesBuffer = new byte[65536];
|
||||
}
|
||||
|
||||
public int[,] Read(int[,] buff)
|
||||
{
|
||||
return AudioSamples.Read(this, buff);
|
||||
@@ -96,7 +114,7 @@ namespace CUETools.Codecs.ALAC
|
||||
return (uint)offset;
|
||||
get_sample_info(_iSample, out sampleDuration, out sampleSize);
|
||||
_IO.Read(_framesBuffer, 0, (int) sampleSize);
|
||||
decodeFrame(sampleDuration, sampleSize);
|
||||
decodeFrame(sampleSize);
|
||||
if (sampleDuration != _samplesInBuffer)
|
||||
throw new Exception("sample count mismatch");
|
||||
_samplesInBuffer -= _samplesBufferOffset;
|
||||
@@ -157,6 +175,8 @@ namespace CUETools.Codecs.ALAC
|
||||
_IO.Position = _saved_mdat_pos + fileOffs;
|
||||
return;
|
||||
}
|
||||
if ((int)_iSample >= _sample_byte_size.Length)
|
||||
throw new Exception("seeking past end of stream");
|
||||
get_sample_info(_iSample, out sampleDuration, out sampleSize);
|
||||
durOffs += sampleDuration;
|
||||
fileOffs += sampleSize;
|
||||
@@ -225,6 +245,8 @@ namespace CUETools.Codecs.ALAC
|
||||
while (_time_to_sample_count[duration_cur_index] + duration_index_accum <= iSample)
|
||||
{
|
||||
duration_index_accum += _time_to_sample_count[duration_cur_index];
|
||||
if (duration_cur_index == _time_to_sample_count.Length - 1)
|
||||
throw new Exception("seeking past end of stream");
|
||||
duration_cur_index ++;
|
||||
}
|
||||
sampleDuration = _time_to_sample_duration[duration_cur_index];
|
||||
@@ -250,7 +272,7 @@ namespace CUETools.Codecs.ALAC
|
||||
get_sample_info(_iSample, out sampleDuration, out sampleSize);
|
||||
InitTables();
|
||||
_IO.Read(_framesBuffer, 0, (int)sampleSize);
|
||||
decodeFrame(sampleDuration, sampleSize);
|
||||
decodeFrame(sampleSize);
|
||||
if (_samplesInBuffer < sampleDuration)
|
||||
{
|
||||
_time_to_sample_duration = new uint[2] { sample_duration_0, _samplesInBuffer };
|
||||
@@ -395,11 +417,6 @@ namespace CUETools.Codecs.ALAC
|
||||
return result;
|
||||
}
|
||||
|
||||
private static uint SIGN_EXTENDED32(uint val, int bits)
|
||||
{
|
||||
return ((val << (32 - bits)) >> (32 - bits));
|
||||
}
|
||||
|
||||
private void unreadbits(ref int pos, int bits)
|
||||
{
|
||||
int new_accumulator = (_bitaccumulator - bits);
|
||||
@@ -412,7 +429,7 @@ namespace CUETools.Codecs.ALAC
|
||||
|
||||
private static int count_leading_zeroes(uint input)
|
||||
{
|
||||
int zeroes = 1;
|
||||
int zeroes = 0;
|
||||
uint shifted_input = input >> 16;
|
||||
if (shifted_input == 0)
|
||||
zeroes += 16;
|
||||
@@ -423,22 +440,7 @@ namespace CUETools.Codecs.ALAC
|
||||
zeroes += 8;
|
||||
else
|
||||
input = shifted_input;
|
||||
shifted_input = input >> 4;
|
||||
if (shifted_input == 0)
|
||||
zeroes += 4;
|
||||
else
|
||||
input = shifted_input;
|
||||
shifted_input = input >> 2;
|
||||
if (shifted_input == 0)
|
||||
zeroes += 2;
|
||||
else
|
||||
input = shifted_input;
|
||||
shifted_input = input >> 1;
|
||||
if (shifted_input == 0)
|
||||
zeroes ++;
|
||||
else
|
||||
input = shifted_input;
|
||||
return zeroes - (int)input;
|
||||
return zeroes + BitReader.byte_to_unary_table[input];
|
||||
}
|
||||
|
||||
private unsafe void readPredictor(ref int pos, ref predictor_t predictor_info)
|
||||
@@ -462,30 +464,14 @@ namespace CUETools.Codecs.ALAC
|
||||
|
||||
private unsafe int decode_scalar(byte * buff, ref int pos, int k, int limit, int readsamplesize)
|
||||
{
|
||||
int x = 0;
|
||||
uint next = peekbits_9(buff, pos);
|
||||
if (next == 0x1ff) /* RICE THRESHOLD 9 bits */
|
||||
int x = (next >> 8 == 0) ? 0 :
|
||||
1 + BitReader.byte_to_unary_table[(~next) & 0xff];
|
||||
if (x == 9) /* RICE THRESHOLD 9 bits */
|
||||
{
|
||||
skipbits(ref pos, 9);
|
||||
return (int)readbits(buff, ref pos, readsamplesize);
|
||||
}
|
||||
if ((next & 0x1e0) == 0x1e0)
|
||||
{
|
||||
x += 4;
|
||||
next <<= 4;
|
||||
}
|
||||
if ((next & 0x180) == 0x180)
|
||||
{
|
||||
x += 2;
|
||||
next <<= 2;
|
||||
}
|
||||
if ((next & 0x100) == 0x100)
|
||||
{
|
||||
x += 1;
|
||||
next <<= 1;
|
||||
}
|
||||
x += (int) (next & 0x100) >> 8;
|
||||
//x = count_leading_zeroes((~next) & 0x1ff) - 23;
|
||||
skipbits(ref pos, x + 1);
|
||||
if (k >= limit)
|
||||
k = limit;
|
||||
@@ -637,7 +623,7 @@ namespace CUETools.Codecs.ALAC
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void deinterlace(int[,] samplesBuffer, uint offset, uint sampleCount)
|
||||
internal unsafe void deinterlace(int[,] samplesBuffer, uint offset, uint sampleCount)
|
||||
{
|
||||
if (sampleCount <= 0 || sampleCount > _samplesInBuffer)
|
||||
return;
|
||||
@@ -670,17 +656,18 @@ namespace CUETools.Codecs.ALAC
|
||||
}
|
||||
|
||||
/* otherwise basic interlacing took place */
|
||||
for (i = 0; i < sampleCount; i++)
|
||||
{
|
||||
int a = buf_a[i];
|
||||
int b = buf_b[i];
|
||||
buf_s[i * 2] = a;
|
||||
buf_s[i * 2 + 1] = b;
|
||||
}
|
||||
AudioSamples.Interlace(buf_s, buf_a, buf_b, (int)sampleCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void decodeFrame(ulong sampleDuration, uint sampleSize)
|
||||
internal int DecodeFrame(byte[] buffer, int pos, int len)
|
||||
{
|
||||
Array.Copy(buffer, pos, _framesBuffer, 0, len);
|
||||
decodeFrame((uint)len);
|
||||
return len; // pos
|
||||
}
|
||||
|
||||
private void decodeFrame(uint sampleSize)
|
||||
{
|
||||
_bitaccumulator = 0;
|
||||
int pos = 0;
|
||||
@@ -723,8 +710,6 @@ namespace CUETools.Codecs.ALAC
|
||||
else
|
||||
{
|
||||
/* not compressed, easy case */
|
||||
if (_bitsPerSample != 16)
|
||||
throw new Exception("Not 16 bit");
|
||||
for (int i = 0; i < outputSamples; i++)
|
||||
{
|
||||
_outputsamples_buffer_a[i] = extend_sign32((int)readbits(_framesBuffer, ref pos, _bitsPerSample), _bitsPerSample);
|
||||
@@ -735,8 +720,8 @@ namespace CUETools.Codecs.ALAC
|
||||
_interlacing_leftweight = 0;
|
||||
}
|
||||
|
||||
if (_bitsPerSample != 16)
|
||||
throw new Exception("Not 16 bit");
|
||||
if (readbits(_framesBuffer, ref pos, 3) != 7)
|
||||
throw new Exception("Invalid frame.");
|
||||
|
||||
_samplesInBuffer = outputSamples;
|
||||
}
|
||||
|
||||
1725
CUETools.Codecs.ALAC/ALACWriter.cs
Normal file
1725
CUETools.Codecs.ALAC/ALACWriter.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -84,6 +84,8 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ALACDotNet.cs" />
|
||||
<Compile Include="ALACWriter.cs" />
|
||||
<Compile Include="ALAC.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user