Files
cuetools.net/CUETools.Codecs.ALAC/ALACWriter.cs

1976 lines
66 KiB
C#
Raw Normal View History

2009-08-30 21:58:54 +00:00
/**
* CUETools.Codecs.ALAC: pure managed ALAC 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
2009-08-30 21:58:54 +00:00
* 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
*/
2009-12-24 16:14:41 +00:00
#define INTEROP
2009-08-30 21:58:54 +00:00
using System;
2010-05-18 17:18:37 +00:00
using System.ComponentModel;
2009-08-30 21:58:54 +00:00
using System.Text;
using System.IO;
using System.Collections.Generic;
2009-12-24 16:14:41 +00:00
#if INTEROP
using System.Runtime.InteropServices;
#endif
2009-08-30 21:58:54 +00:00
using CUETools.Codecs;
using Newtonsoft.Json;
2009-08-30 21:58:54 +00:00
namespace CUETools.Codecs.ALAC
{
[JsonObject(MemberSerialization.OptIn)]
public class EncoderSettings: IAudioEncoderSettings
2010-05-18 17:18:37 +00:00
{
#region IAudioEncoderSettings implementation
[Browsable(false)]
public string Extension => "m4a";
[Browsable(false)]
public string Name => "cuetools";
[Browsable(false)]
public Type EncoderType => typeof(AudioEncoder);
[Browsable(false)]
public bool Lossless => true;
[Browsable(false)]
public int Priority => 1;
[Browsable(false)]
public string SupportedModes => "0 1 2 3 4 5 6 7 8 9 10";
[Browsable(false)]
public string DefaultMode => "5";
[Browsable(false)]
[DefaultValue("")]
[JsonProperty]
public string EncoderMode { get; set; }
[Browsable(false)]
public AudioPCMConfig PCM { get; set; }
[Browsable(false)]
public int BlockSize { get; set; }
[Browsable(false)]
[DefaultValue(4096)]
public int Padding { get; set; }
public IAudioEncoderSettings Clone()
{
return MemberwiseClone() as IAudioEncoderSettings;
}
#endregion
public EncoderSettings()
{
this.Init();
}
public void Validate()
{
if (this.GetEncoderModeIndex() < 0
|| Padding < 0
|| (BlockSize != 0 && (BlockSize < 256 || BlockSize >= Int32.MaxValue)))
throw new Exception("unsupported encoder settings");
}
2010-05-18 17:18:37 +00:00
[DefaultValue(false)]
[DisplayName("Verify")]
[Description("Decode each frame and compare with original")]
[JsonProperty]
public bool DoVerify { get; set; }
2010-05-18 17:18:37 +00:00
}
public class AudioEncoder : IAudioDest
2009-08-30 21:58:54 +00:00
{
Stream _IO = null;
2010-04-18 22:42:23 +00:00
bool _pathGiven = false;
2009-08-30 21:58:54 +00:00
string _path;
long _position;
2010-04-18 22:42:23 +00:00
const int max_header_len = 709 + 38; // minimum 38 bytes in padding
2009-08-30 21:58:54 +00:00
// total stream samples
2010-02-06 23:17:07 +00:00
// if < 0, stream length is unknown
int sample_count = -1;
2009-08-30 21:58:54 +00:00
ALACEncodeParams eparams;
// maximum frame size in bytes
// this can be used to allocate memory for output
int max_frame_size;
2009-12-24 16:14:41 +00:00
int initial_history = 10, history_mult = 40, k_modifier = 14;
2009-08-30 21:58:54 +00:00
byte[] frame_buffer = null;
int frame_count = 0;
2010-04-18 22:42:23 +00:00
int first_frame_offset = 0;
2009-08-30 21:58:54 +00:00
2009-12-24 16:14:41 +00:00
#if INTEROP
2009-08-30 21:58:54 +00:00
TimeSpan _userProcessorTime;
2009-12-24 16:14:41 +00:00
#endif
2009-08-30 21:58:54 +00:00
uint[] _sample_byte_size;
int[] samplesBuffer;
int[] verifyBuffer;
int[] residualBuffer;
2009-12-24 16:14:41 +00:00
float[] windowBuffer;
WindowFunction[] windowType;
LpcWindowSection[,] windowSections;
int samplesInBuffer = 0;
2009-08-30 21:58:54 +00:00
int m_blockSize = 0;
2009-08-30 21:58:54 +00:00
int _totalSize = 0;
int _windowsize = 0, _windowcount = 0;
ALACFrame frame;
AudioDecoder verify;
2009-08-30 21:58:54 +00:00
bool inited = false;
List<int> chunk_pos;
public AudioEncoder(EncoderSettings settings, string path, Stream IO)
2009-08-30 21:58:54 +00:00
{
m_settings = settings;
m_settings.Validate();
2010-02-06 23:17:07 +00:00
if (Settings.PCM.BitsPerSample != 16)
2009-08-30 21:58:54 +00:00
throw new Exception("Bits per sample must be 16.");
if (Settings.PCM.ChannelCount != 2)
2009-08-30 21:58:54 +00:00
throw new Exception("ChannelCount must be 2.");
_path = path;
_IO = IO;
2010-04-18 22:42:23 +00:00
_pathGiven = _IO == null;
if (_IO != null && !_IO.CanSeek)
throw new NotSupportedException("stream doesn't support seeking");
2009-08-30 21:58:54 +00:00
samplesBuffer = new int[Alac.MAX_BLOCKSIZE * (Settings.PCM.ChannelCount == 2 ? 5 : Settings.PCM.ChannelCount)];
residualBuffer = new int[Alac.MAX_BLOCKSIZE * (Settings.PCM.ChannelCount == 2 ? 6 : Settings.PCM.ChannelCount + 1)];
windowBuffer = new float[Alac.MAX_BLOCKSIZE * 2 * lpc.MAX_LPC_WINDOWS];
windowType = new WindowFunction[lpc.MAX_LPC_WINDOWS];
windowSections = new LpcWindowSection[lpc.MAX_LPC_WINDOWS, lpc.MAX_LPC_SECTIONS];
2009-08-30 21:58:54 +00:00
eparams.set_defaults(m_settings.GetEncoderModeIndex());
2009-08-30 21:58:54 +00:00
frame = new ALACFrame(Settings.PCM.ChannelCount == 2 ? 5 : Settings.PCM.ChannelCount);
2009-08-30 21:58:54 +00:00
chunk_pos = new List<int>();
}
public int TotalSize
{
get
{
return _totalSize;
}
}
EncoderSettings m_settings;
2010-05-18 17:18:37 +00:00
public IAudioEncoderSettings Settings => m_settings;
2009-08-30 21:58:54 +00:00
2009-12-24 16:14:41 +00:00
#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
2009-08-30 21:58:54 +00:00
void chunk_start(BitWriter bitwriter)
{
bitwriter.flush();
chunk_pos.Add(bitwriter.Length);
bitwriter.writebits(32, 0); // length placeholder
}
void chunk_end(BitWriter bitwriter)
{
bitwriter.flush();
int pos = chunk_pos[chunk_pos.Count - 1];
chunk_pos.RemoveAt(chunk_pos.Count - 1);
int chunk_end = bitwriter.Length;
bitwriter.Length = pos;
bitwriter.writebits(32, chunk_end - pos);
bitwriter.Length = chunk_end;
}
void DoClose()
{
if (inited)
{
while (samplesInBuffer > 0)
output_frame(samplesInBuffer);
2010-04-18 22:42:23 +00:00
int mdat_len = (int)_IO.Position - first_frame_offset;
int header_len = first_frame_offset;
if (sample_count <= 0 && _position != 0)
2009-08-30 21:58:54 +00:00
{
2010-04-18 22:42:23 +00:00
sample_count = (int)_position;
header_len = max_header_len
+ m_settings.Padding
2010-04-18 22:42:23 +00:00
+ frame_count * 4 // stsz
+ frame_count * 4 / eparams.chunk_size; // stco
//if (header_len % 0x400 != 0)
// header_len += 0x400 - (header_len % 0x400);
}
if (!_creationTime.HasValue)
_creationTime = DateTime.Now;
if (header_len > first_frame_offset)
{
// if frame_count is high, need to rewrite
// the whole file to increase first_frame_offset
//System.Diagnostics.Trace.WriteLine(String.Format("Rewriting whole file: {0}/{1} + {2}", header_len, first_frame_offset, mdat_len));
// assert(_pathGiven);
string tmpPath = _path + ".tmp"; // TODO: make sure tmpPath is unique?
FileStream IO2 = new FileStream(tmpPath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
byte[] header = write_headers(header_len, mdat_len);
IO2.Write(header, 0, header_len);
_IO.Position = first_frame_offset;
int bufSize = Math.Min(mdat_len, 0x2000);
byte[] buffer = new byte[bufSize];
int n;
do
{
n = _IO.Read(buffer, 0, buffer.Length);
IO2.Write(buffer, 0, n);
} while (n != 0);
IO2.Close();
_IO.Close();
File.Delete(_path);
File.Move(tmpPath, _path);
}
else
{
//System.Diagnostics.Trace.WriteLine(String.Format("{0}/{1}", header_len, first_frame_offset));
byte[] header = write_headers(first_frame_offset, mdat_len);
_IO.Position = 0;
_IO.Write(header, 0, first_frame_offset);
_IO.Close();
2009-08-30 21:58:54 +00:00
}
inited = false;
}
2009-12-24 16:14:41 +00:00
#if INTEROP
long fake, KernelStart, UserStart;
GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart);
_userProcessorTime = new TimeSpan(UserStart);
#endif
2009-08-30 21:58:54 +00:00
}
public void Close()
{
DoClose();
2010-02-06 23:17:07 +00:00
if (sample_count > 0 && _position != sample_count)
2009-08-30 21:58:54 +00:00
throw new Exception("Samples written differs from the expected sample count.");
}
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 StereoMethod StereoMethod
{
get { return eparams.stereo_method; }
set { eparams.stereo_method = value; }
}
2009-12-24 16:14:41 +00:00
public WindowMethod WindowMethod
{
get { return eparams.window_method; }
set { eparams.window_method = value; }
}
2009-08-30 21:58:54 +00:00
public WindowFunction WindowFunction
{
get { return eparams.window_function; }
set { eparams.window_function = value; }
}
public bool DoSeekTable
{
get { return eparams.do_seektable; }
set { eparams.do_seektable = value; }
}
public int MinLPCOrder
{
get
{
return eparams.min_prediction_order;
}
set
{
if (value < 1)
throw new Exception("invalid MinLPCOrder " + value.ToString());
eparams.min_prediction_order = value;
if (eparams.max_prediction_order < value)
eparams.max_prediction_order = value;
}
}
public int MaxLPCOrder
{
get
{
return eparams.max_prediction_order;
}
set
{
if (value > 30 || value < eparams.min_prediction_order)
throw new Exception("invalid MaxLPCOrder " + value.ToString());
eparams.max_prediction_order = value;
if (eparams.min_prediction_order > value)
eparams.min_prediction_order = value;
}
}
public int MinHistoryModifier
{
get
{
return eparams.min_modifier;
}
set
{
if (value < 1)
throw new Exception("invalid MinHistoryModifier " + value.ToString());
eparams.min_modifier = value;
if (eparams.max_modifier < value)
eparams.max_modifier = value;
}
}
public int MaxHistoryModifier
{
get
{
return eparams.max_modifier;
}
set
{
2009-12-24 16:14:41 +00:00
if (value > 7)
2009-08-30 21:58:54 +00:00
throw new Exception("invalid MaxHistoryModifier " + value.ToString());
eparams.max_modifier = value;
if (eparams.min_modifier > value)
eparams.min_modifier = value;
}
}
2009-12-24 16:14:41 +00:00
public int HistoryMult
{
get
{
return history_mult;
}
set
{
if (value < 1 || value > 255)
throw new Exception("invalid history_mult");
history_mult = value;
}
}
public int InitialHistory
{
get
{
return initial_history;
}
set
{
if (value < 1 || value > 255)
throw new Exception("invalid initial_history");
initial_history = value;
}
}
2009-08-30 21:58:54 +00:00
public int EstimationDepth
{
get
{
return eparams.estimation_depth;
}
set
{
if (value > 32 || value < 1)
throw new Exception("invalid estimation_depth " + value.ToString());
eparams.estimation_depth = value;
}
}
2009-12-24 16:14:41 +00:00
public int AdaptivePasses
{
get
{
return eparams.adaptive_passes;
}
set
{
if (value >= lpc.MAX_LPC_PRECISIONS || value < 0)
throw new Exception("invalid adaptive_passes " + value.ToString());
eparams.adaptive_passes = value;
}
}
2009-08-30 21:58:54 +00:00
public TimeSpan UserProcessorTime
{
2009-12-24 16:14:41 +00:00
get
{
#if INTEROP
return _userProcessorTime;
#else
return TimeSpan(0);
#endif
}
2009-08-30 21:58:54 +00:00
}
/// <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 (Settings.PCM.ChannelCount == 2)
2009-08-30 21:58:54 +00:00
AudioSamples.Deinterlace(fsamples + samplesInBuffer, fsamples + Alac.MAX_BLOCKSIZE + samplesInBuffer, src, block);
else
for (int ch = 0; ch < Settings.PCM.ChannelCount; ch++)
2009-08-30 21:58:54 +00:00
{
int* psamples = fsamples + ch * Alac.MAX_BLOCKSIZE + samplesInBuffer;
int channels = Settings.PCM.ChannelCount;
2009-08-30 21:58:54 +00:00
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, int leftweight, int shift)
{
for (int i = 0; i < blocksize; i++)
{
leftM[i] = rightS[i] + ((leftS[i] - rightS[i]) * leftweight >> shift);
rightM[i] = leftS[i] - rightS[i];
}
}
private static int extend_sign32(int val, int bits)
{
return (val << (32 - bits)) >> (32 - bits);
}
2009-12-24 16:14:41 +00:00
private static int sign_only(int val)
2009-08-30 21:58:54 +00:00
{
2009-12-24 16:14:41 +00:00
return (val >> 31) + ((val - 1) >> 31) + 1;
2009-08-30 21:58:54 +00:00
}
unsafe static void alac_encode_residual_31(int* res, int* smp, int n)
{
res[0] = smp[0];
for (int i = 1; i < n; i++)
res[i] = smp[i] - smp[i - 1];
res[n] = 1; // Stop byte to help alac_entropy_coder;
}
2009-08-30 21:58:54 +00:00
unsafe static void alac_encode_residual_0(int* res, int* smp, int n)
2009-08-30 21:58:54 +00:00
{
AudioSamples.MemCpy(res, smp, n);
res[n] = 1; // Stop byte to help alac_entropy_coder;
}
2009-08-30 21:58:54 +00:00
unsafe static void alac_encode_residual(int* res, int* smp, int n, int order, int* coefs, int shift, int bps)
2009-08-30 21:58:54 +00:00
{
int csum = 0;
2009-12-24 16:14:41 +00:00
2009-08-30 21:58:54 +00:00
for (int i = order - 1; i >= 0; i--)
csum += coefs[i];
if (n <= order || order <= 0 || order > 30)
throw new Exception("invalid output");
/* generate warm-up samples */
res[0] = smp[0];
for (int i = 1; i <= order; i++)
2018-02-19 20:46:10 -05:00
res[i] = extend_sign32(smp[i] - smp[i - 1], bps);
2009-08-30 21:58:54 +00:00
2011-11-15 10:56:49 +00:00
#if aaa
// contains errors: if (resval * orig_sign <= 0) continue; is not the same as sign(resval) * sign(orig), because 0x80000000 * 0xffffffff < 0!
// probably should be if (resval == 0 || sign_only(resval) != orig_sign)
// sign_only(d0 ^ resval); is the same as sign_only(d0) * sign_only(resval);
// but sign_only(d0) * orig_sign is not the same (when d0 == 0x80000000)
switch (order)
{
case 8:
{
const int constOrder = 8;
int c0 = coefs[0], c1 = coefs[1], c2 = coefs[2], c3 = coefs[3], c4 = coefs[4], c5 = coefs[5], c6 = coefs[6], c7 = coefs[7];
int denhalf = 1 << (shift - 1);
for (int i = constOrder + 1; i < n; i++)
{
int sample = *(smp++);
int d0 = smp[0] - sample, d1 = smp[1] - sample, d2 = smp[2] - sample, d3 = smp[3] - sample, d4 = smp[4] - sample, d5 = smp[5] - sample, d6 = smp[6] - sample, d7 = smp[7] - sample;
int sum = denhalf + d0 * c0 + d1 * c1 + d2 * c2 + d3 * c3 + d4 * c4 + d5 * c5 + d6 * c6 + d7 * c7;
int resval = extend_sign32(smp[constOrder] - sample - (int)(sum >> shift), bps);
res[i] = resval;
if (resval == 0) continue;
int orig_sign = sign_only(resval);
int sign = sign_only(d0 ^ resval);
c0 += sign;
resval -= (d0 * sign >> shift) * (0 + 1);
if (resval * orig_sign <= 0) continue;
sign = sign_only(d1 ^ resval);
c1 += sign;
resval -= (d1 * sign >> shift) * (1 + 1);
if (resval * orig_sign <= 0) continue;
sign = sign_only(d2 ^ resval);
c2 += sign;
resval -= (d2 * sign >> shift) * (2 + 1);
if (resval * orig_sign <= 0) continue;
sign = sign_only(d3 ^ resval);
c3 += sign;
resval -= (d3 * sign >> shift) * (3 + 1);
if (resval * orig_sign <= 0) continue;
sign = sign_only(d4 ^ resval);
c4 += sign;
resval -= (d4 * sign >> shift) * (4 + 1);
if (resval * orig_sign <= 0) continue;
sign = sign_only(d5 ^ resval);
c5 += sign;
resval -= (d5 * sign >> shift) * (5 + 1);
if (resval * orig_sign <= 0) continue;
sign = sign_only(d6 ^ resval);
c6 += sign;
resval -= (d6 * sign >> shift) * (6 + 1);
if (resval * orig_sign <= 0) continue;
sign = sign_only(d7 ^ resval);
c7 += sign;
resval -= (d7 * sign >> shift) * (7 + 1);
}
coefs[0] = c0;
coefs[1] = c1;
coefs[2] = c2;
coefs[3] = c3;
coefs[4] = c4;
coefs[5] = c5;
coefs[6] = c6;
coefs[7] = c7;
res[n] = 1; // Stop byte to help alac_entropy_coder;
return;
}
}
#endif
/* general case */
2018-02-19 20:46:10 -05:00
for (int i = order + 1; i < n; i++)
2009-08-30 21:58:54 +00:00
{
int sample = *(smp++);
2009-12-24 16:14:41 +00:00
int/*long*/ sum = (1 << (shift - 1));
for (int j = 0; j < order; j++)
sum += (smp[j] - sample) * coefs[j];
int resval = extend_sign32(smp[order] - sample - (int)(sum >> shift), bps);
res[i] = resval;
if (resval > 0)
2009-08-30 21:58:54 +00:00
{
2009-12-24 16:14:41 +00:00
for (int j = 0; j < order && resval > 0; j++)
{
int val = smp[j] - sample;
int sign = sign_only(val);
coefs[j] += sign;
resval -= (val * sign >> shift) * (j + 1);
}
2009-08-30 21:58:54 +00:00
}
2009-12-24 16:14:41 +00:00
else
2009-08-30 21:58:54 +00:00
{
2009-12-24 16:14:41 +00:00
for (int j = 0; j < order && resval < 0; j++)
{
int val = smp[j] - sample;
int sign = -sign_only(val);
coefs[j] += sign;
resval -= (val * sign >> shift) * (j + 1);
}
2009-08-30 21:58:54 +00:00
}
}
res[n] = 1; // Stop byte to help alac_entropy_coder;
}
unsafe static int encode_scalar(int x, int k, int bps)
{
int divisor = (1 << k) - 1;
int q = x / divisor;
int r = x % divisor;
return q > 8 ? 9 + bps : q + k + (r - 1 >> 31) + 1;//== 0 ? 0 : 1);
}
unsafe void encode_scalar(BitWriter bitwriter, int x, int k, int bps)
{
k = Math.Min(k, k_modifier);
int divisor = (1 << k) - 1;
int q = x / divisor;
int r = x % divisor;
if (q > 8)
{
// write escape code and sample value directly
bitwriter.writebits(9, 0x1ff);
bitwriter.writebits(bps, x);
return;
}
// q times one, then 1 zero, e.g. q == 3 is written as 1110
int unary = ((1 << (q + 1)) - 2);
if (r == 0)
{
bitwriter.writebits(q + k, unary << (k - 1));
return;
}
bitwriter.writebits(q + 1 + k, (unary << k) + r + 1);
}
unsafe int alac_entropy_coder(int* res, int n, int bps, out int modifier)
{
int size = 1 << 30;
modifier = eparams.min_modifier;
for (int i = eparams.min_modifier; i <= eparams.max_modifier; i++)
{
2009-12-24 16:14:41 +00:00
int newsize = alac_entropy_estimate(res, n, bps, i);
2009-08-30 21:58:54 +00:00
if (size > newsize)
{
size = newsize;
modifier = i;
}
}
return size;
}
2011-11-15 10:56:49 +00:00
//unsafe int alac_entropy_coder(int* res, int n, int bps, int modifier)
//{
// int history = initial_history;
// int sign_modifier = 0;
// int rice_historymult = modifier * history_mult / 4;
// int size = 0;
// int* fin = res + n;
// while (res < fin)
// {
// int k = BitReader.log2i((history >> 9) + 3);
// int x = *(res++);
// x = (x << 1) ^ (x >> 31);
// size += encode_scalar(x - sign_modifier, Math.Min(k, k_modifier), bps);
// history += x * rice_historymult - ((history * rice_historymult) >> 9);
// sign_modifier = 0;
// if (x > 0xFFFF)
// history = 0xFFFF;
// if (history < 128 && res < fin)
// {
// k = 7 - BitReader.log2i(history) + ((history + 16) >> 6);
// int* res1 = res;
// while (*res == 0) // we have a stop byte, so need not check if res < fin
// res++;
// int block_size = (int)(res - res1);
// size += encode_scalar(block_size, Math.Min(k, k_modifier), 16);
// //sign_modifier = (block_size <= 0xFFFF) ? 1 : 0; //never happens
// sign_modifier = 1;
// history = 0;
// }
// }
// return size;
//}
2009-08-30 21:58:54 +00:00
2009-12-24 16:14:41 +00:00
/// <summary>
/// Crude estimation of entropy length
/// </summary>
/// <param name="res"></param>
/// <param name="n"></param>
/// <param name="bps"></param>
/// <param name="modifier"></param>
/// <returns></returns>
unsafe int alac_entropy_estimate(int* res, int n, int bps, int modifier)
{
int history = initial_history;
int rice_historymult = modifier * history_mult / 4;
int size = 0;
int* fin = res + n;
while (res < fin)
{
int x = *(res++);
x = (x << 1) ^ (x >> 31);
int k = BitReader.log2i((history >> 9) + 3);
k = k > k_modifier ? k_modifier : k;
size += (x >> k) > 8 ? 9 + bps : (x >> k) + k + 1;
history += x * rice_historymult - ((history * rice_historymult) >> 9);
2018-02-19 20:46:10 -05:00
if (x > 0xFFFF)
history = 0xFFFF;
}
return size;
2009-12-24 16:14:41 +00:00
}
2009-08-30 21:58:54 +00:00
unsafe void alac_entropy_coder(BitWriter bitwriter, int* res, int n, int bps, int modifier)
{
int history = initial_history;
int sign_modifier = 0;
int rice_historymult = modifier * history_mult / 4;
2009-12-24 16:14:41 +00:00
int* fin = res + n;
2009-08-30 21:58:54 +00:00
2009-12-24 16:14:41 +00:00
while (res < fin)
2009-08-30 21:58:54 +00:00
{
int k = BitReader.log2i((history >> 9) + 3);
2009-12-24 16:14:41 +00:00
int x = *(res++);
x = (x << 1) ^ (x >> 31);
2009-08-30 21:58:54 +00:00
encode_scalar(bitwriter, x - sign_modifier, k, bps);
history += x * rice_historymult - ((history * rice_historymult) >> 9);
sign_modifier = 0;
2018-02-19 20:46:10 -05:00
// this werid if actually hurts compression speed and ratio, but
// unfortunately cannot be removed because the decoder does the same.
if (x > 0xFFFF)
2009-08-30 21:58:54 +00:00
history = 0xFFFF;
2009-12-24 16:14:41 +00:00
if (history < 128 && res < fin)
2009-08-30 21:58:54 +00:00
{
k = 7 - BitReader.log2i(history) + ((history + 16) >> 6);
2009-12-24 16:14:41 +00:00
int* res1 = res;
while (*res == 0) // we have a stop byte, so need not check if res < fin
res++;
int block_size = (int)(res - res1);
2009-08-30 21:58:54 +00:00
encode_scalar(bitwriter, block_size, k, 16);
sign_modifier = (block_size <= 0xFFFF) ? 1 : 0;
history = 0;
}
}
}
2009-12-24 16:14:41 +00:00
unsafe void encode_residual_lpc_sub(ALACFrame frame, float* lpcs, int iWindow, int order, int ch)
2009-08-30 21:58:54 +00:00
{
2009-12-24 16:14:41 +00:00
// check if we already calculated with this order, window and precision
if ((frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[eparams.adaptive_passes] & (1U << (order - 1))) == 0)
{
frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[eparams.adaptive_passes] |= (1U << (order - 1));
2009-08-30 21:58:54 +00:00
2009-12-24 16:14:41 +00:00
uint cbits = 15U;
2009-08-30 21:58:54 +00:00
2009-12-24 16:14:41 +00:00
frame.current.order = order;
frame.current.window = iWindow;
2009-08-30 21:58:54 +00:00
int bps = Settings.PCM.BitsPerSample + Settings.PCM.ChannelCount - 1;
2009-08-30 21:58:54 +00:00
2009-12-24 16:14:41 +00:00
int* coefs = stackalloc int[lpc.MAX_LPC_ORDER];
2009-08-30 21:58:54 +00:00
2009-12-24 16:14:41 +00:00
//if (frame.subframes[ch].best.order == order && frame.subframes[ch].best.window == iWindow)
//{
// frame.current.shift = frame.subframes[ch].best.shift;
// for (int i = 0; i < frame.current.order; i++)
// frame.current.coefs[i] = frame.subframes[ch].best.coefs_adapted[i];
//}
//else
{
2009-08-30 21:58:54 +00:00
lpc.quantize_lpc_coefs(lpcs + (frame.current.order - 1) * lpc.MAX_LPC_ORDER,
frame.current.order, cbits, coefs, out frame.current.shift, 15, 1);
if (frame.current.shift < 0 || frame.current.shift > 15)
throw new Exception("negative shift");
for (int i = 0; i < frame.current.order; i++)
frame.current.coefs[i] = coefs[i];
2009-12-24 16:14:41 +00:00
}
for (int i = 0; i < frame.current.order; i++)
coefs[i] = frame.current.coefs[frame.current.order - 1 - i];
for (int i = frame.current.order; i < lpc.MAX_LPC_ORDER; i++)
coefs[i] = 0;
2009-08-30 21:58:54 +00:00
2009-12-24 16:14:41 +00:00
alac_encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize,
frame.current.order, coefs, frame.current.shift, bps);
for (int i = 0; i < frame.current.order; i++)
frame.current.coefs_adapted[i] = coefs[frame.current.order - 1 - i];
for (int adaptive_pass = 0; adaptive_pass < eparams.adaptive_passes; adaptive_pass++)
{
2009-08-30 21:58:54 +00:00
for (int i = 0; i < frame.current.order; i++)
2009-12-24 16:14:41 +00:00
frame.current.coefs[i] = frame.current.coefs_adapted[i];
2009-08-30 21:58:54 +00:00
alac_encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize,
frame.current.order, coefs, frame.current.shift, bps);
2009-12-24 16:14:41 +00:00
for (int i = 0; i < frame.current.order; i++)
frame.current.coefs_adapted[i] = coefs[frame.current.order - 1 - i];
2009-08-30 21:58:54 +00:00
}
2009-12-24 16:14:41 +00:00
frame.current.size = (uint)(alac_entropy_estimate(frame.current.residual, frame.blocksize, bps, eparams.max_modifier) + 16 + 16 * order);
frame.ChooseBestSubframe(ch);
}
2009-08-30 21:58:54 +00:00
}
unsafe void encode_residual(ALACFrame frame, int ch, int pass, int best_windows)
{
int* smp = frame.subframes[ch].samples;
int i, n = frame.blocksize;
int bps = Settings.PCM.BitsPerSample + Settings.PCM.ChannelCount - 1;
2009-08-30 21:58:54 +00:00
// FIXED
//if (0 == (2 & frame.subframes[ch].done_fixed) && (pass != 1 || n < eparams.max_prediction_order))
//{
// frame.subframes[ch].done_fixed |= 2;
// frame.current.order = 31;
// frame.current.window = -1;
// alac_encode_residual_31(frame.current.residual, frame.subframes[ch].samples, frame.blocksize);
// frame.current.size = (uint)(alac_entropy_coder(frame.current.residual, frame.blocksize, bps, out frame.current.ricemodifier) + 16);
// frame.ChooseBestSubframe(ch);
//}
//if (0 == (1 & frame.subframes[ch].done_fixed) && (pass != 1 || n < eparams.max_prediction_order))
if (0 == (1 & frame.subframes[ch].done_fixed) && n < eparams.max_prediction_order)
{
frame.subframes[ch].done_fixed |= 1;
frame.current.order = 0;
frame.current.window = -1;
alac_encode_residual_0(frame.current.residual, frame.subframes[ch].samples, frame.blocksize);
frame.current.size = (uint)(alac_entropy_coder(frame.current.residual, frame.blocksize, bps, out frame.current.ricemodifier) + 16);
frame.ChooseBestSubframe(ch);
}
2009-08-30 21:58:54 +00:00
2018-02-19 20:46:10 -05:00
// Before returning, we must call ChooseBestSubframe at least once.
if (n < eparams.max_prediction_order) return;
float* lpcs = stackalloc float[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER];
2009-08-30 21:58:54 +00:00
int min_order = eparams.min_prediction_order;
int max_order = eparams.max_prediction_order;
for (int iWindow = 0; iWindow < _windowcount; iWindow++)
{
if (0 == (best_windows & (1 << iWindow)))
2009-08-30 21:58:54 +00:00
continue;
LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow];
fixed (LpcWindowSection* sections = &windowSections[iWindow, 0])
lpc_ctx.GetReflection(
frame.subframes[ch].sf, max_order, frame.blocksize, smp,
frame.window_buffer + iWindow * Alac.MAX_BLOCKSIZE * 2, sections);
2009-08-30 21:58:54 +00:00
lpc_ctx.ComputeLPC(lpcs);
2012-07-12 00:11:39 +00:00
lpc_ctx.SortOrdersAkaike(frame.blocksize, eparams.estimation_depth, min_order, max_order, 5.0, 1.0/18);
2009-12-24 16:14:41 +00:00
for (i = 0; i < eparams.estimation_depth && i < max_order; i++)
encode_residual_lpc_sub(frame, lpcs, iWindow, lpc_ctx.best_orders[i], ch);
2009-08-30 21:58:54 +00:00
}
}
unsafe void output_frame_header(ALACFrame frame, BitWriter bitwriter)
{
bitwriter.writebits(3, Settings.PCM.ChannelCount - 1);
2009-08-30 21:58:54 +00:00
bitwriter.writebits(16, 0);
bitwriter.writebits(1, frame.blocksize != m_blockSize ? 1 : 0); // sample count is in the header
2009-08-30 21:58:54 +00:00
bitwriter.writebits(2, 0); // wasted bytes
bitwriter.writebits(1, frame.type == FrameType.Verbatim ? 1 : 0); // is verbatim
if (frame.blocksize != m_blockSize)
2009-08-30 21:58:54 +00:00
bitwriter.writebits(32, frame.blocksize);
if (frame.type != FrameType.Verbatim)
{
bitwriter.writebits(8, frame.interlacing_shift);
bitwriter.writebits(8, frame.interlacing_leftweight);
for (int ch = 0; ch < Settings.PCM.ChannelCount; ch++)
2009-08-30 21:58:54 +00:00
{
bitwriter.writebits(4, 0); // prediction type
bitwriter.writebits(4, frame.subframes[ch].best.shift);
bitwriter.writebits(3, frame.subframes[ch].best.ricemodifier);
bitwriter.writebits(5, frame.subframes[ch].best.order);
if (frame.subframes[ch].best.order != 31)
for (int c = 0; c < frame.subframes[ch].best.order; c++)
bitwriter.writebits_signed(16, frame.subframes[ch].best.coefs[c]);
}
}
}
void output_frame_footer(BitWriter bitwriter)
{
bitwriter.writebits(3, 7);
bitwriter.flush();
}
unsafe void encode_residual_pass1(ALACFrame frame, int ch, int best_windows)
2009-08-30 21:58:54 +00:00
{
int max_prediction_order = eparams.max_prediction_order;
int estimation_depth = eparams.estimation_depth;
int min_modifier = eparams.min_modifier;
2009-12-24 16:14:41 +00:00
int adaptive_passes = eparams.adaptive_passes;
2009-08-30 21:58:54 +00:00
eparams.max_prediction_order = Math.Min(8,eparams.max_prediction_order);
eparams.estimation_depth = 1;
eparams.min_modifier = eparams.max_modifier;
2009-12-24 16:14:41 +00:00
eparams.adaptive_passes = 0;
encode_residual(frame, ch, 1, best_windows);
2009-08-30 21:58:54 +00:00
eparams.max_prediction_order = max_prediction_order;
eparams.estimation_depth = estimation_depth;
eparams.min_modifier = min_modifier;
2009-12-24 16:14:41 +00:00
eparams.adaptive_passes = adaptive_passes;
2009-08-30 21:58:54 +00:00
}
unsafe void encode_residual_pass2(ALACFrame frame, int ch)
{
2009-12-24 16:14:41 +00:00
encode_residual(frame, ch, 2, estimate_best_window(frame, ch));
2009-08-30 21:58:54 +00:00
}
unsafe int estimate_best_windows_akaike(ALACFrame 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]++;
}
int order = Math.Min(4, eparams.max_prediction_order);
float* err = stackalloc float[lpc.MAX_LPC_ORDER];
for (int i = 0; i < _windowcount; i++)
{
LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[i];
fixed (LpcWindowSection* sections = &windowSections[i, 0])
lpc_ctx.GetReflection(
frame.subframes[ch].sf, order, frame.blocksize,
frame.subframes[ch].samples,
frame.window_buffer + i * Alac.MAX_BLOCKSIZE * 2, sections);
lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, order, 4.5, 0.0);
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;
}
2009-12-24 16:14:41 +00:00
unsafe int estimate_best_window(ALACFrame frame, int ch)
2009-08-30 21:58:54 +00:00
{
2009-12-24 16:14:41 +00:00
if (_windowcount == 1)
return 1;
2009-12-24 16:14:41 +00:00
switch (eparams.window_method)
2009-08-30 21:58:54 +00:00
{
2009-12-24 16:14:41 +00:00
case WindowMethod.Estimate:
return estimate_best_windows_akaike(frame, ch, 1, false);
case WindowMethod.EstimateN:
return estimate_best_windows_akaike(frame, ch, 1, true);
case WindowMethod.EvaluateN:
encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 1, true));
return 1 << frame.subframes[ch].best.window;
case WindowMethod.Evaluate:
2009-12-24 16:14:41 +00:00
encode_residual_pass1(frame, ch, -1);
return 1 << frame.subframes[ch].best.window;
2009-12-24 16:14:41 +00:00
case WindowMethod.Search:
return -1;
2009-08-30 21:58:54 +00:00
}
2009-12-24 16:14:41 +00:00
return -1;
2009-08-30 21:58:54 +00:00
}
unsafe void estimate_frame(ALACFrame frame, bool do_midside)
{
int subframes = do_midside ? 5 : Settings.PCM.ChannelCount;
2009-08-30 21:58:54 +00:00
switch (eparams.stereo_method)
{
2009-12-24 16:14:41 +00:00
case StereoMethod.Estimate:
2009-08-30 21:58:54 +00:00
for (int ch = 0; ch < subframes; ch++)
{
int iWindow = 0;
LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow];
int stereo_order = Math.Min(8, eparams.max_prediction_order);
2009-12-24 16:14:41 +00:00
double alpha = 1.5; // 4.5 + eparams.max_prediction_order / 10.0;
fixed (LpcWindowSection* sections = &windowSections[iWindow, 0])
lpc_ctx.GetReflection(
frame.subframes[ch].sf, stereo_order, frame.blocksize,
frame.subframes[ch].samples,
frame.window_buffer + iWindow * Alac.MAX_BLOCKSIZE * 2, sections);
2012-07-12 00:11:39 +00:00
lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, stereo_order, alpha, 0);
2009-12-24 16:14:41 +00:00
frame.subframes[ch].best.size = (uint)Math.Max(0, lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], alpha, 0));
2009-08-30 21:58:54 +00:00
}
break;
2009-12-24 16:14:41 +00:00
case StereoMethod.Evaluate:
for (int ch = 0; ch < subframes; ch++)
encode_residual_pass1(frame, ch, 1);
2009-12-24 16:14:41 +00:00
break;
2009-08-30 21:58:54 +00:00
case StereoMethod.Search:
for (int ch = 0; ch < subframes; ch++)
2009-12-24 16:14:41 +00:00
encode_residual_pass2(frame, ch);
2009-08-30 21:58:54 +00:00
break;
}
}
unsafe uint measure_frame_size(ALACFrame frame, bool do_midside)
{
// crude estimation of header/footer size
uint total = 16 + 3;
if (do_midside)
{
uint bitsBest = frame.subframes[0].best.size + frame.subframes[1].best.size;
frame.interlacing_leftweight = 0;
frame.interlacing_shift = 0;
if (bitsBest > frame.subframes[3].best.size + frame.subframes[0].best.size) // leftside
{
bitsBest = frame.subframes[3].best.size + frame.subframes[0].best.size;
frame.interlacing_leftweight = 1;
frame.interlacing_shift = 0;
}
if (bitsBest > frame.subframes[3].best.size + frame.subframes[2].best.size) // midside
{
bitsBest = frame.subframes[3].best.size + frame.subframes[2].best.size;
frame.interlacing_leftweight = 1;
frame.interlacing_shift = 1;
}
if (bitsBest > frame.subframes[3].best.size + frame.subframes[4].best.size) // rightside
{
bitsBest = frame.subframes[3].best.size + frame.subframes[4].best.size;
frame.interlacing_leftweight = 1;
frame.interlacing_shift = 31;
}
return total + bitsBest;
}
for (int ch = 0; ch < Settings.PCM.ChannelCount; ch++)
2009-08-30 21:58:54 +00:00
total += frame.subframes[ch].best.size;
return total;
}
unsafe void encode_estimated_frame(ALACFrame frame)
{
switch (eparams.stereo_method)
{
2009-12-24 16:14:41 +00:00
case StereoMethod.Estimate:
for (int ch = 0; ch < Settings.PCM.ChannelCount; ch++)
2009-08-30 21:58:54 +00:00
{
2009-12-24 16:14:41 +00:00
frame.subframes[ch].best.size = AudioSamples.UINT32_MAX;
2009-08-30 21:58:54 +00:00
encode_residual_pass2(frame, ch);
}
break;
2009-12-24 16:14:41 +00:00
case StereoMethod.Evaluate:
for (int ch = 0; ch < Settings.PCM.ChannelCount; ch++)
2009-12-24 16:14:41 +00:00
encode_residual_pass2(frame, ch);
break;
2009-08-30 21:58:54 +00:00
case StereoMethod.Search:
break;
}
}
2009-12-24 16:14:41 +00:00
unsafe delegate void window_function(float* window, int size);
2009-08-30 21:58:54 +00:00
2009-12-24 16:14:41 +00:00
unsafe void calculate_window(float * window, window_function func, WindowFunction flag)
2009-08-30 21:58:54 +00:00
{
if ((eparams.window_function & flag) == 0 || _windowcount == lpc.MAX_LPC_WINDOWS)
2009-08-30 21:58:54 +00:00
return;
int sz = _windowsize;
2009-12-24 16:14:41 +00:00
float* pos = window + _windowcount * Alac.MAX_BLOCKSIZE * 2;
2009-08-30 21:58:54 +00:00
do
{
windowSections[_windowcount, 0].setData(0, sz);
for (int j = 1; j < lpc.MAX_LPC_SECTIONS; j++)
windowSections[_windowcount, j].setZero(sz, sz);
func(pos, sz);
break;
2009-08-30 21:58:54 +00:00
if ((sz & 1) != 0)
break;
pos += sz;
sz >>= 1;
} while (sz >= 32);
windowType[_windowcount] = flag;
_windowcount++;
2009-08-30 21:58:54 +00:00
}
unsafe int encode_frame(ref int size)
{
fixed (int* s = samplesBuffer, r = residualBuffer)
2009-12-24 16:14:41 +00:00
fixed (float * window = windowBuffer)
2009-08-30 21:58:54 +00:00
{
frame.InitSize(size);
if (frame.blocksize != _windowsize && frame.blocksize > 4)
{
_windowsize = frame.blocksize;
_windowcount = 0;
2009-12-24 16:14:41 +00:00
calculate_window(window, lpc.window_welch, WindowFunction.Welch);
calculate_window(window, lpc.window_bartlett, WindowFunction.Bartlett);
calculate_window(window, (w, wsz) =>
{
lpc.window_tukey(w, wsz, 0.5);
}, WindowFunction.Tukey);
2009-12-24 16:14:41 +00:00
calculate_window(window, lpc.window_hann, WindowFunction.Hann);
calculate_window(window, lpc.window_flattop, WindowFunction.Flattop);
int tukey_parts = 2;
double overlap = -0.3;
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, 0.1, 0.1,
m / (tukey_parts + overlap_units),
(m + 1 + overlap_units) / (tukey_parts + overlap_units));
}, WindowFunction.PartialTukey);
tukey_parts = 3;
overlap = -0.1;
//overlap = 0.1;
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, 0.1, 0.1,
m / (tukey_parts + overlap_units),
(m + 1 + overlap_units) / (tukey_parts + overlap_units));
}, WindowFunction.PunchoutTukey);
if (_windowcount == 0)
2009-08-30 21:58:54 +00:00
throw new Exception("invalid windowfunction");
fixed (LpcWindowSection* sections = &windowSections[0, 0])
LpcWindowSection.Detect(_windowcount, window, Alac.MAX_BLOCKSIZE * 2, _windowsize, Settings.PCM.BitsPerSample, sections);
}
2009-08-30 21:58:54 +00:00
frame.window_buffer = window;
int bps = Settings.PCM.BitsPerSample + Settings.PCM.ChannelCount - 1;
if (Settings.PCM.ChannelCount != 2 || frame.blocksize <= 32 || eparams.stereo_method == StereoMethod.Independent)
2009-08-30 21:58:54 +00:00
{
frame.current.residual = r + Settings.PCM.ChannelCount * Alac.MAX_BLOCKSIZE;
2009-08-30 21:58:54 +00:00
for (int ch = 0; ch < Settings.PCM.ChannelCount; ch++)
2009-08-30 21:58:54 +00:00
frame.subframes[ch].Init(s + ch * Alac.MAX_BLOCKSIZE, r + ch * Alac.MAX_BLOCKSIZE);
for (int ch = 0; ch < Settings.PCM.ChannelCount; ch++)
2009-12-24 16:14:41 +00:00
encode_residual_pass2(frame, ch);
2009-08-30 21:58:54 +00:00
}
else
{
channel_decorrelation(s, s + Alac.MAX_BLOCKSIZE, s + 2 * Alac.MAX_BLOCKSIZE, s + 3 * Alac.MAX_BLOCKSIZE, frame.blocksize, 1, 1);
channel_decorrelation(s, s + Alac.MAX_BLOCKSIZE, s + 4 * Alac.MAX_BLOCKSIZE, s + 3 * Alac.MAX_BLOCKSIZE, frame.blocksize, 1, 31);
frame.current.residual = r + 5 * Alac.MAX_BLOCKSIZE;
for (int ch = 0; ch < 5; ch++)
frame.subframes[ch].Init(s + ch * Alac.MAX_BLOCKSIZE, r + ch * Alac.MAX_BLOCKSIZE);
estimate_frame(frame, true);
measure_frame_size(frame, true);
frame.ChooseSubframes();
encode_estimated_frame(frame);
}
2009-12-24 16:14:41 +00:00
for (int ch = 0; ch < Settings.PCM.ChannelCount; ch++)
2009-12-24 16:14:41 +00:00
{
if (eparams.min_modifier == eparams.max_modifier)
frame.subframes[ch].best.ricemodifier = eparams.max_modifier;
else
/*frame.subframes[ch].best.size = 16 + 16 * order + */
alac_entropy_coder(frame.subframes[ch].best.residual, frame.blocksize, bps, out frame.subframes[ch].best.ricemodifier);
}
2009-08-30 21:58:54 +00:00
uint fs = measure_frame_size(frame, false);
frame.type = ((int)fs > frame.blocksize * Settings.PCM.ChannelCount * bps) ? FrameType.Verbatim : FrameType.Compressed;
2009-08-30 21:58:54 +00:00
BitWriter bitwriter = new BitWriter(frame_buffer, 0, max_frame_size);
output_frame_header(frame, bitwriter);
if (frame.type == FrameType.Verbatim)
{
int obps = Settings.PCM.BitsPerSample;
2018-02-19 20:46:10 -05:00
// Second call to ChooseSubframes will undo the
// channel swap
frame.ChooseSubframes();
for (int i = 0; i < frame.blocksize; i++)
for (int ch = 0; ch < Settings.PCM.ChannelCount; ch++)
2010-02-06 23:17:07 +00:00
bitwriter.writebits_signed(obps, frame.subframes[ch].samples[i]);
2009-08-30 21:58:54 +00:00
}
else if (frame.type == FrameType.Compressed)
{
for (int ch = 0; ch < Settings.PCM.ChannelCount; ch++)
2009-08-30 21:58:54 +00:00
alac_entropy_coder(bitwriter, frame.subframes[ch].best.residual, frame.blocksize,
bps, frame.subframes[ch].best.ricemodifier);
}
output_frame_footer(bitwriter);
2018-02-19 20:46:10 -05:00
if (bitwriter.Length >= max_frame_size)
throw new Exception("buffer overflow");
2009-08-30 21:58:54 +00:00
2018-02-19 20:46:10 -05:00
if (_sample_byte_size.Length <= frame_count)
2010-02-06 23:17:07 +00:00
{
uint[] tmp = new uint[frame_count * 2];
Array.Copy(_sample_byte_size, tmp, _sample_byte_size.Length);
_sample_byte_size = tmp;
}
2009-08-30 21:58:54 +00:00
_sample_byte_size[frame_count++] = (uint)bitwriter.Length;
size = frame.blocksize;
return bitwriter.Length;
}
}
unsafe int output_frame(int blocksize)
{
if (verify != null)
{
fixed (int* s = verifyBuffer, r = samplesBuffer)
for (int ch = 0; ch < Settings.PCM.ChannelCount; ch++)
AudioSamples.MemCpy(s + ch * Alac.MAX_BLOCKSIZE, r + ch * Alac.MAX_BLOCKSIZE, blocksize);
2009-08-30 21:58:54 +00:00
}
//if (0 != eparams.variable_block_size && 0 == (m_blockSize & 7) && m_blockSize >= 128)
2009-08-30 21:58:54 +00:00
// fs = encode_frame_vbs();
//else
int bs = blocksize;
int fs = encode_frame(ref bs);
_position += bs;
_IO.Write(frame_buffer, 0, fs);
_totalSize += fs;
if (verify != null)
{
int decoded = verify.DecodeFrame(frame_buffer, 0, fs);
2010-02-06 23:17:07 +00:00
if (decoded != fs || verify.Remaining != bs)
2009-08-30 21:58:54 +00:00
throw new Exception("validation failed!");
int[,] deinterlaced = new int[bs, Settings.PCM.ChannelCount];
2010-02-06 23:17:07 +00:00
verify.deinterlace(deinterlaced, 0, bs);
2009-08-30 21:58:54 +00:00
fixed (int* s = verifyBuffer, r = deinterlaced)
{
int channels = Settings.PCM.ChannelCount;
2009-08-30 21:58:54 +00:00
for (int i = 0; i < bs; i++)
for (int ch = 0; ch < Settings.PCM.ChannelCount; ch++)
2009-08-30 21:58:54 +00:00
if (r[i * channels + ch] != s[ch * Alac.MAX_BLOCKSIZE + i])
throw new Exception("validation failed!");
}
}
if (bs < blocksize)
{
fixed (int* s = samplesBuffer)
for (int ch = 0; ch < Settings.PCM.ChannelCount; ch++)
AudioSamples.MemCpy(s + ch * Alac.MAX_BLOCKSIZE, s + bs + ch * Alac.MAX_BLOCKSIZE, blocksize - bs);
2009-08-30 21:58:54 +00:00
}
samplesInBuffer -= bs;
return bs;
}
2010-02-06 23:17:07 +00:00
public void Write(AudioBuffer buff)
2009-08-30 21:58:54 +00:00
{
if (!inited)
{
2010-04-18 22:42:23 +00:00
if (!_pathGiven && sample_count <= 0)
throw new NotSupportedException("input and output are both pipes");
2009-08-30 21:58:54 +00:00
if (_IO == null)
2010-04-18 22:42:23 +00:00
_IO = new FileStream(_path, FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
if (_IO != null && !_IO.CanSeek)
throw new NotSupportedException("stream doesn't support seeking");
encode_init();
2009-08-30 21:58:54 +00:00
inited = true;
}
2010-02-06 23:17:07 +00:00
buff.Prepare(this);
int pos = 0;
int len = buff.Length;
2009-08-30 21:58:54 +00:00
while (len > 0)
{
int block = Math.Min(len, m_blockSize - samplesInBuffer);
2009-08-30 21:58:54 +00:00
2010-02-06 23:17:07 +00:00
copy_samples(buff.Samples, pos, block);
2009-08-30 21:58:54 +00:00
len -= block;
pos += block;
while (samplesInBuffer >= m_blockSize)
output_frame(m_blockSize);
2009-08-30 21:58:54 +00:00
}
}
public string Path { get { return _path; } }
2010-02-06 23:17:07 +00:00
private DateTime? _creationTime = null;
public DateTime CreationTime
{
set
{
_creationTime = value;
}
}
public static string Vendor
2010-02-06 23:17:07 +00:00
{
get
{
var version = typeof(AudioEncoder).Assembly.GetName().Version;
return vendor_string ?? "CUETools " + version.Major + "." + version.Minor + "." + version.Build;
}
2010-02-06 23:17:07 +00:00
set
{
vendor_string = value;
}
}
static string vendor_string = null;
2009-08-30 21:58:54 +00:00
int select_blocksize(int samplerate, int time_ms)
{
int target = (samplerate * time_ms) / 1000;
int blocksize = 1024;
while (target >= blocksize)
blocksize <<= 1;
return blocksize >> 1;
}
2010-04-18 22:42:23 +00:00
void write_chunk_mvhd(BitWriter bitwriter)
2009-08-30 21:58:54 +00:00
{
chunk_start(bitwriter);
{
bitwriter.write('m', 'v', 'h', 'd');
bitwriter.writebits(32, 0);
2010-04-18 22:42:23 +00:00
bitwriter.writebits(_creationTime.Value);
bitwriter.writebits(_creationTime.Value);
bitwriter.writebits(32, Settings.PCM.SampleRate);
2009-08-30 21:58:54 +00:00
bitwriter.writebits(32, sample_count);
bitwriter.writebits(32, 0x00010000); // reserved (preferred rate) 1.0 = normal
bitwriter.writebits(16, 0x0100); // reserved (preferred volume) 1.0 = normal
bitwriter.writebytes(10, 0); // reserved
bitwriter.writebits(32, 0x00010000); // reserved (matrix structure)
bitwriter.writebits(32, 0x00000000); // reserved (matrix structure)
bitwriter.writebits(32, 0x00000000); // reserved (matrix structure)
bitwriter.writebits(32, 0x00000000); // reserved (matrix structure)
bitwriter.writebits(32, 0x00010000); // reserved (matrix structure)
bitwriter.writebits(32, 0x00000000); // reserved (matrix structure)
bitwriter.writebits(32, 0x00000000); // reserved (matrix structure)
bitwriter.writebits(32, 0x00000000); // reserved (matrix structure)
bitwriter.writebits(32, 0x40000000); // reserved (matrix structure)
bitwriter.writebits(32, 0); // preview time
bitwriter.writebits(32, 0); // preview duration
bitwriter.writebits(32, 0); // poster time
bitwriter.writebits(32, 0); // selection time
bitwriter.writebits(32, 0); // selection duration
bitwriter.writebits(32, 0); // current time
bitwriter.writebits(32, 2); // next track ID
}
chunk_end(bitwriter);
}
2010-04-18 22:42:23 +00:00
void write_chunk_minf(BitWriter bitwriter, int header_len)
2009-08-30 21:58:54 +00:00
{
chunk_start(bitwriter);
{
bitwriter.write('m', 'i', 'n', 'f');
chunk_start(bitwriter);
{
bitwriter.write('s', 'm', 'h', 'd');
bitwriter.writebits(32, 0); // version & flags
bitwriter.writebits(16, 0); // reserved (balance)
bitwriter.writebits(16, 0); // reserved
}
chunk_end(bitwriter);
chunk_start(bitwriter);
{
bitwriter.write('d', 'i', 'n', 'f');
chunk_start(bitwriter);
{
bitwriter.write('d', 'r', 'e', 'f');
bitwriter.writebits(32, 0); // version & flags
bitwriter.writebits(32, 1); // entry count
chunk_start(bitwriter);
{
bitwriter.write('u', 'r', 'l', ' ');
bitwriter.writebits(32, 1); // version & flags
}
chunk_end(bitwriter);
}
chunk_end(bitwriter);
}
chunk_end(bitwriter);
chunk_start(bitwriter);
{
bitwriter.write('s', 't', 'b', 'l');
chunk_start(bitwriter);
{
bitwriter.write('s', 't', 's', 'd');
bitwriter.writebits(32, 0); // version & flags
bitwriter.writebits(32, 1); // entry count
chunk_start(bitwriter);
{
bitwriter.write('a', 'l', 'a', 'c');
bitwriter.writebits(32, 0); // reserved
bitwriter.writebits(16, 0); // reserved
bitwriter.writebits(16, 1); // data reference index
bitwriter.writebits(16, 0); // version
bitwriter.writebits(16, 0); // revision
bitwriter.writebits(32, 0); // reserved
bitwriter.writebits(16, 2); // reserved channels
bitwriter.writebits(16, 16); // reserved bps
bitwriter.writebits(16, 0); // reserved compression ID
bitwriter.writebits(16, 0); // packet size
bitwriter.writebits(16, Settings.PCM.SampleRate); // time scale
2009-08-30 21:58:54 +00:00
bitwriter.writebits(16, 0); // reserved
chunk_start(bitwriter);
{
2010-04-18 22:42:23 +00:00
int max_fs = 0;
long sum_fs = 0;
for (int i = 0; i < frame_count; i++)
{
max_fs = Math.Max(max_fs, (int)_sample_byte_size[i]);
sum_fs += (int)_sample_byte_size[i];
}
2009-08-30 21:58:54 +00:00
bitwriter.write('a', 'l', 'a', 'c');
bitwriter.writebits(32, 0); // reserved
bitwriter.writebits(32, m_blockSize); // max frame size
2009-08-30 21:58:54 +00:00
bitwriter.writebits(8, 0); // reserved
bitwriter.writebits(8, Settings.PCM.BitsPerSample);
2009-08-30 21:58:54 +00:00
bitwriter.writebits(8, history_mult);
bitwriter.writebits(8, initial_history);
bitwriter.writebits(8, k_modifier);
bitwriter.writebits(8, Settings.PCM.ChannelCount); // channels
2010-04-18 22:42:23 +00:00
bitwriter.writebits(16, 0); // reserved or 0x00 0xff????
bitwriter.writebits(32, max_fs);
bitwriter.writebits(32, (int)(8 * sum_fs * Settings.PCM.SampleRate / sample_count)); // average bitrate
bitwriter.writebits(32, Settings.PCM.SampleRate);
2009-08-30 21:58:54 +00:00
}
chunk_end(bitwriter);
}
chunk_end(bitwriter);
}
chunk_end(bitwriter);
chunk_start(bitwriter);
{
bitwriter.write('s', 't', 't', 's');
bitwriter.writebits(32, 0); // version & flags
if (sample_count % m_blockSize == 0)
2009-08-30 21:58:54 +00:00
{
bitwriter.writebits(32, 1); // entries
bitwriter.writebits(32, sample_count / m_blockSize);
bitwriter.writebits(32, m_blockSize);
2009-08-30 21:58:54 +00:00
}
else
{
bitwriter.writebits(32, 2); // entries
bitwriter.writebits(32, sample_count / m_blockSize);
bitwriter.writebits(32, m_blockSize);
2009-08-30 21:58:54 +00:00
bitwriter.writebits(32, 1);
bitwriter.writebits(32, sample_count % m_blockSize);
2009-08-30 21:58:54 +00:00
}
}
chunk_end(bitwriter);
chunk_start(bitwriter);
{
bitwriter.write('s', 't', 's', 'c');
bitwriter.writebits(32, 0); // version & flags
2010-04-18 22:42:23 +00:00
if (frame_count % eparams.chunk_size == 0)
{
bitwriter.writebits(32, 1); // entries
bitwriter.writebits(32, 1); // first chunk
bitwriter.writebits(32, eparams.chunk_size); // samples in chunk
bitwriter.writebits(32, 1); // sample description index
}
else
{
bitwriter.writebits(32, 2); // entries
bitwriter.writebits(32, 1); // first chunk
bitwriter.writebits(32, eparams.chunk_size); // samples in chunk
bitwriter.writebits(32, 1); // sample description index
bitwriter.writebits(32, 1 + frame_count / eparams.chunk_size); // first chunk
bitwriter.writebits(32, frame_count % eparams.chunk_size); // samples in chunk
bitwriter.writebits(32, 1); // sample description index
}
2009-08-30 21:58:54 +00:00
}
chunk_end(bitwriter);
chunk_start(bitwriter);
{
2010-04-18 22:42:23 +00:00
bitwriter.write('s', 't', 's', 'z'); // stsz
2009-08-30 21:58:54 +00:00
bitwriter.writebits(32, 0); // version & flags
bitwriter.writebits(32, 0); // sample size (0 == variable)
bitwriter.writebits(32, frame_count); // entry count
for (int i = 0; i < frame_count; i++)
bitwriter.writebits(32, _sample_byte_size[i]);
}
chunk_end(bitwriter);
chunk_start(bitwriter);
{
2010-04-18 22:42:23 +00:00
bitwriter.write('s', 't', 'c', 'o'); // stco
2009-08-30 21:58:54 +00:00
bitwriter.writebits(32, 0); // version & flags
2010-04-18 22:42:23 +00:00
bitwriter.writebits(32, (frame_count + eparams.chunk_size - 1) / eparams.chunk_size); // entry count
int pos = header_len;
2009-08-30 21:58:54 +00:00
for (int i = 0; i < frame_count; i++)
{
2010-04-18 22:42:23 +00:00
if (i % eparams.chunk_size == 0) bitwriter.writebits(32, pos);
pos += (int)_sample_byte_size[i];
2009-08-30 21:58:54 +00:00
}
}
chunk_end(bitwriter);
}
chunk_end(bitwriter);
}
chunk_end(bitwriter);
}
2010-04-18 22:42:23 +00:00
void write_chunk_mdia(BitWriter bitwriter, int header_len)
2009-08-30 21:58:54 +00:00
{
chunk_start(bitwriter);
{
bitwriter.write('m', 'd', 'i', 'a');
chunk_start(bitwriter);
{
bitwriter.write('m', 'd', 'h', 'd');
bitwriter.writebits(32, 0); // version & flags
2010-04-18 22:42:23 +00:00
bitwriter.writebits(_creationTime.Value);
bitwriter.writebits(_creationTime.Value);
bitwriter.writebits(32, Settings.PCM.SampleRate);
2009-08-30 21:58:54 +00:00
bitwriter.writebits(32, sample_count);
bitwriter.writebits(16, 0x55c4); // language
bitwriter.writebits(16, 0); // quality
}
chunk_end(bitwriter);
chunk_start(bitwriter);
{
bitwriter.write('h', 'd', 'l', 'r');
bitwriter.writebits(32, 0); // version & flags
bitwriter.writebits(32, 0); // hdlr
bitwriter.write('s', 'o', 'u', 'n');
bitwriter.writebits(32, 0); // reserved
bitwriter.writebits(32, 0); // reserved
bitwriter.writebits(32, 0); // reserved
2010-04-18 22:42:23 +00:00
bitwriter.writebits(8, 0); //bitwriter.writebits(8, "SoundHandler".Length);
bitwriter.writebits(8, 0); //bitwriter.write("SoundHandler");
2009-08-30 21:58:54 +00:00
}
chunk_end(bitwriter);
2010-04-18 22:42:23 +00:00
write_chunk_minf(bitwriter, header_len);
2009-08-30 21:58:54 +00:00
}
chunk_end(bitwriter);
}
2010-04-18 22:42:23 +00:00
void write_chunk_trak(BitWriter bitwriter, int header_len)
2009-08-30 21:58:54 +00:00
{
chunk_start(bitwriter);
{
bitwriter.write('t', 'r', 'a', 'k');
chunk_start(bitwriter);
{
bitwriter.write('t', 'k', 'h', 'd');
2010-04-18 22:42:23 +00:00
bitwriter.writebits(32, 7); // version
bitwriter.writebits(_creationTime.Value);
bitwriter.writebits(_creationTime.Value);
2009-08-30 21:58:54 +00:00
bitwriter.writebits(32, 1); // track ID
bitwriter.writebits(32, 0); // reserved
2010-04-18 22:42:23 +00:00
bitwriter.writebits(32, sample_count);
2009-08-30 21:58:54 +00:00
bitwriter.writebits(32, 0); // reserved
bitwriter.writebits(32, 0); // reserved
bitwriter.writebits(32, 0); // reserved (layer & alternate group)
bitwriter.writebits(16, 0x0100); // reserved (preferred volume) 1.0 = normal
bitwriter.writebits(16, 0); // reserved
bitwriter.writebits(32, 0x00010000); // reserved (matrix structure)
bitwriter.writebits(32, 0x00000000); // reserved (matrix structure)
bitwriter.writebits(32, 0x00000000); // reserved (matrix structure)
bitwriter.writebits(32, 0x00000000); // reserved (matrix structure)
bitwriter.writebits(32, 0x00010000); // reserved (matrix structure)
bitwriter.writebits(32, 0x00000000); // reserved (matrix structure)
bitwriter.writebits(32, 0x00000000); // reserved (matrix structure)
bitwriter.writebits(32, 0x00000000); // reserved (matrix structure)
bitwriter.writebits(32, 0x40000000); // reserved (matrix structure)
bitwriter.writebits(32, 0); // reserved (width)
bitwriter.writebits(32, 0); // reserved (height)
}
chunk_end(bitwriter);
2010-04-18 22:42:23 +00:00
write_chunk_mdia(bitwriter, header_len);
2009-08-30 21:58:54 +00:00
}
chunk_end(bitwriter);
}
void write_chunk_udta(BitWriter bitwriter)
{
chunk_start(bitwriter);
{
bitwriter.write('u', 'd', 't', 'a');
chunk_start(bitwriter);
{
bitwriter.write('m', 'e', 't', 'a');
bitwriter.writebits(32, 0);
chunk_start(bitwriter);
{
bitwriter.write('h', 'd', 'l', 'r');
bitwriter.writebits(32, 0);
bitwriter.writebits(32, 0);
bitwriter.write('m', 'd', 'i', 'r');
bitwriter.write('a', 'p', 'p', 'l');
bitwriter.writebits(32, 0);
bitwriter.writebits(32, 0);
bitwriter.writebits(16, 0);
}
chunk_end(bitwriter);
chunk_start(bitwriter);
{
bitwriter.write('i', 'l', 's', 't');
chunk_start(bitwriter);
{
bitwriter.write((char)0xA9, 't', 'o', 'o');
chunk_start(bitwriter);
{
bitwriter.write('d', 'a', 't', 'a');
bitwriter.writebits(32, 1);
bitwriter.writebits(32, 0);
bitwriter.write(Vendor);
2009-08-30 21:58:54 +00:00
}
chunk_end(bitwriter);
}
chunk_end(bitwriter);
}
chunk_end(bitwriter);
2010-04-18 22:42:23 +00:00
chunk_start(bitwriter); // padding
{
bitwriter.write('f', 'r', 'e', 'e');
bitwriter.writebytes(m_settings.Padding, 0);
2010-04-18 22:42:23 +00:00
}
chunk_end(bitwriter);
2009-08-30 21:58:54 +00:00
}
chunk_end(bitwriter);
}
chunk_end(bitwriter);
}
2010-04-18 22:42:23 +00:00
byte[] write_headers(int header_len, int mdat_len)
2009-08-30 21:58:54 +00:00
{
2010-04-18 22:42:23 +00:00
byte[] header = new byte[header_len];
2009-08-30 21:58:54 +00:00
BitWriter bitwriter = new BitWriter(header, 0, header.Length);
2010-04-18 22:42:23 +00:00
2009-08-30 21:58:54 +00:00
chunk_start(bitwriter);
{
2010-04-18 22:42:23 +00:00
bitwriter.write('f', 't', 'y', 'p');
bitwriter.write('M', '4', 'A', ' ');
bitwriter.writebits(32, 0x200); // minor version
bitwriter.write('M', '4', 'A', ' ');
bitwriter.write('m', 'p', '4', '2');
bitwriter.write('i', 's', 'o', 'm');
bitwriter.writebits(32, 0);
2010-02-06 23:17:07 +00:00
}
chunk_end(bitwriter);
2009-08-30 21:58:54 +00:00
chunk_start(bitwriter);
2010-04-18 22:42:23 +00:00
{
bitwriter.write('m', 'o', 'o', 'v');
write_chunk_mvhd(bitwriter);
write_chunk_trak(bitwriter, header_len);
write_chunk_udta(bitwriter);
}
2009-08-30 21:58:54 +00:00
chunk_end(bitwriter);
2010-02-06 23:17:07 +00:00
chunk_start(bitwriter); // padding
{
bitwriter.write('f', 'r', 'e', 'e');
2010-04-18 22:42:23 +00:00
int padding_len = header_len - bitwriter.Length - 8;
if (padding_len < 0)
throw new Exception("padding length too small");
bitwriter.writebytes(padding_len, 0);
2010-02-06 23:17:07 +00:00
}
chunk_end(bitwriter);
2010-04-18 22:42:23 +00:00
bitwriter.writebits(32, mdat_len + 8);
2009-08-30 21:58:54 +00:00
bitwriter.write('m', 'd', 'a', 't');
2010-04-18 22:42:23 +00:00
bitwriter.flush();
2009-08-30 21:58:54 +00:00
2010-04-18 22:42:23 +00:00
return header;
2009-08-30 21:58:54 +00:00
}
2010-04-18 22:42:23 +00:00
void encode_init()
2009-08-30 21:58:54 +00:00
{
// FIXME: For now, only 44100 samplerate is supported
if (Settings.PCM.SampleRate != 44100)
2009-08-30 21:58:54 +00:00
throw new Exception("non-standard samplerate");
// FIXME: For now, only 16-bit encoding is supported
if (Settings.PCM.BitsPerSample != 16)
2009-08-30 21:58:54 +00:00
throw new Exception("non-standard bps");
m_blockSize =
m_settings.BlockSize != 0 ? m_settings.BlockSize :
select_blocksize(Settings.PCM.SampleRate, eparams.block_time_ms);
2009-08-30 21:58:54 +00:00
// set maximum encoded frame size (if larger, re-encodes in verbatim mode)
if (Settings.PCM.ChannelCount == 2)
2018-02-19 20:46:10 -05:00
max_frame_size = 16 + ((m_blockSize * Settings.PCM.ChannelCount * (Settings.PCM.BitsPerSample + Settings.PCM.ChannelCount - 1) + 7) >> 3);
2009-08-30 21:58:54 +00:00
else
max_frame_size = 16 + ((m_blockSize * Settings.PCM.ChannelCount * Settings.PCM.BitsPerSample + 7) >> 3);
2009-08-30 21:58:54 +00:00
frame_buffer = new byte[max_frame_size];
_sample_byte_size = new uint[Math.Max(0x100, sample_count / m_blockSize + 1)];
2009-08-30 21:58:54 +00:00
if (m_settings.DoVerify)
2009-08-30 21:58:54 +00:00
{
verify = new AudioDecoder(Settings.PCM, history_mult, initial_history, k_modifier, m_blockSize);
verifyBuffer = new int[Alac.MAX_BLOCKSIZE * Settings.PCM.ChannelCount];
2009-08-30 21:58:54 +00:00
}
if (sample_count < 0)
throw new InvalidOperationException("FinalSampleCount unknown");
int frames = sample_count / m_blockSize;
2010-04-18 22:42:23 +00:00
int header_len = max_header_len
+ m_settings.Padding
2010-04-18 22:42:23 +00:00
+ frames * 4 // stsz
+ frames * 4 / eparams.chunk_size; // stco
//if (header_len % 0x400 != 0)
// header_len += 0x400 - (header_len % 0x400);
first_frame_offset = header_len;
_IO.Write(new byte[first_frame_offset], 0, first_frame_offset);
2009-08-30 21:58:54 +00:00
}
}
struct ALACEncodeParams
{
// compression quality
// set by user prior to calling encode_init
// standard values are 0 to 8
// 0 is lower compression, faster encoding
// 8 is higher compression, slower encoding
// extended values 9 to 12 are slower and/or use
// higher prediction orders
public int compression;
// prediction order selection method
// set by user prior to calling 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;
// stereo decorrelation method
// set by user prior to calling encode_init
// if set to less than 0, it is chosen based on compression.
// valid values are 0 to 2
// 0 = independent L+R channels
// 1 = mid-side encoding
public StereoMethod stereo_method;
2009-12-24 16:14:41 +00:00
public WindowMethod window_method;
2010-04-18 22:42:23 +00:00
public int chunk_size;
2009-08-30 21:58:54 +00:00
// block time in milliseconds
// set by the user prior to calling 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;
// minimum LPC order
// set by user prior to calling encode_init
// if set to less than 0, it is chosen based on compression.
// valid values are 1 to 32
public int min_prediction_order;
// maximum LPC order
// set by user prior to calling encode_init
// if set to less than 0, it is chosen based on compression.
// valid values are 1 to 32
public int max_prediction_order;
// Number of LPC orders to try (for estimate mode)
// set by user prior to calling encode_init
// if set to less than 0, it is chosen based on compression.
// valid values are 1 to 32
public int estimation_depth;
2009-12-24 16:14:41 +00:00
public int adaptive_passes;
2009-08-30 21:58:54 +00:00
public int min_modifier, max_modifier;
public WindowFunction window_function;
public bool do_seektable;
public int set_defaults(int lvl)
{
compression = lvl;
if ((lvl < 0 || lvl > 12) && (lvl != 99))
{
return -1;
}
// default to level 5 params
window_function = WindowFunction.PartialTukey | WindowFunction.PunchoutTukey;
2009-08-30 21:58:54 +00:00
order_method = OrderMethod.Estimate;
stereo_method = StereoMethod.Estimate;
window_method = WindowMethod.Estimate;
2009-08-30 21:58:54 +00:00
block_time_ms = 105;
min_modifier = 4;
max_modifier = 4;
min_prediction_order = 1;
max_prediction_order = 12;
estimation_depth = 1;
2009-12-24 16:14:41 +00:00
adaptive_passes = 0;
2009-08-30 21:58:54 +00:00
do_seektable = false;
2010-04-18 22:42:23 +00:00
chunk_size = 5;
2009-08-30 21:58:54 +00:00
// differences from level 6
switch (lvl)
{
case 0:
stereo_method = StereoMethod.Independent;
max_prediction_order = 6;
break;
case 1:
stereo_method = StereoMethod.Independent;
max_prediction_order = 8;
break;
case 2:
max_prediction_order = 6;
break;
case 3:
window_function = WindowFunction.PartialTukey;
2009-08-30 21:58:54 +00:00
max_prediction_order = 8;
break;
case 4:
window_function = WindowFunction.PunchoutTukey;
2009-12-24 16:14:41 +00:00
max_prediction_order = 8;
2009-08-30 21:58:54 +00:00
break;
case 5:
window_function = WindowFunction.PunchoutTukey;
2009-08-30 21:58:54 +00:00
break;
2009-12-24 16:14:41 +00:00
case 6:
window_method = WindowMethod.EvaluateN;
2009-08-30 21:58:54 +00:00
break;
case 7:
window_method = WindowMethod.EvaluateN;
2009-12-24 16:14:41 +00:00
adaptive_passes = 1;
min_modifier = 2;
2009-08-30 21:58:54 +00:00
break;
case 8:
stereo_method = StereoMethod.Evaluate;
window_method = WindowMethod.EvaluateN;
2009-12-24 16:14:41 +00:00
adaptive_passes = 1;
min_modifier = 2;
break;
case 9:
stereo_method = StereoMethod.Evaluate;
window_method = WindowMethod.EvaluateN;
2009-12-24 16:14:41 +00:00
adaptive_passes = 1;
max_prediction_order = 30;
min_modifier = 2;
break;
case 10:
stereo_method = StereoMethod.Evaluate;
window_method = WindowMethod.EvaluateN;
2009-12-24 16:14:41 +00:00
estimation_depth = 2;
adaptive_passes = 2;
2009-08-30 21:58:54 +00:00
max_prediction_order = 30;
min_modifier = 2;
break;
}
return 0;
}
}
}