Files
cuetools.net/CUETools.ALACEnc/Program.cs

297 lines
11 KiB
C#
Raw Normal View History

2010-02-06 23:17:07 +00:00
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using CUETools.Codecs;
using CUETools.Codecs.ALAC;
namespace CUETools.ALACEnc
{
class Program
{
static void Usage()
{
Console.WriteLine("Usage : CUETools.ALACEnc.exe [options] <input.wav>");
Console.WriteLine();
Console.WriteLine("Options:");
Console.WriteLine();
Console.WriteLine(" -0 .. -10 Compression level, default 5.");
Console.WriteLine(" -o <file> Output filename, or \"-\" for stdout, or nul.");
//Console.WriteLine(" -p # Padding bytes.");
Console.WriteLine(" -q --quiet Quiet mode.");
Console.WriteLine(" --verify Verify during encoding.");
//Console.WriteLine(" --no-seektable Don't generate a seektable.");
Console.WriteLine();
Console.WriteLine("Advanced Options:");
Console.WriteLine();
2010-04-18 22:42:23 +00:00
Console.WriteLine(" -b # Block size (samples).");
Console.WriteLine(" -p # Padding (bytes).");
2010-02-06 23:17:07 +00:00
Console.WriteLine(" -s <method> Stereo decorrelation (independent,estimate,evaluate,search).");
Console.WriteLine(" --history-modifier # Rice history modifier {max} or {min},{max}, default 4,4.");
Console.WriteLine();
Console.WriteLine("LPC options:");
Console.WriteLine();
Console.WriteLine(" -m <method> Prediction order search (estimate,estsearch,logfast,search).");
Console.WriteLine(" -e # Estimation depth (1..30).");
Console.WriteLine(" -w <func>[,<func>] One or more window functions (welch,hann,flattop,tukey).");
Console.WriteLine(" --window-method Window selection method (estimate,evaluate,search).");
Console.WriteLine(" -l #[,#] Prediction order {max} or {min},{max} (1..30).");
Console.WriteLine();
}
static int Main(string[] args)
{
TextWriter stdout = Console.Out;
Console.SetOut(Console.Error);
DateTime start = DateTime.Now;
TimeSpan lastPrint = TimeSpan.FromMilliseconds(0);
bool debug = false, quiet = false;
string stereo_method = null;
string window_method = null;
string order_method = null;
string window_function = null;
string input_file = null;
string output_file = null;
int min_lpc_order = -1, max_lpc_order = -1,
estimation_depth = -1,
2010-02-06 23:17:07 +00:00
min_modifier = -1, max_modifier = -1;
int intarg = -1;
2010-02-06 23:17:07 +00:00
int initial_history = -1, history_mult = -1;
int adaptive_passes = -1;
bool do_seektable = true;
2010-02-06 23:17:07 +00:00
bool buffered = false;
var settings = new Codecs.ALAC.EncoderSettings();
2010-02-06 23:17:07 +00:00
for (int arg = 0; arg < args.Length; arg++)
{
bool ok = true;
if (args[arg].Length == 0)
ok = false;
else if (args[arg] == "--debug")
debug = true;
else if ((args[arg] == "-q" || args[arg] == "--quiet"))
quiet = true;
else if (args[arg] == "--verify")
settings.DoVerify = true;
2010-02-06 23:17:07 +00:00
else if (args[arg] == "--no-seektable")
do_seektable = false;
else if (args[arg] == "--buffered")
buffered = true;
else if ((args[arg] == "-o" || args[arg] == "--output") && ++arg < args.Length)
output_file = args[arg];
else if ((args[arg] == "-s" || args[arg] == "--stereo") && ++arg < args.Length)
stereo_method = args[arg];
else if ((args[arg] == "-m" || args[arg] == "--order-method") && ++arg < args.Length)
order_method = args[arg];
else if ((args[arg] == "-w" || args[arg] == "--window") && ++arg < args.Length)
window_function = args[arg];
else if (args[arg] == "--window-method" && ++arg < args.Length)
window_method = args[arg];
else if ((args[arg] == "-l" || args[arg] == "--lpc-order") && ++arg < args.Length)
{
ok = (args[arg].Split(',').Length == 2 &&
int.TryParse(args[arg].Split(',')[0], out min_lpc_order) &&
int.TryParse(args[arg].Split(',')[1], out max_lpc_order)) ||
int.TryParse(args[arg], out max_lpc_order);
}
else if ((args[arg] == "--history-modifier") && ++arg < args.Length)
{
ok = (args[arg].Split(',').Length == 2 &&
int.TryParse(args[arg].Split(',')[0], out min_modifier) &&
int.TryParse(args[arg].Split(',')[1], out max_modifier)) ||
int.TryParse(args[arg], out max_modifier);
}
else if ((args[arg] == "--initial-history") && ++arg < args.Length)
ok = int.TryParse(args[arg], out initial_history);
else if ((args[arg] == "--adaptive-passes") && ++arg < args.Length)
ok = int.TryParse(args[arg], out adaptive_passes);
else if ((args[arg] == "--history-mult") && ++arg < args.Length)
ok = int.TryParse(args[arg], out history_mult);
else if ((args[arg] == "-e" || args[arg] == "--estimation-depth") && ++arg < args.Length)
ok = int.TryParse(args[arg], out estimation_depth);
else if ((args[arg] == "-b" || args[arg] == "--blocksize") && ++arg < args.Length)
{
ok = int.TryParse(args[arg], out intarg);
settings.BlockSize = intarg;
}
else if ((args[arg] == "-p" || args[arg] == "--padding") && ++arg < args.Length)
{
ok = int.TryParse(args[arg], out intarg);
settings.Padding = intarg;
}
else if (args[arg] != "-" && args[arg][0] == '-' && int.TryParse(args[arg].Substring(1), out intarg))
{
ok = intarg >= 0 && intarg <= 11;
settings.SetEncoderModeIndex(intarg);
}
else if ((args[arg][0] != '-' || args[arg] == "-") && input_file == null)
input_file = args[arg];
else
ok = false;
2010-02-06 23:17:07 +00:00
if (!ok)
{
Usage();
return 1;
}
}
if (input_file == null || ((input_file == "-" || Path.GetExtension(input_file) == ".m4a") && output_file == null))
{
Usage();
return 2;
}
if (!quiet)
{
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
Console.WriteLine("CUETools.ALACEnc, Copyright (C) 2009-2020 Grigory Chudov.");
2010-02-06 23:17:07 +00:00
Console.WriteLine("Based on ffdshow ALAC audio encoder");
Console.WriteLine("Copyright (c) 2008 Jaikrishnan Menon, <realityman@gmx.net>");
Console.WriteLine("This is free software under the GNU GPLv3+ license; There is NO WARRANTY, to");
Console.WriteLine("the extent permitted by law. <http://www.gnu.org/licenses/> for details.");
}
//byte [] b = new byte[0x10000];
//int len = 0;
//Stream si = Console.OpenStandardInput();
//Stream so = new FileStream(output_file, FileMode.Create);
//do
//{
// len = si.Read(b, 0, 0x10000);
// so.Write(b, 0, len);
//} while (len > 0);
//return 0;
IAudioSource audioSource;
if (input_file == "-")
audioSource = new Codecs.WAV.AudioDecoder(new Codecs.WAV.DecoderSettings() { IgnoreChunkSizes = true }, "", Console.OpenStandardInput());
2010-02-06 23:17:07 +00:00
else if (File.Exists(input_file) && Path.GetExtension(input_file) == ".wav")
audioSource = new Codecs.WAV.AudioDecoder(new Codecs.WAV.DecoderSettings(), input_file);
2010-02-06 23:17:07 +00:00
else if (File.Exists(input_file) && Path.GetExtension(input_file) == ".m4a")
audioSource = new Codecs.ALAC.AudioDecoder(new Codecs.ALAC.DecoderSettings(), input_file);
2010-02-06 23:17:07 +00:00
else
{
Usage();
return 2;
}
if (buffered)
audioSource = new AudioPipe(audioSource, 0x10000);
2010-02-06 23:17:07 +00:00
if (output_file == null)
output_file = Path.ChangeExtension(input_file, "m4a");
settings.PCM = audioSource.PCM;
2018-04-07 13:55:01 -04:00
Codecs.ALAC.AudioEncoder alac = new Codecs.ALAC.AudioEncoder(settings,
(output_file == "-" || output_file == "nul") ? "" : output_file,
2010-02-06 23:17:07 +00:00
output_file == "-" ? Console.OpenStandardOutput() :
2018-04-07 13:55:01 -04:00
output_file == "nul" ? new NullStream() : null);
2010-04-18 22:42:23 +00:00
alac.FinalSampleCount = audioSource.Length;
2010-02-06 23:17:07 +00:00
IAudioDest audioDest = alac;
AudioBuffer buff = new AudioBuffer(audioSource, 0x10000);
try
{
if (stereo_method != null)
alac.StereoMethod = Alac.LookupStereoMethod(stereo_method);
if (order_method != null)
alac.OrderMethod = Alac.LookupOrderMethod(order_method);
if (window_function != null)
alac.WindowFunction = Alac.LookupWindowFunction(window_function);
if (window_method != null)
alac.WindowMethod = Alac.LookupWindowMethod(window_method);
if (max_lpc_order >= 0)
alac.MaxLPCOrder = max_lpc_order;
if (min_lpc_order >= 0)
alac.MinLPCOrder = min_lpc_order;
if (max_modifier >= 0)
alac.MaxHistoryModifier = max_modifier;
if (min_modifier >= 0)
alac.MinHistoryModifier = min_modifier;
if (history_mult >= 0)
alac.HistoryMult = history_mult;
if (initial_history >= 0)
alac.InitialHistory = initial_history;
if (estimation_depth >= 0)
alac.EstimationDepth = estimation_depth;
if (adaptive_passes >= 0)
alac.AdaptivePasses = adaptive_passes;
alac.DoSeekTable = do_seektable;
}
catch (Exception ex)
{
Usage();
Console.WriteLine("");
Console.WriteLine("Error: {0}.", ex.Message);
return 3;
}
if (!quiet)
{
Console.WriteLine("Filename : {0}", input_file);
Console.WriteLine("File Info : {0}kHz; {1} channel; {2} bit; {3}", audioSource.PCM.SampleRate, audioSource.PCM.ChannelCount, audioSource.PCM.BitsPerSample, TimeSpan.FromSeconds(audioSource.Length * 1.0 / audioSource.PCM.SampleRate));
}
#if !DEBUG
try
#endif
{
while (audioSource.Read(buff, -1) != 0)
{
audioDest.Write(buff);
TimeSpan elapsed = DateTime.Now - start;
if (!quiet)
{
if ((elapsed - lastPrint).TotalMilliseconds > 60)
{
Console.Error.Write("\rProgress : {0:00}%; {1:0.00}x; {2}/{3}",
100.0 * audioSource.Position / audioSource.Length,
audioSource.Position / elapsed.TotalSeconds / audioSource.PCM.SampleRate,
elapsed,
TimeSpan.FromMilliseconds(elapsed.TotalMilliseconds / audioSource.Position * audioSource.Length)
);
lastPrint = elapsed;
}
}
}
audioDest.Close();
}
#if !DEBUG
catch (Exception ex)
{
Console.Error.Write("\r \r");
Console.WriteLine("Error : {0}", ex.Message);
audioDest.Delete();
audioSource.Close();
return 4;
}
#endif
if (!quiet)
{
TimeSpan totalElapsed = DateTime.Now - start;
Console.Error.Write("\r \r");
Console.WriteLine("Results : {0:0.00}x; {1}",
audioSource.Position / totalElapsed.TotalSeconds / audioSource.PCM.SampleRate,
totalElapsed
);
}
audioSource.Close();
if (debug)
{
Console.SetOut(stdout);
Console.Out.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}..{6}\t{7}..{8}\t{9}",
alac.TotalSize,
alac.UserProcessorTime.TotalSeconds,
alac.StereoMethod.ToString().PadRight(15),
(alac.OrderMethod.ToString() + (alac.OrderMethod == OrderMethod.Estimate ? "(" + alac.EstimationDepth.ToString() + ")" : "")).PadRight(15),
alac.WindowFunction.ToString() + "(" + alac.AdaptivePasses.ToString() + ")",
alac.MinLPCOrder,
alac.MaxLPCOrder,
alac.MinHistoryModifier,
alac.MaxHistoryModifier,
alac.Settings.BlockSize
2010-02-06 23:17:07 +00:00
);
}
//File.SetAttributes(output_file, FileAttributes.ReadOnly);
return 0;
}
}
}