Code cleanup; Reader classes renamed to Decoders, Writers to Encoders, every Decoder must have a corresponding Settings class now just like Encoders. UserDefinedEncoders renamed to CommandLineEncoders, etc.

This commit is contained in:
Grigory Chudov
2018-03-23 20:26:28 -04:00
parent 16fccfe5c9
commit ca8bb2fff6
22 changed files with 1301 additions and 1292 deletions

View File

@@ -12,15 +12,15 @@ namespace CUETools.Codecs
public class CUEToolsCodecsConfig public class CUEToolsCodecsConfig
{ {
public Dictionary<string, CUEToolsFormat> formats; public Dictionary<string, CUEToolsFormat> formats;
public CUEToolsUDCEncoderList encoders; public EncoderListViewModel encoders;
public CUEToolsUDCDecoderList decoders; public DecoderListViewModel decoders;
public CUEToolsCodecsConfig(CUEToolsCodecsConfig src) public CUEToolsCodecsConfig(CUEToolsCodecsConfig src)
{ {
encoders = new CUEToolsUDCEncoderList(); encoders = new EncoderListViewModel();
foreach (var enc in src.encoders) foreach (var enc in src.encoders)
encoders.Add(enc.Clone()); encoders.Add(enc.Clone());
decoders = new CUEToolsUDCDecoderList(); decoders = new DecoderListViewModel();
foreach (var dec in src.decoders) foreach (var dec in src.decoders)
decoders.Add(dec.Clone()); decoders.Add(dec.Clone());
formats = new Dictionary<string, CUEToolsFormat>(); formats = new Dictionary<string, CUEToolsFormat>();
@@ -30,7 +30,7 @@ namespace CUETools.Codecs
public CUEToolsCodecsConfig(List<Type> encs, List<Type> decs) public CUEToolsCodecsConfig(List<Type> encs, List<Type> decs)
{ {
encoders = new CUEToolsUDCEncoderList(); encoders = new EncoderListViewModel();
foreach (Type type in encs) foreach (Type type in encs)
foreach (AudioEncoderClassAttribute enc in Attribute.GetCustomAttributes(type, typeof(AudioEncoderClassAttribute))) foreach (AudioEncoderClassAttribute enc in Attribute.GetCustomAttributes(type, typeof(AudioEncoderClassAttribute)))
try try
@@ -42,7 +42,7 @@ namespace CUETools.Codecs
System.Diagnostics.Trace.WriteLine(ex.Message); System.Diagnostics.Trace.WriteLine(ex.Message);
} }
decoders = new CUEToolsUDCDecoderList(); decoders = new DecoderListViewModel();
foreach (Type type in decs) foreach (Type type in decs)
foreach (AudioDecoderClassAttribute dec in Attribute.GetCustomAttributes(type, typeof(AudioDecoderClassAttribute))) foreach (AudioDecoderClassAttribute dec in Attribute.GetCustomAttributes(type, typeof(AudioDecoderClassAttribute)))
try try
@@ -66,8 +66,8 @@ namespace CUETools.Codecs
encoders.Add(new AudioEncoderSettingsViewModel("nero aac", "m4a", false, "0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9", "0.4", "neroAacEnc.exe", "-q %M -if - -of %O")); encoders.Add(new AudioEncoderSettingsViewModel("nero aac", "m4a", false, "0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9", "0.4", "neroAacEnc.exe", "-q %M -if - -of %O"));
encoders.Add(new AudioEncoderSettingsViewModel("qaac tvbr", "m4a", false, "10 20 30 40 50 60 70 80 90 100 110 127", "80", "qaac.exe", "-s -V %M -q 2 - -o %O")); encoders.Add(new AudioEncoderSettingsViewModel("qaac tvbr", "m4a", false, "10 20 30 40 50 60 70 80 90 100 110 127", "80", "qaac.exe", "-s -V %M -q 2 - -o %O"));
decoders.Add(new AudioDecoderSettingsViewModel(new CommandLineDecoderSettings("takc", "tak", "takc.exe", "-d %I -"))); decoders.Add(new AudioDecoderSettingsViewModel(new CommandLine.DecoderSettings("takc", "tak", "takc.exe", "-d %I -")));
decoders.Add(new AudioDecoderSettingsViewModel(new CommandLineDecoderSettings("ffmpeg alac", "m4a", "ffmpeg.exe", "-v 0 -i %I -f wav -"))); decoders.Add(new AudioDecoderSettingsViewModel(new CommandLine.DecoderSettings("ffmpeg alac", "m4a", "ffmpeg.exe", "-v 0 -i %I -f wav -")));
} }
else else
{ {

View File

@@ -1,271 +0,0 @@
using Newtonsoft.Json;
using System;
using System.ComponentModel;
namespace CUETools.Codecs
{
[JsonObject(MemberSerialization.OptIn)]
public class AudioEncoderSettingsViewModel : INotifyPropertyChanged
{
[JsonProperty]
public AudioEncoderSettings settings = null;
public event PropertyChangedEventHandler PropertyChanged;
[JsonConstructor]
private AudioEncoderSettingsViewModel()
{
}
public AudioEncoderSettingsViewModel(
string _name,
string _extension,
bool _lossless,
string _supported_modes,
string _default_mode,
string _path,
string _parameters
)
{
settings = new CommandLineEncoderSettings() { name = _name, extension = _extension, SupportedModes = _supported_modes, EncoderMode = _default_mode, Path = _path, Parameters = _parameters, lossless = _lossless };
}
public AudioEncoderSettingsViewModel(AudioEncoderClassAttribute enc)
{
settings = Activator.CreateInstance(enc.Settings) as AudioEncoderSettings;
if (settings == null)
throw new InvalidOperationException("invalid codec");
}
public AudioEncoderSettingsViewModel Clone()
{
var res = this.MemberwiseClone() as AudioEncoderSettingsViewModel;
if (settings != null) res.settings = settings.Clone();
return res;
}
public override string ToString()
{
return Name;
}
public string FullName => Name + " [" + Extension + "]";
public string Path
{
get
{
if (settings is CommandLineEncoderSettings)
return (settings as CommandLineEncoderSettings).Path;
return "";
}
set
{
var settings = this.settings as CommandLineEncoderSettings;
if (settings == null) throw new InvalidOperationException();
settings.Path = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Path"));
}
}
public string Parameters
{
get
{
if (settings is CommandLineEncoderSettings)
return (settings as CommandLineEncoderSettings).Parameters;
return "";
}
set
{
var settings = this.settings as CommandLineEncoderSettings;
if (settings == null) throw new InvalidOperationException();
settings.Parameters = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Parameters"));
}
}
public bool Lossless
{
get => settings.Lossless;
set
{
var settings = this.settings as CommandLineEncoderSettings;
if (settings == null) throw new InvalidOperationException();
settings.lossless = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Lossless"));
}
}
public string Name
{
get => settings.Name;
set
{
var settings = this.settings as CommandLineEncoderSettings;
if (settings == null) throw new InvalidOperationException();
settings.name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
}
}
public string Extension
{
get => settings.Extension;
set
{
var settings = this.settings as CommandLineEncoderSettings;
if (settings == null) throw new InvalidOperationException();
settings.extension = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Extension"));
}
}
public string DotExtension => "." + Extension;
public string SupportedModesStr
{
get
{
string defaultMode;
return this.settings.GetSupportedModes(out defaultMode);
}
set
{
var settings = this.settings as CommandLineEncoderSettings;
if (settings == null) throw new InvalidOperationException();
settings.SupportedModes = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SupportedModesStr"));
}
}
public string[] SupportedModes => this.SupportedModesStr.Split(' ');
public int EncoderModeIndex
{
get
{
string[] modes = this.SupportedModes;
if (modes == null || modes.Length < 2)
return -1;
for (int i = 0; i < modes.Length; i++)
if (modes[i] == this.settings.EncoderMode)
return i;
return -1;
}
}
public bool CanBeDeleted => settings is CommandLineEncoderSettings;
public bool IsValid =>
(settings != null)
&& (settings is CommandLineEncoderSettings ? (settings as CommandLineEncoderSettings).Path != "" : true);
}
[JsonObject(MemberSerialization.OptIn)]
public class AudioDecoderSettingsViewModel : INotifyPropertyChanged
{
[JsonProperty]
public AudioDecoderSettings decoderSettings = null;
public event PropertyChangedEventHandler PropertyChanged;
[JsonConstructor]
private AudioDecoderSettingsViewModel()
{
}
public AudioDecoderSettingsViewModel(AudioDecoderSettings settings)
{
decoderSettings = settings;
}
public AudioDecoderSettingsViewModel Clone()
{
var res = this.MemberwiseClone() as AudioDecoderSettingsViewModel;
if (decoderSettings != null) res.decoderSettings = decoderSettings.Clone();
return res;
}
public override string ToString()
{
return Name;
}
public string FullName => Name + " [" + Extension + "]";
public string Path
{
get
{
if (decoderSettings is CommandLineDecoderSettings)
return (decoderSettings as CommandLineDecoderSettings).Path;
return "";
}
set
{
if (decoderSettings is CommandLineDecoderSettings)
(decoderSettings as CommandLineDecoderSettings).Path = value;
else throw new InvalidOperationException();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Path"));
}
}
public string Parameters
{
get
{
if (decoderSettings is CommandLineDecoderSettings)
return (decoderSettings as CommandLineDecoderSettings).Parameters;
return "";
}
set
{
if (decoderSettings is CommandLineDecoderSettings)
(decoderSettings as CommandLineDecoderSettings).Parameters = value;
else throw new InvalidOperationException();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Parameters"));
}
}
public bool Lossless
{
get => true;
set {
throw new InvalidOperationException();
}
}
public string Name
{
get => decoderSettings.Name;
set
{
if (decoderSettings is CommandLineDecoderSettings)
(decoderSettings as CommandLineDecoderSettings).name = value;
else throw new InvalidOperationException();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
}
}
public string Extension
{
get => decoderSettings.Extension;
set
{
if (decoderSettings is CommandLineDecoderSettings)
(decoderSettings as CommandLineDecoderSettings).extension = value;
else throw new InvalidOperationException();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Extension"));
}
}
public string DotExtension => "." + Extension;
public bool CanBeDeleted => decoderSettings is CommandLineDecoderSettings;
public bool IsValid =>
(decoderSettings != null)
&& (decoderSettings is CommandLineDecoderSettings ? (decoderSettings as CommandLineDecoderSettings).Path != "" : true);
}
}

View File

@@ -1,88 +0,0 @@
using System;
using System.ComponentModel;
namespace CUETools.Codecs
{
public class CUEToolsUDCEncoderList : BindingList<AudioEncoderSettingsViewModel>
{
public CUEToolsUDCEncoderList()
: base()
{
AddingNew += OnAddingNew;
}
private void OnAddingNew(object sender, AddingNewEventArgs e)
{
e.NewObject = new AudioEncoderSettingsViewModel("new", "wav", true, "", "", "", "");
}
public bool TryGetValue(string extension, bool lossless, string name, out AudioEncoderSettingsViewModel result)
{
//result = this.Where(udc => udc.settings.Extension == extension && udc.settings.Lossless == lossless && udc.settings.Name == name).First();
foreach (AudioEncoderSettingsViewModel udc in this)
{
if (udc.settings.Extension == extension && udc.settings.Lossless == lossless && udc.settings.Name == name)
{
result = udc;
return true;
}
}
result = null;
return false;
}
public AudioEncoderSettingsViewModel GetDefault(string extension, bool lossless)
{
AudioEncoderSettingsViewModel result = null;
foreach (AudioEncoderSettingsViewModel udc in this)
{
if (udc.settings.Extension == extension && udc.settings.Lossless == lossless && (result == null || result.settings.Priority < udc.settings.Priority))
{
result = udc;
}
}
return result;
}
}
public class CUEToolsUDCDecoderList : BindingList<AudioDecoderSettingsViewModel>
{
public CUEToolsUDCDecoderList()
: base()
{
AddingNew += OnAddingNew;
}
private void OnAddingNew(object sender, AddingNewEventArgs e)
{
e.NewObject = new AudioDecoderSettingsViewModel(new CommandLineDecoderSettings("new", "wav", "", ""));
}
public bool TryGetValue(string extension, bool lossless, string name, out AudioDecoderSettingsViewModel result)
{
foreach (AudioDecoderSettingsViewModel udc in this)
{
if (udc.decoderSettings.Extension == extension && udc.decoderSettings.Lossless == lossless && udc.decoderSettings.Name == name)
{
result = udc;
return true;
}
}
result = null;
return false;
}
public AudioDecoderSettingsViewModel GetDefault(string extension, bool lossless)
{
AudioDecoderSettingsViewModel result = null;
foreach (AudioDecoderSettingsViewModel udc in this)
{
if (udc.decoderSettings.Extension == extension && udc.decoderSettings.Lossless == lossless && (result == null || result.decoderSettings.Priority < udc.decoderSettings.Priority))
{
result = udc;
}
}
return result;
}
}
}

View File

@@ -1,112 +1,112 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
namespace CUETools.Codecs namespace CUETools.Codecs.CommandLine
{ {
public class CommandLineDecoder : IAudioSource public class AudioDecoder : IAudioSource
{ {
string _path; string _path;
Process _decoderProcess; Process _decoderProcess;
WAV.AudioDecoder rdr; WAV.AudioDecoder rdr;
private CommandLineDecoderSettings m_settings; private DecoderSettings m_settings;
public AudioDecoderSettings Settings => m_settings; public AudioDecoderSettings Settings => m_settings;
public long Position public long Position
{ {
get get
{ {
Initialize(); Initialize();
return rdr.Position; return rdr.Position;
} }
set set
{ {
Initialize(); Initialize();
rdr.Position = value; rdr.Position = value;
} }
} }
public long Length public long Length
{ {
get get
{ {
Initialize(); Initialize();
return rdr.Length; return rdr.Length;
} }
} }
public long Remaining public long Remaining
{ {
get get
{ {
Initialize(); Initialize();
return rdr.Remaining; return rdr.Remaining;
} }
} }
public AudioPCMConfig PCM public AudioPCMConfig PCM
{ {
get get
{ {
Initialize(); Initialize();
return rdr.PCM; return rdr.PCM;
} }
} }
public string Path { get { return _path; } } public string Path { get { return _path; } }
public CommandLineDecoder(CommandLineDecoderSettings settings, string path, Stream IO) public AudioDecoder(DecoderSettings settings, string path, Stream IO)
{ {
m_settings = settings; m_settings = settings;
_path = path; _path = path;
_decoderProcess = null; _decoderProcess = null;
rdr = null; rdr = null;
} }
void Initialize() void Initialize()
{ {
if (_decoderProcess != null) if (_decoderProcess != null)
return; return;
_decoderProcess = new Process(); _decoderProcess = new Process();
_decoderProcess.StartInfo.FileName = m_settings.Path; _decoderProcess.StartInfo.FileName = m_settings.Path;
_decoderProcess.StartInfo.Arguments = m_settings.Parameters.Replace("%I", "\"" + _path + "\""); _decoderProcess.StartInfo.Arguments = m_settings.Parameters.Replace("%I", "\"" + _path + "\"");
_decoderProcess.StartInfo.CreateNoWindow = true; _decoderProcess.StartInfo.CreateNoWindow = true;
_decoderProcess.StartInfo.RedirectStandardOutput = true; _decoderProcess.StartInfo.RedirectStandardOutput = true;
_decoderProcess.StartInfo.UseShellExecute = false; _decoderProcess.StartInfo.UseShellExecute = false;
bool started = false; bool started = false;
Exception ex = null; Exception ex = null;
try try
{ {
started = _decoderProcess.Start(); started = _decoderProcess.Start();
if (started) if (started)
_decoderProcess.PriorityClass = Process.GetCurrentProcess().PriorityClass; _decoderProcess.PriorityClass = Process.GetCurrentProcess().PriorityClass;
} }
catch (Exception _ex) catch (Exception _ex)
{ {
ex = _ex; ex = _ex;
} }
if (!started) if (!started)
{ {
_decoderProcess = null; _decoderProcess = null;
throw new Exception(m_settings.Path + ": " + (ex == null ? "please check the path" : ex.Message)); throw new Exception(m_settings.Path + ": " + (ex == null ? "please check the path" : ex.Message));
} }
rdr = new WAV.AudioDecoder(new WAV.DecoderSettings(), _path, _decoderProcess.StandardOutput.BaseStream); rdr = new WAV.AudioDecoder(new WAV.DecoderSettings(), _path, _decoderProcess.StandardOutput.BaseStream);
} }
public void Close() public void Close()
{ {
if (rdr != null) if (rdr != null)
rdr.Close(); rdr.Close();
if (_decoderProcess != null && !_decoderProcess.HasExited) if (_decoderProcess != null && !_decoderProcess.HasExited)
try { _decoderProcess.Kill(); _decoderProcess.WaitForExit(); } try { _decoderProcess.Kill(); _decoderProcess.WaitForExit(); }
catch { } catch { }
} }
public int Read(AudioBuffer buff, int maxLength) public int Read(AudioBuffer buff, int maxLength)
{ {
Initialize(); Initialize();
return rdr.Read(buff, maxLength); return rdr.Read(buff, maxLength);
} }
} }
} }

View File

@@ -1,137 +1,137 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
namespace CUETools.Codecs namespace CUETools.Codecs.CommandLine
{ {
public class CommandLineEncoder : IAudioDest public class AudioEncoder : IAudioDest
{ {
string _path; string _path;
Process _encoderProcess; Process _encoderProcess;
WAV.AudioEncoder wrt; WAV.AudioEncoder wrt;
CyclicBuffer outputBuffer = null; CyclicBuffer outputBuffer = null;
bool useTempFile = false; bool useTempFile = false;
string tempFile = null; string tempFile = null;
long _finalSampleCount = -1; long _finalSampleCount = -1;
bool closed = false; bool closed = false;
public long Position public long Position
{ {
get get
{ {
return wrt.Position; return wrt.Position;
} }
} }
public long FinalSampleCount public long FinalSampleCount
{ {
set { _finalSampleCount = wrt.FinalSampleCount = value; } set { _finalSampleCount = wrt.FinalSampleCount = value; }
} }
// !!!! Must not start the process in constructor, so that we can set CompressionLevel via Settings! // !!!! Must not start the process in constructor, so that we can set CompressionLevel via Settings!
private CommandLineEncoderSettings m_settings; private EncoderSettings m_settings;
public AudioEncoderSettings Settings => m_settings; public AudioEncoderSettings Settings => m_settings;
public string Path { get { return _path; } } public string Path { get { return _path; } }
public CommandLineEncoder(CommandLineEncoderSettings settings, string path, Stream IO = null) public AudioEncoder(EncoderSettings settings, string path, Stream IO = null)
{ {
m_settings = settings; m_settings = settings;
_path = path; _path = path;
useTempFile = m_settings.Parameters.Contains("%I"); useTempFile = m_settings.Parameters.Contains("%I");
tempFile = path + ".tmp.wav"; tempFile = path + ".tmp.wav";
_encoderProcess = new Process(); _encoderProcess = new Process();
_encoderProcess.StartInfo.FileName = m_settings.Path; _encoderProcess.StartInfo.FileName = m_settings.Path;
_encoderProcess.StartInfo.Arguments = m_settings.Parameters.Replace("%O", "\"" + path + "\"").Replace("%M", m_settings.EncoderMode).Replace("%P", m_settings.Padding.ToString()).Replace("%I", "\"" + tempFile + "\""); _encoderProcess.StartInfo.Arguments = m_settings.Parameters.Replace("%O", "\"" + path + "\"").Replace("%M", m_settings.EncoderMode).Replace("%P", m_settings.Padding.ToString()).Replace("%I", "\"" + tempFile + "\"");
_encoderProcess.StartInfo.CreateNoWindow = true; _encoderProcess.StartInfo.CreateNoWindow = true;
if (!useTempFile) if (!useTempFile)
_encoderProcess.StartInfo.RedirectStandardInput = true; _encoderProcess.StartInfo.RedirectStandardInput = true;
_encoderProcess.StartInfo.UseShellExecute = false; _encoderProcess.StartInfo.UseShellExecute = false;
if (!m_settings.Parameters.Contains("%O")) if (!m_settings.Parameters.Contains("%O"))
_encoderProcess.StartInfo.RedirectStandardOutput = true; _encoderProcess.StartInfo.RedirectStandardOutput = true;
if (useTempFile) if (useTempFile)
{ {
wrt = new WAV.AudioEncoder(new WAV.EncoderSettings(settings.PCM), tempFile); wrt = new WAV.AudioEncoder(new WAV.EncoderSettings(settings.PCM), tempFile);
return; return;
} }
bool started = false; bool started = false;
Exception ex = null; Exception ex = null;
try try
{ {
started = _encoderProcess.Start(); started = _encoderProcess.Start();
if (started) if (started)
_encoderProcess.PriorityClass = Process.GetCurrentProcess().PriorityClass; _encoderProcess.PriorityClass = Process.GetCurrentProcess().PriorityClass;
} }
catch (Exception _ex) catch (Exception _ex)
{ {
ex = _ex; ex = _ex;
} }
if (!started) if (!started)
throw new Exception(m_settings.Path + ": " + (ex == null ? "please check the path" : ex.Message)); throw new Exception(m_settings.Path + ": " + (ex == null ? "please check the path" : ex.Message));
if (_encoderProcess.StartInfo.RedirectStandardOutput) if (_encoderProcess.StartInfo.RedirectStandardOutput)
{ {
Stream outputStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read); Stream outputStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
outputBuffer = new CyclicBuffer(2 * 1024 * 1024, _encoderProcess.StandardOutput.BaseStream, outputStream); outputBuffer = new CyclicBuffer(2 * 1024 * 1024, _encoderProcess.StandardOutput.BaseStream, outputStream);
} }
Stream inputStream = new CyclicBufferOutputStream(_encoderProcess.StandardInput.BaseStream, 128 * 1024); Stream inputStream = new CyclicBufferOutputStream(_encoderProcess.StandardInput.BaseStream, 128 * 1024);
wrt = new WAV.AudioEncoder(new WAV.EncoderSettings(settings.PCM), path, inputStream); wrt = new WAV.AudioEncoder(new WAV.EncoderSettings(settings.PCM), path, inputStream);
} }
public void Close() public void Close()
{ {
if (closed) if (closed)
return; return;
closed = true; closed = true;
wrt.Close(); wrt.Close();
if (useTempFile && (_finalSampleCount < 0 || wrt.Position == _finalSampleCount)) if (useTempFile && (_finalSampleCount < 0 || wrt.Position == _finalSampleCount))
{ {
bool started = false; bool started = false;
Exception ex = null; Exception ex = null;
try try
{ {
started = _encoderProcess.Start(); started = _encoderProcess.Start();
if (started) if (started)
_encoderProcess.PriorityClass = Process.GetCurrentProcess().PriorityClass; _encoderProcess.PriorityClass = Process.GetCurrentProcess().PriorityClass;
} }
catch (Exception _ex) catch (Exception _ex)
{ {
ex = _ex; ex = _ex;
} }
if (!started) if (!started)
throw new Exception(m_settings.Path + ": " + (ex == null ? "please check the path" : ex.Message)); throw new Exception(m_settings.Path + ": " + (ex == null ? "please check the path" : ex.Message));
} }
wrt = null; wrt = null;
if (!_encoderProcess.HasExited) if (!_encoderProcess.HasExited)
_encoderProcess.WaitForExit(); _encoderProcess.WaitForExit();
if (useTempFile) if (useTempFile)
File.Delete(tempFile); File.Delete(tempFile);
if (outputBuffer != null) if (outputBuffer != null)
outputBuffer.Close(); outputBuffer.Close();
if (_encoderProcess.ExitCode != 0) if (_encoderProcess.ExitCode != 0)
throw new Exception(String.Format("{0} returned error code {1}", m_settings.Path, _encoderProcess.ExitCode)); throw new Exception(String.Format("{0} returned error code {1}", m_settings.Path, _encoderProcess.ExitCode));
} }
public void Delete() public void Delete()
{ {
Close(); Close();
File.Delete(_path); File.Delete(_path);
} }
public void Write(AudioBuffer buff) public void Write(AudioBuffer buff)
{ {
try try
{ {
wrt.Write(buff); wrt.Write(buff);
} }
catch (IOException ex) catch (IOException ex)
{ {
if (_encoderProcess.HasExited) if (_encoderProcess.HasExited)
throw new IOException(string.Format("{0} has exited prematurely with code {1}", m_settings.Path, _encoderProcess.ExitCode), ex); throw new IOException(string.Format("{0} has exited prematurely with code {1}", m_settings.Path, _encoderProcess.ExitCode), ex);
else else
throw ex; throw ex;
} }
//_sampleLen += sampleCount; //_sampleLen += sampleCount;
} }
} }
} }

View File

@@ -4,21 +4,21 @@ using System.Text;
using System.ComponentModel; using System.ComponentModel;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace CUETools.Codecs namespace CUETools.Codecs.CommandLine
{ {
[JsonObject(MemberSerialization.OptIn)] [JsonObject(MemberSerialization.OptIn)]
public class CommandLineDecoderSettings : AudioDecoderSettings public class DecoderSettings : AudioDecoderSettings
{ {
public override string Name => name; public override string Name => name;
public override string Extension => extension; public override string Extension => extension;
public CommandLineDecoderSettings() public DecoderSettings()
: base() : base()
{ {
} }
public CommandLineDecoderSettings( public DecoderSettings(
string _name, string _name,
string _extension, string _extension,
string _path, string _path,

View File

@@ -1,63 +1,63 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.ComponentModel; using System.ComponentModel;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace CUETools.Codecs namespace CUETools.Codecs.CommandLine
{ {
[JsonObject(MemberSerialization.OptIn)] [JsonObject(MemberSerialization.OptIn)]
public class CommandLineEncoderSettings : AudioEncoderSettings public class EncoderSettings : AudioEncoderSettings
{ {
public override string Name => name; public override string Name => name;
public override string Extension => extension; public override string Extension => extension;
public override Type EncoderType => typeof(CommandLineEncoder); public override Type EncoderType => typeof(AudioEncoder);
public override bool Lossless => lossless; public override bool Lossless => lossless;
public CommandLineEncoderSettings() public EncoderSettings()
: base() : base()
{ {
} }
[JsonProperty] [JsonProperty]
public string name; public string name;
[JsonProperty] [JsonProperty]
public string extension; public string extension;
[JsonProperty] [JsonProperty]
public bool lossless; public bool lossless;
[DefaultValue(null)] [DefaultValue(null)]
[JsonProperty] [JsonProperty]
public string Path public string Path
{ {
get; get;
set; set;
} }
[DefaultValue(null)] [DefaultValue(null)]
[JsonProperty] [JsonProperty]
public string Parameters public string Parameters
{ {
get; get;
set; set;
} }
[JsonProperty] [JsonProperty]
public string SupportedModes public string SupportedModes
{ {
get get
{ {
return m_supported_modes; return m_supported_modes;
} }
set set
{ {
m_supported_modes = value; m_supported_modes = value;
} }
} }
} }
} }

View File

@@ -1,41 +0,0 @@
using System;
namespace CUETools.Codecs
{
public class DummyWriter : IAudioDest
{
AudioEncoderSettings m_settings;
public DummyWriter(string path, AudioEncoderSettings settings)
{
m_settings = settings;
}
public void Close()
{
}
public void Delete()
{
}
public long FinalSampleCount
{
set { }
}
public AudioEncoderSettings Settings
{
get
{
return m_settings;
}
}
public void Write(AudioBuffer buff)
{
}
public string Path { get { return null; } }
}
}

View File

@@ -1,61 +1,61 @@
namespace CUETools.Codecs namespace CUETools.Codecs.NULL
{ {
public class SilenceGenerator : IAudioSource public class AudioDecoder : IAudioSource
{ {
private long _sampleOffset, _sampleCount; private long _sampleOffset, _sampleCount;
private AudioPCMConfig pcm; private AudioPCMConfig pcm;
private int _sampleVal; private int _sampleVal;
public AudioDecoderSettings Settings { get { return null; } } public AudioDecoderSettings Settings { get { return null; } }
public long Length public long Length
{ {
get { return _sampleCount; } get { return _sampleCount; }
} }
public long Remaining public long Remaining
{ {
get { return _sampleCount - _sampleOffset; } get { return _sampleCount - _sampleOffset; }
} }
public long Position public long Position
{ {
get { return _sampleOffset; } get { return _sampleOffset; }
set { _sampleOffset = value; } set { _sampleOffset = value; }
} }
public AudioPCMConfig PCM { get { return pcm; } } public AudioPCMConfig PCM { get { return pcm; } }
public string Path { get { return null; } } public string Path { get { return null; } }
public SilenceGenerator(AudioPCMConfig pcm, long sampleCount, int sampleVal) public AudioDecoder(AudioPCMConfig pcm, long sampleCount, int sampleVal)
{ {
this._sampleVal = sampleVal; this._sampleVal = sampleVal;
this._sampleOffset = 0; this._sampleOffset = 0;
this._sampleCount = sampleCount; this._sampleCount = sampleCount;
this.pcm = pcm; this.pcm = pcm;
} }
public SilenceGenerator(long sampleCount) public AudioDecoder(long sampleCount)
: this(AudioPCMConfig.RedBook, sampleCount, 0) : this(AudioPCMConfig.RedBook, sampleCount, 0)
{ {
} }
public int Read(AudioBuffer buff, int maxLength) public int Read(AudioBuffer buff, int maxLength)
{ {
buff.Prepare(this, maxLength); buff.Prepare(this, maxLength);
int[,] samples = buff.Samples; int[,] samples = buff.Samples;
for (int i = 0; i < buff.Length; i++) for (int i = 0; i < buff.Length; i++)
for (int j = 0; j < PCM.ChannelCount; j++) for (int j = 0; j < PCM.ChannelCount; j++)
samples[i, j] = _sampleVal; samples[i, j] = _sampleVal;
_sampleOffset += buff.Length; _sampleOffset += buff.Length;
return buff.Length; return buff.Length;
} }
public void Close() public void Close()
{ {
} }
} }
} }

View File

@@ -0,0 +1,35 @@
using System;
namespace CUETools.Codecs.NULL
{
public class AudioEncoder : IAudioDest
{
AudioEncoderSettings m_settings;
public AudioEncoder(string path, AudioEncoderSettings settings)
{
m_settings = settings;
}
public void Close()
{
}
public void Delete()
{
}
public long FinalSampleCount
{
set { }
}
public AudioEncoderSettings Settings => m_settings;
public void Write(AudioBuffer buff)
{
}
public string Path => null;
}
}

View File

@@ -0,0 +1,112 @@
using Newtonsoft.Json;
using System;
using System.ComponentModel;
namespace CUETools.Codecs
{
[JsonObject(MemberSerialization.OptIn)]
public class AudioDecoderSettingsViewModel : INotifyPropertyChanged
{
[JsonProperty]
public AudioDecoderSettings decoderSettings = null;
public event PropertyChangedEventHandler PropertyChanged;
[JsonConstructor]
private AudioDecoderSettingsViewModel()
{
}
public AudioDecoderSettingsViewModel(AudioDecoderSettings settings)
{
decoderSettings = settings;
}
public AudioDecoderSettingsViewModel Clone()
{
var res = this.MemberwiseClone() as AudioDecoderSettingsViewModel;
if (decoderSettings != null) res.decoderSettings = decoderSettings.Clone();
return res;
}
public override string ToString()
{
return Name;
}
public string FullName => Name + " [" + Extension + "]";
public string Path
{
get
{
if (decoderSettings is CommandLine.DecoderSettings)
return (decoderSettings as CommandLine.DecoderSettings).Path;
return "";
}
set
{
if (decoderSettings is CommandLine.DecoderSettings)
(decoderSettings as CommandLine.DecoderSettings).Path = value;
else throw new InvalidOperationException();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Path"));
}
}
public string Parameters
{
get
{
if (decoderSettings is CommandLine.DecoderSettings)
return (decoderSettings as CommandLine.DecoderSettings).Parameters;
return "";
}
set
{
if (decoderSettings is CommandLine.DecoderSettings)
(decoderSettings as CommandLine.DecoderSettings).Parameters = value;
else throw new InvalidOperationException();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Parameters"));
}
}
public bool Lossless
{
get => true;
set {
throw new InvalidOperationException();
}
}
public string Name
{
get => decoderSettings.Name;
set
{
if (decoderSettings is CommandLine.DecoderSettings)
(decoderSettings as CommandLine.DecoderSettings).name = value;
else throw new InvalidOperationException();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
}
}
public string Extension
{
get => decoderSettings.Extension;
set
{
if (decoderSettings is CommandLine.DecoderSettings)
(decoderSettings as CommandLine.DecoderSettings).extension = value;
else throw new InvalidOperationException();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Extension"));
}
}
public string DotExtension => "." + Extension;
public bool CanBeDeleted => decoderSettings is CommandLine.DecoderSettings;
public bool IsValid =>
(decoderSettings != null)
&& (decoderSettings is CommandLine.DecoderSettings ? (decoderSettings as CommandLine.DecoderSettings).Path != "" : true);
}
}

View File

@@ -0,0 +1,165 @@
using Newtonsoft.Json;
using System;
using System.ComponentModel;
namespace CUETools.Codecs
{
[JsonObject(MemberSerialization.OptIn)]
public class AudioEncoderSettingsViewModel : INotifyPropertyChanged
{
[JsonProperty]
public AudioEncoderSettings settings = null;
public event PropertyChangedEventHandler PropertyChanged;
[JsonConstructor]
private AudioEncoderSettingsViewModel()
{
}
public AudioEncoderSettingsViewModel(
string _name,
string _extension,
bool _lossless,
string _supported_modes,
string _default_mode,
string _path,
string _parameters
)
{
settings = new CommandLine.EncoderSettings() { name = _name, extension = _extension, SupportedModes = _supported_modes, EncoderMode = _default_mode, Path = _path, Parameters = _parameters, lossless = _lossless };
}
public AudioEncoderSettingsViewModel(AudioEncoderClassAttribute enc)
{
settings = Activator.CreateInstance(enc.Settings) as AudioEncoderSettings;
if (settings == null)
throw new InvalidOperationException("invalid codec");
}
public AudioEncoderSettingsViewModel Clone()
{
var res = this.MemberwiseClone() as AudioEncoderSettingsViewModel;
if (settings != null) res.settings = settings.Clone();
return res;
}
public override string ToString()
{
return Name;
}
public string FullName => Name + " [" + Extension + "]";
public string Path
{
get
{
if (settings is CommandLine.EncoderSettings)
return (settings as CommandLine.EncoderSettings).Path;
return "";
}
set
{
var settings = this.settings as CommandLine.EncoderSettings;
if (settings == null) throw new InvalidOperationException();
settings.Path = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Path"));
}
}
public string Parameters
{
get
{
if (settings is CommandLine.EncoderSettings)
return (settings as CommandLine.EncoderSettings).Parameters;
return "";
}
set
{
var settings = this.settings as CommandLine.EncoderSettings;
if (settings == null) throw new InvalidOperationException();
settings.Parameters = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Parameters"));
}
}
public bool Lossless
{
get => settings.Lossless;
set
{
var settings = this.settings as CommandLine.EncoderSettings;
if (settings == null) throw new InvalidOperationException();
settings.lossless = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Lossless"));
}
}
public string Name
{
get => settings.Name;
set
{
var settings = this.settings as CommandLine.EncoderSettings;
if (settings == null) throw new InvalidOperationException();
settings.name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
}
}
public string Extension
{
get => settings.Extension;
set
{
var settings = this.settings as CommandLine.EncoderSettings;
if (settings == null) throw new InvalidOperationException();
settings.extension = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Extension"));
}
}
public string DotExtension => "." + Extension;
public string SupportedModesStr
{
get
{
string defaultMode;
return this.settings.GetSupportedModes(out defaultMode);
}
set
{
var settings = this.settings as CommandLine.EncoderSettings;
if (settings == null) throw new InvalidOperationException();
settings.SupportedModes = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SupportedModesStr"));
}
}
public string[] SupportedModes => this.SupportedModesStr.Split(' ');
public int EncoderModeIndex
{
get
{
string[] modes = this.SupportedModes;
if (modes == null || modes.Length < 2)
return -1;
for (int i = 0; i < modes.Length; i++)
if (modes[i] == this.settings.EncoderMode)
return i;
return -1;
}
}
public bool CanBeDeleted => settings is CommandLine.EncoderSettings;
public bool IsValid =>
(settings != null)
&& (settings is CommandLine.EncoderSettings ? (settings as CommandLine.EncoderSettings).Path != "" : true);
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.ComponentModel;
namespace CUETools.Codecs
{
public class DecoderListViewModel : BindingList<AudioDecoderSettingsViewModel>
{
public DecoderListViewModel()
: base()
{
AddingNew += OnAddingNew;
}
private void OnAddingNew(object sender, AddingNewEventArgs e)
{
e.NewObject = new AudioDecoderSettingsViewModel(new CommandLine.DecoderSettings("new", "wav", "", ""));
}
public bool TryGetValue(string extension, bool lossless, string name, out AudioDecoderSettingsViewModel result)
{
foreach (AudioDecoderSettingsViewModel udc in this)
{
if (udc.decoderSettings.Extension == extension && udc.decoderSettings.Lossless == lossless && udc.decoderSettings.Name == name)
{
result = udc;
return true;
}
}
result = null;
return false;
}
public AudioDecoderSettingsViewModel GetDefault(string extension, bool lossless)
{
AudioDecoderSettingsViewModel result = null;
foreach (AudioDecoderSettingsViewModel udc in this)
{
if (udc.decoderSettings.Extension == extension && udc.decoderSettings.Lossless == lossless && (result == null || result.decoderSettings.Priority < udc.decoderSettings.Priority))
{
result = udc;
}
}
return result;
}
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.ComponentModel;
namespace CUETools.Codecs
{
public class EncoderListViewModel : BindingList<AudioEncoderSettingsViewModel>
{
public EncoderListViewModel()
: base()
{
AddingNew += OnAddingNew;
}
private void OnAddingNew(object sender, AddingNewEventArgs e)
{
e.NewObject = new AudioEncoderSettingsViewModel("new", "wav", true, "", "", "", "");
}
public bool TryGetValue(string extension, bool lossless, string name, out AudioEncoderSettingsViewModel result)
{
//result = this.Where(udc => udc.settings.Extension == extension && udc.settings.Lossless == lossless && udc.settings.Name == name).First();
foreach (AudioEncoderSettingsViewModel udc in this)
{
if (udc.settings.Extension == extension && udc.settings.Lossless == lossless && udc.settings.Name == name)
{
result = udc;
return true;
}
}
result = null;
return false;
}
public AudioEncoderSettingsViewModel GetDefault(string extension, bool lossless)
{
AudioEncoderSettingsViewModel result = null;
foreach (AudioEncoderSettingsViewModel udc in this)
{
if (udc.settings.Extension == extension && udc.settings.Lossless == lossless && (result == null || result.settings.Priority < udc.settings.Priority))
{
result = udc;
}
}
return result;
}
}
}

View File

@@ -1,285 +1,270 @@
using System; using System;
using System.IO; using System.IO;
namespace CUETools.Codecs.WAV namespace CUETools.Codecs.WAV
{ {
public class DecoderSettings : AudioDecoderSettings [AudioDecoderClass(typeof(DecoderSettings))]
{ public class AudioDecoder : IAudioSource
public override string Name => "cuetools"; {
Stream _IO;
public override string Extension => "wav"; BinaryReader _br;
long _dataOffset, _samplePos, _sampleLen;
public override Type DecoderType => typeof(AudioDecoder); private AudioPCMConfig pcm;
long _dataLen;
public override int Priority => 2; bool _largeFile;
string _path;
public DecoderSettings() : base() {}
private DecoderSettings m_settings;
public bool IgnoreChunkSizes; public AudioDecoderSettings Settings => m_settings;
}
public long Position
[AudioDecoderClass(typeof(DecoderSettings))] {
public class AudioDecoder : IAudioSource get
{ {
Stream _IO; return _samplePos;
BinaryReader _br; }
long _dataOffset, _samplePos, _sampleLen; set
private AudioPCMConfig pcm; {
long _dataLen; long seekPos;
bool _largeFile;
string _path; if (_samplePos == value)
{
private DecoderSettings m_settings; return;
public AudioDecoderSettings Settings => m_settings; }
public long Position var oldSamplePos = _samplePos;
{ if (_sampleLen >= 0 && value > _sampleLen)
get _samplePos = _sampleLen;
{ else
return _samplePos; _samplePos = value;
}
set if (_IO.CanSeek || _samplePos < oldSamplePos)
{ {
long seekPos; seekPos = _dataOffset + _samplePos * PCM.BlockAlign;
_IO.Seek(seekPos, SeekOrigin.Begin);
if (_samplePos == value) }
{ else
return; {
} int offs = (int)(_samplePos - oldSamplePos) * PCM.BlockAlign;
while (offs > 0)
var oldSamplePos = _samplePos; {
if (_sampleLen >= 0 && value > _sampleLen) int chunk = Math.Min(offs, 16536);
_samplePos = _sampleLen; _br.ReadBytes(chunk);
else offs -= chunk;
_samplePos = value; }
}
if (_IO.CanSeek || _samplePos < oldSamplePos) }
{ }
seekPos = _dataOffset + _samplePos * PCM.BlockAlign;
_IO.Seek(seekPos, SeekOrigin.Begin); public long Length
} {
else get
{ {
int offs = (int)(_samplePos - oldSamplePos) * PCM.BlockAlign; return _sampleLen;
while (offs > 0) }
{ }
int chunk = Math.Min(offs, 16536);
_br.ReadBytes(chunk); public long Remaining
offs -= chunk; {
} get
} {
} return _sampleLen - _samplePos;
} }
}
public long Length
{ public AudioPCMConfig PCM { get { return pcm; } }
get
{ public string Path { get { return _path; } }
return _sampleLen;
} public AudioDecoder(DecoderSettings settings, string path, Stream IO = null)
} {
m_settings = settings;
public long Remaining _path = path;
{ _IO = IO ?? new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000, FileOptions.SequentialScan);
get _br = new BinaryReader(_IO);
{
return _sampleLen - _samplePos; ParseHeaders();
}
} if (_dataLen < 0 || m_settings.IgnoreChunkSizes)
_sampleLen = -1;
public AudioPCMConfig PCM { get { return pcm; } } else
_sampleLen = _dataLen / pcm.BlockAlign;
public string Path { get { return _path; } } }
public AudioDecoder(DecoderSettings settings, string path, Stream IO = null) public AudioDecoder(DecoderSettings settings, string path, Stream IO, AudioPCMConfig _pcm)
{ {
m_settings = settings; m_settings = settings;
_path = path; _path = path;
_IO = IO ?? new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000, FileOptions.SequentialScan); _IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000, FileOptions.SequentialScan);
_br = new BinaryReader(_IO); _br = new BinaryReader(_IO);
ParseHeaders(); _largeFile = false;
_dataOffset = 0;
if (_dataLen < 0 || m_settings.IgnoreChunkSizes) _samplePos = 0;
_sampleLen = -1; pcm = _pcm;
else _dataLen = _IO.CanSeek ? _IO.Length : -1;
_sampleLen = _dataLen / pcm.BlockAlign; if (_dataLen < 0)
} _sampleLen = -1;
else
public AudioDecoder(DecoderSettings settings, string path, Stream IO, AudioPCMConfig _pcm) {
{ _sampleLen = _dataLen / pcm.BlockAlign;
m_settings = settings; if ((_dataLen % pcm.BlockAlign) != 0)
_path = path; throw new Exception("odd file size");
_IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000, FileOptions.SequentialScan); }
_br = new BinaryReader(_IO); }
_largeFile = false; public static AudioBuffer ReadAllSamples(DecoderSettings settings, string path, Stream IO = null)
_dataOffset = 0; {
_samplePos = 0; AudioDecoder reader = new AudioDecoder(settings, path, IO);
pcm = _pcm; AudioBuffer buff = new AudioBuffer(reader, (int)reader.Length);
_dataLen = _IO.CanSeek ? _IO.Length : -1; reader.Read(buff, -1);
if (_dataLen < 0) if (reader.Remaining != 0)
_sampleLen = -1; throw new Exception("couldn't read the whole file");
else reader.Close();
{ return buff;
_sampleLen = _dataLen / pcm.BlockAlign; }
if ((_dataLen % pcm.BlockAlign) != 0)
throw new Exception("odd file size"); public void Close()
} {
} if (_br != null)
{
public static AudioBuffer ReadAllSamples(DecoderSettings settings, string path, Stream IO = null) _br.Close();
{ _br = null;
AudioDecoder reader = new AudioDecoder(settings, path, IO); }
AudioBuffer buff = new AudioBuffer(reader, (int)reader.Length); _IO = null;
reader.Read(buff, -1); }
if (reader.Remaining != 0)
throw new Exception("couldn't read the whole file"); private void ParseHeaders()
reader.Close(); {
return buff; const long maxFileSize = 0x7FFFFFFEL;
} const uint fccRIFF = 0x46464952;
const uint fccWAVE = 0x45564157;
public void Close() const uint fccFormat = 0x20746D66;
{ const uint fccData = 0x61746164;
if (_br != null)
{ uint lenRIFF;
_br.Close(); bool foundFormat, foundData;
_br = null;
} if (_br.ReadUInt32() != fccRIFF)
_IO = null; {
} throw new Exception("Not a valid RIFF file.");
}
private void ParseHeaders()
{ lenRIFF = _br.ReadUInt32();
const long maxFileSize = 0x7FFFFFFEL;
const uint fccRIFF = 0x46464952; if (_br.ReadUInt32() != fccWAVE)
const uint fccWAVE = 0x45564157; {
const uint fccFormat = 0x20746D66; throw new Exception("Not a valid WAVE file.");
const uint fccData = 0x61746164; }
uint lenRIFF; _largeFile = false;
bool foundFormat, foundData; foundFormat = false;
foundData = false;
if (_br.ReadUInt32() != fccRIFF) long pos = 12;
{ do
throw new Exception("Not a valid RIFF file."); {
} uint ckID, ckSize, ckSizePadded;
long ckEnd;
lenRIFF = _br.ReadUInt32();
ckID = _br.ReadUInt32();
if (_br.ReadUInt32() != fccWAVE) ckSize = _br.ReadUInt32();
{ ckSizePadded = (ckSize + 1U) & ~1U;
throw new Exception("Not a valid WAVE file."); pos += 8;
} ckEnd = pos + (long)ckSizePadded;
_largeFile = false; if (ckID == fccFormat)
foundFormat = false; {
foundData = false; foundFormat = true;
long pos = 12;
do uint fmtTag = _br.ReadUInt16();
{ int _channelCount = _br.ReadInt16();
uint ckID, ckSize, ckSizePadded; int _sampleRate = _br.ReadInt32();
long ckEnd; _br.ReadInt32(); // bytes per second
int _blockAlign = _br.ReadInt16();
ckID = _br.ReadUInt32(); int _bitsPerSample = _br.ReadInt16();
ckSize = _br.ReadUInt32(); int _channelMask = 0;
ckSizePadded = (ckSize + 1U) & ~1U; pos += 16;
pos += 8;
ckEnd = pos + (long)ckSizePadded; if (fmtTag == 0xFFFEU && ckSize >= 34) // WAVE_FORMAT_EXTENSIBLE
{
if (ckID == fccFormat) _br.ReadInt16(); // CbSize
{ _br.ReadInt16(); // ValidBitsPerSample
foundFormat = true; _channelMask = _br.ReadInt32();
fmtTag = _br.ReadUInt16();
uint fmtTag = _br.ReadUInt16(); pos += 10;
int _channelCount = _br.ReadInt16(); }
int _sampleRate = _br.ReadInt32();
_br.ReadInt32(); // bytes per second if (fmtTag != 1) // WAVE_FORMAT_PCM
int _blockAlign = _br.ReadInt16(); throw new Exception("WAVE format tag not WAVE_FORMAT_PCM.");
int _bitsPerSample = _br.ReadInt16();
int _channelMask = 0; pcm = new AudioPCMConfig(_bitsPerSample, _channelCount, _sampleRate, (AudioPCMConfig.SpeakerConfig)_channelMask);
pos += 16; if (pcm.BlockAlign != _blockAlign)
throw new Exception("WAVE has strange BlockAlign");
if (fmtTag == 0xFFFEU && ckSize >= 34) // WAVE_FORMAT_EXTENSIBLE }
{ else if (ckID == fccData)
_br.ReadInt16(); // CbSize {
_br.ReadInt16(); // ValidBitsPerSample foundData = true;
_channelMask = _br.ReadInt32();
fmtTag = _br.ReadUInt16(); _dataOffset = pos;
pos += 10; if (!_IO.CanSeek || _IO.Length <= maxFileSize)
} {
if (ckSize == 0 || ckSize >= 0x7fffffff)
if (fmtTag != 1) // WAVE_FORMAT_PCM _dataLen = -1;
throw new Exception("WAVE format tag not WAVE_FORMAT_PCM."); else
_dataLen = (long)ckSize;
pcm = new AudioPCMConfig(_bitsPerSample, _channelCount, _sampleRate, (AudioPCMConfig.SpeakerConfig)_channelMask); }
if (pcm.BlockAlign != _blockAlign) else
throw new Exception("WAVE has strange BlockAlign"); {
} _largeFile = true;
else if (ckID == fccData) _dataLen = _IO.Length - pos;
{ }
foundData = true; }
_dataOffset = pos; if ((foundFormat & foundData) || _largeFile)
if (!_IO.CanSeek || _IO.Length <= maxFileSize) break;
{ if (_IO.CanSeek)
if (ckSize == 0 || ckSize >= 0x7fffffff) _IO.Seek(ckEnd, SeekOrigin.Begin);
_dataLen = -1; else
else _br.ReadBytes((int)(ckEnd - pos));
_dataLen = (long)ckSize; pos = ckEnd;
} } while (true);
else
{ if ((foundFormat & foundData) == false || pcm == null)
_largeFile = true; throw new Exception("Format or data chunk not found.");
_dataLen = _IO.Length - pos; if (pcm.ChannelCount <= 0)
} throw new Exception("Channel count is invalid.");
} if (pcm.SampleRate <= 0)
throw new Exception("Sample rate is invalid.");
if ((foundFormat & foundData) || _largeFile) if ((pcm.BitsPerSample <= 0) || (pcm.BitsPerSample > 32))
break; throw new Exception("Bits per sample is invalid.");
if (_IO.CanSeek) if (pos != _dataOffset)
_IO.Seek(ckEnd, SeekOrigin.Begin); Position = 0;
else }
_br.ReadBytes((int)(ckEnd - pos));
pos = ckEnd; public int Read(AudioBuffer buff, int maxLength)
} while (true); {
buff.Prepare(this, maxLength);
if ((foundFormat & foundData) == false || pcm == null)
throw new Exception("Format or data chunk not found."); byte[] bytes = buff.Bytes;
if (pcm.ChannelCount <= 0) int byteCount = (int)buff.ByteLength;
throw new Exception("Channel count is invalid."); int pos = 0;
if (pcm.SampleRate <= 0)
throw new Exception("Sample rate is invalid."); while (pos < byteCount)
if ((pcm.BitsPerSample <= 0) || (pcm.BitsPerSample > 32)) {
throw new Exception("Bits per sample is invalid."); int len = _IO.Read(bytes, pos, byteCount - pos);
if (pos != _dataOffset) if (len <= 0)
Position = 0; {
} if ((pos % PCM.BlockAlign) != 0 || _sampleLen >= 0)
throw new Exception("Incomplete file read.");
public int Read(AudioBuffer buff, int maxLength) buff.Length = pos / PCM.BlockAlign;
{ _samplePos += buff.Length;
buff.Prepare(this, maxLength); _sampleLen = _samplePos;
return buff.Length;
byte[] bytes = buff.Bytes; }
int byteCount = (int)buff.ByteLength; pos += len;
int pos = 0; }
_samplePos += buff.Length;
while (pos < byteCount) return buff.Length;
{ }
int len = _IO.Read(bytes, pos, byteCount - pos); }
if (len <= 0) }
{
if ((pos % PCM.BlockAlign) != 0 || _sampleLen >= 0)
throw new Exception("Incomplete file read.");
buff.Length = pos / PCM.BlockAlign;
_samplePos += buff.Length;
_sampleLen = _samplePos;
return buff.Length;
}
pos += len;
}
_samplePos += buff.Length;
return buff.Length;
}
}
}

View File

@@ -1,181 +1,181 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
namespace CUETools.Codecs.WAV namespace CUETools.Codecs.WAV
{ {
[AudioEncoderClass(typeof(EncoderSettings))] [AudioEncoderClass(typeof(EncoderSettings))]
public class AudioEncoder : IAudioDest public class AudioEncoder : IAudioDest
{ {
private Stream _IO; private Stream _IO;
private BinaryWriter _bw; private BinaryWriter _bw;
private long _sampleLen; private long _sampleLen;
private string _path; private string _path;
private long hdrLen = 0; private long hdrLen = 0;
private bool _headersWritten = false; private bool _headersWritten = false;
private long _finalSampleCount = -1; private long _finalSampleCount = -1;
private List<byte[]> _chunks = null; private List<byte[]> _chunks = null;
private List<uint> _chunkFCCs = null; private List<uint> _chunkFCCs = null;
public long Position public long Position
{ {
get get
{ {
return _sampleLen; return _sampleLen;
} }
} }
public long FinalSampleCount public long FinalSampleCount
{ {
set { _finalSampleCount = value; } set { _finalSampleCount = value; }
} }
private EncoderSettings m_settings; private EncoderSettings m_settings;
public AudioEncoderSettings Settings => m_settings; public AudioEncoderSettings Settings => m_settings;
public string Path { get { return _path; } } public string Path { get { return _path; } }
public AudioEncoder(EncoderSettings settings, string path, Stream IO = null) public AudioEncoder(EncoderSettings settings, string path, Stream IO = null)
{ {
m_settings = settings; m_settings = settings;
_path = path; _path = path;
_IO = IO ?? new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read); _IO = IO ?? new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
_bw = new BinaryWriter(_IO); _bw = new BinaryWriter(_IO);
} }
public void WriteChunk(uint fcc, byte[] data) public void WriteChunk(uint fcc, byte[] data)
{ {
if (_sampleLen > 0) if (_sampleLen > 0)
throw new Exception("data already written, no chunks allowed"); throw new Exception("data already written, no chunks allowed");
if (_chunks == null) if (_chunks == null)
{ {
_chunks = new List<byte[]>(); _chunks = new List<byte[]>();
_chunkFCCs = new List<uint>(); _chunkFCCs = new List<uint>();
} }
_chunkFCCs.Add(fcc); _chunkFCCs.Add(fcc);
_chunks.Add(data); _chunks.Add(data);
hdrLen += 8 + data.Length + (data.Length & 1); hdrLen += 8 + data.Length + (data.Length & 1);
} }
private void WriteHeaders() private void WriteHeaders()
{ {
const uint fccRIFF = 0x46464952; const uint fccRIFF = 0x46464952;
const uint fccWAVE = 0x45564157; const uint fccWAVE = 0x45564157;
const uint fccFormat = 0x20746D66; const uint fccFormat = 0x20746D66;
const uint fccData = 0x61746164; const uint fccData = 0x61746164;
bool wavex = (Settings.PCM.BitsPerSample != 16 && Settings.PCM.BitsPerSample != 24) || Settings.PCM.ChannelMask != AudioPCMConfig.GetDefaultChannelMask(Settings.PCM.ChannelCount); bool wavex = (Settings.PCM.BitsPerSample != 16 && Settings.PCM.BitsPerSample != 24) || Settings.PCM.ChannelMask != AudioPCMConfig.GetDefaultChannelMask(Settings.PCM.ChannelCount);
hdrLen += 36 + (wavex ? 24 : 0) + 8; hdrLen += 36 + (wavex ? 24 : 0) + 8;
uint dataLen = (uint)(_finalSampleCount * Settings.PCM.BlockAlign); uint dataLen = (uint)(_finalSampleCount * Settings.PCM.BlockAlign);
uint dataLenPadded = dataLen + (dataLen & 1); uint dataLenPadded = dataLen + (dataLen & 1);
_bw.Write(fccRIFF); _bw.Write(fccRIFF);
if (_finalSampleCount <= 0) if (_finalSampleCount <= 0)
_bw.Write((uint)0xffffffff); _bw.Write((uint)0xffffffff);
else else
_bw.Write((uint)(dataLenPadded + hdrLen - 8)); _bw.Write((uint)(dataLenPadded + hdrLen - 8));
_bw.Write(fccWAVE); _bw.Write(fccWAVE);
_bw.Write(fccFormat); _bw.Write(fccFormat);
if (wavex) if (wavex)
{ {
_bw.Write((uint)40); _bw.Write((uint)40);
_bw.Write((ushort)0xfffe); // WAVEX follows _bw.Write((ushort)0xfffe); // WAVEX follows
} }
else else
{ {
_bw.Write((uint)16); _bw.Write((uint)16);
_bw.Write((ushort)1); // PCM _bw.Write((ushort)1); // PCM
} }
_bw.Write((ushort)Settings.PCM.ChannelCount); _bw.Write((ushort)Settings.PCM.ChannelCount);
_bw.Write((uint)Settings.PCM.SampleRate); _bw.Write((uint)Settings.PCM.SampleRate);
_bw.Write((uint)(Settings.PCM.SampleRate * Settings.PCM.BlockAlign)); _bw.Write((uint)(Settings.PCM.SampleRate * Settings.PCM.BlockAlign));
_bw.Write((ushort)Settings.PCM.BlockAlign); _bw.Write((ushort)Settings.PCM.BlockAlign);
_bw.Write((ushort)((Settings.PCM.BitsPerSample + 7) / 8 * 8)); _bw.Write((ushort)((Settings.PCM.BitsPerSample + 7) / 8 * 8));
if (wavex) if (wavex)
{ {
_bw.Write((ushort)22); // length of WAVEX structure _bw.Write((ushort)22); // length of WAVEX structure
_bw.Write((ushort)Settings.PCM.BitsPerSample); _bw.Write((ushort)Settings.PCM.BitsPerSample);
_bw.Write((uint)Settings.PCM.ChannelMask); _bw.Write((uint)Settings.PCM.ChannelMask);
_bw.Write((ushort)1); // PCM Guid _bw.Write((ushort)1); // PCM Guid
_bw.Write((ushort)0); _bw.Write((ushort)0);
_bw.Write((ushort)0); _bw.Write((ushort)0);
_bw.Write((ushort)0x10); _bw.Write((ushort)0x10);
_bw.Write((byte)0x80); _bw.Write((byte)0x80);
_bw.Write((byte)0x00); _bw.Write((byte)0x00);
_bw.Write((byte)0x00); _bw.Write((byte)0x00);
_bw.Write((byte)0xaa); _bw.Write((byte)0xaa);
_bw.Write((byte)0x00); _bw.Write((byte)0x00);
_bw.Write((byte)0x38); _bw.Write((byte)0x38);
_bw.Write((byte)0x9b); _bw.Write((byte)0x9b);
_bw.Write((byte)0x71); _bw.Write((byte)0x71);
} }
if (_chunks != null) if (_chunks != null)
for (int i = 0; i < _chunks.Count; i++) for (int i = 0; i < _chunks.Count; i++)
{ {
_bw.Write(_chunkFCCs[i]); _bw.Write(_chunkFCCs[i]);
_bw.Write((uint)_chunks[i].Length); _bw.Write((uint)_chunks[i].Length);
_bw.Write(_chunks[i]); _bw.Write(_chunks[i]);
if ((_chunks[i].Length & 1) != 0) if ((_chunks[i].Length & 1) != 0)
_bw.Write((byte)0); _bw.Write((byte)0);
} }
_bw.Write(fccData); _bw.Write(fccData);
if (_finalSampleCount <= 0) if (_finalSampleCount <= 0)
_bw.Write((uint)0xffffffff); _bw.Write((uint)0xffffffff);
else else
_bw.Write(dataLen); _bw.Write(dataLen);
_headersWritten = true; _headersWritten = true;
} }
public void Close() public void Close()
{ {
if (_finalSampleCount <= 0 && _IO.CanSeek) if (_finalSampleCount <= 0 && _IO.CanSeek)
{ {
long dataLen = _sampleLen * Settings.PCM.BlockAlign; long dataLen = _sampleLen * Settings.PCM.BlockAlign;
long dataLenPadded = dataLen + (dataLen & 1); long dataLenPadded = dataLen + (dataLen & 1);
if (dataLenPadded + hdrLen - 8 < 0xffffffff) if (dataLenPadded + hdrLen - 8 < 0xffffffff)
{ {
if ((dataLen & 1) == 1) if ((dataLen & 1) == 1)
_bw.Write((byte)0); _bw.Write((byte)0);
_bw.Seek(4, SeekOrigin.Begin); _bw.Seek(4, SeekOrigin.Begin);
_bw.Write((uint)(dataLenPadded + hdrLen - 8)); _bw.Write((uint)(dataLenPadded + hdrLen - 8));
_bw.Seek((int)hdrLen - 4, SeekOrigin.Begin); _bw.Seek((int)hdrLen - 4, SeekOrigin.Begin);
_bw.Write((uint)dataLen); _bw.Write((uint)dataLen);
} }
} }
_bw.Close(); _bw.Close();
_bw = null; _bw = null;
_IO = null; _IO = null;
if (_finalSampleCount > 0 && _sampleLen != _finalSampleCount) if (_finalSampleCount > 0 && _sampleLen != _finalSampleCount)
throw new Exception("Samples written differs from the expected sample count."); throw new Exception("Samples written differs from the expected sample count.");
} }
public void Delete() public void Delete()
{ {
_bw.Close(); _bw.Close();
_bw = null; _bw = null;
_IO = null; _IO = null;
if (_path != "") if (_path != "")
File.Delete(_path); File.Delete(_path);
} }
public void Write(AudioBuffer buff) public void Write(AudioBuffer buff)
{ {
if (buff.Length == 0) if (buff.Length == 0)
return; return;
buff.Prepare(this); buff.Prepare(this);
if (!_headersWritten) if (!_headersWritten)
WriteHeaders(); WriteHeaders();
_IO.Write(buff.Bytes, 0, buff.ByteLength); _IO.Write(buff.Bytes, 0, buff.ByteLength);
_sampleLen += buff.Length; _sampleLen += buff.Length;
} }
} }
} }

View File

@@ -0,0 +1,19 @@
using System;
namespace CUETools.Codecs.WAV
{
public class DecoderSettings : AudioDecoderSettings
{
public override string Name => "cuetools";
public override string Extension => "wav";
public override Type DecoderType => typeof(AudioDecoder);
public override int Priority => 2;
public DecoderSettings() : base() { }
public bool IgnoreChunkSizes;
}
}

View File

@@ -1,29 +1,29 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
namespace CUETools.Codecs.WAV namespace CUETools.Codecs.WAV
{ {
public class EncoderSettings : AudioEncoderSettings public class EncoderSettings : AudioEncoderSettings
{ {
public override string Extension => "wav"; public override string Extension => "wav";
public override string Name => "cuetools"; public override string Name => "cuetools";
public override Type EncoderType => typeof(WAV.AudioEncoder); public override Type EncoderType => typeof(WAV.AudioEncoder);
public override int Priority => 10; public override int Priority => 10;
public override bool Lossless => true; public override bool Lossless => true;
public EncoderSettings() public EncoderSettings()
: this(null) : this(null)
{ {
} }
public EncoderSettings(AudioPCMConfig pcm) public EncoderSettings(AudioPCMConfig pcm)
: base(pcm) : base(pcm)
{ {
} }
} }
} }

View File

@@ -244,7 +244,7 @@ namespace CUETools.FLACCL.cmd
if (input_file == "-") if (input_file == "-")
audioSource = new Codecs.WAV.AudioDecoder(new Codecs.WAV.DecoderSettings() { IgnoreChunkSizes = true }, "", Console.OpenStandardInput()); audioSource = new Codecs.WAV.AudioDecoder(new Codecs.WAV.DecoderSettings() { IgnoreChunkSizes = true }, "", Console.OpenStandardInput());
else if (input_file == "nul") else if (input_file == "nul")
audioSource = new SilenceGenerator(new AudioPCMConfig(input_bps, input_ch, input_rate), input_len, input_val); audioSource = new Codecs.NULL.AudioDecoder(new AudioPCMConfig(input_bps, input_ch, input_rate), input_len, input_val);
else if (File.Exists(input_file) && Path.GetExtension(input_file) == ".wav") else if (File.Exists(input_file) && Path.GetExtension(input_file) == ".wav")
audioSource = new Codecs.WAV.AudioDecoder(new Codecs.WAV.DecoderSettings(), input_file); audioSource = new Codecs.WAV.AudioDecoder(new Codecs.WAV.DecoderSettings(), input_file);
else if (File.Exists(input_file) && Path.GetExtension(input_file) == ".flac") else if (File.Exists(input_file) && Path.GetExtension(input_file) == ".flac")

View File

@@ -15,7 +15,7 @@ namespace CUETools.Processor
long len; long len;
if (!long.TryParse(slen, out len)) if (!long.TryParse(slen, out len))
len = CDImageLayout.TimeFromString(slen) * 588; len = CDImageLayout.TimeFromString(slen) * 588;
return new SilenceGenerator(len); return new Codecs.NULL.AudioDecoder(len);
} }
} }
if (extension == ".bin") if (extension == ".bin")
@@ -51,7 +51,7 @@ namespace CUETools.Processor
{ {
IAudioDest dest; IAudioDest dest;
if (audioEncoderType == AudioEncoderType.NoAudio || extension == ".dummy") if (audioEncoderType == AudioEncoderType.NoAudio || extension == ".dummy")
return new DummyWriter(path, new AudioEncoderSettings(pcm)) { FinalSampleCount = finalSampleCount }; return new Codecs.NULL.AudioEncoder(path, new AudioEncoderSettings(pcm)) { FinalSampleCount = finalSampleCount };
CUEToolsFormat fmt; CUEToolsFormat fmt;
if (!extension.StartsWith(".") || !config.formats.TryGetValue(extension.Substring(1), out fmt)) if (!extension.StartsWith(".") || !config.formats.TryGetValue(extension.Substring(1), out fmt))
throw new Exception("Unsupported audio type: " + path); throw new Exception("Unsupported audio type: " + path);

View File

@@ -71,8 +71,8 @@ namespace CUETools.Processor
public bool CopyAlbumArt { get; set; } public bool CopyAlbumArt { get; set; }
public string ArLogFilenameFormat { get; set; } public string ArLogFilenameFormat { get; set; }
public string AlArtFilenameFormat { get; set; } public string AlArtFilenameFormat { get; set; }
public CUEToolsUDCEncoderList Encoders => encoders; public EncoderListViewModel Encoders => encoders;
public CUEToolsUDCDecoderList Decoders => decoders; public DecoderListViewModel Decoders => decoders;
public CUEConfig() public CUEConfig()
: base(CUEProcessorPlugins.encs, CUEProcessorPlugins.decs) : base(CUEProcessorPlugins.encs, CUEProcessorPlugins.decs)
@@ -312,9 +312,9 @@ namespace CUETools.Processor
int nDecoders = 0; int nDecoders = 0;
foreach (var decoder in decoders) foreach (var decoder in decoders)
if (decoder.decoderSettings is CommandLineDecoderSettings) if (decoder.decoderSettings is Codecs.CommandLine.DecoderSettings)
{ {
var settings = decoder.decoderSettings as CommandLineDecoderSettings; var settings = decoder.decoderSettings as Codecs.CommandLine.DecoderSettings;
sw.Save(string.Format("ExternalDecoder{0}Name", nDecoders), settings.Name); sw.Save(string.Format("ExternalDecoder{0}Name", nDecoders), settings.Name);
sw.Save(string.Format("ExternalDecoder{0}Extension", nDecoders), settings.Extension); sw.Save(string.Format("ExternalDecoder{0}Extension", nDecoders), settings.Extension);
sw.Save(string.Format("ExternalDecoder{0}Path", nDecoders), settings.Path); sw.Save(string.Format("ExternalDecoder{0}Path", nDecoders), settings.Path);
@@ -434,14 +434,14 @@ namespace CUETools.Processor
try try
{ {
var jsonObject = JsonConvert.DeserializeObject(sr.Load("Encoders"), var jsonObject = JsonConvert.DeserializeObject(sr.Load("Encoders"),
typeof(CUEToolsUDCEncoderList), typeof(EncoderListViewModel),
new JsonSerializerSettings { new JsonSerializerSettings {
DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate,
TypeNameHandling = TypeNameHandling.Auto TypeNameHandling = TypeNameHandling.Auto
}); });
if (jsonObject as CUEToolsUDCEncoderList == null) if (jsonObject as EncoderListViewModel == null)
throw new Exception(); throw new Exception();
encoders = jsonObject as CUEToolsUDCEncoderList; encoders = jsonObject as EncoderListViewModel;
encoders.Where(x => !x.IsValid).ToList().ForEach(x => encoders.Remove(x)); encoders.Where(x => !x.IsValid).ToList().ForEach(x => encoders.Remove(x));
AudioEncoderSettingsViewModel tmp; AudioEncoderSettingsViewModel tmp;
encodersBackup.Where(x => !encoders.TryGetValue(x.Extension, x.Lossless, x.Name, out tmp)).ToList().ForEach(x => encoders.Add(x)); encodersBackup.Where(x => !encoders.TryGetValue(x.Extension, x.Lossless, x.Name, out tmp)).ToList().ForEach(x => encoders.Add(x));
@@ -503,7 +503,7 @@ namespace CUETools.Processor
string parameters = sr.Load(string.Format("ExternalDecoder{0}Parameters", nDecoders)); string parameters = sr.Load(string.Format("ExternalDecoder{0}Parameters", nDecoders));
AudioDecoderSettingsViewModel decoder; AudioDecoderSettingsViewModel decoder;
if (!decoders.TryGetValue(extension, true, name, out decoder)) if (!decoders.TryGetValue(extension, true, name, out decoder))
decoders.Add(new AudioDecoderSettingsViewModel(new CommandLineDecoderSettings(name, extension, path, parameters))); decoders.Add(new AudioDecoderSettingsViewModel(new Codecs.CommandLine.DecoderSettings(name, extension, path, parameters)));
else else
{ {
decoder.Extension = extension; decoder.Extension = extension;

View File

@@ -3972,7 +3972,7 @@ namespace CUETools.Processor
if (sourceInfo.Path == null) if (sourceInfo.Path == null)
{ {
audioSource = new SilenceGenerator(sourceInfo.Offset + sourceInfo.Length); audioSource = new Codecs.NULL.AudioDecoder(sourceInfo.Offset + sourceInfo.Length);
} }
else else
{ {