diff --git a/CUERipper/CUERipper.csproj b/CUERipper/CUERipper.csproj index 1c7c718..5ec5661 100644 --- a/CUERipper/CUERipper.csproj +++ b/CUERipper/CUERipper.csproj @@ -252,7 +252,7 @@ - + diff --git a/CUERipper/frmCUERipper.cs b/CUERipper/frmCUERipper.cs index b02d285..ec92194 100644 --- a/CUERipper/frmCUERipper.cs +++ b/CUERipper/frmCUERipper.cs @@ -1002,7 +1002,7 @@ namespace CUERipper { encoder.Settings.PCM = AudioPCMConfig.RedBook; buttonEncoderSettings.Enabled = encoder.Settings.HasBrowsableAttributes(); - string[] modes = encoder.SupportedModes; + string[] modes = encoder.SupportedModes.Split(' '); if (modes == null || modes.Length < 2) { trackBarEncoderMode.Visible = false; @@ -1013,11 +1013,7 @@ namespace CUERipper else { if (encoder.EncoderModeIndex == -1) - { - string defaultMode; - encoder.Settings.GetSupportedModes(out defaultMode); - encoder.Settings.EncoderMode = defaultMode; - } + encoder.Settings.EncoderMode = encoder.Settings.DefaultMode; trackBarEncoderMode.Maximum = modes.Length - 1; trackBarEncoderMode.Value = encoder.EncoderModeIndex == -1 ? modes.Length - 1 : encoder.EncoderModeIndex; labelEncoderMode.Text = encoder.Settings.EncoderMode; @@ -1047,7 +1043,7 @@ namespace CUERipper private void trackBarEncoderMode_Scroll(object sender, EventArgs e) { var encoder = bnComboBoxEncoder.SelectedItem as AudioEncoderSettingsViewModel; - string[] modes = encoder.SupportedModes; + string[] modes = encoder.SupportedModes.Split(' '); encoder.Settings.EncoderMode = modes[trackBarEncoderMode.Value]; labelEncoderMode.Text = encoder.Settings.EncoderMode; } diff --git a/CUETools.ALACEnc/Program.cs b/CUETools.ALACEnc/Program.cs index ddff495..43494a0 100644 --- a/CUETools.ALACEnc/Program.cs +++ b/CUETools.ALACEnc/Program.cs @@ -123,7 +123,7 @@ namespace CUETools.ALACEnc else if (args[arg] != "-" && args[arg][0] == '-' && int.TryParse(args[arg].Substring(1), out intarg)) { ok = intarg >= 0 && intarg <= 11; - settings.EncoderModeIndex = intarg; + settings.SetEncoderModeIndex(intarg); } else if ((args[arg][0] != '-' || args[arg] == "-") && input_file == null) input_file = args[arg]; diff --git a/CUETools.AccurateRip/AccurateRip.cs b/CUETools.AccurateRip/AccurateRip.cs index 04d75fd..1e77ec0 100644 --- a/CUETools.AccurateRip/AccurateRip.cs +++ b/CUETools.AccurateRip/AccurateRip.cs @@ -930,25 +930,13 @@ namespace CUETools.AccurateRip throw new Exception("unsupported"); } - public AudioEncoderSettings Settings - { - get - { - return new AudioEncoderSettings(AudioPCMConfig.RedBook); - } - } + public IAudioEncoderSettings Settings => new Codecs.WAV.EncoderSettings(AudioPCMConfig.RedBook); - public CDImageLayout TOC - { - get { return _toc; } - } + public CDImageLayout TOC => _toc; public long FinalSampleCount { - get - { - return _finalSampleCount; - } + get => _finalSampleCount; set { if (value != _finalSampleCount) diff --git a/CUETools.AccurateRip/CDRepair.cs b/CUETools.AccurateRip/CDRepair.cs index 3457c52..889c78e 100644 --- a/CUETools.AccurateRip/CDRepair.cs +++ b/CUETools.AccurateRip/CDRepair.cs @@ -413,15 +413,9 @@ namespace CUETools.AccurateRip throw new Exception("unsupported"); } - public AudioEncoderSettings Settings - { - get - { - return new AudioEncoderSettings(AudioPCMConfig.RedBook); - } - } + public IAudioEncoderSettings Settings => new Codecs.WAV.EncoderSettings(AudioPCMConfig.RedBook); - public string Path + public string Path { get { throw new Exception("unsupported"); } } diff --git a/CUETools.CLParity/CUETools.CLParity.csproj b/CUETools.CLParity/CUETools.CLParity.csproj index cb8ab92..0643522 100644 --- a/CUETools.CLParity/CUETools.CLParity.csproj +++ b/CUETools.CLParity/CUETools.CLParity.csproj @@ -72,9 +72,9 @@ - + {082D6B9E-326E-4D15-9798-EDAE9EDE70A6} - CUETools.Codecs.FLAKE + CUETools.Codecs.Flake False diff --git a/CUETools.Codecs.ALAC/ALACDotNet.cs b/CUETools.Codecs.ALAC/ALACDotNet.cs index 7106f4f..00e6186 100644 --- a/CUETools.Codecs.ALAC/ALACDotNet.cs +++ b/CUETools.Codecs.ALAC/ALACDotNet.cs @@ -4,6 +4,8 @@ using System.IO; using System.Collections.Generic; using System.Collections.Specialized; using CUETools.Codecs; +using System.ComponentModel; +using Newtonsoft.Json; //Copyright (c) 2008 Grigory Chudov. //This library is based on ALAC decoder by David Hammerton. @@ -27,17 +29,32 @@ using CUETools.Codecs; namespace CUETools.Codecs.ALAC { - public class DecoderSettings : AudioDecoderSettings + [JsonObject(MemberSerialization.OptIn)] + public class DecoderSettings : IAudioDecoderSettings { - public override string Extension => "m4a"; + #region IAudioDecoderSettings implementation + [Browsable(false)] + public string Extension => "m4a"; - public override string Name => "cuetools"; + [Browsable(false)] + public string Name => "cuetools"; - public override Type DecoderType => typeof(AudioDecoder); + [Browsable(false)] + public Type DecoderType => typeof(AudioDecoder); - public override int Priority => 2; + [Browsable(false)] + public int Priority => 2; - public DecoderSettings() : base() { } + public IAudioDecoderSettings Clone() + { + return MemberwiseClone() as IAudioDecoderSettings; + } + #endregion + + public DecoderSettings() + { + this.Init(); + } } public class AudioDecoder : IAudioSource @@ -73,7 +90,7 @@ namespace CUETools.Codecs.ALAC } private DecoderSettings m_settings; - public AudioDecoderSettings Settings => m_settings; + public IAudioDecoderSettings Settings => m_settings; private void InitTables() { diff --git a/CUETools.Codecs.ALAC/ALACWriter.cs b/CUETools.Codecs.ALAC/ALACWriter.cs index 8c90c4e..47e14bf 100644 --- a/CUETools.Codecs.ALAC/ALACWriter.cs +++ b/CUETools.Codecs.ALAC/ALACWriter.cs @@ -35,26 +35,59 @@ using Newtonsoft.Json; namespace CUETools.Codecs.ALAC { [JsonObject(MemberSerialization.OptIn)] - public class EncoderSettings: AudioEncoderSettings + public class EncoderSettings: IAudioEncoderSettings { - public override string Extension => "m4a"; + #region IAudioEncoderSettings implementation + [Browsable(false)] + public string Extension => "m4a"; - public override string Name => "cuetools"; + [Browsable(false)] + public string Name => "cuetools"; - public override Type EncoderType => typeof(AudioEncoder); + [Browsable(false)] + public Type EncoderType => typeof(AudioEncoder); - public override int Priority => 1; + [Browsable(false)] + public bool Lossless => true; - public override 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() - : base("0 1 2 3 4 5 6 7 8 9 10", "5") { + this.Init(); } public void Validate() { - if (EncoderModeIndex < 0 + if (this.GetEncoderModeIndex() < 0 || Padding < 0 || (BlockSize != 0 && (BlockSize < 256 || BlockSize >= Int32.MaxValue))) throw new Exception("unsupported encoder settings"); @@ -140,7 +173,7 @@ namespace CUETools.Codecs.ALAC windowType = new WindowFunction[lpc.MAX_LPC_WINDOWS]; windowSections = new LpcWindowSection[lpc.MAX_LPC_WINDOWS, lpc.MAX_LPC_SECTIONS]; - eparams.set_defaults(m_settings.EncoderModeIndex); + eparams.set_defaults(m_settings.GetEncoderModeIndex()); frame = new ALACFrame(Settings.PCM.ChannelCount == 2 ? 5 : Settings.PCM.ChannelCount); chunk_pos = new List(); @@ -161,13 +194,7 @@ namespace CUETools.Codecs.ALAC EncoderSettings m_settings; - public AudioEncoderSettings Settings - { - get - { - return m_settings; - } - } + public IAudioEncoderSettings Settings => m_settings; #if INTEROP [DllImport("kernel32.dll")] diff --git a/CUETools.Codecs.APE/CUETools.Codecs.APE.cpp b/CUETools.Codecs.APE/CUETools.Codecs.APE.cpp index facee84..199fa52 100644 --- a/CUETools.Codecs.APE/CUETools.Codecs.APE.cpp +++ b/CUETools.Codecs.APE/CUETools.Codecs.APE.cpp @@ -82,35 +82,44 @@ namespace CUETools { namespace Codecs { namespace APE { ref class AudioDecoder; - public ref class DecoderSettings : public AudioDecoderSettings + [Newtonsoft::Json::JsonObject(Newtonsoft::Json::MemberSerialization::OptIn)] + public ref class DecoderSettings : public IAudioDecoderSettings { public: DecoderSettings() - : AudioDecoderSettings() { } + [System::ComponentModel::Browsable(false)] virtual property String^ Name { - String^ get() override { return "MAC_SDK"; } + String^ get() { return "MAC_SDK"; } } + [System::ComponentModel::Browsable(false)] virtual property String^ Extension { - String^ get() override { return "ape"; } + String^ get() { return "ape"; } } + [System::ComponentModel::Browsable(false)] virtual property Type^ DecoderType { - Type^ get() override { return AudioDecoder::typeid; } + Type^ get() { return AudioDecoder::typeid; } } + [System::ComponentModel::Browsable(false)] virtual property int Priority { - int get() override { return 1; } + int get() { return 1; } } - virtual property String^ Version + virtual IAudioDecoderSettings^ Clone() + { + return (IAudioDecoderSettings^)MemberwiseClone(); + } + + property String^ Version { String^ get() { @@ -248,8 +257,8 @@ namespace CUETools { namespace Codecs { namespace APE { return buff->Length; } - virtual property AudioDecoderSettings^ Settings { - AudioDecoderSettings^ get(void) { + virtual property IAudioDecoderSettings^ Settings { + IAudioDecoderSettings^ get(void) { return m_settings; } } @@ -280,46 +289,103 @@ namespace CUETools { namespace Codecs { namespace APE { ref class AudioEncoder; - public ref class EncoderSettings : public AudioEncoderSettings + [Newtonsoft::Json::JsonObject(Newtonsoft::Json::MemberSerialization::OptIn)] + public ref class EncoderSettings : public IAudioEncoderSettings { - public: - EncoderSettings() - : AudioEncoderSettings("fast normal high extra insane", "high") - { - } - + public: + [System::ComponentModel::Browsable(false)] virtual property String^ Name { - String^ get() override { return "MAC_SDK"; } + String^ get() { return "MAC_SDK"; } } + [System::ComponentModel::Browsable(false)] virtual property String^ Extension { - String^ get() override { return "ape"; } + String^ get() { return "ape"; } } + [System::ComponentModel::Browsable(false)] virtual property Type^ EncoderType { - Type^ get() override { return AudioEncoder::typeid; } + Type^ get() { return AudioEncoder::typeid; } } + [System::ComponentModel::Browsable(false)] virtual property bool Lossless { - bool get() override { return true; } + bool get() { return true; } } + [System::ComponentModel::Browsable(false)] virtual property int Priority { - int get() override { return 1; } + int get() { return 1; } } - virtual property String^ Version + [System::ComponentModel::Browsable(false)] + virtual property String^ SupportedModes + { + String^ get() { return "fast normal high extra insane"; } + } + + [System::ComponentModel::Browsable(false)] + virtual property String^ DefaultMode + { + String^ get() { return "high"; } + } + + [System::ComponentModel::Browsable(false)] + [Newtonsoft::Json::JsonProperty] + virtual property String^ EncoderMode + { + String^ get() { return encoderMode; } + void set(String^ value) { encoderMode = value; } + } + + [System::ComponentModel::Browsable(false)] + virtual property AudioPCMConfig^ PCM + { + AudioPCMConfig^ get() { return pcm; } + void set(AudioPCMConfig^ value) { pcm = value; } + } + + [System::ComponentModel::Browsable(false)] + virtual property int BlockSize + { + int get() { return blockSize; } + void set(int value) { blockSize = value; } + } + + [System::ComponentModel::Browsable(false)] + virtual property int Padding + { + int get() { return padding; } + void set(int value) { padding = value; } + } + + virtual IAudioEncoderSettings^ Clone() + { + return (IAudioEncoderSettings^)MemberwiseClone(); + } + + EncoderSettings() + { + IAudioEncoderSettingsExtensions::Init(this, nullptr); + } + + property String^ Version { String^ get() { return MAC_VERSION_STRING; } - } + } + private: + String ^ encoderMode; + AudioPCMConfig^ pcm; + int blockSize; + int padding; }; public ref class AudioEncoder : IAudioDest @@ -420,9 +486,9 @@ namespace CUETools { namespace Codecs { namespace APE { } } - virtual property AudioEncoderSettings^ Settings + virtual property IAudioEncoderSettings^ Settings { - AudioEncoderSettings^ get() + IAudioEncoderSettings^ get() { return _settings; } @@ -452,7 +518,7 @@ namespace CUETools { namespace Codecs { namespace APE { WAVEFORMATEX waveFormat; FillWaveFormatEx (&waveFormat, _settings->PCM->SampleRate, _settings->PCM->BitsPerSample, _settings->PCM->ChannelCount); - Int32 _compressionLevel = (_settings->EncoderModeIndex + 1) * 1000; + Int32 _compressionLevel = (IAudioEncoderSettingsExtensions::GetEncoderModeIndex(_settings) + 1) * 1000; int res = pAPECompress->StartEx (_winFileIO, &waveFormat, diff --git a/CUETools.Codecs.APE/CUETools.Codecs.APE.vcxproj b/CUETools.Codecs.APE/CUETools.Codecs.APE.vcxproj index 79dd2c1..7f7e1db 100644 --- a/CUETools.Codecs.APE/CUETools.Codecs.APE.vcxproj +++ b/CUETools.Codecs.APE/CUETools.Codecs.APE.vcxproj @@ -184,6 +184,12 @@ + + ..\bin\Release\net40\Newtonsoft.Json.dll + false + false + true + true true diff --git a/CUETools.Codecs.BDLPCM/BDLPCMReader.cs b/CUETools.Codecs.BDLPCM/BDLPCMReader.cs index cd334c4..59c94d3 100644 --- a/CUETools.Codecs.BDLPCM/BDLPCMReader.cs +++ b/CUETools.Codecs.BDLPCM/BDLPCMReader.cs @@ -26,7 +26,7 @@ namespace CUETools.Codecs.BDLPCM settings = new DecoderSettings(); } - public AudioDecoderSettings Settings { get { return settings; } } + public IAudioDecoderSettings Settings => settings; public void Close() { diff --git a/CUETools.Codecs.BDLPCM/BDLPCMReaderSettings.cs b/CUETools.Codecs.BDLPCM/BDLPCMReaderSettings.cs index 4bdadde..d697ee4 100644 --- a/CUETools.Codecs.BDLPCM/BDLPCMReaderSettings.cs +++ b/CUETools.Codecs.BDLPCM/BDLPCMReaderSettings.cs @@ -7,25 +7,39 @@ using Newtonsoft.Json; namespace CUETools.Codecs.BDLPCM { [JsonObject(MemberSerialization.OptIn)] - public class DecoderSettings : AudioDecoderSettings + public class DecoderSettings : IAudioDecoderSettings { - public override string Extension => "m2ts"; + #region IAudioDecoderSettings implementation + [Browsable(false)] + public string Extension => "m2ts"; - public override string Name => "cuetools"; + [Browsable(false)] + public string Name => "cuetools"; - public override Type DecoderType => typeof(AudioDecoder); + [Browsable(false)] + public Type DecoderType => typeof(AudioDecoder); - public override int Priority => 2; + [Browsable(false)] + public int Priority => 2; + public IAudioDecoderSettings Clone() + { + return MemberwiseClone() as IAudioDecoderSettings; + } + #endregion + + public DecoderSettings() + { + this.Init(); + } + + [DefaultValue(true)] public bool IgnoreShortItems { get; set; } + [Browsable(false)] public int? Stream { get; set; } + [Browsable(false)] public ushort? Pid { get; set; } - - public DecoderSettings() : base() - { - IgnoreShortItems = true; - } } } diff --git a/CUETools.Codecs.BDLPCM/MPLSReader.cs b/CUETools.Codecs.BDLPCM/MPLSReader.cs index 8025f17..1a587bc 100644 --- a/CUETools.Codecs.BDLPCM/MPLSReader.cs +++ b/CUETools.Codecs.BDLPCM/MPLSReader.cs @@ -277,7 +277,7 @@ namespace CUETools.Codecs.BDLPCM return mark; } - public AudioDecoderSettings Settings { get { return settings; } } + public IAudioDecoderSettings Settings => settings; public void Close() { diff --git a/CUETools.Codecs.BDLPCM/MPLSReaderSettings.cs b/CUETools.Codecs.BDLPCM/MPLSReaderSettings.cs index 760f5a6..7f8f506 100644 --- a/CUETools.Codecs.BDLPCM/MPLSReaderSettings.cs +++ b/CUETools.Codecs.BDLPCM/MPLSReaderSettings.cs @@ -7,25 +7,39 @@ using Newtonsoft.Json; namespace CUETools.Codecs.MPLS { [JsonObject(MemberSerialization.OptIn)] - public class DecoderSettings : AudioDecoderSettings + public class DecoderSettings : IAudioDecoderSettings { - public override string Extension => "mpls"; + #region IAudioDecoderSettings implementation + [Browsable(false)] + public string Extension => "mpls"; - public override string Name => "cuetools"; + [Browsable(false)] + public string Name => "cuetools"; - public override Type DecoderType => typeof(BDLPCM.MPLSDecoder); + [Browsable(false)] + public Type DecoderType => typeof(BDLPCM.MPLSDecoder); - public override int Priority => 2; + [Browsable(false)] + public int Priority => 2; + public IAudioDecoderSettings Clone() + { + return MemberwiseClone() as IAudioDecoderSettings; + } + #endregion + + public DecoderSettings() + { + this.Init(); + } + + [DefaultValue(true)] public bool IgnoreShortItems { get; set; } + [Browsable(false)] public int? Stream { get; set; } + [Browsable(false)] public ushort? Pid { get; set; } - - public DecoderSettings() : base() - { - IgnoreShortItems = true; - } } } diff --git a/CUETools.Codecs.CoreAudio/WasapiOut.cs b/CUETools.Codecs.CoreAudio/WasapiOut.cs index 045cbfd..d5c0774 100644 --- a/CUETools.Codecs.CoreAudio/WasapiOut.cs +++ b/CUETools.Codecs.CoreAudio/WasapiOut.cs @@ -28,7 +28,7 @@ namespace CUETools.Codecs.CoreAudio private long _sampleOffset; private NAudio.Wave.WaveFormatExtensible outputFormat; WaitHandle[] waitHandles; - AudioEncoderSettings m_settings; + Codecs.WAV.EncoderSettings m_settings; /// /// Playback Stopped @@ -67,7 +67,7 @@ namespace CUETools.Codecs.CoreAudio /// public WasapiOut(MMDevice device, AudioClientShareMode shareMode, bool useEventSync, int latency, AudioPCMConfig pcm) { - this.m_settings = new AudioEncoderSettings(pcm); + this.m_settings = new Codecs.WAV.EncoderSettings(pcm); this.audioClient = device.AudioClient; this.shareMode = shareMode; this.isUsingEventSync = useEventSync; @@ -474,30 +474,18 @@ namespace CUETools.Codecs.CoreAudio #endregion - #region IAudioDest Members + #region IAudioDest Members - public long Position - { - get - { - return _sampleOffset; - } - } + public long Position => _sampleOffset; public long FinalSampleCount { set { ; } } - public AudioEncoderSettings Settings - { - get - { - return m_settings; - } - } + public IAudioEncoderSettings Settings => m_settings; - public string Path { get { return null; } } + public string Path => null; #endregion diff --git a/CUETools.Codecs.FLACCL/CUETools.Codecs.FLACCL.csproj b/CUETools.Codecs.FLACCL/CUETools.Codecs.FLACCL.csproj index e732c82..384c4c6 100644 --- a/CUETools.Codecs.FLACCL/CUETools.Codecs.FLACCL.csproj +++ b/CUETools.Codecs.FLACCL/CUETools.Codecs.FLACCL.csproj @@ -71,9 +71,9 @@ - + {082D6B9E-326E-4D15-9798-EDAE9EDE70A6} - CUETools.Codecs.FLAKE + CUETools.Codecs.Flake False diff --git a/CUETools.Codecs.FLACCL/FLACCLWriter.cs b/CUETools.Codecs.FLACCL/FLACCLWriter.cs index 4d23c07..d6f62f9 100644 --- a/CUETools.Codecs.FLACCL/FLACCLWriter.cs +++ b/CUETools.Codecs.FLACCL/FLACCLWriter.cs @@ -26,37 +26,64 @@ using System.Threading; using System.Text; using System.Runtime.InteropServices; using CUETools.Codecs; -using CUETools.Codecs.FLAKE; +using CUETools.Codecs.Flake; using OpenCLNet; using Newtonsoft.Json; namespace CUETools.Codecs.FLACCL { [JsonObject(MemberSerialization.OptIn)] - public class EncoderSettings : AudioEncoderSettings + public class EncoderSettings : IAudioEncoderSettings { - public override string Extension => "flac"; + #region IAudioEncoderSettings implementation + [Browsable(false)] + public string Extension => "flac"; - public override string Name => "FLACCL"; + [Browsable(false)] + public string Name => "FLACCL"; - public override Type EncoderType => typeof(AudioEncoder); + [Browsable(false)] + public Type EncoderType => typeof(AudioEncoder); - public override int Priority => 2; + [Browsable(false)] + public bool Lossless => true; - public override bool Lossless => true; + [Browsable(false)] + public int Priority => 2; + + [Browsable(false)] + public string SupportedModes => this.AllowNonSubset || (this.PCM != null && this.PCM.SampleRate > 48000) ? "0 1 2 3 4 5 6 7 8 9 10 11" : "0 1 2 3 4 5 6 7 8"; + + [Browsable(false)] + public string DefaultMode => "8"; + + [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 internal IntPtr m_platform = IntPtr.Zero; internal IntPtr m_device = IntPtr.Zero; public EncoderSettings() - : base() { - } - - public override string GetSupportedModes(out string defaultMode) - { - defaultMode = "8"; - return this.AllowNonSubset || (this.PCM != null && this.PCM.SampleRate > 48000) ? "0 1 2 3 4 5 6 7 8 9 10 11" : "0 1 2 3 4 5 6 7 8"; + this.Init(); } public bool IsSubset() @@ -74,7 +101,7 @@ namespace CUETools.Codecs.FLACCL public void Validate() { - if (EncoderModeIndex < 0) + if (this.GetEncoderModeIndex() < 0) throw new Exception("unsupported encoder mode"); if (OpenCL.NumberOfPlatforms < 1) throw new Exception("no opencl platforms found"); @@ -109,7 +136,7 @@ namespace CUETools.Codecs.FLACCL Device = devices[0].Name; DriverVersion = devices[0].DriverVersion; } - SetDefaultValuesForMode(); + this.SetDefaultValuesForMode(); if (Padding < 0) throw new Exception("unsupported padding value " + Padding.ToString()); if (BlockSize != 0 && (BlockSize < 256 || BlockSize >= FlakeConstants.MAX_BLOCKSIZE)) @@ -384,7 +411,7 @@ namespace CUETools.Codecs.FLACCL internal EncoderSettings m_settings; - public AudioEncoderSettings Settings + public IAudioEncoderSettings Settings { get { @@ -2310,7 +2337,7 @@ namespace CUETools.Codecs.FLACCL public int flake_set_defaults(EncoderSettings settings) { - int lvl = settings.EncoderModeIndex; + int lvl = settings.GetEncoderModeIndex(); // default to level 5 params window_function = WindowFunction.Flattop | WindowFunction.Tukey; do_midside = true; @@ -2630,7 +2657,7 @@ namespace CUETools.Codecs.FLACCL frame.writer = new BitWriter(outputBuffer, 0, outputBuffer.Length); if (writer.m_settings.DoVerify) - verify = new FLAKE.AudioDecoder(writer.Settings.PCM); + verify = new Flake.AudioDecoder(writer.Settings.PCM); } public void Dispose() diff --git a/CUETools.Codecs.FLAKE/ChannelMode.cs b/CUETools.Codecs.FLAKE/ChannelMode.cs index ab144cf..fd8993b 100644 --- a/CUETools.Codecs.FLAKE/ChannelMode.cs +++ b/CUETools.Codecs.FLAKE/ChannelMode.cs @@ -1,4 +1,4 @@ -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs.Flake { public enum ChannelMode { diff --git a/CUETools.Codecs.FLAKE/FlacFrame.cs b/CUETools.Codecs.FLAKE/FlacFrame.cs index a1c1416..bd20f19 100644 --- a/CUETools.Codecs.FLAKE/FlacFrame.cs +++ b/CUETools.Codecs.FLAKE/FlacFrame.cs @@ -1,4 +1,4 @@ -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs.Flake { unsafe public class FlacFrame { diff --git a/CUETools.Codecs.FLAKE/FlacSubframe.cs b/CUETools.Codecs.FLAKE/FlacSubframe.cs index e3ab85e..fa7685d 100644 --- a/CUETools.Codecs.FLAKE/FlacSubframe.cs +++ b/CUETools.Codecs.FLAKE/FlacSubframe.cs @@ -1,4 +1,4 @@ -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs.Flake { unsafe public class FlacSubframe { diff --git a/CUETools.Codecs.FLAKE/FlacSubframeInfo.cs b/CUETools.Codecs.FLAKE/FlacSubframeInfo.cs index 441b9d0..4445416 100644 --- a/CUETools.Codecs.FLAKE/FlacSubframeInfo.cs +++ b/CUETools.Codecs.FLAKE/FlacSubframeInfo.cs @@ -1,6 +1,6 @@ using System; -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs.Flake { unsafe public class FlacSubframeInfo { diff --git a/CUETools.Codecs.FLAKE/Flake.cs b/CUETools.Codecs.FLAKE/Flake.cs index 372d0f2..e2ef689 100644 --- a/CUETools.Codecs.FLAKE/Flake.cs +++ b/CUETools.Codecs.FLAKE/Flake.cs @@ -20,7 +20,7 @@ */ using System; -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs.Flake { public class FlakeConstants { diff --git a/CUETools.Codecs.FLAKE/MetadataType.cs b/CUETools.Codecs.FLAKE/MetadataType.cs index 6122174..9d2685a 100644 --- a/CUETools.Codecs.FLAKE/MetadataType.cs +++ b/CUETools.Codecs.FLAKE/MetadataType.cs @@ -1,4 +1,4 @@ -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs.Flake { public enum MetadataType { diff --git a/CUETools.Codecs.FLAKE/OrderMethod.cs b/CUETools.Codecs.FLAKE/OrderMethod.cs index e691e53..bd58911 100644 --- a/CUETools.Codecs.FLAKE/OrderMethod.cs +++ b/CUETools.Codecs.FLAKE/OrderMethod.cs @@ -1,4 +1,4 @@ -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs.Flake { public enum OrderMethod { diff --git a/CUETools.Codecs.FLAKE/PredictionType.cs b/CUETools.Codecs.FLAKE/PredictionType.cs index ddf0d86..1eff3ed 100644 --- a/CUETools.Codecs.FLAKE/PredictionType.cs +++ b/CUETools.Codecs.FLAKE/PredictionType.cs @@ -1,4 +1,4 @@ -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs.Flake { /// /// Type of linear prediction diff --git a/CUETools.Codecs.FLAKE/Properties/Resources.Designer.cs b/CUETools.Codecs.FLAKE/Properties/Resources.Designer.cs index 4e1afb2..947eb3e 100644 --- a/CUETools.Codecs.FLAKE/Properties/Resources.Designer.cs +++ b/CUETools.Codecs.FLAKE/Properties/Resources.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace CUETools.Codecs.FLAKE.Properties { +namespace CUETools.Codecs.Flake.Properties { using System; @@ -39,7 +39,7 @@ namespace CUETools.Codecs.FLAKE.Properties { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CUETools.Codecs.FLAKE.Properties.Resources", typeof(Resources).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CUETools.Codecs.Flake.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; diff --git a/CUETools.Codecs.FLAKE/RiceContext.cs b/CUETools.Codecs.FLAKE/RiceContext.cs index a2d349d..7421b0f 100644 --- a/CUETools.Codecs.FLAKE/RiceContext.cs +++ b/CUETools.Codecs.FLAKE/RiceContext.cs @@ -1,4 +1,4 @@ -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs.Flake { unsafe public class RiceContext { diff --git a/CUETools.Codecs.FLAKE/SeekPoint.cs b/CUETools.Codecs.FLAKE/SeekPoint.cs index c4a8005..ac0dbbb 100644 --- a/CUETools.Codecs.FLAKE/SeekPoint.cs +++ b/CUETools.Codecs.FLAKE/SeekPoint.cs @@ -1,4 +1,4 @@ -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs.Flake { public struct SeekPoint { diff --git a/CUETools.Codecs.FLAKE/StereoMethod.cs b/CUETools.Codecs.FLAKE/StereoMethod.cs index 3e42574..3a2d529 100644 --- a/CUETools.Codecs.FLAKE/StereoMethod.cs +++ b/CUETools.Codecs.FLAKE/StereoMethod.cs @@ -1,5 +1,5 @@  -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs.Flake { public enum StereoMethod { diff --git a/CUETools.Codecs.FLAKE/SubframeType.cs b/CUETools.Codecs.FLAKE/SubframeType.cs index 0c60963..a9e240d 100644 --- a/CUETools.Codecs.FLAKE/SubframeType.cs +++ b/CUETools.Codecs.FLAKE/SubframeType.cs @@ -1,4 +1,4 @@ -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs.Flake { public enum SubframeType { diff --git a/CUETools.Codecs.FLAKE/WindowFunction.cs b/CUETools.Codecs.FLAKE/WindowFunction.cs index ff6deeb..eded3f8 100644 --- a/CUETools.Codecs.FLAKE/WindowFunction.cs +++ b/CUETools.Codecs.FLAKE/WindowFunction.cs @@ -1,4 +1,4 @@ -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs.Flake { public enum WindowFunction { diff --git a/CUETools.Codecs.FLAKE/WindowMethod.cs b/CUETools.Codecs.FLAKE/WindowMethod.cs index 45c3bd1..577d7e6 100644 --- a/CUETools.Codecs.FLAKE/WindowMethod.cs +++ b/CUETools.Codecs.FLAKE/WindowMethod.cs @@ -1,4 +1,4 @@ -namespace CUETools.Codecs.FLAKE +namespace CUETools.Codecs.Flake { public enum WindowMethod { diff --git a/CUETools.Codecs.FlaCuda/CUETools.Codecs.FlaCuda.csproj b/CUETools.Codecs.FlaCuda/CUETools.Codecs.FlaCuda.csproj index 68338f5..af86368 100644 --- a/CUETools.Codecs.FlaCuda/CUETools.Codecs.FlaCuda.csproj +++ b/CUETools.Codecs.FlaCuda/CUETools.Codecs.FlaCuda.csproj @@ -72,9 +72,9 @@ - + {082D6B9E-326E-4D15-9798-EDAE9EDE70A6} - CUETools.Codecs.FLAKE + CUETools.Codecs.Flake False diff --git a/CUETools.Codecs.FLAKE/FlakeReader.cs b/CUETools.Codecs.Flake/AudioDecoder.cs similarity index 95% rename from CUETools.Codecs.FLAKE/FlakeReader.cs rename to CUETools.Codecs.Flake/AudioDecoder.cs index 7c599ff..bc456fc 100644 --- a/CUETools.Codecs.FLAKE/FlakeReader.cs +++ b/CUETools.Codecs.Flake/AudioDecoder.cs @@ -1,778 +1,765 @@ -/** - * CUETools.Flake: pure managed FLAC audio encoder - * Copyright (c) 2009 Grigory Chudov - * Based on Flake encoder, http://flake-enc.sourceforge.net/ - * Copyright (c) 2006-2009 Justin Ruggles - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -namespace CUETools.Codecs.FLAKE -{ - public class DecoderSettings : AudioDecoderSettings - { - public override string Extension => "flac"; - - public override string Name => "cuetools"; - - public override Type DecoderType => typeof(AudioDecoder); - - public override int Priority => 2; - - public DecoderSettings() : base() { } - } - - public class AudioDecoder: IAudioSource - { - int[] samplesBuffer; - int[] residualBuffer; - - byte[] _framesBuffer; - int _framesBufferLength = 0, _framesBufferOffset = 0; - long first_frame_offset; - - SeekPoint[] seek_table; - - Crc8 crc8; - FlacFrame frame; - BitReader framereader; - AudioPCMConfig pcm; - - uint min_block_size = 0; - uint max_block_size = 0; - uint min_frame_size = 0; - uint max_frame_size = 0; - - int _samplesInBuffer, _samplesBufferOffset; - long _sampleCount = 0, _sampleOffset = 0; - - bool do_crc = true; - - string _path; - Stream _IO; - - public bool DoCRC - { - get - { - return do_crc; - } - set - { - do_crc = value; - } - } - - public int[] Samples - { - get - { - return samplesBuffer; - } - } - - public AudioDecoder(DecoderSettings settings, string path, Stream IO = null) - { - m_settings = settings; - - _path = path; - _IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000); - - crc8 = new Crc8(); - - _framesBuffer = new byte[0x20000]; - decode_metadata(); - - frame = new FlacFrame(PCM.ChannelCount); - framereader = new BitReader(); - - //max_frame_size = 16 + ((Flake.MAX_BLOCKSIZE * PCM.BitsPerSample * PCM.ChannelCount + 1) + 7) >> 3); - if (((int)max_frame_size * PCM.BitsPerSample * PCM.ChannelCount * 2 >> 3) > _framesBuffer.Length) - { - byte[] temp = _framesBuffer; - _framesBuffer = new byte[((int)max_frame_size * PCM.BitsPerSample * PCM.ChannelCount * 2 >> 3)]; - if (_framesBufferLength > 0) - Array.Copy(temp, _framesBufferOffset, _framesBuffer, 0, _framesBufferLength); - _framesBufferOffset = 0; - } - _samplesInBuffer = 0; - - if (PCM.BitsPerSample != 16 && PCM.BitsPerSample != 24) - throw new Exception("invalid flac file"); - - samplesBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * PCM.ChannelCount]; - residualBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * PCM.ChannelCount]; - } - - public AudioDecoder(AudioPCMConfig _pcm) - { - pcm = _pcm; - crc8 = new Crc8(); - - samplesBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * PCM.ChannelCount]; - residualBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * PCM.ChannelCount]; - frame = new FlacFrame(PCM.ChannelCount); - framereader = new BitReader(); - } - - private DecoderSettings m_settings; - public AudioDecoderSettings Settings => m_settings; - - public void Close() - { - _IO.Close(); - } - - public long Length - { - get - { - return _sampleCount; - } - } - - public long Remaining - { - get - { - return Length - Position; - } - } - - public long Position - { - get - { - return _sampleOffset - _samplesInBuffer; - } - set - { - if (value > _sampleCount) - throw new Exception("seeking past end of stream"); - if (value < Position || value > _sampleOffset) - { - if (seek_table != null && _IO.CanSeek) - { - int best_st = -1; - for (int st = 0; st < seek_table.Length; st++) - { - if (seek_table[st].number <= value && - (best_st == -1 || seek_table[st].number > seek_table[best_st].number)) - best_st = st; - } - if (best_st != -1) - { - _framesBufferLength = 0; - _samplesInBuffer = 0; - _samplesBufferOffset = 0; - _IO.Position = (long)seek_table[best_st].offset + first_frame_offset; - _sampleOffset = seek_table[best_st].number; - } - } - if (value < Position) - throw new Exception("cannot seek backwards without seek table"); - } - while (value > _sampleOffset) - { - _samplesInBuffer = 0; - _samplesBufferOffset = 0; - - fill_frames_buffer(); - if (_framesBufferLength == 0) - throw new Exception("seek failed"); - - int bytesDecoded = DecodeFrame(_framesBuffer, _framesBufferOffset, _framesBufferLength); - _framesBufferLength -= bytesDecoded; - _framesBufferOffset += bytesDecoded; - - _sampleOffset += _samplesInBuffer; - }; - int diff = _samplesInBuffer - (int)(_sampleOffset - value); - _samplesInBuffer -= diff; - _samplesBufferOffset += diff; - } - } - - public AudioPCMConfig PCM - { - get - { - return pcm; - } - } - - public string Path - { - get - { - return _path; - } - } - - unsafe void interlace(AudioBuffer buff, int offset, int count) - { - if (PCM.ChannelCount == 2) - { - fixed (int* src = &samplesBuffer[_samplesBufferOffset]) - buff.Interlace(offset, src, src + FlakeConstants.MAX_BLOCKSIZE, count); - } - else - { - for (int ch = 0; ch < PCM.ChannelCount; ch++) - fixed (int* res = &buff.Samples[offset, ch], src = &samplesBuffer[_samplesBufferOffset + ch * FlakeConstants.MAX_BLOCKSIZE]) - { - int* psrc = src; - for (int i = 0; i < count; i++) - res[i * PCM.ChannelCount] = *(psrc++); - } - } - } - - public int Read(AudioBuffer buff, int maxLength) - { - buff.Prepare(this, maxLength); - - int offset = 0; - int sampleCount = buff.Length; - - while (_samplesInBuffer < sampleCount) - { - if (_samplesInBuffer > 0) - { - interlace(buff, offset, _samplesInBuffer); - sampleCount -= _samplesInBuffer; - offset += _samplesInBuffer; - _samplesInBuffer = 0; - _samplesBufferOffset = 0; - } - - fill_frames_buffer(); - - if (_framesBufferLength == 0) - return buff.Length = offset; - - int bytesDecoded = DecodeFrame(_framesBuffer, _framesBufferOffset, _framesBufferLength); - _framesBufferLength -= bytesDecoded; - _framesBufferOffset += bytesDecoded; - - _samplesInBuffer -= _samplesBufferOffset; // can be set by Seek, otherwise zero - _sampleOffset += _samplesInBuffer; - } - - interlace(buff, offset, sampleCount); - _samplesInBuffer -= sampleCount; - _samplesBufferOffset += sampleCount; - if (_samplesInBuffer == 0) - _samplesBufferOffset = 0; - return buff.Length = offset + sampleCount; - } - - unsafe void fill_frames_buffer() - { - if (_framesBufferLength == 0) - _framesBufferOffset = 0; - else if (_framesBufferLength < _framesBuffer.Length / 2 && _framesBufferOffset >= _framesBuffer.Length / 2) - { - fixed (byte* buff = _framesBuffer) - AudioSamples.MemCpy(buff, buff + _framesBufferOffset, _framesBufferLength); - _framesBufferOffset = 0; - } - while (_framesBufferLength < _framesBuffer.Length / 2) - { - int read = _IO.Read(_framesBuffer, _framesBufferOffset + _framesBufferLength, _framesBuffer.Length - _framesBufferOffset - _framesBufferLength); - _framesBufferLength += read; - if (read == 0) - break; - } - } - - unsafe void decode_frame_header(BitReader bitreader, FlacFrame frame) - { - int header_start = bitreader.Position; - - if (bitreader.readbits(15) != 0x7FFC) - throw new Exception("invalid frame"); - uint vbs = bitreader.readbit(); - frame.bs_code0 = (int) bitreader.readbits(4); - uint sr_code0 = bitreader.readbits(4); - frame.ch_mode = (ChannelMode)bitreader.readbits(4); - uint bps_code = bitreader.readbits(3); - if (FlakeConstants.flac_bitdepths[bps_code] != PCM.BitsPerSample) - throw new Exception("unsupported bps coding"); - uint t1 = bitreader.readbit(); // == 0????? - if (t1 != 0) - throw new Exception("unsupported frame coding"); - frame.frame_number = (int)bitreader.read_utf8(); - - // custom block size - if (frame.bs_code0 == 6) - { - frame.bs_code1 = (int)bitreader.readbits(8); - frame.blocksize = frame.bs_code1 + 1; - } - else if (frame.bs_code0 == 7) - { - frame.bs_code1 = (int)bitreader.readbits(16); - frame.blocksize = frame.bs_code1 + 1; - } - else - frame.blocksize = FlakeConstants.flac_blocksizes[frame.bs_code0]; - - // custom sample rate - if (sr_code0 < 1 || sr_code0 > 11) - { - // sr_code0 == 12 -> sr == bitreader.readbits(8) * 1000; - // sr_code0 == 13 -> sr == bitreader.readbits(16); - // sr_code0 == 14 -> sr == bitreader.readbits(16) * 10; - throw new Exception("invalid sample rate mode"); - } - - int frame_channels = (int)frame.ch_mode + 1; - if (frame_channels > 11) - throw new Exception("invalid channel mode"); - if (frame_channels == 2 || frame_channels > 8) // Mid/Left/Right Side Stereo - frame_channels = 2; - else - frame.ch_mode = ChannelMode.NotStereo; - if (frame_channels != PCM.ChannelCount) - throw new Exception("invalid channel mode"); - - // CRC-8 of frame header - byte crc = do_crc ? crc8.ComputeChecksum(bitreader.Buffer, header_start, bitreader.Position - header_start) : (byte)0; - frame.crc8 = (byte)bitreader.readbits(8); - if (do_crc && frame.crc8 != crc) - throw new Exception("header crc mismatch"); - } - - unsafe void decode_subframe_constant(BitReader bitreader, FlacFrame frame, int ch) - { - int obits = frame.subframes[ch].obits; - frame.subframes[ch].best.residual[0] = bitreader.readbits_signed(obits); - } - - unsafe void decode_subframe_verbatim(BitReader bitreader, FlacFrame frame, int ch) - { - int obits = frame.subframes[ch].obits; - for (int i = 0; i < frame.blocksize; i++) - frame.subframes[ch].best.residual[i] = bitreader.readbits_signed(obits); - } - - unsafe void decode_residual(BitReader bitreader, FlacFrame frame, int ch) - { - // rice-encoded block - // coding method - frame.subframes[ch].best.rc.coding_method = (int)bitreader.readbits(2); // ????? == 0 - if (frame.subframes[ch].best.rc.coding_method != 0 && frame.subframes[ch].best.rc.coding_method != 1) - throw new Exception("unsupported residual coding"); - // partition order - frame.subframes[ch].best.rc.porder = (int)bitreader.readbits(4); - if (frame.subframes[ch].best.rc.porder > 8) - throw new Exception("invalid partition order"); - int psize = frame.blocksize >> frame.subframes[ch].best.rc.porder; - int res_cnt = psize - frame.subframes[ch].best.order; - - int rice_len = 4 + frame.subframes[ch].best.rc.coding_method; - // residual - int j = frame.subframes[ch].best.order; - int* r = frame.subframes[ch].best.residual + j; - for (int p = 0; p < (1 << frame.subframes[ch].best.rc.porder); p++) - { - if (p == 1) res_cnt = psize; - int n = Math.Min(res_cnt, frame.blocksize - j); - - int k = frame.subframes[ch].best.rc.rparams[p] = (int)bitreader.readbits(rice_len); - if (k == (1 << rice_len) - 1) - { - k = frame.subframes[ch].best.rc.esc_bps[p] = (int)bitreader.readbits(5); - for (int i = n; i > 0; i--) - *(r++) = bitreader.readbits_signed((int)k); - } - else - { - bitreader.read_rice_block(n, (int)k, r); - r += n; - } - j += n; - } - } - - unsafe void decode_subframe_fixed(BitReader bitreader, FlacFrame frame, int ch) - { - // warm-up samples - int obits = frame.subframes[ch].obits; - for (int i = 0; i < frame.subframes[ch].best.order; i++) - frame.subframes[ch].best.residual[i] = bitreader.readbits_signed(obits); - - // residual - decode_residual(bitreader, frame, ch); - } - - unsafe void decode_subframe_lpc(BitReader bitreader, FlacFrame frame, int ch) - { - // warm-up samples - int obits = frame.subframes[ch].obits; - for (int i = 0; i < frame.subframes[ch].best.order; i++) - frame.subframes[ch].best.residual[i] = bitreader.readbits_signed(obits); - - // LPC coefficients - frame.subframes[ch].best.cbits = (int)bitreader.readbits(4) + 1; // lpc_precision - if (frame.subframes[ch].best.cbits >= 16) - throw new Exception("cbits >= 16"); - frame.subframes[ch].best.shift = bitreader.readbits_signed(5); - if (frame.subframes[ch].best.shift < 0) - throw new Exception("negative shift"); - for (int i = 0; i < frame.subframes[ch].best.order; i++) - frame.subframes[ch].best.coefs[i] = bitreader.readbits_signed(frame.subframes[ch].best.cbits); - - // residual - decode_residual(bitreader, frame, ch); - } - - unsafe void decode_subframes(BitReader bitreader, FlacFrame frame) - { - fixed (int *r = residualBuffer, s = samplesBuffer) - for (int ch = 0; ch < PCM.ChannelCount; ch++) - { - // subframe header - uint t1 = bitreader.readbit(); // ?????? == 0 - if (t1 != 0) - throw new Exception("unsupported subframe coding (ch == " + ch.ToString() + ")"); - int type_code = (int)bitreader.readbits(6); - frame.subframes[ch].wbits = (int)bitreader.readbit(); - if (frame.subframes[ch].wbits != 0) - frame.subframes[ch].wbits += (int)bitreader.read_unary(); - - frame.subframes[ch].obits = PCM.BitsPerSample - frame.subframes[ch].wbits; - switch (frame.ch_mode) - { - case ChannelMode.MidSide: frame.subframes[ch].obits += ch; break; - case ChannelMode.LeftSide: frame.subframes[ch].obits += ch; break; - case ChannelMode.RightSide: frame.subframes[ch].obits += 1 - ch; break; - } - - frame.subframes[ch].best.type = (SubframeType)type_code; - frame.subframes[ch].best.order = 0; - - if ((type_code & (uint)SubframeType.LPC) != 0) - { - frame.subframes[ch].best.order = (type_code - (int)SubframeType.LPC) + 1; - frame.subframes[ch].best.type = SubframeType.LPC; - } - else if ((type_code & (uint)SubframeType.Fixed) != 0) - { - frame.subframes[ch].best.order = (type_code - (int)SubframeType.Fixed); - frame.subframes[ch].best.type = SubframeType.Fixed; - } - - frame.subframes[ch].best.residual = r + ch * FlakeConstants.MAX_BLOCKSIZE; - frame.subframes[ch].samples = s + ch * FlakeConstants.MAX_BLOCKSIZE; - - // subframe - switch (frame.subframes[ch].best.type) - { - case SubframeType.Constant: - decode_subframe_constant(bitreader, frame, ch); - break; - case SubframeType.Verbatim: - decode_subframe_verbatim(bitreader, frame, ch); - break; - case SubframeType.Fixed: - decode_subframe_fixed(bitreader, frame, ch); - break; - case SubframeType.LPC: - decode_subframe_lpc(bitreader, frame, ch); - break; - default: - throw new Exception("invalid subframe type"); - } - } - } - - unsafe void restore_samples_fixed(FlacFrame frame, int ch) - { - FlacSubframeInfo sub = frame.subframes[ch]; - - AudioSamples.MemCpy(sub.samples, sub.best.residual, sub.best.order); - int* data = sub.samples + sub.best.order; - int* residual = sub.best.residual + sub.best.order; - int data_len = frame.blocksize - sub.best.order; - int s0, s1, s2; - switch (sub.best.order) - { - case 0: - AudioSamples.MemCpy(data, residual, data_len); - break; - case 1: - s1 = data[-1]; - for (int i = data_len; i > 0; i--) - { - s1 += *(residual++); - *(data++) = s1; - } - //data[i] = residual[i] + data[i - 1]; - break; - case 2: - s2 = data[-2]; - s1 = data[-1]; - for (int i = data_len; i > 0; i--) - { - s0 = *(residual++) + (s1 << 1) - s2; - *(data++) = s0; - s2 = s1; - s1 = s0; - } - //data[i] = residual[i] + data[i - 1] * 2 - data[i - 2]; - break; - case 3: - for (int i = 0; i < data_len; i++) - data[i] = residual[i] + (((data[i - 1] - data[i - 2]) << 1) + (data[i - 1] - data[i - 2])) + data[i - 3]; - break; - case 4: - for (int i = 0; i < data_len; i++) - data[i] = residual[i] + ((data[i - 1] + data[i - 3]) << 2) - ((data[i - 2] << 2) + (data[i - 2] << 1)) - data[i - 4]; - break; - } - } - - unsafe void restore_samples_lpc(FlacFrame frame, int ch) - { - FlacSubframeInfo sub = frame.subframes[ch]; - ulong csum = 0; - fixed (int* coefs = sub.best.coefs) - { - for (int i = sub.best.order; i > 0; i--) - csum += (ulong)Math.Abs(coefs[i - 1]); - if ((csum << sub.obits) >= 1UL << 32) - lpc.decode_residual_long(sub.best.residual, sub.samples, frame.blocksize, sub.best.order, coefs, sub.best.shift); - else - lpc.decode_residual(sub.best.residual, sub.samples, frame.blocksize, sub.best.order, coefs, sub.best.shift); - } - } - - unsafe void restore_samples(FlacFrame frame) - { - for (int ch = 0; ch < PCM.ChannelCount; ch++) - { - switch (frame.subframes[ch].best.type) - { - case SubframeType.Constant: - AudioSamples.MemSet(frame.subframes[ch].samples, frame.subframes[ch].best.residual[0], frame.blocksize); - break; - case SubframeType.Verbatim: - AudioSamples.MemCpy(frame.subframes[ch].samples, frame.subframes[ch].best.residual, frame.blocksize); - break; - case SubframeType.Fixed: - restore_samples_fixed(frame, ch); - break; - case SubframeType.LPC: - restore_samples_lpc(frame, ch); - break; - } - if (frame.subframes[ch].wbits != 0) - { - int* s = frame.subframes[ch].samples; - int x = (int) frame.subframes[ch].wbits; - for (int i = frame.blocksize; i > 0; i--) - *(s++) <<= x; - } - } - if (frame.ch_mode != ChannelMode.NotStereo) - { - int* l = frame.subframes[0].samples; - int* r = frame.subframes[1].samples; - switch (frame.ch_mode) - { - case ChannelMode.LeftRight: - break; - case ChannelMode.MidSide: - for (int i = frame.blocksize; i > 0; i--) - { - int mid = *l; - int side = *r; - mid <<= 1; - mid |= (side & 1); /* i.e. if 'side' is odd... */ - *(l++) = (mid + side) >> 1; - *(r++) = (mid - side) >> 1; - } - break; - case ChannelMode.LeftSide: - for (int i = frame.blocksize; i > 0; i--) - { - int _l = *(l++), _r = *r; - *(r++) = _l - _r; - } - break; - case ChannelMode.RightSide: - for (int i = frame.blocksize; i > 0; i--) - *(l++) += *(r++); - break; - } - } - } - - public unsafe int DecodeFrame(byte[] buffer, int pos, int len) - { - fixed (byte* buf = buffer) - { - framereader.Reset(buf, pos, len); - decode_frame_header(framereader, frame); - decode_subframes(framereader, frame); - framereader.flush(); - ushort crc_1 = framereader.get_crc16(); - ushort crc_2 = framereader.read_ushort(); - if (do_crc && crc_1 != crc_2) - throw new Exception("frame crc mismatch"); - restore_samples(frame); - _samplesInBuffer = frame.blocksize; - return framereader.Position - pos; - } - } - - - bool skip_bytes(int bytes) - { - for (int j = 0; j < bytes; j++) - if (0 == _IO.Read(_framesBuffer, 0, 1)) - return false; - return true; - } - - unsafe void decode_metadata() - { - byte x; - int i, id; - //bool first = true; - byte[] FLAC__STREAM_SYNC_STRING = new byte[] { (byte)'f', (byte)'L', (byte)'a', (byte)'C' }; - byte[] ID3V2_TAG_ = new byte[] { (byte)'I', (byte)'D', (byte)'3' }; - - for (i = id = 0; i < 4; ) - { - if (_IO.Read(_framesBuffer, 0, 1) == 0) - throw new Exception("FLAC stream not found"); - x = _framesBuffer[0]; - if (x == FLAC__STREAM_SYNC_STRING[i]) - { - //first = true; - i++; - id = 0; - continue; - } - if (id < 3 && x == ID3V2_TAG_[id]) - { - id++; - i = 0; - if (id == 3) - { - if (!skip_bytes(3)) - throw new Exception("FLAC stream not found"); - int skip = 0; - for (int j = 0; j < 4; j++) - { - if (0 == _IO.Read(_framesBuffer, 0, 1)) - throw new Exception("FLAC stream not found"); - skip <<= 7; - skip |= ((int)_framesBuffer[0] & 0x7f); - } - if (!skip_bytes(skip)) - throw new Exception("FLAC stream not found"); - } - continue; - } - id = 0; - if (x == 0xff) /* MAGIC NUMBER for the first 8 frame sync bits */ - { - do - { - if (_IO.Read(_framesBuffer, 0, 1) == 0) - throw new Exception("FLAC stream not found"); - x = _framesBuffer[0]; - } while (x == 0xff); - if (x >> 2 == 0x3e) /* MAGIC NUMBER for the last 6 sync bits */ - { - //_IO.Position -= 2; - // state = frame - throw new Exception("headerless file unsupported"); - } - } - throw new Exception("FLAC stream not found"); - } - - do - { - fill_frames_buffer(); - fixed (byte* buf = _framesBuffer) - { - BitReader bitreader = new BitReader(buf, _framesBufferOffset, _framesBufferLength - _framesBufferOffset); - bool is_last = bitreader.readbit() != 0; - MetadataType type = (MetadataType)bitreader.readbits(7); - int len = (int)bitreader.readbits(24); - - if (type == MetadataType.StreamInfo) - { - const int FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */ - const int FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */ - const int FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = 24; /* bits */ - const int FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = 24; /* bits */ - const int FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = 20; /* bits */ - const int FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = 3; /* bits */ - const int FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = 5; /* bits */ - const int FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = 36; /* bits */ - const int FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = 128; /* bits */ - - min_block_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN); - max_block_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN); - min_frame_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN); - max_frame_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN); - int sample_rate = (int)bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN); - int channels = 1 + (int)bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN); - int bits_per_sample = 1 + (int)bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN); - pcm = new AudioPCMConfig(bits_per_sample, channels, sample_rate); - _sampleCount = (long)bitreader.readbits64(FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN); - bitreader.skipbits(FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN); - } - else if (type == MetadataType.Seektable) - { - int num_entries = len / 18; - seek_table = new SeekPoint[num_entries]; - for (int e = 0; e < num_entries; e++) - { - seek_table[e].number = bitreader.read_long(); - seek_table[e].offset = bitreader.read_long(); - seek_table[e].framesize = (int)bitreader.read_ushort(); - } - } - if (_framesBufferLength < 4 + len) - { - _IO.Position += 4 + len - _framesBufferLength; - _framesBufferLength = 0; - } - else - { - _framesBufferLength -= 4 + len; - _framesBufferOffset += 4 + len; - } - if (is_last) - break; - } - } while (true); - first_frame_offset = _IO.Position - _framesBufferLength; - } - } -} +/** + * CUETools.Flake: pure managed FLAC audio encoder + * Copyright (c) 2009 Grigory Chudov + * Based on Flake encoder, http://flake-enc.sourceforge.net/ + * Copyright (c) 2006-2009 Justin Ruggles + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace CUETools.Codecs.Flake +{ + public class AudioDecoder: IAudioSource + { + int[] samplesBuffer; + int[] residualBuffer; + + byte[] _framesBuffer; + int _framesBufferLength = 0, _framesBufferOffset = 0; + long first_frame_offset; + + SeekPoint[] seek_table; + + Crc8 crc8; + FlacFrame frame; + BitReader framereader; + AudioPCMConfig pcm; + + uint min_block_size = 0; + uint max_block_size = 0; + uint min_frame_size = 0; + uint max_frame_size = 0; + + int _samplesInBuffer, _samplesBufferOffset; + long _sampleCount = 0, _sampleOffset = 0; + + bool do_crc = true; + + string _path; + Stream _IO; + + public bool DoCRC + { + get + { + return do_crc; + } + set + { + do_crc = value; + } + } + + public int[] Samples + { + get + { + return samplesBuffer; + } + } + + public AudioDecoder(DecoderSettings settings, string path, Stream IO = null) + { + m_settings = settings; + + _path = path; + _IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000); + + crc8 = new Crc8(); + + _framesBuffer = new byte[0x20000]; + decode_metadata(); + + frame = new FlacFrame(PCM.ChannelCount); + framereader = new BitReader(); + + //max_frame_size = 16 + ((Flake.MAX_BLOCKSIZE * PCM.BitsPerSample * PCM.ChannelCount + 1) + 7) >> 3); + if (((int)max_frame_size * PCM.BitsPerSample * PCM.ChannelCount * 2 >> 3) > _framesBuffer.Length) + { + byte[] temp = _framesBuffer; + _framesBuffer = new byte[((int)max_frame_size * PCM.BitsPerSample * PCM.ChannelCount * 2 >> 3)]; + if (_framesBufferLength > 0) + Array.Copy(temp, _framesBufferOffset, _framesBuffer, 0, _framesBufferLength); + _framesBufferOffset = 0; + } + _samplesInBuffer = 0; + + if (PCM.BitsPerSample != 16 && PCM.BitsPerSample != 24) + throw new Exception("invalid flac file"); + + samplesBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * PCM.ChannelCount]; + residualBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * PCM.ChannelCount]; + } + + public AudioDecoder(AudioPCMConfig _pcm) + { + pcm = _pcm; + crc8 = new Crc8(); + + samplesBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * PCM.ChannelCount]; + residualBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * PCM.ChannelCount]; + frame = new FlacFrame(PCM.ChannelCount); + framereader = new BitReader(); + } + + private DecoderSettings m_settings; + public IAudioDecoderSettings Settings => m_settings; + + public void Close() + { + _IO.Close(); + } + + public long Length + { + get + { + return _sampleCount; + } + } + + public long Remaining + { + get + { + return Length - Position; + } + } + + public long Position + { + get + { + return _sampleOffset - _samplesInBuffer; + } + set + { + if (value > _sampleCount) + throw new Exception("seeking past end of stream"); + if (value < Position || value > _sampleOffset) + { + if (seek_table != null && _IO.CanSeek) + { + int best_st = -1; + for (int st = 0; st < seek_table.Length; st++) + { + if (seek_table[st].number <= value && + (best_st == -1 || seek_table[st].number > seek_table[best_st].number)) + best_st = st; + } + if (best_st != -1) + { + _framesBufferLength = 0; + _samplesInBuffer = 0; + _samplesBufferOffset = 0; + _IO.Position = (long)seek_table[best_st].offset + first_frame_offset; + _sampleOffset = seek_table[best_st].number; + } + } + if (value < Position) + throw new Exception("cannot seek backwards without seek table"); + } + while (value > _sampleOffset) + { + _samplesInBuffer = 0; + _samplesBufferOffset = 0; + + fill_frames_buffer(); + if (_framesBufferLength == 0) + throw new Exception("seek failed"); + + int bytesDecoded = DecodeFrame(_framesBuffer, _framesBufferOffset, _framesBufferLength); + _framesBufferLength -= bytesDecoded; + _framesBufferOffset += bytesDecoded; + + _sampleOffset += _samplesInBuffer; + }; + int diff = _samplesInBuffer - (int)(_sampleOffset - value); + _samplesInBuffer -= diff; + _samplesBufferOffset += diff; + } + } + + public AudioPCMConfig PCM + { + get + { + return pcm; + } + } + + public string Path + { + get + { + return _path; + } + } + + unsafe void interlace(AudioBuffer buff, int offset, int count) + { + if (PCM.ChannelCount == 2) + { + fixed (int* src = &samplesBuffer[_samplesBufferOffset]) + buff.Interlace(offset, src, src + FlakeConstants.MAX_BLOCKSIZE, count); + } + else + { + for (int ch = 0; ch < PCM.ChannelCount; ch++) + fixed (int* res = &buff.Samples[offset, ch], src = &samplesBuffer[_samplesBufferOffset + ch * FlakeConstants.MAX_BLOCKSIZE]) + { + int* psrc = src; + for (int i = 0; i < count; i++) + res[i * PCM.ChannelCount] = *(psrc++); + } + } + } + + public int Read(AudioBuffer buff, int maxLength) + { + buff.Prepare(this, maxLength); + + int offset = 0; + int sampleCount = buff.Length; + + while (_samplesInBuffer < sampleCount) + { + if (_samplesInBuffer > 0) + { + interlace(buff, offset, _samplesInBuffer); + sampleCount -= _samplesInBuffer; + offset += _samplesInBuffer; + _samplesInBuffer = 0; + _samplesBufferOffset = 0; + } + + fill_frames_buffer(); + + if (_framesBufferLength == 0) + return buff.Length = offset; + + int bytesDecoded = DecodeFrame(_framesBuffer, _framesBufferOffset, _framesBufferLength); + _framesBufferLength -= bytesDecoded; + _framesBufferOffset += bytesDecoded; + + _samplesInBuffer -= _samplesBufferOffset; // can be set by Seek, otherwise zero + _sampleOffset += _samplesInBuffer; + } + + interlace(buff, offset, sampleCount); + _samplesInBuffer -= sampleCount; + _samplesBufferOffset += sampleCount; + if (_samplesInBuffer == 0) + _samplesBufferOffset = 0; + return buff.Length = offset + sampleCount; + } + + unsafe void fill_frames_buffer() + { + if (_framesBufferLength == 0) + _framesBufferOffset = 0; + else if (_framesBufferLength < _framesBuffer.Length / 2 && _framesBufferOffset >= _framesBuffer.Length / 2) + { + fixed (byte* buff = _framesBuffer) + AudioSamples.MemCpy(buff, buff + _framesBufferOffset, _framesBufferLength); + _framesBufferOffset = 0; + } + while (_framesBufferLength < _framesBuffer.Length / 2) + { + int read = _IO.Read(_framesBuffer, _framesBufferOffset + _framesBufferLength, _framesBuffer.Length - _framesBufferOffset - _framesBufferLength); + _framesBufferLength += read; + if (read == 0) + break; + } + } + + unsafe void decode_frame_header(BitReader bitreader, FlacFrame frame) + { + int header_start = bitreader.Position; + + if (bitreader.readbits(15) != 0x7FFC) + throw new Exception("invalid frame"); + uint vbs = bitreader.readbit(); + frame.bs_code0 = (int) bitreader.readbits(4); + uint sr_code0 = bitreader.readbits(4); + frame.ch_mode = (ChannelMode)bitreader.readbits(4); + uint bps_code = bitreader.readbits(3); + if (FlakeConstants.flac_bitdepths[bps_code] != PCM.BitsPerSample) + throw new Exception("unsupported bps coding"); + uint t1 = bitreader.readbit(); // == 0????? + if (t1 != 0) + throw new Exception("unsupported frame coding"); + frame.frame_number = (int)bitreader.read_utf8(); + + // custom block size + if (frame.bs_code0 == 6) + { + frame.bs_code1 = (int)bitreader.readbits(8); + frame.blocksize = frame.bs_code1 + 1; + } + else if (frame.bs_code0 == 7) + { + frame.bs_code1 = (int)bitreader.readbits(16); + frame.blocksize = frame.bs_code1 + 1; + } + else + frame.blocksize = FlakeConstants.flac_blocksizes[frame.bs_code0]; + + // custom sample rate + if (sr_code0 < 1 || sr_code0 > 11) + { + // sr_code0 == 12 -> sr == bitreader.readbits(8) * 1000; + // sr_code0 == 13 -> sr == bitreader.readbits(16); + // sr_code0 == 14 -> sr == bitreader.readbits(16) * 10; + throw new Exception("invalid sample rate mode"); + } + + int frame_channels = (int)frame.ch_mode + 1; + if (frame_channels > 11) + throw new Exception("invalid channel mode"); + if (frame_channels == 2 || frame_channels > 8) // Mid/Left/Right Side Stereo + frame_channels = 2; + else + frame.ch_mode = ChannelMode.NotStereo; + if (frame_channels != PCM.ChannelCount) + throw new Exception("invalid channel mode"); + + // CRC-8 of frame header + byte crc = do_crc ? crc8.ComputeChecksum(bitreader.Buffer, header_start, bitreader.Position - header_start) : (byte)0; + frame.crc8 = (byte)bitreader.readbits(8); + if (do_crc && frame.crc8 != crc) + throw new Exception("header crc mismatch"); + } + + unsafe void decode_subframe_constant(BitReader bitreader, FlacFrame frame, int ch) + { + int obits = frame.subframes[ch].obits; + frame.subframes[ch].best.residual[0] = bitreader.readbits_signed(obits); + } + + unsafe void decode_subframe_verbatim(BitReader bitreader, FlacFrame frame, int ch) + { + int obits = frame.subframes[ch].obits; + for (int i = 0; i < frame.blocksize; i++) + frame.subframes[ch].best.residual[i] = bitreader.readbits_signed(obits); + } + + unsafe void decode_residual(BitReader bitreader, FlacFrame frame, int ch) + { + // rice-encoded block + // coding method + frame.subframes[ch].best.rc.coding_method = (int)bitreader.readbits(2); // ????? == 0 + if (frame.subframes[ch].best.rc.coding_method != 0 && frame.subframes[ch].best.rc.coding_method != 1) + throw new Exception("unsupported residual coding"); + // partition order + frame.subframes[ch].best.rc.porder = (int)bitreader.readbits(4); + if (frame.subframes[ch].best.rc.porder > 8) + throw new Exception("invalid partition order"); + int psize = frame.blocksize >> frame.subframes[ch].best.rc.porder; + int res_cnt = psize - frame.subframes[ch].best.order; + + int rice_len = 4 + frame.subframes[ch].best.rc.coding_method; + // residual + int j = frame.subframes[ch].best.order; + int* r = frame.subframes[ch].best.residual + j; + for (int p = 0; p < (1 << frame.subframes[ch].best.rc.porder); p++) + { + if (p == 1) res_cnt = psize; + int n = Math.Min(res_cnt, frame.blocksize - j); + + int k = frame.subframes[ch].best.rc.rparams[p] = (int)bitreader.readbits(rice_len); + if (k == (1 << rice_len) - 1) + { + k = frame.subframes[ch].best.rc.esc_bps[p] = (int)bitreader.readbits(5); + for (int i = n; i > 0; i--) + *(r++) = bitreader.readbits_signed((int)k); + } + else + { + bitreader.read_rice_block(n, (int)k, r); + r += n; + } + j += n; + } + } + + unsafe void decode_subframe_fixed(BitReader bitreader, FlacFrame frame, int ch) + { + // warm-up samples + int obits = frame.subframes[ch].obits; + for (int i = 0; i < frame.subframes[ch].best.order; i++) + frame.subframes[ch].best.residual[i] = bitreader.readbits_signed(obits); + + // residual + decode_residual(bitreader, frame, ch); + } + + unsafe void decode_subframe_lpc(BitReader bitreader, FlacFrame frame, int ch) + { + // warm-up samples + int obits = frame.subframes[ch].obits; + for (int i = 0; i < frame.subframes[ch].best.order; i++) + frame.subframes[ch].best.residual[i] = bitreader.readbits_signed(obits); + + // LPC coefficients + frame.subframes[ch].best.cbits = (int)bitreader.readbits(4) + 1; // lpc_precision + if (frame.subframes[ch].best.cbits >= 16) + throw new Exception("cbits >= 16"); + frame.subframes[ch].best.shift = bitreader.readbits_signed(5); + if (frame.subframes[ch].best.shift < 0) + throw new Exception("negative shift"); + for (int i = 0; i < frame.subframes[ch].best.order; i++) + frame.subframes[ch].best.coefs[i] = bitreader.readbits_signed(frame.subframes[ch].best.cbits); + + // residual + decode_residual(bitreader, frame, ch); + } + + unsafe void decode_subframes(BitReader bitreader, FlacFrame frame) + { + fixed (int *r = residualBuffer, s = samplesBuffer) + for (int ch = 0; ch < PCM.ChannelCount; ch++) + { + // subframe header + uint t1 = bitreader.readbit(); // ?????? == 0 + if (t1 != 0) + throw new Exception("unsupported subframe coding (ch == " + ch.ToString() + ")"); + int type_code = (int)bitreader.readbits(6); + frame.subframes[ch].wbits = (int)bitreader.readbit(); + if (frame.subframes[ch].wbits != 0) + frame.subframes[ch].wbits += (int)bitreader.read_unary(); + + frame.subframes[ch].obits = PCM.BitsPerSample - frame.subframes[ch].wbits; + switch (frame.ch_mode) + { + case ChannelMode.MidSide: frame.subframes[ch].obits += ch; break; + case ChannelMode.LeftSide: frame.subframes[ch].obits += ch; break; + case ChannelMode.RightSide: frame.subframes[ch].obits += 1 - ch; break; + } + + frame.subframes[ch].best.type = (SubframeType)type_code; + frame.subframes[ch].best.order = 0; + + if ((type_code & (uint)SubframeType.LPC) != 0) + { + frame.subframes[ch].best.order = (type_code - (int)SubframeType.LPC) + 1; + frame.subframes[ch].best.type = SubframeType.LPC; + } + else if ((type_code & (uint)SubframeType.Fixed) != 0) + { + frame.subframes[ch].best.order = (type_code - (int)SubframeType.Fixed); + frame.subframes[ch].best.type = SubframeType.Fixed; + } + + frame.subframes[ch].best.residual = r + ch * FlakeConstants.MAX_BLOCKSIZE; + frame.subframes[ch].samples = s + ch * FlakeConstants.MAX_BLOCKSIZE; + + // subframe + switch (frame.subframes[ch].best.type) + { + case SubframeType.Constant: + decode_subframe_constant(bitreader, frame, ch); + break; + case SubframeType.Verbatim: + decode_subframe_verbatim(bitreader, frame, ch); + break; + case SubframeType.Fixed: + decode_subframe_fixed(bitreader, frame, ch); + break; + case SubframeType.LPC: + decode_subframe_lpc(bitreader, frame, ch); + break; + default: + throw new Exception("invalid subframe type"); + } + } + } + + unsafe void restore_samples_fixed(FlacFrame frame, int ch) + { + FlacSubframeInfo sub = frame.subframes[ch]; + + AudioSamples.MemCpy(sub.samples, sub.best.residual, sub.best.order); + int* data = sub.samples + sub.best.order; + int* residual = sub.best.residual + sub.best.order; + int data_len = frame.blocksize - sub.best.order; + int s0, s1, s2; + switch (sub.best.order) + { + case 0: + AudioSamples.MemCpy(data, residual, data_len); + break; + case 1: + s1 = data[-1]; + for (int i = data_len; i > 0; i--) + { + s1 += *(residual++); + *(data++) = s1; + } + //data[i] = residual[i] + data[i - 1]; + break; + case 2: + s2 = data[-2]; + s1 = data[-1]; + for (int i = data_len; i > 0; i--) + { + s0 = *(residual++) + (s1 << 1) - s2; + *(data++) = s0; + s2 = s1; + s1 = s0; + } + //data[i] = residual[i] + data[i - 1] * 2 - data[i - 2]; + break; + case 3: + for (int i = 0; i < data_len; i++) + data[i] = residual[i] + (((data[i - 1] - data[i - 2]) << 1) + (data[i - 1] - data[i - 2])) + data[i - 3]; + break; + case 4: + for (int i = 0; i < data_len; i++) + data[i] = residual[i] + ((data[i - 1] + data[i - 3]) << 2) - ((data[i - 2] << 2) + (data[i - 2] << 1)) - data[i - 4]; + break; + } + } + + unsafe void restore_samples_lpc(FlacFrame frame, int ch) + { + FlacSubframeInfo sub = frame.subframes[ch]; + ulong csum = 0; + fixed (int* coefs = sub.best.coefs) + { + for (int i = sub.best.order; i > 0; i--) + csum += (ulong)Math.Abs(coefs[i - 1]); + if ((csum << sub.obits) >= 1UL << 32) + lpc.decode_residual_long(sub.best.residual, sub.samples, frame.blocksize, sub.best.order, coefs, sub.best.shift); + else + lpc.decode_residual(sub.best.residual, sub.samples, frame.blocksize, sub.best.order, coefs, sub.best.shift); + } + } + + unsafe void restore_samples(FlacFrame frame) + { + for (int ch = 0; ch < PCM.ChannelCount; ch++) + { + switch (frame.subframes[ch].best.type) + { + case SubframeType.Constant: + AudioSamples.MemSet(frame.subframes[ch].samples, frame.subframes[ch].best.residual[0], frame.blocksize); + break; + case SubframeType.Verbatim: + AudioSamples.MemCpy(frame.subframes[ch].samples, frame.subframes[ch].best.residual, frame.blocksize); + break; + case SubframeType.Fixed: + restore_samples_fixed(frame, ch); + break; + case SubframeType.LPC: + restore_samples_lpc(frame, ch); + break; + } + if (frame.subframes[ch].wbits != 0) + { + int* s = frame.subframes[ch].samples; + int x = (int) frame.subframes[ch].wbits; + for (int i = frame.blocksize; i > 0; i--) + *(s++) <<= x; + } + } + if (frame.ch_mode != ChannelMode.NotStereo) + { + int* l = frame.subframes[0].samples; + int* r = frame.subframes[1].samples; + switch (frame.ch_mode) + { + case ChannelMode.LeftRight: + break; + case ChannelMode.MidSide: + for (int i = frame.blocksize; i > 0; i--) + { + int mid = *l; + int side = *r; + mid <<= 1; + mid |= (side & 1); /* i.e. if 'side' is odd... */ + *(l++) = (mid + side) >> 1; + *(r++) = (mid - side) >> 1; + } + break; + case ChannelMode.LeftSide: + for (int i = frame.blocksize; i > 0; i--) + { + int _l = *(l++), _r = *r; + *(r++) = _l - _r; + } + break; + case ChannelMode.RightSide: + for (int i = frame.blocksize; i > 0; i--) + *(l++) += *(r++); + break; + } + } + } + + public unsafe int DecodeFrame(byte[] buffer, int pos, int len) + { + fixed (byte* buf = buffer) + { + framereader.Reset(buf, pos, len); + decode_frame_header(framereader, frame); + decode_subframes(framereader, frame); + framereader.flush(); + ushort crc_1 = framereader.get_crc16(); + ushort crc_2 = framereader.read_ushort(); + if (do_crc && crc_1 != crc_2) + throw new Exception("frame crc mismatch"); + restore_samples(frame); + _samplesInBuffer = frame.blocksize; + return framereader.Position - pos; + } + } + + + bool skip_bytes(int bytes) + { + for (int j = 0; j < bytes; j++) + if (0 == _IO.Read(_framesBuffer, 0, 1)) + return false; + return true; + } + + unsafe void decode_metadata() + { + byte x; + int i, id; + //bool first = true; + byte[] FLAC__STREAM_SYNC_STRING = new byte[] { (byte)'f', (byte)'L', (byte)'a', (byte)'C' }; + byte[] ID3V2_TAG_ = new byte[] { (byte)'I', (byte)'D', (byte)'3' }; + + for (i = id = 0; i < 4; ) + { + if (_IO.Read(_framesBuffer, 0, 1) == 0) + throw new Exception("FLAC stream not found"); + x = _framesBuffer[0]; + if (x == FLAC__STREAM_SYNC_STRING[i]) + { + //first = true; + i++; + id = 0; + continue; + } + if (id < 3 && x == ID3V2_TAG_[id]) + { + id++; + i = 0; + if (id == 3) + { + if (!skip_bytes(3)) + throw new Exception("FLAC stream not found"); + int skip = 0; + for (int j = 0; j < 4; j++) + { + if (0 == _IO.Read(_framesBuffer, 0, 1)) + throw new Exception("FLAC stream not found"); + skip <<= 7; + skip |= ((int)_framesBuffer[0] & 0x7f); + } + if (!skip_bytes(skip)) + throw new Exception("FLAC stream not found"); + } + continue; + } + id = 0; + if (x == 0xff) /* MAGIC NUMBER for the first 8 frame sync bits */ + { + do + { + if (_IO.Read(_framesBuffer, 0, 1) == 0) + throw new Exception("FLAC stream not found"); + x = _framesBuffer[0]; + } while (x == 0xff); + if (x >> 2 == 0x3e) /* MAGIC NUMBER for the last 6 sync bits */ + { + //_IO.Position -= 2; + // state = frame + throw new Exception("headerless file unsupported"); + } + } + throw new Exception("FLAC stream not found"); + } + + do + { + fill_frames_buffer(); + fixed (byte* buf = _framesBuffer) + { + BitReader bitreader = new BitReader(buf, _framesBufferOffset, _framesBufferLength - _framesBufferOffset); + bool is_last = bitreader.readbit() != 0; + MetadataType type = (MetadataType)bitreader.readbits(7); + int len = (int)bitreader.readbits(24); + + if (type == MetadataType.StreamInfo) + { + const int FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */ + const int FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */ + const int FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = 24; /* bits */ + const int FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = 24; /* bits */ + const int FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = 20; /* bits */ + const int FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = 3; /* bits */ + const int FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = 5; /* bits */ + const int FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = 36; /* bits */ + const int FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = 128; /* bits */ + + min_block_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN); + max_block_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN); + min_frame_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN); + max_frame_size = bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN); + int sample_rate = (int)bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN); + int channels = 1 + (int)bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN); + int bits_per_sample = 1 + (int)bitreader.readbits(FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN); + pcm = new AudioPCMConfig(bits_per_sample, channels, sample_rate); + _sampleCount = (long)bitreader.readbits64(FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN); + bitreader.skipbits(FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN); + } + else if (type == MetadataType.Seektable) + { + int num_entries = len / 18; + seek_table = new SeekPoint[num_entries]; + for (int e = 0; e < num_entries; e++) + { + seek_table[e].number = bitreader.read_long(); + seek_table[e].offset = bitreader.read_long(); + seek_table[e].framesize = (int)bitreader.read_ushort(); + } + } + if (_framesBufferLength < 4 + len) + { + _IO.Position += 4 + len - _framesBufferLength; + _framesBufferLength = 0; + } + else + { + _framesBufferLength -= 4 + len; + _framesBufferOffset += 4 + len; + } + if (is_last) + break; + } + } while (true); + first_frame_offset = _IO.Position - _framesBufferLength; + } + } +} diff --git a/CUETools.Codecs.FLAKE/FlakeWriter.cs b/CUETools.Codecs.Flake/AudioEncoder.cs similarity index 87% rename from CUETools.Codecs.FLAKE/FlakeWriter.cs rename to CUETools.Codecs.Flake/AudioEncoder.cs index 44518eb..891ceca 100644 --- a/CUETools.Codecs.FLAKE/FlakeWriter.cs +++ b/CUETools.Codecs.Flake/AudioEncoder.cs @@ -1,2638 +1,2402 @@ -/** - * CUETools.Flake: pure managed FLAC audio encoder - * Copyright (c) 2009 Grigory Chudov - * Based on Flake encoder, http://flake-enc.sourceforge.net/ - * Copyright (c) 2006-2009 Justin Ruggles - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define NOINTEROP -#define VARIANT1 - -using System; -using System.ComponentModel; -using System.Text; -using System.IO; -using System.Collections.Generic; -using System.Security.Cryptography; -#if INTEROP -using System.Runtime.InteropServices; -#endif -using CUETools.Codecs; -using Newtonsoft.Json; - -namespace CUETools.Codecs.FLAKE -{ - [JsonObject(MemberSerialization.OptIn)] - public class EncoderSettings : AudioEncoderSettings - { - public override string Extension => "flac"; - - public override string Name => "cuetools"; - - public override Type EncoderType => typeof(AudioEncoder); - - public override int Priority => 4; - - public override bool Lossless => true; - - public EncoderSettings() - : base() - { - } - - public override string GetSupportedModes(out string defaultMode) - { - defaultMode = "5"; - return this.AllowNonSubset || (this.PCM != null && this.PCM.SampleRate > 48000) ? "0 1 2 3 4 5 6 7 8 9 10 11" : "0 1 2 3 4 5 6 7 8"; - } - - public bool IsSubset() - { - return (BlockSize == 0 || (BlockSize <= 16384 && (PCM.SampleRate > 48000 || BlockSize <= 4608))) - && (PCM.SampleRate > 48000 || MaxLPCOrder <= 12) - && MaxPartitionOrder <= 8 - ; - //The blocksize bits in the frame header must be 0001-1110. The blocksize must be <=16384; if the sample rate is <= 48000Hz, the blocksize must be <=4608. - //The sample rate bits in the frame header must be 0001-1110. - //The bits-per-sample bits in the frame header must be 001-111. - //If the sample rate is <= 48000Hz, the filter order in LPC subframes must be less than or equal to 12, i.e. the subframe type bits in the subframe header may not be 101100-111111. - //The Rice partition order in a Rice-coded residual section must be less than or equal to 8. - } - - public void Validate() - { - if (EncoderModeIndex < 0) - throw new Exception("unsupported encoder mode"); - SetDefaultValuesForMode(); - if (Padding < 0) - throw new Exception("unsupported padding value " + Padding.ToString()); - if (BlockSize != 0 && (BlockSize < 256 || BlockSize >= FlakeConstants.MAX_BLOCKSIZE)) - throw new Exception("unsupported block size " + BlockSize.ToString()); - if (MinLPCOrder > MaxLPCOrder || MaxLPCOrder > lpc.MAX_LPC_ORDER) - throw new Exception("invalid MaxLPCOrder " + MaxLPCOrder.ToString()); - if (MinFixedOrder < 0 || MinFixedOrder > 4) - throw new Exception("invalid MinFixedOrder " + MinFixedOrder.ToString()); - if (MaxFixedOrder < 0 || MaxFixedOrder > 4) - throw new Exception("invalid MaxFixedOrder " + MaxFixedOrder.ToString()); - if (MinPartitionOrder < 0) - throw new Exception("invalid MinPartitionOrder " + MinPartitionOrder.ToString()); - if (MinPartitionOrder > MaxPartitionOrder || MaxPartitionOrder > 8) - throw new Exception("invalid MaxPartitionOrder " + MaxPartitionOrder.ToString()); - if (PredictionType == PredictionType.None) - throw new Exception("invalid PredictionType " + PredictionType.ToString()); - if (PredictionType != PredictionType.Fixed) - { - if (WindowMethod == WindowMethod.Invalid) - throw new InvalidOperationException("invalid WindowMethod " + WindowMethod.ToString()); - if (WindowFunctions == WindowFunction.None) - throw new InvalidOperationException("invalid WindowFunctions " + WindowFunctions.ToString()); - if (EstimationDepth > 32 || EstimationDepth < 1) - throw new InvalidOperationException("invalid EstimationDepth " + EstimationDepth.ToString()); - if (MinPrecisionSearch < 0 || MinPrecisionSearch >= lpc.MAX_LPC_PRECISIONS) - throw new Exception("unsupported MinPrecisionSearch value"); - if (MaxPrecisionSearch < 0 || MaxPrecisionSearch >= lpc.MAX_LPC_PRECISIONS) - throw new Exception("unsupported MaxPrecisionSearch value"); - if (MaxPrecisionSearch < MinPrecisionSearch) - throw new Exception("unsupported MaxPrecisionSearch value"); - } - if (!AllowNonSubset && !IsSubset()) - throw new Exception("the encoding parameters specified do not conform to the FLAC Subset"); - } - - [DefaultValue(-1)] - [DefaultValueForMode(2, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0)] - [Browsable(false)] - [DisplayName("MinFixedOrder")] - [SRDescription(typeof(Properties.Resources), "MinFixedOrderDescription")] - public int MinFixedOrder { get; set; } - - [DefaultValue(-1)] - [DefaultValueForMode(2, 4, 4, 4, 2, 2, 4, 4, 4, 4, 4, 4)] - [Browsable(false)] - [DisplayName("MaxFixedOrder")] - [SRDescription(typeof(Properties.Resources), "MaxFixedOrderDescription")] - public int MaxFixedOrder { get; set; } - - [DefaultValue(1)] - [Browsable(false)] - [DisplayName("MinLPCOrder")] - [SRDescription(typeof(Properties.Resources), "MinLPCOrderDescription")] - public int MinLPCOrder { get; set; } - - [DefaultValue(-1)] - [DefaultValueForMode(8, 8, 8, 12, 12, 12, 12, 12, 12, 32, 32, 32)] - [Browsable(false)] - [DisplayName("MaxLPCOrder")] - [SRDescription(typeof(Properties.Resources), "MaxLPCOrderDescription")] - public int MaxLPCOrder { get; set; } - - [DefaultValue(0)] - [DisplayName("MinPartitionOrder")] - [Browsable(false)] - [SRDescription(typeof(Properties.Resources), "MinPartitionOrderDescription")] - public int MinPartitionOrder { get; set; } - - [DefaultValue(-1)] - [DefaultValueForMode(6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 8)] - [DisplayName("MaxPartitionOrder")] - [Browsable(false)] - [SRDescription(typeof(Properties.Resources), "MaxPartitionOrderDescription")] - public int MaxPartitionOrder { get; set; } - - [DefaultValue(false)] - [DisplayName("Verify")] - [SRDescription(typeof(Properties.Resources), "DoVerifyDescription")] - [JsonProperty] - public bool DoVerify { get; set; } - - [DefaultValue(true)] - [DisplayName("MD5")] - [SRDescription(typeof(Properties.Resources), "DoMD5Description")] - [JsonProperty] - public bool DoMD5 { get; set; } - - [DefaultValue(false)] - [DisplayName("Allow Non-subset")] - [SRDescription(typeof(Properties.Resources), "AllowNonSubsetDescription")] - [JsonProperty] - public bool AllowNonSubset { get; set; } - - [DefaultValue(StereoMethod.Invalid)] - [DefaultValueForMode( - /* 0 */ StereoMethod.Independent, - /* 1 */ StereoMethod.EstimateFixed, - /* 2 */ StereoMethod.Estimate, - /* 3 */ StereoMethod.Estimate, - /* 4 */ StereoMethod.Evaluate, - /* 5 */ StereoMethod.Evaluate, - /* 6 */ StereoMethod.Evaluate, - /* 7 */ StereoMethod.Evaluate, - /* 8 */ StereoMethod.Evaluate, - /* 9 */ StereoMethod.Evaluate, - /* 10 */ StereoMethod.Evaluate, - /* 11 */ StereoMethod.Evaluate)] - [Browsable(false)] - public StereoMethod StereoMethod { get; set; } - - [DefaultValue(PredictionType.None)] - [DefaultValueForMode( - /* 0 */ PredictionType.Fixed, - /* 1 */ PredictionType.Fixed, - /* 2 */ PredictionType.Levinson, - /* 3 */ PredictionType.Levinson, - /* 4 */ PredictionType.Search, - /* 5 */ PredictionType.Search, - /* 6 */ PredictionType.Search, - /* 7 */ PredictionType.Search, - /* 8 */ PredictionType.Search, - /* 9 */ PredictionType.Levinson, - /* 10 */ PredictionType.Search, - /* 11 */ PredictionType.Search)] - [Browsable(false)] - public PredictionType PredictionType { get; set; } - - [DefaultValue(WindowMethod.Invalid)] - [DefaultValueForMode( - /* 0 */ WindowMethod.Invalid, - /* 1 */ WindowMethod.Invalid, - /* 2 */ WindowMethod.Estimate, - /* 3 */ WindowMethod.Estimate, - /* 4 */ WindowMethod.Estimate, - /* 5 */ WindowMethod.EvaluateN, - /* 6 */ WindowMethod.EvaluateN, - /* 7 */ WindowMethod.EvaluateN, - /* 8 */ WindowMethod.EvaluateN, - /* 9 */ WindowMethod.EvaluateN, - /* 10 */ WindowMethod.EvaluateN, - /* 11 */ WindowMethod.EvaluateN)] - [Browsable(false)] - public WindowMethod WindowMethod { get; set; } - - [DefaultValue(WindowFunction.None)] - [DefaultValueForMode( - /* 0 */ WindowFunction.None, - /* 1 */ WindowFunction.None, - /* 2 */ WindowFunction.Tukey3, - /* 3 */ WindowFunction.Tukey4, - /* 4 */ WindowFunction.Tukey4, - /* 5 */ WindowFunction.Tukey4 | WindowFunction.Tukey3, - /* 6 */ WindowFunction.Tukey4 | WindowFunction.Tukey3 | WindowFunction.Tukey, - /* 7 */ WindowFunction.Tukey4 | WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey, - /* 8 */ WindowFunction.Tukey4 | WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey, - /* 9 */ WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey, - /* 10 */ WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey, - /* 11 */ WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey)] - [Browsable(false)] - [DisplayName("WindowFunctions")] - [SRDescription(typeof(Properties.Resources), "WindowFunctionsDescription")] - public WindowFunction WindowFunctions { get; set; } - - [DefaultValue(0)] - [DefaultValueForMode(0, 0, 1, 1, 1, 1, 1, 1, 3, 1, 1, 5)] - [Browsable(false)] - public int EstimationDepth { get; set; } - - [DefaultValue(-1)] - [DefaultValueForMode(1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1)] - [Browsable(false)] - public int MinPrecisionSearch { get; set; } - - [DefaultValue(-1)] - [DefaultValueForMode(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)] - [Browsable(false)] - public int MaxPrecisionSearch { get; set; } - - [DefaultValue(0)] - [Browsable(false)] - public int TukeyParts { get; set; } - - [DefaultValue(1.0)] - [Browsable(false)] - public double TukeyOverlap { get; set; } - - [DefaultValue(1.0)] - [Browsable(false)] - public double TukeyP { get; set; } - - [Browsable(false)] - public string[] Tags { get; set; } - } - - public class AudioEncoder : IAudioDest - { - Stream _IO = null; - string _path; - long _position; - - // number of audio channels - // set by user prior to calling flake_encode_init - // valid values are 1 to 8 - int channels, ch_code; - - // audio sample rate in Hz - // set by user prior to calling flake_encode_init - int sr_code0, sr_code1; - - // sample size in bits - // set by user prior to calling flake_encode_init - // only 16-bit is currently supported - int bps_code; - - // total stream samples - // set by user prior to calling flake_encode_init - // if 0, stream length is unknown - int sample_count = -1; - - FlakeEncodeParams eparams; - - // maximum frame size in bytes - // set by flake_encode_init - // this can be used to allocate memory for output - int max_frame_size; - - byte[] frame_buffer = null; - - int frame_count = 0; - - long first_frame_offset = 0; - -#if INTEROP - TimeSpan _userProcessorTime; -#endif - - // header bytes - // allocated by flake_encode_init and freed by flake_encode_close - byte[] header; - - int[] samplesBuffer; - int[] verifyBuffer; - int[] residualBuffer; - float[] windowBuffer; - double[] windowScale; - LpcWindowSection[, ,] windowSections; - - WindowFunction[] windowType; - int samplesInBuffer = 0; - - int m_blockSize = 0; - int _totalSize = 0; - int _windowsize = 0, _windowcount = 0; - - Crc8 crc8; - MD5 md5; - - FlacFrame frame; - AudioDecoder verify; - - SeekPoint[] seek_table; - int seek_table_offset = -1; - - bool inited = false; - - public AudioEncoder(EncoderSettings settings, string path, Stream IO = null) - { - m_settings = settings.Clone() as EncoderSettings; - m_settings.Validate(); - - //if (Settings.PCM.BitsPerSample != 16) - // throw new Exception("Bits per sample must be 16."); - //if (Settings.PCM.ChannelCount != 2) - // throw new Exception("ChannelCount must be 2."); - - channels = Settings.PCM.ChannelCount; - - // flake_validate_params - - _path = path; - _IO = IO; - - samplesBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * (channels == 2 ? 4 : channels)]; - residualBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * (channels == 2 ? 10 : channels + 1)]; - windowBuffer = new float[FlakeConstants.MAX_BLOCKSIZE * 2 * lpc.MAX_LPC_WINDOWS]; - windowScale = new double[lpc.MAX_LPC_WINDOWS]; - windowType = new WindowFunction[lpc.MAX_LPC_WINDOWS]; - windowSections = new LpcWindowSection[12, lpc.MAX_LPC_WINDOWS, lpc.MAX_LPC_SECTIONS]; - - eparams.flake_set_defaults(m_settings); - - crc8 = new Crc8(); - frame = new FlacFrame(channels * 2); - } - - public int TotalSize - { - get - { - return _totalSize; - } - } - - EncoderSettings m_settings; - - public AudioEncoderSettings Settings => m_settings; - -#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 - - void DoClose() - { - if (inited) - { - while (samplesInBuffer > 0) - { - m_blockSize = samplesInBuffer; - output_frame(); - } - - if (_IO.CanSeek) - { - if (sample_count <= 0 && _position != 0) - { - BitWriter bitwriter = new BitWriter(header, 0, 4); - bitwriter.writebits(32, (int)_position); - bitwriter.flush(); - _IO.Position = 22; - _IO.Write(header, 0, 4); - } - - if (md5 != null) - { - md5.TransformFinalBlock(frame_buffer, 0, 0); - _IO.Position = 26; - _IO.Write(md5.Hash, 0, md5.Hash.Length); - } - - if (seek_table != null) - { - _IO.Position = seek_table_offset; - int len = write_seekpoints(header, 0, 0); - _IO.Write(header, 4, len - 4); - } - } - _IO.Close(); - inited = false; - } - -#if INTEROP - long fake, KernelStart, UserStart; - GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart); - _userProcessorTime = new TimeSpan(UserStart); -#endif - } - - public void Close() - { - DoClose(); - if (sample_count > 0 && _position != sample_count) - throw new Exception(Properties.Resources.ExceptionSampleCount); - } - - 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 int DevelopmentMode - { - get { return eparams.development_mode; } - set { eparams.development_mode = value; } - } - - public bool DoSeekTable - { - get { return eparams.do_seektable; } - set { eparams.do_seektable = value; } - } - - public int VBRMode - { - get { return eparams.variable_block_size; } - set { eparams.variable_block_size = value; } - } - - public TimeSpan UserProcessorTime - { - get - { -#if INTEROP - return _userProcessorTime; -#else - return new TimeSpan(0); -#endif - } - } - - unsafe int get_wasted_bits(int* signal, int samples) - { - int i, shift; - int x = 0; - - for (i = 0; i < samples && 0 == (x & 1); i++) - x |= signal[i]; - - if (x == 0) - { - shift = 0; - } - else - { - for (shift = 0; 0 == (x & 1); shift++) - x >>= 1; - } - - if (shift > 0) - { - for (i = 0; i < samples; i++) - signal[i] >>= shift; - } - - return shift; - } - - /// - /// Copy channel-interleaved input samples into separate subframes - /// - /// - /// - /// - unsafe void copy_samples(int[,] samples, int pos, int block) - { - fixed (int* fsamples = samplesBuffer, src = &samples[pos, 0]) - { - if (channels == 2) - { - if (m_settings.StereoMethod == StereoMethod.Independent) - AudioSamples.Deinterlace(fsamples + samplesInBuffer, fsamples + FlakeConstants.MAX_BLOCKSIZE + samplesInBuffer, src, block); - else - { - int* left = fsamples + samplesInBuffer; - int* right = left + FlakeConstants.MAX_BLOCKSIZE; - int* leftM = right + FlakeConstants.MAX_BLOCKSIZE; - int* rightM = leftM + FlakeConstants.MAX_BLOCKSIZE; - for (int i = 0; i < block; i++) - { - int l = src[2 * i]; - int r = src[2 * i + 1]; - left[i] = l; - right[i] = r; - leftM[i] = (l + r) >> 1; - rightM[i] = l - r; - } - } - } - else - for (int ch = 0; ch < channels; ch++) - { - int* psamples = fsamples + ch * FlakeConstants.MAX_BLOCKSIZE + samplesInBuffer; - 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) - //{ - // for (int i = 0; i < blocksize; i++) - // { - // leftM[i] = (leftS[i] + rightS[i]) >> 1; - // rightM[i] = leftS[i] - rightS[i]; - // } - //} - - unsafe void encode_residual_verbatim(int* res, int* smp, uint n) - { - AudioSamples.MemCpy(res, smp, (int) n); - } - - unsafe static ulong encode_residual_fixed_partition(int* res, int* smp, int* end, int order, int* last_errors) - { - ulong sum = 0UL; - switch (order) - { - case 0: - { - while (smp < end) - { - int error = *(res++) = *(smp++); - sum += (uint)((error << 1) ^ (error >> 31)); - } - break; - } - case 1: - { - int last_error_0 = last_errors[0]; - while (smp < end) - { - int error, save; - error = *(smp++); save = error; - error -= last_error_0; *(res++) = error; last_error_0 = save; - sum += (uint)((error << 1) ^ (error >> 31)); - } - last_errors[0] = last_error_0; - break; - } - case 2: - { - int last_error_0 = last_errors[0], last_error_1 = last_errors[1]; - while (smp < end) - { - int error, save; - error = *(smp++); save = error; - error -= last_error_0; last_error_0 = save; save = error; - error -= last_error_1; *(res++) = error; last_error_1 = save; - sum += (uint)((error << 1) ^ (error >> 31)); - } - last_errors[0] = last_error_0; last_errors[1] = last_error_1; ; - break; - } - case 3: - { - int last_error_0 = last_errors[0], last_error_1 = last_errors[1], last_error_2 = last_errors[2]; - while (smp < end) - { - int error, save; - error = *(smp++); save = error; - error -= last_error_0; last_error_0 = save; save = error; - error -= last_error_1; last_error_1 = save; save = error; - error -= last_error_2; *(res++) = error; last_error_2 = save; - sum += (uint)((error << 1) ^ (error >> 31)); - } - last_errors[0] = last_error_0; last_errors[1] = last_error_1; last_errors[2] = last_error_2; - break; - } - case 4: - { - int last_error_0 = last_errors[0], last_error_1 = last_errors[1], last_error_2 = last_errors[2], last_error_3 = last_errors[3]; - while (smp < end) - { - int error, save; - error = *(smp++); save = error; - error -= last_error_0; last_error_0 = save; save = error; - error -= last_error_1; last_error_1 = save; save = error; - error -= last_error_2; last_error_2 = save; save = error; - error -= last_error_3; *(res++) = error; last_error_3 = save; - sum += (uint)((error << 1) ^ (error >> 31)); - } - last_errors[0] = last_error_0; last_errors[1] = last_error_1; last_errors[2] = last_error_2; last_errors[3] = last_error_3; - break; - } - default: - throw new ArgumentOutOfRangeException(); - } - return sum; - } - - unsafe static void encode_residual_fixed(int* res, int* smp, int n, int order, ulong* sums, int pmax) - { - int* last_errors = stackalloc int[4]; - int* end = smp + n; - int* seg_end = smp + (n >> pmax); - - if (order > 4) - throw new ArgumentOutOfRangeException(); - - for (int i = 0; i < order; i++) - { - int* next_errors = stackalloc int[4]; - next_errors[0] = *(res++) = *(smp++); - for (int j = 0; j < i; j++) - next_errors[j + 1] = next_errors[j] - last_errors[j]; - for (int j = 0; j <= i; j++) - last_errors[j] = next_errors[j]; - } - - while (smp < end) - { - *(sums++) = encode_residual_fixed_partition(res, smp, seg_end, order, last_errors); - res += seg_end - smp; - smp = seg_end; - seg_end += n >> pmax; - } - } - -#if XXX - unsafe static int encode_residual_fixed_estimate_best_order(int* res, int* smp, int n, int order) - { - int next_error_0, next_error_1, next_error_2, next_error_3, next_error_4; - int last_error_0, last_error_1, last_error_2, last_error_3; - int* end = smp + n; - ulong total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; - - if (order == 0) - { - AudioSamples.MemCpy(res, smp, n); - return 0; - } - - next_error_0 = *(res++) = *(smp++); - last_error_0 = next_error_0; - - if (order == 1) - { - while (smp < end) - { - next_error_0 = *(smp++); - next_error_1 = next_error_0 - last_error_0; - - last_error_0 = next_error_0; - - total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31)); - total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31)); - - *(res++) = (int)next_error_1; - } - - if ((total_error_0 < total_error_1)) - return 0; - return 1; - } - - next_error_0 = *(res++) = *(smp++); - next_error_1 = next_error_0 - last_error_0; - last_error_0 = next_error_0; - last_error_1 = next_error_1; - - if (order == 2) - { - while (smp < end) - { - next_error_0 = *(smp++); - next_error_1 = next_error_0 - last_error_0; - next_error_2 = next_error_1 - last_error_1; - - last_error_0 = next_error_0; - last_error_1 = next_error_1; - - total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31)); - total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31)); - total_error_2 += (ulong)((next_error_2 << 1) ^ (next_error_2 >> 31)); - - *(res++) = (int)next_error_2; - } - - if ((total_error_0 < total_error_1) & (total_error_0 < total_error_2)) - return 0; - else if ((total_error_1 < total_error_2)) - return 1; - return 2; - } - - next_error_0 = *(res++) = *(smp++); - next_error_1 = next_error_0 - last_error_0; - next_error_2 = next_error_1 - last_error_1; - last_error_0 = next_error_0; - last_error_1 = next_error_1; - last_error_2 = next_error_2; - - if (order == 3) - { - while (smp < end) - { - next_error_0 = *(smp++); - next_error_1 = next_error_0 - last_error_0; - next_error_2 = next_error_1 - last_error_1; - next_error_3 = next_error_2 - last_error_2; - - last_error_0 = next_error_0; - last_error_1 = next_error_1; - last_error_2 = next_error_2; - - total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31)); - total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31)); - total_error_2 += (ulong)((next_error_2 << 1) ^ (next_error_2 >> 31)); - total_error_3 += (ulong)((next_error_3 << 1) ^ (next_error_3 >> 31)); - - *(res++) = (int)next_error_3; - } - - if ((total_error_0 < total_error_1) & (total_error_0 < total_error_2) & (total_error_0 < total_error_3)) - return 0; - else if ((total_error_1 < total_error_2) & (total_error_1 < total_error_3)) - return 1; - else if ((total_error_2 < total_error_3)) - return 2; - return 3; - } - - next_error_0 = *(res++) = *(smp++); - next_error_1 = next_error_0 - last_error_0; - next_error_2 = next_error_1 - last_error_1; - next_error_3 = next_error_2 - last_error_2; - last_error_0 = next_error_0; - last_error_1 = next_error_1; - last_error_2 = next_error_2; - last_error_3 = next_error_3; - - if (order == 4) - { - while (smp < end) - { - next_error_0 = *(smp++); - next_error_1 = next_error_0 - last_error_0; - next_error_2 = next_error_1 - last_error_1; - next_error_3 = next_error_2 - last_error_2; - next_error_4 = next_error_3 - last_error_3; - - last_error_0 = next_error_0; - last_error_1 = next_error_1; - last_error_2 = next_error_2; - last_error_3 = next_error_3; - - total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31)); - total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31)); - total_error_2 += (ulong)((next_error_2 << 1) ^ (next_error_2 >> 31)); - total_error_3 += (ulong)((next_error_3 << 1) ^ (next_error_3 >> 31)); - total_error_4 += (ulong)((next_error_4 << 1) ^ (next_error_4 >> 31)); - - *(res++) = (int)next_error_4; - } - - if ((total_error_0 < total_error_1) & (total_error_0 < total_error_2) & (total_error_0 < total_error_3) & (total_error_0 < total_error_4)) - return 0; - else if ((total_error_1 < total_error_2) & (total_error_1 < total_error_3) & (total_error_1 < total_error_4)) - return 1; - else if ((total_error_2 < total_error_3) & (total_error_2 < total_error_4)) - return 2; - else if (total_error_3 < total_error_4) - return 3; - return 4; - } - - throw new ArgumentOutOfRangeException(); - } -#endif - static unsafe uint calc_optimal_rice_params(int porder, int* parm, ulong* sums, uint n, uint pred_order, ref int method) - { - uint part = (1U << porder); - uint cnt = (n >> porder) - pred_order; - int maxK = method > 0 ? 30 : FlakeConstants.MAX_RICE_PARAM; - int k = cnt > 0 ? Math.Min(maxK, BitReader.log2i(sums[0] / cnt)) : 0; - int realMaxK0 = k; - ulong all_bits = cnt * ((uint)k + 1U) + (sums[0] >> k); - parm[0] = k; - cnt = (n >> porder); - int logcnt = BitReader.log2i(cnt); - if (cnt == 1 << logcnt) - { - for (uint i = 1; i < part; i++) - { - ulong s = sums[i]; - ulong u = s >> logcnt; - k = u >> maxK != 0 ? maxK : BitReader.log2i((uint)u); - realMaxK0 = Math.Max(realMaxK0, k); - all_bits += ((uint)k << logcnt) + (s >> k); - parm[i] = k; - } - } - else - { - for (uint i = 1; i < part; i++) - { - ulong s = sums[i]; - ulong u = s / cnt; - k = u >> maxK != 0 ? maxK : BitReader.log2i((uint)u); - realMaxK0 = Math.Max(realMaxK0, k); - all_bits += cnt * (uint)k + (s >> k); - parm[i] = k; - } - } - all_bits += cnt * (part - 1U); - method = realMaxK0 > FlakeConstants.MAX_RICE_PARAM ? 1 : 0; - return (uint)all_bits + ((4U + (uint)method) * part); - } - - static unsafe void calc_lower_sums(int pmin, int pmax, ulong* sums) - { - for (int i = pmax - 1; i >= pmin; i--) - { - for (int j = 0; j < (1 << i); j++) - { - sums[i * FlakeConstants.MAX_PARTITIONS + j] = - sums[(i + 1) * FlakeConstants.MAX_PARTITIONS + 2 * j] + - sums[(i + 1) * FlakeConstants.MAX_PARTITIONS + 2 * j + 1]; - } - } - } - - static unsafe uint calc_rice_params_sums(RiceContext rc, int pmin, int pmax, ulong* sums, uint n, uint pred_order, int bps) - { - int* parm = stackalloc int[(pmax + 1) * FlakeConstants.MAX_PARTITIONS]; - //uint* bits = stackalloc uint[FlakeConstants.MAX_PARTITION_ORDER]; - - //assert(pmin >= 0 && pmin <= FlakeConstants.MAX_PARTITION_ORDER); - //assert(pmax >= 0 && pmax <= FlakeConstants.MAX_PARTITION_ORDER); - //assert(pmin <= pmax); - - // sums for lower levels - calc_lower_sums(pmin, pmax, sums); - - uint opt_bits = AudioSamples.UINT32_MAX; - int opt_porder = pmin; - int opt_method = 0; - for (int i = pmin; i <= pmax; i++) - { - int method = bps > 16 ? 1 : 0; - uint bits = calc_optimal_rice_params(i, parm + i * FlakeConstants.MAX_PARTITIONS, sums + i * FlakeConstants.MAX_PARTITIONS, n, pred_order, ref method); - if (bits <= opt_bits) - { - opt_bits = bits; - opt_porder = i; - opt_method = method; - } - } - - rc.porder = opt_porder; - rc.coding_method = opt_method; - fixed (int* rparms = rc.rparams) - AudioSamples.MemCpy(rparms, parm + opt_porder * FlakeConstants.MAX_PARTITIONS, (1 << opt_porder)); - - return opt_bits; - } - - static int get_max_p_order(int max_porder, int n, int order) - { - int porder = Math.Min(max_porder, BitReader.log2i(n ^ (n - 1))); - if (order > 0) - porder = Math.Min(porder, BitReader.log2i(n / order)); - return porder; - } - -// private static int[,] best_x = new int[14,8193]; - private static int[][] good_x = new int[][] { -new int[] {}, // 0 -new int[] { // 1 -0x03,0x01,0x00,0x02 -}, -new int[] {// 2 -0x01,0x07,0x06,0x02, 0x03,0x04,0x00,0x05 -}, -new int[] { // 3 -0x0b,0x0f,0x0e,0x0d, 0x03,0x01,0x05,0x02 -}, -new int[] { //4 -0x17,0x09,0x03,0x0a, 0x06,0x1d,0x1f,0x05, 0x1c,0x0d,0x07,0x0c, -}, -new int[] { // 5 -0x2b,0x3d,0x37,0x07, 0x11,0x15,0x36,0x3f, -}, -new int[] { // 6 -0x6b,0x15,0x7e,0x31, 0x07,0x1a,0x29,0x26, 0x5d,0x23,0x6f,0x19, 0x56,0x75 -}, -new int[] { // 7 -0xdb,0xef,0xb5,0x47, 0xee,0x63,0x0b,0xfd, 0x31,0xbe,0xed,0x33, 0xff,0xfb,0xd6,0xbb -}, -new int[] { // 8 -0x1bb,0x1c7,0x069,0x087, 0x1fd,0x16e,0x095,0x1de, 0x066,0x071,0x055,0x09a, -}, -new int[] { // 9 -0x36b,0x3bd,0x097,0x0c3, 0x0e3,0x0b1,0x107,0x2de, 0x3ef,0x2fb,0x3d5,0x139 -}, -new int[] { // 10 -//0x0e3,0x199,0x383,0x307, 0x1e3,0x01f,0x269,0x0f1, 0x266,0x03f,0x2cd,0x1c3, 0x19a,0x387,0x339,0x259, -0x6eb,0x187,0x77d,0x271, 0x195,0x259,0x5ae,0x169, -}, -new int[] { // 11 -0xddb,0xf77,0xb6d,0x587, 0x2c3,0x03b,0xef5,0x1e3, 0xdbe, -}, -new int[] { // 12 -0x1aeb,0x0587,0x0a71,0x1dbd, 0x0559,0x0aa5,0x0a2e,0x0d43, 0x05aa,0x00f3,0x0696,0x03c6, -}, -new int[] { // 13 -0x35d7,0x2f6f,0x0aa3,0x1569, 0x150f,0x3d79,0x0dc3,0x309f/*?*/, -}, -new int[] { // 14 -0x75d7,0x5f7b,0x6a8f,0x29a3, -}, -new int[] { // 15 -0xddd7,0xaaaf,0x55c3,0xf77b, -}, -new int[] { // 16 -0x1baeb,0x1efaf,0x1d5bf,0x1cff3, -}, -new int[] { // 17 -0x36dd7,0x3bb7b,0x3df6f,0x2d547, -}, -new int[] { // 18 -0x75dd7,0x6f77b,0x7aaaf,0x5ddd3, -}, -new int[] { // 19 -0xdddd7,0xf777b,0xd5547,0xb6ddb, -}, -new int[] { // 20 -0x1baeeb,0x1efbaf,0x1aaabf,0x17bbeb, -}, -new int[] { // 21 -0x376dd7,0x3ddf7b,0x2d550f,0x0aaaa3, -}, -new int[] { // 22 -0x6eddd7,0x77777b,0x5dcd4f,0x5d76f9, -}, -new int[] { // 23 -0xdeddd7,0xb5b6eb,0x55552b,0x2aaac3, -}, -new int[] { // 24 -0x1dddbb7,0x1b76eeb,0x17bbf5f,0x1eeaa9f, -}, -new int[] { // 25 -}, -new int[] { // 26 -}, -new int[] { // 27 -}, -new int[] { // 28 -}, -new int[] { // 29 -}, -new int[] { // 30 -}, - }; - - unsafe void postprocess_coefs(FlacFrame frame, FlacSubframe sf, int ch) - { - if (eparams.development_mode < 0) - return; - if (sf.type != SubframeType.LPC || sf.order > 30) - return; - int orig_window = sf.window; - int orig_order = sf.order; - int orig_shift = sf.shift; - int orig_cbits = sf.cbits; - uint orig_size = sf.size; - var orig_coefs = stackalloc int[orig_order]; - for (int i = 0; i < orig_order; i++) orig_coefs[i] = sf.coefs[i]; - int orig_xx = -1; - int orig_seq = 0; - int maxxx = Math.Min(good_x[orig_order].Length, eparams.development_mode); - var pmax = get_max_p_order(m_settings.MaxPartitionOrder, frame.blocksize, orig_order); - var pmin = Math.Min(m_settings.MinPartitionOrder, pmax); - ulong* sums = stackalloc ulong[(pmax + 1) * FlakeConstants.MAX_PARTITIONS]; - - while (true) - { - var best_coefs = stackalloc int[orig_order]; - int best_shift = orig_shift; - int best_cbits = orig_cbits; - uint best_size = orig_size; - int best_xx = -1; - for (int xx = -1; xx < maxxx; xx++) - { - int x = xx; - if (xx < 0) - { - if (orig_xx < 0 || maxxx < 1/*3*/)// || (orig_xx >> orig_order) != 0) - continue; - x = orig_xx; - orig_seq++; - } - else - { - orig_seq = 0; - if (orig_order < good_x.Length && good_x[orig_order] != null) - x = good_x[orig_order][xx]; - } - - frame.current.type = SubframeType.LPC; - frame.current.order = orig_order; - frame.current.window = orig_window; - frame.current.shift = orig_shift; - frame.current.cbits = orig_cbits; - - if (((x >> orig_order) & 1) != 0) - { - frame.current.shift--; - frame.current.cbits--; - if (frame.current.shift < 0 || frame.current.cbits < 2) - continue; - } - - ulong csum = 0; - int qmax = (1 << (frame.current.cbits - 1)) - 1; - for (int i = 0; i < frame.current.order; i++) - { - int shift = (x >> orig_order) & 1; - int increment = (x == 1 << orig_order) ? 0 : (((x >> i) & 1) << 1) - 1; - frame.current.coefs[i] = (orig_coefs[i] + (increment << orig_seq)) >> shift; - if (frame.current.coefs[i] < -(qmax + 1)) frame.current.coefs[i] = -(qmax + 1); - if (frame.current.coefs[i] > qmax) frame.current.coefs[i] = qmax; - csum += (ulong)Math.Abs(frame.current.coefs[i]); - } - - fixed (int* coefs = frame.current.coefs) - { - if ((csum << frame.subframes[ch].obits) >= 1UL << 32) - lpc.encode_residual_long(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax); - else - lpc.encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax); - } - - var cur_size = calc_rice_params_sums(frame.current.rc, pmin, pmax, sums, (uint)frame.blocksize, (uint)frame.current.order, Settings.PCM.BitsPerSample); - frame.current.size = (uint)(frame.current.order * frame.subframes[ch].obits + 4 + 5 + frame.current.order * frame.current.cbits + 6 + (int)cur_size); - - if (frame.current.size < best_size) - { - //var dif = best_size - frame.current.size; - for (int i = 0; i < frame.current.order; i++) best_coefs[i] = frame.current.coefs[i]; - best_shift = frame.current.shift; - best_cbits = frame.current.cbits; - best_size = frame.current.size; - best_xx = x; - frame.ChooseBestSubframe(ch); - //if (dif > orig_order * 5) - // break; - } - - if (xx < 0 && best_size < orig_size) - break; - } - - if (best_size < orig_size) - { - //if (best_xx >= 0) best_x[order, best_xx]++; - //if (orig_size != 0x7FFFFFFF) - // System.Console.Write(string.Format(" {0}[{1:x}]", orig_size - best_size, best_xx)); - for (int i = 0; i < orig_order; i++) orig_coefs[i] = best_coefs[i]; - orig_shift = best_shift; - orig_cbits = best_cbits; - orig_size = best_size; - orig_xx = best_xx; - } - else - { - break; - } - } - - //if (orig_size != 0x7FFFFFFF) - // System.Console.WriteLine(); - - //if (frame_count % 0x400 == 0) - //{ - // for (int o = 0; o < best_x.GetLength(0); o++) - // { - // //for (int x = 0; x <= (1 << o); x++) - // // if (best_x[o, x] != 0) - // // System.Console.WriteLine(string.Format("{0:x2}\t{1:x4}\t{2}", o, x, best_x[o, x])); - // var s = new List>(); - // for (int x = 0; x < (1 << o); x++) - // if (best_x[o, x] != 0) - // s.Add(new KeyValuePair(x, best_x[o, x])); - // s.Sort((x, y) => y.Value.CompareTo(x.Value)); - // foreach (var x in s) - // System.Console.WriteLine(string.Format("{0:x2}\t{1:x4}\t{2}", o, x.Key, x.Value)); - // int i = 0; - // foreach (var x in s) - // { - // System.Console.Write(string.Format(o <= 8 ? "0x{0:x2}," : "0x{0:x3},", x.Key)); - // if ((++i) % 16 == 0) - // System.Console.WriteLine(); - // } - // System.Console.WriteLine(); - // } - //} - } - - public static void SetCoefs(int order, int[] coefs) - { - good_x[order] = new int[coefs.Length]; - for (int i = 0; i < coefs.Length; i++) - good_x[order][i] = coefs[i]; - } - - unsafe void encode_residual_lpc_sub(FlacFrame frame, float* lpcs, int iWindow, int order, int ch) - { - // select LPC precision based on block size - uint lpc_precision; - if (frame.blocksize <= 192) lpc_precision = 7U; - else if (frame.blocksize <= 384) lpc_precision = 8U; - else if (frame.blocksize <= 576) lpc_precision = 9U; - else if (frame.blocksize <= 1152) lpc_precision = 10U; - else if (frame.blocksize <= 2304) lpc_precision = 11U; - else if (frame.blocksize <= 4608) lpc_precision = 12U; - else if (frame.blocksize <= 8192) lpc_precision = 13U; - else if (frame.blocksize <= 16384) lpc_precision = 14U; - else lpc_precision = 15; - - for (int i_precision = m_settings.MinPrecisionSearch; i_precision <= m_settings.MaxPrecisionSearch && lpc_precision + i_precision < 16; i_precision++) - // check if we already calculated with this order, window and precision - if ((frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[i_precision] & (1U << (order - 1))) == 0) - { - frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[i_precision] |= (1U << (order - 1)); - - uint cbits = lpc_precision + (uint)i_precision; - - frame.current.type = SubframeType.LPC; - frame.current.order = order; - frame.current.window = iWindow; - frame.current.cbits = (int)cbits; - - int pmax = get_max_p_order(m_settings.MaxPartitionOrder, frame.blocksize, frame.current.order); - int pmin = Math.Min(m_settings.MinPartitionOrder, pmax); - ulong* sums = stackalloc ulong[(pmax + 1) * FlakeConstants.MAX_PARTITIONS]; - ulong csum = 0; - fixed (int* coefs = frame.current.coefs) - { - lpc.quantize_lpc_coefs(lpcs + (frame.current.order - 1) * lpc.MAX_LPC_ORDER, - frame.current.order, cbits, coefs, out frame.current.shift, 15, 0); - - if (frame.current.shift < 0 || frame.current.shift > 15) - throw new Exception("negative shift"); - - for (int i = frame.current.order; i > 0; i--) - csum += (ulong)Math.Abs(coefs[i - 1]); - - if ((csum << frame.subframes[ch].obits) >= 1UL << 32) - lpc.encode_residual_long(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax); - else - lpc.encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax); - - } - uint best_size = calc_rice_params_sums(frame.current.rc, pmin, pmax, sums, (uint)frame.blocksize, (uint)frame.current.order, Settings.PCM.BitsPerSample); - frame.current.size = (uint)(frame.current.order * frame.subframes[ch].obits + 4 + 5 + frame.current.order * (int)cbits + 6 + (int)best_size); - frame.ChooseBestSubframe(ch); - //if (frame.current.size >= frame.subframes[ch].best.size) - // postprocess_coefs(frame, frame.current, ch); - //else - //{ - // frame.ChooseBestSubframe(ch); - // postprocess_coefs(frame, frame.subframes[ch].best, ch); - //} - } - } - - unsafe void encode_residual_fixed_sub(FlacFrame frame, int order, int ch) - { - if ((frame.subframes[ch].done_fixed & (1U << order)) != 0) - return; // already calculated; - - frame.current.order = order; - frame.current.type = SubframeType.Fixed; - -#if XXX - int best_order = order; - if (frame.subframes[ch].done_fixed == 0) - { - best_order = encode_residual_fixed_estimate_best_order(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order); - if (best_order != order) - { - //frame.subframes[ch].done_fixed |= (1U << order); - order = best_order; - frame.current.order = order; - encode_residual_fixed(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order); - } - } - else -#endif - int pmax = get_max_p_order(m_settings.MaxPartitionOrder, frame.blocksize, frame.current.order); - int pmin = Math.Min(m_settings.MinPartitionOrder, pmax); - ulong* sums = stackalloc ulong[(pmax + 1) * FlakeConstants.MAX_PARTITIONS]; - encode_residual_fixed(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax); - - frame.current.size = (uint)(frame.current.order * frame.subframes[ch].obits) + 6 - + calc_rice_params_sums(frame.current.rc, pmin, pmax, sums, (uint)frame.blocksize, (uint)frame.current.order, Settings.PCM.BitsPerSample); - - frame.subframes[ch].done_fixed |= (1U << order); - - frame.ChooseBestSubframe(ch); - } - - unsafe void fixed_compute_best_predictor(int* data, uint data_len, ulong* errors)//, float* residual_bits_per_sample) - { - long last_error_0 = data[-1]; - long last_error_1 = data[-1] - data[-2]; - long last_error_2 = last_error_1 - (data[-2] - data[-3]); - long last_error_3 = last_error_2 - (data[-2] - 2 * data[-3] + data[-4]); - ulong total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; - -#if VARIANT1 - long error, save; - int* finish = data + data_len; - while (data < finish) - { - error = *(data++); total_error_0 += (ulong)((error << 1) ^ (error >> 63)); save = error; - error -= last_error_0; total_error_1 += (ulong)((error << 1) ^ (error >> 63)); last_error_0 = save; save = error; - error -= last_error_1; total_error_2 += (ulong)((error << 1) ^ (error >> 63)); last_error_1 = save; save = error; - error -= last_error_2; total_error_3 += (ulong)((error << 1) ^ (error >> 63)); last_error_2 = save; save = error; - error -= last_error_3; total_error_4 += (ulong)((error << 1) ^ (error >> 63)); last_error_3 = save; - } -#else - int* finish = data + data_len; - while (data < finish) - { - long next_error_0 = *(data++); - long next_error_1 = next_error_0 - last_error_0; - long next_error_2 = next_error_1 - last_error_1; - long next_error_3 = next_error_2 - last_error_2; - long next_error_4 = next_error_3 - last_error_3; - - last_error_0 = next_error_0; - last_error_1 = next_error_1; - last_error_2 = next_error_2; - last_error_3 = next_error_3; - - total_error_0 += (ulong)((last_error_0 << 1) ^ (last_error_0 >> 63)); - total_error_1 += (ulong)((last_error_1 << 1) ^ (last_error_1 >> 63)); - total_error_2 += (ulong)((last_error_2 << 1) ^ (last_error_2 >> 63)); - total_error_3 += (ulong)((last_error_3 << 1) ^ (last_error_3 >> 63)); - total_error_4 += (ulong)((next_error_4 << 1) ^ (next_error_4 >> 63)); - } -#endif - - errors[0] = total_error_0; - errors[1] = total_error_1; - errors[2] = total_error_2; - errors[3] = total_error_3; - errors[4] = total_error_4; - - //residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); - //residual_bits_per_sample[1] = (float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); - //residual_bits_per_sample[2] = (float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); - //residual_bits_per_sample[3] = (float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); - //residual_bits_per_sample[4] = (float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); - } - - unsafe int fixed_compute_best_predictor_order(ulong* error) - { - int order; - if ((error[0] < error[1]) & (error[0] < error[2]) & (error[0] < error[3]) & (error[0] < error[4])) - order = 0; - else if ((error[1] < error[2]) & (error[1] < error[3]) & (error[1] < error[4])) - order = 1; - else if ((error[2] < error[3]) & (error[2] < error[4])) - order = 2; - else if (error[3] < error[4]) - order = 3; - else - order = 4; - return order; - } - - unsafe void encode_residual(FlacFrame frame, int ch, PredictionType predict, OrderMethod omethod, int pass, int windows_mask) - { - int* smp = frame.subframes[ch].samples; - int i, n = frame.blocksize; - // save best.window, because we can overwrite it later with fixed frame - - // CONSTANT - for (i = 1; i < n; i++) - { - if (smp[i] != smp[0]) break; - } - if (i == n) - { - frame.subframes[ch].best.type = SubframeType.Constant; - frame.subframes[ch].best.residual[0] = smp[0]; - frame.subframes[ch].best.size = (uint)frame.subframes[ch].obits; - return; - } - - // VERBATIM - frame.current.type = SubframeType.Verbatim; - frame.current.size = (uint)(frame.subframes[ch].obits * frame.blocksize); - frame.ChooseBestSubframe(ch); - - if (n < 5 || predict == PredictionType.None) - return; - - // LPC - if (n > m_settings.MaxLPCOrder && - (predict == PredictionType.Levinson || - predict == PredictionType.Search) - //predict == PredictionType.Search || - //(pass == 2 && frame.subframes[ch].best.type == SubframeType.LPC)) - ) - { - float* lpcs = stackalloc float[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER]; - int min_order = m_settings.MinLPCOrder; - int max_order = m_settings.MaxLPCOrder; - - for (int iWindow = 0; iWindow < _windowcount; iWindow++) - { - if ((windows_mask & (1 << iWindow)) == 0) - continue; - - LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow]; - fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, iWindow, 0]) - lpc_ctx.GetReflection( - frame.subframes[ch].sf, max_order, frame.blocksize, - smp, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2, sections); - lpc_ctx.ComputeLPC(lpcs); - - //int frameSize = n; - //float* F = stackalloc float[frameSize]; - //float* B = stackalloc float[frameSize]; - //float* PE = stackalloc float[max_order + 1]; - //float* arp = stackalloc float[max_order]; - //float* rc = stackalloc float[max_order]; - - //for (int j = 0; j < frameSize; j++) - // F[j] = B[j] = smp[j]; - - //for (int K = 1; K <= max_order; K++) - //{ - // // BURG: - // float denominator = 0.0f; - // //float denominator = F[K - 1] * F[K - 1] + B[frameSize - K] * B[frameSize - K]; - // for (int j = 0; j < frameSize - K; j++) - // denominator += F[j + K] * F[j + K] + B[j] * B[j]; - // denominator /= 2; - - // // Estimate error - // PE[K - 1] = denominator / (frameSize - K); - - // float reflectionCoeff = 0.0f; - // for (int j = 0; j < frameSize - K; j++) - // reflectionCoeff += F[j + K] * B[j]; - // reflectionCoeff /= denominator; - // rc[K - 1] = arp[K - 1] = reflectionCoeff; - - // // Levinson-Durbin - // for (int j = 0; j < (K - 1) >> 1; j++) - // { - // float arptmp = arp[j]; - // arp[j] -= reflectionCoeff * arp[K - 2 - j]; - // arp[K - 2 - j] -= reflectionCoeff * arptmp; - // } - // if (((K - 1) & 1) != 0) - // arp[(K - 1) >> 1] -= reflectionCoeff * arp[(K - 1) >> 1]; - - // for (int j = 0; j < frameSize - K; j++) - // { - // float f = F[j + K]; - // float b = B[j]; - // F[j + K] = f - reflectionCoeff * b; - // B[j] = b - reflectionCoeff * f; - // } - - // for (int j = 0; j < K; j++) - // lpcs[(K - 1) * lpc.MAX_LPC_ORDER + j] = (float)arp[j]; - //} - - switch (omethod) - { - case OrderMethod.Akaike: - //lpc_ctx.SortOrdersAkaike(frame.blocksize, m_settings.EstimationDepth, max_order, 7.1, 0.0); - lpc_ctx.SortOrdersAkaike(frame.blocksize, m_settings.EstimationDepth, min_order, max_order, 4.5, 0); - break; - default: - throw new Exception("unknown order method"); - } - - for (i = 0; i < m_settings.EstimationDepth && i < max_order; i++) - encode_residual_lpc_sub(frame, lpcs, iWindow, lpc_ctx.best_orders[i], ch); - } - - postprocess_coefs(frame, frame.subframes[ch].best, ch); - } - - // FIXED - if (predict == PredictionType.Fixed || - (predict == PredictionType.Search && pass != 1) || - //predict == PredictionType.Search || - //(pass == 2 && frame.subframes[ch].best.type == SubframeType.Fixed) || - (n > m_settings.MaxFixedOrder && n <= m_settings.MaxLPCOrder)) - { - int max_fixed_order = Math.Min(m_settings.MaxFixedOrder, 4); - int min_fixed_order = Math.Min(m_settings.MinFixedOrder, max_fixed_order); - - if (min_fixed_order == 0 && max_fixed_order == 4) - { - fixed (ulong* fixed_errors = frame.subframes[ch].best_fixed) - { - if ((frame.subframes[ch].done_fixed & (1U << 5)) == 0) - { - fixed_compute_best_predictor(smp + 4, (uint)n - 4, fixed_errors); - frame.subframes[ch].done_fixed |= (1U << 5); - } - i = fixed_compute_best_predictor_order(fixed_errors); - encode_residual_fixed_sub(frame, i, ch); - } - } - else - { - for (i = max_fixed_order; i >= min_fixed_order; i--) - encode_residual_fixed_sub(frame, i, ch); - } - } - - } - - unsafe void output_frame_header(FlacFrame frame, BitWriter bitwriter) - { - bitwriter.writebits(15, 0x7FFC); - bitwriter.writebits(1, eparams.variable_block_size > 0 ? 1 : 0); - bitwriter.writebits(4, frame.bs_code0); - bitwriter.writebits(4, sr_code0); - if (frame.ch_mode == ChannelMode.NotStereo) - bitwriter.writebits(4, ch_code); - else - bitwriter.writebits(4, (int) frame.ch_mode); - bitwriter.writebits(3, bps_code); - bitwriter.writebits(1, 0); - bitwriter.write_utf8(frame_count); - - // custom block size - if (frame.bs_code1 >= 0) - { - if (frame.bs_code1 < 256) - bitwriter.writebits(8, frame.bs_code1); - else - bitwriter.writebits(16, frame.bs_code1); - } - - // custom sample rate - if (sr_code1 > 0) - { - if (sr_code1 < 256) - bitwriter.writebits(8, sr_code1); - else - bitwriter.writebits(16, sr_code1); - } - - // CRC-8 of frame header - bitwriter.flush(); - byte crc = crc8.ComputeChecksum(frame_buffer, 0, bitwriter.Length); - bitwriter.writebits(8, crc); - } - - unsafe void output_residual(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub) - { - // rice-encoded block - bitwriter.writebits(2, sub.best.rc.coding_method); - - // partition order - int porder = sub.best.rc.porder; - int psize = frame.blocksize >> porder; - //assert(porder >= 0); - bitwriter.writebits(4, porder); - int res_cnt = psize - sub.best.order; - - int rice_len = 4 + sub.best.rc.coding_method; - // residual - int j = sub.best.order; - fixed (byte* fixbuf = &frame_buffer[0]) - for (int p = 0; p < (1 << porder); p++) - { - int k = sub.best.rc.rparams[p]; - bitwriter.writebits(rice_len, k); - if (p == 1) res_cnt = psize; - int cnt = Math.Min(res_cnt, frame.blocksize - j); - bitwriter.write_rice_block_signed(fixbuf, k, sub.best.residual + j, cnt); - j += cnt; - } - } - - unsafe void - output_subframe_constant(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub) - { - bitwriter.writebits_signed(sub.obits, sub.best.residual[0]); - } - - unsafe void - output_subframe_verbatim(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub) - { - int n = frame.blocksize; - for (int i = 0; i < n; i++) - bitwriter.writebits_signed(sub.obits, sub.samples[i]); - // Don't use residual here, because we don't copy samples to residual for verbatim frames. - } - - unsafe void - output_subframe_fixed(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub) - { - // warm-up samples - for (int i = 0; i < sub.best.order; i++) - bitwriter.writebits_signed(sub.obits, sub.best.residual[i]); - - // residual - output_residual(frame, bitwriter, sub); - } - - unsafe void - output_subframe_lpc(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub) - { - // warm-up samples - for (int i = 0; i < sub.best.order; i++) - bitwriter.writebits_signed(sub.obits, sub.best.residual[i]); - - // LPC coefficients - int cbits = 1; - for (int i = 0; i < sub.best.order; i++) - while (cbits < 16 && sub.best.coefs[i] != (sub.best.coefs[i] << (32 - cbits)) >> (32 - cbits)) - cbits++; - bitwriter.writebits(4, cbits - 1); - bitwriter.writebits_signed(5, sub.best.shift); - for (int i = 0; i < sub.best.order; i++) - bitwriter.writebits_signed(cbits, sub.best.coefs[i]); - - // residual - output_residual(frame, bitwriter, sub); - } - - unsafe void output_subframes(FlacFrame frame, BitWriter bitwriter) - { - for (int ch = 0; ch < channels; ch++) - { - FlacSubframeInfo sub = frame.subframes[ch]; - // subframe header - int type_code = (int) sub.best.type; - if (sub.best.type == SubframeType.Fixed) - type_code |= sub.best.order; - if (sub.best.type == SubframeType.LPC) - type_code |= sub.best.order - 1; - bitwriter.writebits(1, 0); - bitwriter.writebits(6, type_code); - bitwriter.writebits(1, sub.wbits != 0 ? 1 : 0); - if (sub.wbits > 0) - bitwriter.writebits((int)sub.wbits, 1); - - // subframe - switch (sub.best.type) - { - case SubframeType.Constant: - output_subframe_constant(frame, bitwriter, sub); - break; - case SubframeType.Verbatim: - output_subframe_verbatim(frame, bitwriter, sub); - break; - case SubframeType.Fixed: - output_subframe_fixed(frame, bitwriter, sub); - break; - case SubframeType.LPC: - output_subframe_lpc(frame, bitwriter, sub); - break; - } - } - } - - void output_frame_footer(BitWriter bitwriter) - { - bitwriter.flush(); - ushort crc = bitwriter.get_crc16(); - bitwriter.writebits(16, crc); - bitwriter.flush(); - } - - unsafe void encode_residual_pass1(FlacFrame frame, int ch, int windows_mask) - { - int max_prediction_order = m_settings.MaxLPCOrder; - //int max_fixed_order = m_settings.MaxFixedOrder; - //int min_fixed_order = m_settings.MinFixedOrder; - int lpc_min_precision_search = m_settings.MinPrecisionSearch; - int lpc_max_precision_search = m_settings.MaxPrecisionSearch; - int max_partition_order = m_settings.MaxPartitionOrder; - int estimation_depth = m_settings.EstimationDepth; - var development_mode = eparams.development_mode; - //m_settings.MinFixedOrder = 2; - //m_settings.MaxFixedOrder = 2; - m_settings.MinPrecisionSearch = m_settings.MaxPrecisionSearch; - m_settings.MaxLPCOrder = Math.Min(m_settings.MaxLPCOrder, Math.Max(m_settings.MinLPCOrder, 8)); - m_settings.EstimationDepth = 1; - eparams.development_mode = Math.Min(eparams.development_mode, -1); - encode_residual(frame, ch, m_settings.PredictionType, OrderMethod.Akaike, 1, windows_mask); - //m_settings.MinFixedOrder = min_fixed_order; - //m_settings.MaxFixedOrder = max_fixed_order; - m_settings.MaxLPCOrder = max_prediction_order; - m_settings.MinPrecisionSearch = lpc_min_precision_search; - m_settings.MaxPrecisionSearch = lpc_max_precision_search; - m_settings.MaxPartitionOrder = max_partition_order; - m_settings.EstimationDepth = estimation_depth; - eparams.development_mode = development_mode; - } - - unsafe void encode_residual_pass2(FlacFrame frame, int ch) - { - encode_residual(frame, ch, m_settings.PredictionType, eparams.order_method, 2, estimate_best_windows(frame, ch)); - } - - unsafe int estimate_best_windows_akaike(FlacFrame 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]++; - } - - float* err = stackalloc float[lpc.MAX_LPC_ORDER]; - for (int i = 0; i < _windowcount; i++) - { - LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[i]; - if (onePerType && windows_present[i] <= count) - { - err[i] = 0; - continue; - } - int estimate_order = 4; - fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, i, 0]) - lpc_ctx.GetReflection( - frame.subframes[ch].sf, estimate_order, frame.blocksize, - frame.subframes[ch].samples, frame.window_buffer + i * FlakeConstants.MAX_BLOCKSIZE * 2, sections); - lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, estimate_order, 4.5, 0.0); - //err[i] = (float)(lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0)); - //err[i] = (float)((frame.blocksize * lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1] / windowScale[i]) + lpc_ctx.best_orders[0] * 4.5); - //err[i] = (float)((frame.blocksize * lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1] / windowScale[i]) + lpc_ctx.best_orders[0] * frame.subframes[ch].obits); - - // realistic - //err[i] = (float)(frame.blocksize * Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1]) / Math.Log(2) / 2.5 - //- windowScale[i] / 2 + lpc_ctx.best_orders[0] * frame.subframes[ch].obits / 2); - - //err[i] = (float)(frame.blocksize * Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1]) / Math.Log(2) / 2.5 - //- frame.blocksize * Math.Log(lpc_ctx.autocorr_values[0]) / 2.1 - //+ Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5 / 2.5 / Math.Log(2)); - - // Akaike - //err[i] = (float)(frame.blocksize * (Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1])) + Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5); - - //err[i] = (float)(lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0) - frame.blocksize * (frame.subframes[ch].obits + Math.Log(windowScale[i] / frame.blocksize) / 2)); - - // tested good - 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; - } - - unsafe int estimate_best_windows(FlacFrame frame, int ch) - { - if (_windowcount == 1 || m_settings.PredictionType == PredictionType.Fixed) - return 1; - switch (m_settings.WindowMethod) - { - case WindowMethod.Estimate: - return estimate_best_windows_akaike(frame, ch, 1, false); - case WindowMethod.Estimate2: - return estimate_best_windows_akaike(frame, ch, 2, false); - case WindowMethod.Estimate3: - return estimate_best_windows_akaike(frame, ch, 3, false); - case WindowMethod.EstimateN: - return estimate_best_windows_akaike(frame, ch, 1, true); - case WindowMethod.Evaluate2: - encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 2, false)); - return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; - case WindowMethod.Evaluate3: - encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 3, false)); - return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; - case WindowMethod.EvaluateN: - encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 1, true)); -#if XXX - if (frame.subframes[ch].best.type == SubframeType.LPC && frame.subframes[ch].best.order <= 4) - { - LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[frame.subframes[ch].best.window]; - double err = lpc_ctx.prediction_error[frame.subframes[ch].best.order - 1] / lpc_ctx.autocorr_values[0]; - double est = frame.blocksize * (frame.subframes[ch].obits * (1 - err)); - double est1 = frame.blocksize * (frame.subframes[ch].obits * (err)); - if (est < 0 || est1 < 0) return -1; - } -#endif - return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; - case WindowMethod.Evaluate2N: - encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 2, true)); - return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; - case WindowMethod.Evaluate3N: - encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 3, true)); - return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; - case WindowMethod.Evaluate: - encode_residual_pass1(frame, ch, -1); - return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; - case WindowMethod.Search: - return -1; - } - return -1; - } - - unsafe void estimate_frame(FlacFrame frame, bool do_midside) - { - int subframes = do_midside ? channels * 2 : channels; - - switch (m_settings.StereoMethod) - { - case StereoMethod.Estimate: - for (int ch = 0; ch < subframes; ch++) - { - LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[0]; - int estimate_order = 4; - int iWindow = 0; - fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, iWindow, 0]) - lpc_ctx.GetReflection( - frame.subframes[ch].sf, estimate_order, frame.blocksize, - frame.subframes[ch].samples, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2, sections); - lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, estimate_order, 4.5, 0.0); - frame.subframes[ch].best.size - = (uint)Math.Max(0, frame.blocksize * (Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1])) + Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5 - //= (uint)Math.Max(0, lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0) - //* 2.0 / Math.Log(windowScale[0] / frame.blocksize) - + 7.1 * frame.subframes[ch].obits * m_settings.MaxLPCOrder); - } - break; - case StereoMethod.EstimateFixed: - for (int ch = 0; ch < subframes; ch++) - { - fixed (ulong* fixed_errors = frame.subframes[ch].best_fixed) - { - if ((frame.subframes[ch].done_fixed & (1U << 5)) == 0) - { - fixed_compute_best_predictor(frame.subframes[ch].samples + 4, (uint)frame.blocksize - 4, fixed_errors); - frame.subframes[ch].done_fixed |= (1U << 5); - } - int best_order = fixed_compute_best_predictor_order(fixed_errors); - //residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); - frame.subframes[ch].best.size = (uint)fixed_errors[best_order]; - } - } - break; - case StereoMethod.EstimateX: - for (int ch = 0; ch < subframes; ch++) - { - for (int iWindow = 0; iWindow < _windowcount; iWindow++) - { - LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow]; - int estimate_order = 4; - fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, iWindow, 0]) - lpc_ctx.GetReflection( - frame.subframes[ch].sf, estimate_order, frame.blocksize, - frame.subframes[ch].samples, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2, sections); - lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, estimate_order, 4.5, 0.0); - uint estimate - = (uint)Math.Max(0, frame.blocksize * (Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1])) + Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5 - //= (uint)Math.Max(0, lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0) - //* 2.0 / Math.Log(windowScale[0] / frame.blocksize) - + 7.1 * frame.subframes[ch].obits * m_settings.MaxLPCOrder); - if (iWindow == 0 || frame.subframes[ch].best.size > estimate) - frame.subframes[ch].best.size = estimate; - } - } - break; - case StereoMethod.Evaluate: - for (int ch = 0; ch < subframes; ch++) - encode_residual_pass1(frame, ch, 1); - break; - case StereoMethod.EvaluateX: - for (int ch = 0; ch < subframes; ch++) - encode_residual_pass1(frame, ch, - estimate_best_windows_akaike(frame, ch, 1, false)); - break; - case StereoMethod.Search: - for (int ch = 0; ch < subframes; ch++) - encode_residual_pass2(frame, ch); - break; - } - } - - unsafe uint measure_frame_size(FlacFrame frame, bool do_midside) - { - // crude estimation of header/footer size - uint total = (uint)(32 + ((BitReader.log2i(frame_count) + 4) / 5) * 8 + (eparams.variable_block_size != 0 ? 16 : 0) + 16); - - if (do_midside) - { - uint bitsBest = AudioSamples.UINT32_MAX; - ChannelMode modeBest = ChannelMode.LeftRight; - - if (bitsBest > frame.subframes[2].best.size + frame.subframes[3].best.size) - { - bitsBest = frame.subframes[2].best.size + frame.subframes[3].best.size; - modeBest = ChannelMode.MidSide; - } - if (bitsBest > frame.subframes[3].best.size + frame.subframes[1].best.size) - { - bitsBest = frame.subframes[3].best.size + frame.subframes[1].best.size; - modeBest = ChannelMode.RightSide; - } - if (bitsBest > frame.subframes[3].best.size + frame.subframes[0].best.size) - { - bitsBest = frame.subframes[3].best.size + frame.subframes[0].best.size; - modeBest = ChannelMode.LeftSide; - } - if (bitsBest > frame.subframes[0].best.size + frame.subframes[1].best.size) - { - bitsBest = frame.subframes[0].best.size + frame.subframes[1].best.size; - modeBest = ChannelMode.LeftRight; - } - frame.ch_mode = modeBest; - return total + bitsBest; - } - - for (int ch = 0; ch < channels; ch++) - total += frame.subframes[ch].best.size; - return total; - } - - unsafe void encode_estimated_frame(FlacFrame frame) - { - switch (m_settings.StereoMethod) - { - case StereoMethod.Estimate: - case StereoMethod.EstimateX: - case StereoMethod.EstimateFixed: - for (int ch = 0; ch < channels; ch++) - { - frame.subframes[ch].best.size = AudioSamples.UINT32_MAX; - encode_residual_pass2(frame, ch); - } - break; - case StereoMethod.Evaluate: - case StereoMethod.EvaluateX: - for (int ch = 0; ch < channels; ch++) - encode_residual_pass2(frame, ch); - break; - case StereoMethod.Search: - break; - } - } - - unsafe delegate void window_function(float* window, int size); - - unsafe void calculate_window(float* window, window_function func, WindowFunction flag) - { - if ((m_settings.WindowFunctions & flag) == 0 || _windowcount == lpc.MAX_LPC_WINDOWS) - return; - int sz = _windowsize; - float* pos1 = window + _windowcount * FlakeConstants.MAX_BLOCKSIZE * 2; - float* pos = pos1; - int nSeg = 0; - do - { - windowSections[nSeg, _windowcount, 0].setData(0, sz); - for (int j = 1; j < lpc.MAX_LPC_SECTIONS; j++) - windowSections[nSeg, _windowcount, j].setZero(sz, sz); - - fixed (LpcWindowSection* sections = &windowSections[nSeg, _windowcount, 0]) - func(pos, sz); - if ((sz & 1) != 0) - break; - nSeg++; - pos += sz; - sz >>= 1; - } while (sz >= 32); - double scale = 0.0; - for (int i = 0; i < _windowsize; i++) - scale += pos1[i] * pos1[i]; - windowScale[_windowcount] = scale; - windowType[_windowcount] = flag; - _windowcount++; - } - - class PunchoutTukeyVariant - { - public PunchoutTukeyVariant( - WindowFunction _type, - int _parts, double _overlap, double _p) - { - parts = _parts; - type = _type; - overlap = _overlap; - p = _p; - } - public WindowFunction type; - public int parts; - public double overlap; - public double p; - }; - - unsafe int encode_frame(out int size) - { - fixed (int* s = samplesBuffer, r = residualBuffer) - fixed (float* window = windowBuffer) - { - frame.InitSize(m_blockSize, eparams.variable_block_size != 0); - - if (frame.blocksize != _windowsize && frame.blocksize > 4 && m_settings.PredictionType != PredictionType.Fixed) - { - _windowsize = frame.blocksize; - _windowcount = 0; - calculate_window(window, lpc.window_welch, WindowFunction.Welch); - calculate_window(window, lpc.window_flattop, WindowFunction.Flattop); - calculate_window(window, lpc.window_hann, WindowFunction.Hann); - calculate_window(window, lpc.window_bartlett, WindowFunction.Bartlett); - var tukeys = new PunchoutTukeyVariant[] - { - new PunchoutTukeyVariant(WindowFunction.Tukey4, 4, 0, 0.03), - new PunchoutTukeyVariant(WindowFunction.Tukey4A, 4, 0, 0.03), - new PunchoutTukeyVariant(WindowFunction.Tukey4B, 4, 0, 0.03), - new PunchoutTukeyVariant(WindowFunction.Tukey4X, 4, m_settings.TukeyOverlap, m_settings.TukeyP), - new PunchoutTukeyVariant(WindowFunction.Tukey3, 3, 1.0/3, 0.03), - new PunchoutTukeyVariant(WindowFunction.Tukey3A, 3, 1.0/3, 0.03), - new PunchoutTukeyVariant(WindowFunction.Tukey3B, 3, 1.0/3, 0.03), - new PunchoutTukeyVariant(WindowFunction.Tukey3X, 3, m_settings.TukeyOverlap, m_settings.TukeyP), - new PunchoutTukeyVariant(WindowFunction.Tukey2, 2, 0.25, 0.03), - new PunchoutTukeyVariant(WindowFunction.Tukey2A, 2, 0.25, 0.03), - new PunchoutTukeyVariant(WindowFunction.Tukey2B, 2, 0.25, 0.03), - new PunchoutTukeyVariant(WindowFunction.Tukey2X, 2, m_settings.TukeyOverlap, m_settings.TukeyP), - new PunchoutTukeyVariant(WindowFunction.Tukey, 1, 0.0, 0.03), - new PunchoutTukeyVariant(WindowFunction.Tukey1A, 1, 0.0, 0.03), - new PunchoutTukeyVariant(WindowFunction.Tukey1B, 1, 0.0, 0.03), - new PunchoutTukeyVariant(WindowFunction.Tukey1X, 1, m_settings.TukeyOverlap, m_settings.TukeyP), - }; - - foreach (var tukey in tukeys) - { - if (tukey.parts == 0 || (m_settings.WindowFunctions & tukey.type) == 0) continue; - if (tukey.parts == 1) - { - calculate_window(window, (w, wsz) => - { - lpc.window_tukey(w, wsz, tukey.p); - }, tukey.type); - continue; - } - double overlap = tukey.overlap; - 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, tukey.p, tukey.p, - m / (tukey.parts + overlap_units), - (m + 1 + overlap_units) / (tukey.parts + overlap_units)); - }, tukey.type); - } - - if (_windowcount == 0) - throw new Exception("invalid windowfunction"); - int nSeg = 0; - int sz = _windowsize; - float* window_segment = window; - do - { - fixed (LpcWindowSection* sections = &windowSections[nSeg, 0, 0]) - LpcWindowSection.Detect(_windowcount, window_segment, FlakeConstants.MAX_BLOCKSIZE * 2, sz, Settings.PCM.BitsPerSample, sections); - if ((sz & 1) != 0) - break; - window_segment += sz; - nSeg++; - sz >>= 1; - } while (sz >= 32); -#if NONONO - using (TextWriter tx = File.CreateText(@"H:\ubuntu\flac\w.txt")) - { -#if !NONONO - int totaltotal = 0; - for (int i = 0; i < _windowcount; i++) - { - int total = 0; - for (int sec = 0; sec < lpc.MAX_LPC_SECTIONS; sec++) - if (windowSections[0, i, sec].m_type != LpcWindowSection.SectionType.Zero || windowSections[0, i, sec].m_start != windowSections[0, i, sec].m_end) - { - tx.WriteLine("{0}\t{1}\t{2}\t{3}", windowSections[0, i, sec].m_start, windowSections[0, i, sec].m_end, windowSections[0, i, sec].m_type, windowSections[0, i, sec].m_id); - if (windowSections[0, i, sec].m_type != LpcWindowSection.SectionType.One) - total += windowSections[0, i, sec].m_end - windowSections[0, i, sec].m_start; - } - totaltotal += total; - tx.WriteLine("{0} total window data", total); - } - tx.WriteLine("{0} grand total window data", totaltotal); -#endif - for (int x = 0; x < frame.blocksize; x++) - { - tx.Write("{0}", x); - for (int i = 0; i < _windowcount; i++) - tx.Write("\t{0}", window[i * FlakeConstants.MAX_BLOCKSIZE * 2 + x]); - tx.WriteLine(); - } - } -#endif - } - - if (channels != 2 || frame.blocksize <= 32 || m_settings.StereoMethod == StereoMethod.Independent) - { - frame.window_buffer = window; - frame.nSeg = 0; - frame.current.residual = r + channels * FlakeConstants.MAX_BLOCKSIZE; - frame.ch_mode = channels != 2 ? ChannelMode.NotStereo : ChannelMode.LeftRight; - for (int ch = 0; ch < channels; ch++) - frame.subframes[ch].Init(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE, - Settings.PCM.BitsPerSample, get_wasted_bits(s + ch * FlakeConstants.MAX_BLOCKSIZE, frame.blocksize)); - - for (int ch = 0; ch < channels; ch++) - encode_residual_pass2(frame, ch); - } - else - { - //channel_decorrelation(s, s + FlakeConstants.MAX_BLOCKSIZE, s + 2 * FlakeConstants.MAX_BLOCKSIZE, s + 3 * FlakeConstants.MAX_BLOCKSIZE, frame.blocksize); - frame.window_buffer = window; - frame.nSeg = 0; - frame.current.residual = r + 4 * FlakeConstants.MAX_BLOCKSIZE; - for (int ch = 0; ch < 4; ch++) - frame.subframes[ch].Init(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE, - Settings.PCM.BitsPerSample + (ch == 3 ? 1 : 0), get_wasted_bits(s + ch * FlakeConstants.MAX_BLOCKSIZE, frame.blocksize)); - - //for (int ch = 0; ch < 4; ch++) - // for (int iWindow = 0; iWindow < _windowcount; iWindow++) - // frame.subframes[ch].lpc_ctx[iWindow].GetReflection(32, frame.subframes[ch].samples, frame.blocksize, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2); - - estimate_frame(frame, true); - uint fs = measure_frame_size(frame, true); - - if (0 != eparams.variable_block_size) - { - FlacFrame frame2 = new FlacFrame(channels * 2); - FlacFrame frame3 = new FlacFrame(channels * 2); - int tumbler = 1; - while ((frame.blocksize & 1) == 0 && frame.blocksize >= 1024) - { - frame2.InitSize(frame.blocksize / 2, true); - frame2.window_buffer = frame.window_buffer + frame.blocksize; - frame2.nSeg = frame.nSeg + 1; - frame2.current.residual = r + tumbler * 5 * FlakeConstants.MAX_BLOCKSIZE; - for (int ch = 0; ch < 4; ch++) - frame2.subframes[ch].Init(frame.subframes[ch].samples, frame2.current.residual + (ch + 1) * frame2.blocksize, - frame.subframes[ch].obits + frame.subframes[ch].wbits, frame.subframes[ch].wbits); - estimate_frame(frame2, true); - //measure_frame_size(frame2, true); - //frame2.ChooseSubframes(); - //encode_estimated_frame(frame2); - //uint fs2 = measure_frame_size(frame2, false); - uint fs2 = measure_frame_size(frame2, true); - uint fs3 = fs2; - if (eparams.variable_block_size == 2 || eparams.variable_block_size == 4) - { - frame3.InitSize(frame2.blocksize, true); - frame3.window_buffer = frame2.window_buffer; - frame3.nSeg = frame2.nSeg; - frame3.current.residual = frame2.current.residual + 5 * frame2.blocksize; - for (int ch = 0; ch < 4; ch++) - frame3.subframes[ch].Init(frame2.subframes[ch].samples + frame2.blocksize, frame3.current.residual + (ch + 1) * frame3.blocksize, - frame.subframes[ch].obits + frame.subframes[ch].wbits, frame.subframes[ch].wbits); - estimate_frame(frame3, true); - fs3 = measure_frame_size(frame3, true); - } - if (fs2 + fs3 > fs) - break; - FlacFrame tmp = frame; - frame = frame2; - frame2 = tmp; - fs = fs2; - if (eparams.variable_block_size <= 2) - break; - tumbler = 1 - tumbler; - } - } - - frame.ChooseSubframes(); - encode_estimated_frame(frame); - } - - BitWriter bitwriter = new BitWriter(frame_buffer, 0, max_frame_size); - - output_frame_header(frame, bitwriter); - output_subframes(frame, bitwriter); - output_frame_footer(bitwriter); - - if (bitwriter.Length >= max_frame_size) - throw new Exception("buffer overflow"); - - if (frame_buffer != null) - { - if (eparams.variable_block_size > 0) - frame_count += frame.blocksize; - else - frame_count++; - } - size = frame.blocksize; - return bitwriter.Length; - } - } - - unsafe int output_frame() - { - if (verify != null) - { - fixed (int* s = verifyBuffer, r = samplesBuffer) - for (int ch = 0; ch < channels; ch++) - AudioSamples.MemCpy(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE, m_blockSize); - } - - int fs, bs; - //if (0 != eparams.variable_block_size && 0 == (m_blockSize & 7) && m_blockSize >= 128) - // fs = encode_frame_vbs(); - //else - fs = encode_frame(out bs); - - if (seek_table != null && _IO.CanSeek) - { - for (int sp = 0; sp < seek_table.Length; sp++) - { - if (seek_table[sp].framesize != 0) - continue; - if (seek_table[sp].number > _position + bs) - break; - if (seek_table[sp].number >= _position) - { - seek_table[sp].number = _position; - seek_table[sp].offset = _IO.Position - first_frame_offset; - seek_table[sp].framesize = bs; - } - } - } - - _position += bs; - _IO.Write(frame_buffer, 0, fs); - _totalSize += fs; - - if (verify != null) - try - { - int decoded = verify.DecodeFrame(frame_buffer, 0, fs); - if (decoded != fs || verify.Remaining != bs) - throw new Exception(Properties.Resources.ExceptionValidationFailed); - fixed (int* s = verifyBuffer, r = verify.Samples) - { - for (int ch = 0; ch < channels; ch++) - if (AudioSamples.MemCmp(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE, bs)) - throw new Exception(Properties.Resources.ExceptionValidationFailed); - } - } - catch (Exception ex) - { - //if (channels == 2) - //{ - // var sw = new WAVWriter(string.Format("verify_{0}.wav", this.frame_count), new WAVWriterSettings(this.Settings.PCM)); - // sw.FinalSampleCount = this.frame.blocksize; - // var ab = new AudioBuffer(this.Settings.PCM, this.frame.blocksize); - // ab.Prepare(this.frame.blocksize); - // fixed (int* abs = ab.Samples, s = verifyBuffer) - // AudioSamples.Interlace(abs, s, s + FlakeConstants.MAX_BLOCKSIZE, this.frame.blocksize); - // sw.Write(ab); - // sw.Close(); - //} else - throw ex; - } - - if (bs < m_blockSize) - { - for (int ch = 0; ch < (channels == 2 ? 4 : channels); ch++) - Buffer.BlockCopy(samplesBuffer, (bs + ch * FlakeConstants.MAX_BLOCKSIZE) * sizeof(int), samplesBuffer, ch * FlakeConstants.MAX_BLOCKSIZE * sizeof(int), (m_blockSize - bs) * sizeof(int)); - //fixed (int* s = samplesBuffer) - // for (int ch = 0; ch < channels; ch++) - // AudioSamples.MemCpy(s + ch * FlakeConstants.MAX_BLOCKSIZE, s + bs + ch * FlakeConstants.MAX_BLOCKSIZE, m_blockSize - bs); - } - - samplesInBuffer -= bs; - - return bs; - } - - public void Write(AudioBuffer buff) - { - if (!inited) - { - if (_IO == null) - _IO = new FileStream(_path, FileMode.Create, FileAccess.Write, FileShare.Read, 0x10000); - inited = true; - int header_size = flake_encode_init(); - _IO.Write(header, 0, header_size); - if (_IO.CanSeek) - first_frame_offset = _IO.Position; - } - - buff.Prepare(this); - - int pos = 0; - while (pos < buff.Length) - { - int block = Math.Min(buff.Length - pos, m_blockSize - samplesInBuffer); - - copy_samples(buff.Samples, pos, block); - - pos += block; - - while (samplesInBuffer >= m_blockSize) - output_frame(); - } - - if (md5 != null) - md5.TransformBlock(buff.Bytes, 0, buff.ByteLength, null, 0); - } - - public string Path { get { return _path; } } - - public static string Vendor - { - get - { - var version = typeof(AudioEncoder).Assembly.GetName().Version; - return vendor_string ?? "CUETools " + version.Major + "." + version.Minor + "." + version.Build; - } - set - { - vendor_string = value; - } - } - - static string vendor_string = null; - - int select_blocksize(int samplerate, int time_ms) - { - int blocksize = FlakeConstants.flac_blocksizes[1]; - int target = (samplerate * time_ms) / 1000; - if (eparams.variable_block_size > 0) - { - blocksize = 1024; - while (target >= blocksize) - blocksize <<= 1; - return blocksize >> 1; - } - - for (int i = 8; i < FlakeConstants.flac_blocksizes.Length - 1; i++) - if (target >= FlakeConstants.flac_blocksizes[i] && FlakeConstants.flac_blocksizes[i] > blocksize) - { - blocksize = FlakeConstants.flac_blocksizes[i]; - } - return blocksize; - } - - void write_streaminfo(byte[] header, int pos, int last) - { - Array.Clear(header, pos, 38); - BitWriter bitwriter = new BitWriter(header, pos, 38); - - // metadata header - bitwriter.writebits(1, last); - bitwriter.writebits(7, (int)MetadataType.StreamInfo); - bitwriter.writebits(24, 34); - - if (eparams.variable_block_size > 0) - bitwriter.writebits(16, 0); - else - bitwriter.writebits(16, m_blockSize); - - bitwriter.writebits(16, m_blockSize); - bitwriter.writebits(24, 0); - bitwriter.writebits(24, max_frame_size); - bitwriter.writebits(20, Settings.PCM.SampleRate); - bitwriter.writebits(3, channels - 1); - bitwriter.writebits(5, Settings.PCM.BitsPerSample - 1); - - // total samples - if (sample_count > 0) - { - bitwriter.writebits(4, 0); - bitwriter.writebits(32, sample_count); - } - else - { - bitwriter.writebits(4, 0); - bitwriter.writebits(32, 0); - } - bitwriter.flush(); - } - - /** - * Write vorbis comment metadata block to byte array. - * Just writes the vendor string for now. - */ - int write_vorbis_comment(byte[] comment, int pos, int len, int last) - { - BitWriter bitwriter = new BitWriter(comment, pos, len); - Encoding enc = new UTF8Encoding(); - byte[] str = enc.GetBytes(Vendor); - - // metadata header - bitwriter.writebits(1, last); - bitwriter.writebits(7, (int)MetadataType.VorbisComment); - int tagsLen = 0; - if (m_settings.Tags != null) - foreach (var t in m_settings.Tags) - tagsLen += 4 + enc.GetByteCount(t); - bitwriter.writebits(24, 8 + str.Length + tagsLen); - for (int i = 0; i < 4; i++) - bitwriter.writebits(8, (str.Length >> (i * 8)) & 0xff); - bitwriter.write(str); - int nTags = m_settings.Tags != null ? m_settings.Tags.Length : 0; - for (int i = 0; i < 4; i++) - bitwriter.writebits(8, (nTags >> (i * 8)) & 0xff); - if (m_settings.Tags != null) - foreach (var tag in m_settings.Tags) - { - str = enc.GetBytes(tag); - for (int i = 0; i < 4; i++) - bitwriter.writebits(8, (str.Length >> (i * 8)) & 0xff); - bitwriter.write(str); - } - bitwriter.flush(); - return bitwriter.Length; - } - - int write_seekpoints(byte[] header, int pos, int last) - { - seek_table_offset = pos + 4; - - BitWriter bitwriter = new BitWriter(header, pos, 4 + 18 * seek_table.Length); - - // metadata header - bitwriter.writebits(1, last); - bitwriter.writebits(7, (int)MetadataType.Seektable); - bitwriter.writebits(24, 18 * seek_table.Length); - for (int i = 0; i < seek_table.Length; i++) - { - bitwriter.writebits(FlakeConstants.FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN, (ulong)seek_table[i].number); - bitwriter.writebits(FlakeConstants.FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN, (ulong)seek_table[i].offset); - bitwriter.writebits(FlakeConstants.FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN, seek_table[i].framesize); - } - bitwriter.flush(); - return 4 + 18 * seek_table.Length; - } - - /** - * Write padding metadata block to byte array. - */ - int - write_padding(byte[] padding, int pos, int last, int padlen) - { - BitWriter bitwriter = new BitWriter(padding, pos, 4); - - // metadata header - bitwriter.writebits(1, last); - bitwriter.writebits(7, (int)MetadataType.Padding); - bitwriter.writebits(24, padlen); - - bitwriter.flush(); - return padlen + 4; - } - - int write_headers() - { - int header_size = 0; - int last = 0; - - // stream marker - header[0] = 0x66; - header[1] = 0x4C; - header[2] = 0x61; - header[3] = 0x43; - header_size += 4; - - // streaminfo - write_streaminfo(header, header_size, last); - header_size += 38; - - // seek table - if (_IO.CanSeek && seek_table != null) - header_size += write_seekpoints(header, header_size, last); - - // vorbis comments - if (m_settings.Padding == 0) last = 1; - header_size += write_vorbis_comment(header, header_size, header.Length - header_size, last); - - // padding - if (m_settings.Padding > 0) - { - last = 1; - header_size += write_padding(header, header_size, last, m_settings.Padding); - } - - return header_size; - } - - int flake_encode_init() - { - int i, header_len; - - //if(flake_validate_params(s) < 0) - - ch_code = channels - 1; - - // find samplerate in table - for (i = 1; i < 12; i++) - { - if (Settings.PCM.SampleRate == FlakeConstants.flac_samplerates[i]) - { - sr_code0 = i; - break; - } - } - - // if not in table, samplerate is non-standard - if (i == 12) - throw new Exception("non-standard samplerate"); - - for (i = 1; i < 8; i++) - { - if (Settings.PCM.BitsPerSample == FlakeConstants.flac_bitdepths[i]) - { - bps_code = i; - break; - } - } - if (i == 8) - throw new Exception("non-standard bps"); - - m_blockSize = m_settings.BlockSize != 0 ? m_settings.BlockSize : - select_blocksize(Settings.PCM.SampleRate, eparams.block_time_ms); - - // set maximum encoded frame size (if larger, re-encodes in verbatim mode) - if (channels == 2) - max_frame_size = 16 + ((m_blockSize * (Settings.PCM.BitsPerSample + Settings.PCM.BitsPerSample + 1) + 7) >> 3); - else - max_frame_size = 16 + ((m_blockSize * channels * Settings.PCM.BitsPerSample + 7) >> 3); - - if (_IO.CanSeek && eparams.do_seektable && sample_count > 0) - { - int seek_points_distance = Settings.PCM.SampleRate * 10; - int num_seek_points = 1 + sample_count / seek_points_distance; // 1 seek point per 10 seconds - if (sample_count % seek_points_distance == 0) - num_seek_points--; - seek_table = new SeekPoint[num_seek_points]; - for (int sp = 0; sp < num_seek_points; sp++) - { - seek_table[sp].framesize = 0; - seek_table[sp].offset = 0; - seek_table[sp].number = sp * seek_points_distance; - } - } - - // output header bytes - int tagsLen = 0; - Encoding enc = new UTF8Encoding(); - if (m_settings.Tags != null) - foreach (var t in m_settings.Tags) - tagsLen += 4 + enc.GetByteCount(t); - header = new byte[m_settings.Padding + 1024 + (seek_table == null ? 0 : seek_table.Length * 18) + tagsLen]; - header_len = write_headers(); - - // initialize CRC & MD5 - if (_IO.CanSeek && m_settings.DoMD5) - md5 = new MD5CryptoServiceProvider(); - - if (m_settings.DoVerify) - { - verify = new AudioDecoder(Settings.PCM); - verifyBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * channels]; - } - - frame_buffer = new byte[max_frame_size]; - - return header_len; - } - } - - struct FlakeEncodeParams - { - // prediction order selection method - // set by user prior to calling flake_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; - - // block time in milliseconds - // set by the user prior to calling flake_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; - - // whether to use variable block sizes - // set by user prior to calling flake_encode_init - // 0 = fixed block size - // 1 = variable block size - public int variable_block_size; - - public bool do_seektable; - - public int development_mode; - - public int flake_set_defaults(EncoderSettings settings) - { - order_method = OrderMethod.Akaike; - block_time_ms = 105; - variable_block_size = 0; - do_seektable = true; - development_mode = -1; - - if (settings.EncoderModeIndex == 11) - variable_block_size = 4; - - return 0; - } - } -} +/** + * CUETools.Flake: pure managed FLAC audio encoder + * Copyright (c) 2009 Grigory Chudov + * Based on Flake encoder, http://flake-enc.sourceforge.net/ + * Copyright (c) 2006-2009 Justin Ruggles + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define NOINTEROP +#define VARIANT1 + +using System; +using System.ComponentModel; +using System.Text; +using System.IO; +using System.Collections.Generic; +using System.Security.Cryptography; +#if INTEROP +using System.Runtime.InteropServices; +#endif +using CUETools.Codecs; +using Newtonsoft.Json; + +namespace CUETools.Codecs.Flake +{ + public class AudioEncoder : IAudioDest + { + Stream _IO = null; + string _path; + long _position; + + // number of audio channels + // set by user prior to calling flake_encode_init + // valid values are 1 to 8 + int channels, ch_code; + + // audio sample rate in Hz + // set by user prior to calling flake_encode_init + int sr_code0, sr_code1; + + // sample size in bits + // set by user prior to calling flake_encode_init + // only 16-bit is currently supported + int bps_code; + + // total stream samples + // set by user prior to calling flake_encode_init + // if 0, stream length is unknown + int sample_count = -1; + + FlakeEncodeParams eparams; + + // maximum frame size in bytes + // set by flake_encode_init + // this can be used to allocate memory for output + int max_frame_size; + + byte[] frame_buffer = null; + + int frame_count = 0; + + long first_frame_offset = 0; + +#if INTEROP + TimeSpan _userProcessorTime; +#endif + + // header bytes + // allocated by flake_encode_init and freed by flake_encode_close + byte[] header; + + int[] samplesBuffer; + int[] verifyBuffer; + int[] residualBuffer; + float[] windowBuffer; + double[] windowScale; + LpcWindowSection[, ,] windowSections; + + WindowFunction[] windowType; + int samplesInBuffer = 0; + + int m_blockSize = 0; + int _totalSize = 0; + int _windowsize = 0, _windowcount = 0; + + Crc8 crc8; + MD5 md5; + + FlacFrame frame; + AudioDecoder verify; + + SeekPoint[] seek_table; + int seek_table_offset = -1; + + bool inited = false; + + public AudioEncoder(EncoderSettings settings, string path, Stream IO = null) + { + m_settings = settings.Clone() as EncoderSettings; + m_settings.Validate(); + + //if (Settings.PCM.BitsPerSample != 16) + // throw new Exception("Bits per sample must be 16."); + //if (Settings.PCM.ChannelCount != 2) + // throw new Exception("ChannelCount must be 2."); + + channels = Settings.PCM.ChannelCount; + + // flake_validate_params + + _path = path; + _IO = IO; + + samplesBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * (channels == 2 ? 4 : channels)]; + residualBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * (channels == 2 ? 10 : channels + 1)]; + windowBuffer = new float[FlakeConstants.MAX_BLOCKSIZE * 2 * lpc.MAX_LPC_WINDOWS]; + windowScale = new double[lpc.MAX_LPC_WINDOWS]; + windowType = new WindowFunction[lpc.MAX_LPC_WINDOWS]; + windowSections = new LpcWindowSection[12, lpc.MAX_LPC_WINDOWS, lpc.MAX_LPC_SECTIONS]; + + eparams.flake_set_defaults(m_settings); + + crc8 = new Crc8(); + frame = new FlacFrame(channels * 2); + } + + public int TotalSize + { + get + { + return _totalSize; + } + } + + EncoderSettings m_settings; + + public IAudioEncoderSettings Settings => m_settings; + +#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 + + void DoClose() + { + if (inited) + { + while (samplesInBuffer > 0) + { + m_blockSize = samplesInBuffer; + output_frame(); + } + + if (_IO.CanSeek) + { + if (sample_count <= 0 && _position != 0) + { + BitWriter bitwriter = new BitWriter(header, 0, 4); + bitwriter.writebits(32, (int)_position); + bitwriter.flush(); + _IO.Position = 22; + _IO.Write(header, 0, 4); + } + + if (md5 != null) + { + md5.TransformFinalBlock(frame_buffer, 0, 0); + _IO.Position = 26; + _IO.Write(md5.Hash, 0, md5.Hash.Length); + } + + if (seek_table != null) + { + _IO.Position = seek_table_offset; + int len = write_seekpoints(header, 0, 0); + _IO.Write(header, 4, len - 4); + } + } + _IO.Close(); + inited = false; + } + +#if INTEROP + long fake, KernelStart, UserStart; + GetThreadTimes(GetCurrentThread(), out fake, out fake, out KernelStart, out UserStart); + _userProcessorTime = new TimeSpan(UserStart); +#endif + } + + public void Close() + { + DoClose(); + if (sample_count > 0 && _position != sample_count) + throw new Exception(Properties.Resources.ExceptionSampleCount); + } + + 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 int DevelopmentMode + { + get { return eparams.development_mode; } + set { eparams.development_mode = value; } + } + + public bool DoSeekTable + { + get { return eparams.do_seektable; } + set { eparams.do_seektable = value; } + } + + public int VBRMode + { + get { return eparams.variable_block_size; } + set { eparams.variable_block_size = value; } + } + + public TimeSpan UserProcessorTime + { + get + { +#if INTEROP + return _userProcessorTime; +#else + return new TimeSpan(0); +#endif + } + } + + unsafe int get_wasted_bits(int* signal, int samples) + { + int i, shift; + int x = 0; + + for (i = 0; i < samples && 0 == (x & 1); i++) + x |= signal[i]; + + if (x == 0) + { + shift = 0; + } + else + { + for (shift = 0; 0 == (x & 1); shift++) + x >>= 1; + } + + if (shift > 0) + { + for (i = 0; i < samples; i++) + signal[i] >>= shift; + } + + return shift; + } + + /// + /// Copy channel-interleaved input samples into separate subframes + /// + /// + /// + /// + unsafe void copy_samples(int[,] samples, int pos, int block) + { + fixed (int* fsamples = samplesBuffer, src = &samples[pos, 0]) + { + if (channels == 2) + { + if (m_settings.StereoMethod == StereoMethod.Independent) + AudioSamples.Deinterlace(fsamples + samplesInBuffer, fsamples + FlakeConstants.MAX_BLOCKSIZE + samplesInBuffer, src, block); + else + { + int* left = fsamples + samplesInBuffer; + int* right = left + FlakeConstants.MAX_BLOCKSIZE; + int* leftM = right + FlakeConstants.MAX_BLOCKSIZE; + int* rightM = leftM + FlakeConstants.MAX_BLOCKSIZE; + for (int i = 0; i < block; i++) + { + int l = src[2 * i]; + int r = src[2 * i + 1]; + left[i] = l; + right[i] = r; + leftM[i] = (l + r) >> 1; + rightM[i] = l - r; + } + } + } + else + for (int ch = 0; ch < channels; ch++) + { + int* psamples = fsamples + ch * FlakeConstants.MAX_BLOCKSIZE + samplesInBuffer; + 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) + //{ + // for (int i = 0; i < blocksize; i++) + // { + // leftM[i] = (leftS[i] + rightS[i]) >> 1; + // rightM[i] = leftS[i] - rightS[i]; + // } + //} + + unsafe void encode_residual_verbatim(int* res, int* smp, uint n) + { + AudioSamples.MemCpy(res, smp, (int) n); + } + + unsafe static ulong encode_residual_fixed_partition(int* res, int* smp, int* end, int order, int* last_errors) + { + ulong sum = 0UL; + switch (order) + { + case 0: + { + while (smp < end) + { + int error = *(res++) = *(smp++); + sum += (uint)((error << 1) ^ (error >> 31)); + } + break; + } + case 1: + { + int last_error_0 = last_errors[0]; + while (smp < end) + { + int error, save; + error = *(smp++); save = error; + error -= last_error_0; *(res++) = error; last_error_0 = save; + sum += (uint)((error << 1) ^ (error >> 31)); + } + last_errors[0] = last_error_0; + break; + } + case 2: + { + int last_error_0 = last_errors[0], last_error_1 = last_errors[1]; + while (smp < end) + { + int error, save; + error = *(smp++); save = error; + error -= last_error_0; last_error_0 = save; save = error; + error -= last_error_1; *(res++) = error; last_error_1 = save; + sum += (uint)((error << 1) ^ (error >> 31)); + } + last_errors[0] = last_error_0; last_errors[1] = last_error_1; ; + break; + } + case 3: + { + int last_error_0 = last_errors[0], last_error_1 = last_errors[1], last_error_2 = last_errors[2]; + while (smp < end) + { + int error, save; + error = *(smp++); save = error; + error -= last_error_0; last_error_0 = save; save = error; + error -= last_error_1; last_error_1 = save; save = error; + error -= last_error_2; *(res++) = error; last_error_2 = save; + sum += (uint)((error << 1) ^ (error >> 31)); + } + last_errors[0] = last_error_0; last_errors[1] = last_error_1; last_errors[2] = last_error_2; + break; + } + case 4: + { + int last_error_0 = last_errors[0], last_error_1 = last_errors[1], last_error_2 = last_errors[2], last_error_3 = last_errors[3]; + while (smp < end) + { + int error, save; + error = *(smp++); save = error; + error -= last_error_0; last_error_0 = save; save = error; + error -= last_error_1; last_error_1 = save; save = error; + error -= last_error_2; last_error_2 = save; save = error; + error -= last_error_3; *(res++) = error; last_error_3 = save; + sum += (uint)((error << 1) ^ (error >> 31)); + } + last_errors[0] = last_error_0; last_errors[1] = last_error_1; last_errors[2] = last_error_2; last_errors[3] = last_error_3; + break; + } + default: + throw new ArgumentOutOfRangeException(); + } + return sum; + } + + unsafe static void encode_residual_fixed(int* res, int* smp, int n, int order, ulong* sums, int pmax) + { + int* last_errors = stackalloc int[4]; + int* end = smp + n; + int* seg_end = smp + (n >> pmax); + + if (order > 4) + throw new ArgumentOutOfRangeException(); + + for (int i = 0; i < order; i++) + { + int* next_errors = stackalloc int[4]; + next_errors[0] = *(res++) = *(smp++); + for (int j = 0; j < i; j++) + next_errors[j + 1] = next_errors[j] - last_errors[j]; + for (int j = 0; j <= i; j++) + last_errors[j] = next_errors[j]; + } + + while (smp < end) + { + *(sums++) = encode_residual_fixed_partition(res, smp, seg_end, order, last_errors); + res += seg_end - smp; + smp = seg_end; + seg_end += n >> pmax; + } + } + +#if XXX + unsafe static int encode_residual_fixed_estimate_best_order(int* res, int* smp, int n, int order) + { + int next_error_0, next_error_1, next_error_2, next_error_3, next_error_4; + int last_error_0, last_error_1, last_error_2, last_error_3; + int* end = smp + n; + ulong total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; + + if (order == 0) + { + AudioSamples.MemCpy(res, smp, n); + return 0; + } + + next_error_0 = *(res++) = *(smp++); + last_error_0 = next_error_0; + + if (order == 1) + { + while (smp < end) + { + next_error_0 = *(smp++); + next_error_1 = next_error_0 - last_error_0; + + last_error_0 = next_error_0; + + total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31)); + total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31)); + + *(res++) = (int)next_error_1; + } + + if ((total_error_0 < total_error_1)) + return 0; + return 1; + } + + next_error_0 = *(res++) = *(smp++); + next_error_1 = next_error_0 - last_error_0; + last_error_0 = next_error_0; + last_error_1 = next_error_1; + + if (order == 2) + { + while (smp < end) + { + next_error_0 = *(smp++); + next_error_1 = next_error_0 - last_error_0; + next_error_2 = next_error_1 - last_error_1; + + last_error_0 = next_error_0; + last_error_1 = next_error_1; + + total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31)); + total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31)); + total_error_2 += (ulong)((next_error_2 << 1) ^ (next_error_2 >> 31)); + + *(res++) = (int)next_error_2; + } + + if ((total_error_0 < total_error_1) & (total_error_0 < total_error_2)) + return 0; + else if ((total_error_1 < total_error_2)) + return 1; + return 2; + } + + next_error_0 = *(res++) = *(smp++); + next_error_1 = next_error_0 - last_error_0; + next_error_2 = next_error_1 - last_error_1; + last_error_0 = next_error_0; + last_error_1 = next_error_1; + last_error_2 = next_error_2; + + if (order == 3) + { + while (smp < end) + { + next_error_0 = *(smp++); + next_error_1 = next_error_0 - last_error_0; + next_error_2 = next_error_1 - last_error_1; + next_error_3 = next_error_2 - last_error_2; + + last_error_0 = next_error_0; + last_error_1 = next_error_1; + last_error_2 = next_error_2; + + total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31)); + total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31)); + total_error_2 += (ulong)((next_error_2 << 1) ^ (next_error_2 >> 31)); + total_error_3 += (ulong)((next_error_3 << 1) ^ (next_error_3 >> 31)); + + *(res++) = (int)next_error_3; + } + + if ((total_error_0 < total_error_1) & (total_error_0 < total_error_2) & (total_error_0 < total_error_3)) + return 0; + else if ((total_error_1 < total_error_2) & (total_error_1 < total_error_3)) + return 1; + else if ((total_error_2 < total_error_3)) + return 2; + return 3; + } + + next_error_0 = *(res++) = *(smp++); + next_error_1 = next_error_0 - last_error_0; + next_error_2 = next_error_1 - last_error_1; + next_error_3 = next_error_2 - last_error_2; + last_error_0 = next_error_0; + last_error_1 = next_error_1; + last_error_2 = next_error_2; + last_error_3 = next_error_3; + + if (order == 4) + { + while (smp < end) + { + next_error_0 = *(smp++); + next_error_1 = next_error_0 - last_error_0; + next_error_2 = next_error_1 - last_error_1; + next_error_3 = next_error_2 - last_error_2; + next_error_4 = next_error_3 - last_error_3; + + last_error_0 = next_error_0; + last_error_1 = next_error_1; + last_error_2 = next_error_2; + last_error_3 = next_error_3; + + total_error_0 += (ulong)((next_error_0 << 1) ^ (next_error_0 >> 31)); + total_error_1 += (ulong)((next_error_1 << 1) ^ (next_error_1 >> 31)); + total_error_2 += (ulong)((next_error_2 << 1) ^ (next_error_2 >> 31)); + total_error_3 += (ulong)((next_error_3 << 1) ^ (next_error_3 >> 31)); + total_error_4 += (ulong)((next_error_4 << 1) ^ (next_error_4 >> 31)); + + *(res++) = (int)next_error_4; + } + + if ((total_error_0 < total_error_1) & (total_error_0 < total_error_2) & (total_error_0 < total_error_3) & (total_error_0 < total_error_4)) + return 0; + else if ((total_error_1 < total_error_2) & (total_error_1 < total_error_3) & (total_error_1 < total_error_4)) + return 1; + else if ((total_error_2 < total_error_3) & (total_error_2 < total_error_4)) + return 2; + else if (total_error_3 < total_error_4) + return 3; + return 4; + } + + throw new ArgumentOutOfRangeException(); + } +#endif + static unsafe uint calc_optimal_rice_params(int porder, int* parm, ulong* sums, uint n, uint pred_order, ref int method) + { + uint part = (1U << porder); + uint cnt = (n >> porder) - pred_order; + int maxK = method > 0 ? 30 : FlakeConstants.MAX_RICE_PARAM; + int k = cnt > 0 ? Math.Min(maxK, BitReader.log2i(sums[0] / cnt)) : 0; + int realMaxK0 = k; + ulong all_bits = cnt * ((uint)k + 1U) + (sums[0] >> k); + parm[0] = k; + cnt = (n >> porder); + int logcnt = BitReader.log2i(cnt); + if (cnt == 1 << logcnt) + { + for (uint i = 1; i < part; i++) + { + ulong s = sums[i]; + ulong u = s >> logcnt; + k = u >> maxK != 0 ? maxK : BitReader.log2i((uint)u); + realMaxK0 = Math.Max(realMaxK0, k); + all_bits += ((uint)k << logcnt) + (s >> k); + parm[i] = k; + } + } + else + { + for (uint i = 1; i < part; i++) + { + ulong s = sums[i]; + ulong u = s / cnt; + k = u >> maxK != 0 ? maxK : BitReader.log2i((uint)u); + realMaxK0 = Math.Max(realMaxK0, k); + all_bits += cnt * (uint)k + (s >> k); + parm[i] = k; + } + } + all_bits += cnt * (part - 1U); + method = realMaxK0 > FlakeConstants.MAX_RICE_PARAM ? 1 : 0; + return (uint)all_bits + ((4U + (uint)method) * part); + } + + static unsafe void calc_lower_sums(int pmin, int pmax, ulong* sums) + { + for (int i = pmax - 1; i >= pmin; i--) + { + for (int j = 0; j < (1 << i); j++) + { + sums[i * FlakeConstants.MAX_PARTITIONS + j] = + sums[(i + 1) * FlakeConstants.MAX_PARTITIONS + 2 * j] + + sums[(i + 1) * FlakeConstants.MAX_PARTITIONS + 2 * j + 1]; + } + } + } + + static unsafe uint calc_rice_params_sums(RiceContext rc, int pmin, int pmax, ulong* sums, uint n, uint pred_order, int bps) + { + int* parm = stackalloc int[(pmax + 1) * FlakeConstants.MAX_PARTITIONS]; + //uint* bits = stackalloc uint[FlakeConstants.MAX_PARTITION_ORDER]; + + //assert(pmin >= 0 && pmin <= FlakeConstants.MAX_PARTITION_ORDER); + //assert(pmax >= 0 && pmax <= FlakeConstants.MAX_PARTITION_ORDER); + //assert(pmin <= pmax); + + // sums for lower levels + calc_lower_sums(pmin, pmax, sums); + + uint opt_bits = AudioSamples.UINT32_MAX; + int opt_porder = pmin; + int opt_method = 0; + for (int i = pmin; i <= pmax; i++) + { + int method = bps > 16 ? 1 : 0; + uint bits = calc_optimal_rice_params(i, parm + i * FlakeConstants.MAX_PARTITIONS, sums + i * FlakeConstants.MAX_PARTITIONS, n, pred_order, ref method); + if (bits <= opt_bits) + { + opt_bits = bits; + opt_porder = i; + opt_method = method; + } + } + + rc.porder = opt_porder; + rc.coding_method = opt_method; + fixed (int* rparms = rc.rparams) + AudioSamples.MemCpy(rparms, parm + opt_porder * FlakeConstants.MAX_PARTITIONS, (1 << opt_porder)); + + return opt_bits; + } + + static int get_max_p_order(int max_porder, int n, int order) + { + int porder = Math.Min(max_porder, BitReader.log2i(n ^ (n - 1))); + if (order > 0) + porder = Math.Min(porder, BitReader.log2i(n / order)); + return porder; + } + +// private static int[,] best_x = new int[14,8193]; + private static int[][] good_x = new int[][] { +new int[] {}, // 0 +new int[] { // 1 +0x03,0x01,0x00,0x02 +}, +new int[] {// 2 +0x01,0x07,0x06,0x02, 0x03,0x04,0x00,0x05 +}, +new int[] { // 3 +0x0b,0x0f,0x0e,0x0d, 0x03,0x01,0x05,0x02 +}, +new int[] { //4 +0x17,0x09,0x03,0x0a, 0x06,0x1d,0x1f,0x05, 0x1c,0x0d,0x07,0x0c, +}, +new int[] { // 5 +0x2b,0x3d,0x37,0x07, 0x11,0x15,0x36,0x3f, +}, +new int[] { // 6 +0x6b,0x15,0x7e,0x31, 0x07,0x1a,0x29,0x26, 0x5d,0x23,0x6f,0x19, 0x56,0x75 +}, +new int[] { // 7 +0xdb,0xef,0xb5,0x47, 0xee,0x63,0x0b,0xfd, 0x31,0xbe,0xed,0x33, 0xff,0xfb,0xd6,0xbb +}, +new int[] { // 8 +0x1bb,0x1c7,0x069,0x087, 0x1fd,0x16e,0x095,0x1de, 0x066,0x071,0x055,0x09a, +}, +new int[] { // 9 +0x36b,0x3bd,0x097,0x0c3, 0x0e3,0x0b1,0x107,0x2de, 0x3ef,0x2fb,0x3d5,0x139 +}, +new int[] { // 10 +//0x0e3,0x199,0x383,0x307, 0x1e3,0x01f,0x269,0x0f1, 0x266,0x03f,0x2cd,0x1c3, 0x19a,0x387,0x339,0x259, +0x6eb,0x187,0x77d,0x271, 0x195,0x259,0x5ae,0x169, +}, +new int[] { // 11 +0xddb,0xf77,0xb6d,0x587, 0x2c3,0x03b,0xef5,0x1e3, 0xdbe, +}, +new int[] { // 12 +0x1aeb,0x0587,0x0a71,0x1dbd, 0x0559,0x0aa5,0x0a2e,0x0d43, 0x05aa,0x00f3,0x0696,0x03c6, +}, +new int[] { // 13 +0x35d7,0x2f6f,0x0aa3,0x1569, 0x150f,0x3d79,0x0dc3,0x309f/*?*/, +}, +new int[] { // 14 +0x75d7,0x5f7b,0x6a8f,0x29a3, +}, +new int[] { // 15 +0xddd7,0xaaaf,0x55c3,0xf77b, +}, +new int[] { // 16 +0x1baeb,0x1efaf,0x1d5bf,0x1cff3, +}, +new int[] { // 17 +0x36dd7,0x3bb7b,0x3df6f,0x2d547, +}, +new int[] { // 18 +0x75dd7,0x6f77b,0x7aaaf,0x5ddd3, +}, +new int[] { // 19 +0xdddd7,0xf777b,0xd5547,0xb6ddb, +}, +new int[] { // 20 +0x1baeeb,0x1efbaf,0x1aaabf,0x17bbeb, +}, +new int[] { // 21 +0x376dd7,0x3ddf7b,0x2d550f,0x0aaaa3, +}, +new int[] { // 22 +0x6eddd7,0x77777b,0x5dcd4f,0x5d76f9, +}, +new int[] { // 23 +0xdeddd7,0xb5b6eb,0x55552b,0x2aaac3, +}, +new int[] { // 24 +0x1dddbb7,0x1b76eeb,0x17bbf5f,0x1eeaa9f, +}, +new int[] { // 25 +}, +new int[] { // 26 +}, +new int[] { // 27 +}, +new int[] { // 28 +}, +new int[] { // 29 +}, +new int[] { // 30 +}, + }; + + unsafe void postprocess_coefs(FlacFrame frame, FlacSubframe sf, int ch) + { + if (eparams.development_mode < 0) + return; + if (sf.type != SubframeType.LPC || sf.order > 30) + return; + int orig_window = sf.window; + int orig_order = sf.order; + int orig_shift = sf.shift; + int orig_cbits = sf.cbits; + uint orig_size = sf.size; + var orig_coefs = stackalloc int[orig_order]; + for (int i = 0; i < orig_order; i++) orig_coefs[i] = sf.coefs[i]; + int orig_xx = -1; + int orig_seq = 0; + int maxxx = Math.Min(good_x[orig_order].Length, eparams.development_mode); + var pmax = get_max_p_order(m_settings.MaxPartitionOrder, frame.blocksize, orig_order); + var pmin = Math.Min(m_settings.MinPartitionOrder, pmax); + ulong* sums = stackalloc ulong[(pmax + 1) * FlakeConstants.MAX_PARTITIONS]; + + while (true) + { + var best_coefs = stackalloc int[orig_order]; + int best_shift = orig_shift; + int best_cbits = orig_cbits; + uint best_size = orig_size; + int best_xx = -1; + for (int xx = -1; xx < maxxx; xx++) + { + int x = xx; + if (xx < 0) + { + if (orig_xx < 0 || maxxx < 1/*3*/)// || (orig_xx >> orig_order) != 0) + continue; + x = orig_xx; + orig_seq++; + } + else + { + orig_seq = 0; + if (orig_order < good_x.Length && good_x[orig_order] != null) + x = good_x[orig_order][xx]; + } + + frame.current.type = SubframeType.LPC; + frame.current.order = orig_order; + frame.current.window = orig_window; + frame.current.shift = orig_shift; + frame.current.cbits = orig_cbits; + + if (((x >> orig_order) & 1) != 0) + { + frame.current.shift--; + frame.current.cbits--; + if (frame.current.shift < 0 || frame.current.cbits < 2) + continue; + } + + ulong csum = 0; + int qmax = (1 << (frame.current.cbits - 1)) - 1; + for (int i = 0; i < frame.current.order; i++) + { + int shift = (x >> orig_order) & 1; + int increment = (x == 1 << orig_order) ? 0 : (((x >> i) & 1) << 1) - 1; + frame.current.coefs[i] = (orig_coefs[i] + (increment << orig_seq)) >> shift; + if (frame.current.coefs[i] < -(qmax + 1)) frame.current.coefs[i] = -(qmax + 1); + if (frame.current.coefs[i] > qmax) frame.current.coefs[i] = qmax; + csum += (ulong)Math.Abs(frame.current.coefs[i]); + } + + fixed (int* coefs = frame.current.coefs) + { + if ((csum << frame.subframes[ch].obits) >= 1UL << 32) + lpc.encode_residual_long(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax); + else + lpc.encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax); + } + + var cur_size = calc_rice_params_sums(frame.current.rc, pmin, pmax, sums, (uint)frame.blocksize, (uint)frame.current.order, Settings.PCM.BitsPerSample); + frame.current.size = (uint)(frame.current.order * frame.subframes[ch].obits + 4 + 5 + frame.current.order * frame.current.cbits + 6 + (int)cur_size); + + if (frame.current.size < best_size) + { + //var dif = best_size - frame.current.size; + for (int i = 0; i < frame.current.order; i++) best_coefs[i] = frame.current.coefs[i]; + best_shift = frame.current.shift; + best_cbits = frame.current.cbits; + best_size = frame.current.size; + best_xx = x; + frame.ChooseBestSubframe(ch); + //if (dif > orig_order * 5) + // break; + } + + if (xx < 0 && best_size < orig_size) + break; + } + + if (best_size < orig_size) + { + //if (best_xx >= 0) best_x[order, best_xx]++; + //if (orig_size != 0x7FFFFFFF) + // System.Console.Write(string.Format(" {0}[{1:x}]", orig_size - best_size, best_xx)); + for (int i = 0; i < orig_order; i++) orig_coefs[i] = best_coefs[i]; + orig_shift = best_shift; + orig_cbits = best_cbits; + orig_size = best_size; + orig_xx = best_xx; + } + else + { + break; + } + } + + //if (orig_size != 0x7FFFFFFF) + // System.Console.WriteLine(); + + //if (frame_count % 0x400 == 0) + //{ + // for (int o = 0; o < best_x.GetLength(0); o++) + // { + // //for (int x = 0; x <= (1 << o); x++) + // // if (best_x[o, x] != 0) + // // System.Console.WriteLine(string.Format("{0:x2}\t{1:x4}\t{2}", o, x, best_x[o, x])); + // var s = new List>(); + // for (int x = 0; x < (1 << o); x++) + // if (best_x[o, x] != 0) + // s.Add(new KeyValuePair(x, best_x[o, x])); + // s.Sort((x, y) => y.Value.CompareTo(x.Value)); + // foreach (var x in s) + // System.Console.WriteLine(string.Format("{0:x2}\t{1:x4}\t{2}", o, x.Key, x.Value)); + // int i = 0; + // foreach (var x in s) + // { + // System.Console.Write(string.Format(o <= 8 ? "0x{0:x2}," : "0x{0:x3},", x.Key)); + // if ((++i) % 16 == 0) + // System.Console.WriteLine(); + // } + // System.Console.WriteLine(); + // } + //} + } + + public static void SetCoefs(int order, int[] coefs) + { + good_x[order] = new int[coefs.Length]; + for (int i = 0; i < coefs.Length; i++) + good_x[order][i] = coefs[i]; + } + + unsafe void encode_residual_lpc_sub(FlacFrame frame, float* lpcs, int iWindow, int order, int ch) + { + // select LPC precision based on block size + uint lpc_precision; + if (frame.blocksize <= 192) lpc_precision = 7U; + else if (frame.blocksize <= 384) lpc_precision = 8U; + else if (frame.blocksize <= 576) lpc_precision = 9U; + else if (frame.blocksize <= 1152) lpc_precision = 10U; + else if (frame.blocksize <= 2304) lpc_precision = 11U; + else if (frame.blocksize <= 4608) lpc_precision = 12U; + else if (frame.blocksize <= 8192) lpc_precision = 13U; + else if (frame.blocksize <= 16384) lpc_precision = 14U; + else lpc_precision = 15; + + for (int i_precision = m_settings.MinPrecisionSearch; i_precision <= m_settings.MaxPrecisionSearch && lpc_precision + i_precision < 16; i_precision++) + // check if we already calculated with this order, window and precision + if ((frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[i_precision] & (1U << (order - 1))) == 0) + { + frame.subframes[ch].lpc_ctx[iWindow].done_lpcs[i_precision] |= (1U << (order - 1)); + + uint cbits = lpc_precision + (uint)i_precision; + + frame.current.type = SubframeType.LPC; + frame.current.order = order; + frame.current.window = iWindow; + frame.current.cbits = (int)cbits; + + int pmax = get_max_p_order(m_settings.MaxPartitionOrder, frame.blocksize, frame.current.order); + int pmin = Math.Min(m_settings.MinPartitionOrder, pmax); + ulong* sums = stackalloc ulong[(pmax + 1) * FlakeConstants.MAX_PARTITIONS]; + ulong csum = 0; + fixed (int* coefs = frame.current.coefs) + { + lpc.quantize_lpc_coefs(lpcs + (frame.current.order - 1) * lpc.MAX_LPC_ORDER, + frame.current.order, cbits, coefs, out frame.current.shift, 15, 0); + + if (frame.current.shift < 0 || frame.current.shift > 15) + throw new Exception("negative shift"); + + for (int i = frame.current.order; i > 0; i--) + csum += (ulong)Math.Abs(coefs[i - 1]); + + if ((csum << frame.subframes[ch].obits) >= 1UL << 32) + lpc.encode_residual_long(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax); + else + lpc.encode_residual(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, coefs, frame.current.shift, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax); + + } + uint best_size = calc_rice_params_sums(frame.current.rc, pmin, pmax, sums, (uint)frame.blocksize, (uint)frame.current.order, Settings.PCM.BitsPerSample); + frame.current.size = (uint)(frame.current.order * frame.subframes[ch].obits + 4 + 5 + frame.current.order * (int)cbits + 6 + (int)best_size); + frame.ChooseBestSubframe(ch); + //if (frame.current.size >= frame.subframes[ch].best.size) + // postprocess_coefs(frame, frame.current, ch); + //else + //{ + // frame.ChooseBestSubframe(ch); + // postprocess_coefs(frame, frame.subframes[ch].best, ch); + //} + } + } + + unsafe void encode_residual_fixed_sub(FlacFrame frame, int order, int ch) + { + if ((frame.subframes[ch].done_fixed & (1U << order)) != 0) + return; // already calculated; + + frame.current.order = order; + frame.current.type = SubframeType.Fixed; + +#if XXX + int best_order = order; + if (frame.subframes[ch].done_fixed == 0) + { + best_order = encode_residual_fixed_estimate_best_order(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order); + if (best_order != order) + { + //frame.subframes[ch].done_fixed |= (1U << order); + order = best_order; + frame.current.order = order; + encode_residual_fixed(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order); + } + } + else +#endif + int pmax = get_max_p_order(m_settings.MaxPartitionOrder, frame.blocksize, frame.current.order); + int pmin = Math.Min(m_settings.MinPartitionOrder, pmax); + ulong* sums = stackalloc ulong[(pmax + 1) * FlakeConstants.MAX_PARTITIONS]; + encode_residual_fixed(frame.current.residual, frame.subframes[ch].samples, frame.blocksize, frame.current.order, sums + pmax * FlakeConstants.MAX_PARTITIONS, pmax); + + frame.current.size = (uint)(frame.current.order * frame.subframes[ch].obits) + 6 + + calc_rice_params_sums(frame.current.rc, pmin, pmax, sums, (uint)frame.blocksize, (uint)frame.current.order, Settings.PCM.BitsPerSample); + + frame.subframes[ch].done_fixed |= (1U << order); + + frame.ChooseBestSubframe(ch); + } + + unsafe void fixed_compute_best_predictor(int* data, uint data_len, ulong* errors)//, float* residual_bits_per_sample) + { + long last_error_0 = data[-1]; + long last_error_1 = data[-1] - data[-2]; + long last_error_2 = last_error_1 - (data[-2] - data[-3]); + long last_error_3 = last_error_2 - (data[-2] - 2 * data[-3] + data[-4]); + ulong total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; + +#if VARIANT1 + long error, save; + int* finish = data + data_len; + while (data < finish) + { + error = *(data++); total_error_0 += (ulong)((error << 1) ^ (error >> 63)); save = error; + error -= last_error_0; total_error_1 += (ulong)((error << 1) ^ (error >> 63)); last_error_0 = save; save = error; + error -= last_error_1; total_error_2 += (ulong)((error << 1) ^ (error >> 63)); last_error_1 = save; save = error; + error -= last_error_2; total_error_3 += (ulong)((error << 1) ^ (error >> 63)); last_error_2 = save; save = error; + error -= last_error_3; total_error_4 += (ulong)((error << 1) ^ (error >> 63)); last_error_3 = save; + } +#else + int* finish = data + data_len; + while (data < finish) + { + long next_error_0 = *(data++); + long next_error_1 = next_error_0 - last_error_0; + long next_error_2 = next_error_1 - last_error_1; + long next_error_3 = next_error_2 - last_error_2; + long next_error_4 = next_error_3 - last_error_3; + + last_error_0 = next_error_0; + last_error_1 = next_error_1; + last_error_2 = next_error_2; + last_error_3 = next_error_3; + + total_error_0 += (ulong)((last_error_0 << 1) ^ (last_error_0 >> 63)); + total_error_1 += (ulong)((last_error_1 << 1) ^ (last_error_1 >> 63)); + total_error_2 += (ulong)((last_error_2 << 1) ^ (last_error_2 >> 63)); + total_error_3 += (ulong)((last_error_3 << 1) ^ (last_error_3 >> 63)); + total_error_4 += (ulong)((next_error_4 << 1) ^ (next_error_4 >> 63)); + } +#endif + + errors[0] = total_error_0; + errors[1] = total_error_1; + errors[2] = total_error_2; + errors[3] = total_error_3; + errors[4] = total_error_4; + + //residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); + //residual_bits_per_sample[1] = (float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); + //residual_bits_per_sample[2] = (float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); + //residual_bits_per_sample[3] = (float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); + //residual_bits_per_sample[4] = (float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); + } + + unsafe int fixed_compute_best_predictor_order(ulong* error) + { + int order; + if ((error[0] < error[1]) & (error[0] < error[2]) & (error[0] < error[3]) & (error[0] < error[4])) + order = 0; + else if ((error[1] < error[2]) & (error[1] < error[3]) & (error[1] < error[4])) + order = 1; + else if ((error[2] < error[3]) & (error[2] < error[4])) + order = 2; + else if (error[3] < error[4]) + order = 3; + else + order = 4; + return order; + } + + unsafe void encode_residual(FlacFrame frame, int ch, PredictionType predict, OrderMethod omethod, int pass, int windows_mask) + { + int* smp = frame.subframes[ch].samples; + int i, n = frame.blocksize; + // save best.window, because we can overwrite it later with fixed frame + + // CONSTANT + for (i = 1; i < n; i++) + { + if (smp[i] != smp[0]) break; + } + if (i == n) + { + frame.subframes[ch].best.type = SubframeType.Constant; + frame.subframes[ch].best.residual[0] = smp[0]; + frame.subframes[ch].best.size = (uint)frame.subframes[ch].obits; + return; + } + + // VERBATIM + frame.current.type = SubframeType.Verbatim; + frame.current.size = (uint)(frame.subframes[ch].obits * frame.blocksize); + frame.ChooseBestSubframe(ch); + + if (n < 5 || predict == PredictionType.None) + return; + + // LPC + if (n > m_settings.MaxLPCOrder && + (predict == PredictionType.Levinson || + predict == PredictionType.Search) + //predict == PredictionType.Search || + //(pass == 2 && frame.subframes[ch].best.type == SubframeType.LPC)) + ) + { + float* lpcs = stackalloc float[lpc.MAX_LPC_ORDER * lpc.MAX_LPC_ORDER]; + int min_order = m_settings.MinLPCOrder; + int max_order = m_settings.MaxLPCOrder; + + for (int iWindow = 0; iWindow < _windowcount; iWindow++) + { + if ((windows_mask & (1 << iWindow)) == 0) + continue; + + LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow]; + fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, iWindow, 0]) + lpc_ctx.GetReflection( + frame.subframes[ch].sf, max_order, frame.blocksize, + smp, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2, sections); + lpc_ctx.ComputeLPC(lpcs); + + //int frameSize = n; + //float* F = stackalloc float[frameSize]; + //float* B = stackalloc float[frameSize]; + //float* PE = stackalloc float[max_order + 1]; + //float* arp = stackalloc float[max_order]; + //float* rc = stackalloc float[max_order]; + + //for (int j = 0; j < frameSize; j++) + // F[j] = B[j] = smp[j]; + + //for (int K = 1; K <= max_order; K++) + //{ + // // BURG: + // float denominator = 0.0f; + // //float denominator = F[K - 1] * F[K - 1] + B[frameSize - K] * B[frameSize - K]; + // for (int j = 0; j < frameSize - K; j++) + // denominator += F[j + K] * F[j + K] + B[j] * B[j]; + // denominator /= 2; + + // // Estimate error + // PE[K - 1] = denominator / (frameSize - K); + + // float reflectionCoeff = 0.0f; + // for (int j = 0; j < frameSize - K; j++) + // reflectionCoeff += F[j + K] * B[j]; + // reflectionCoeff /= denominator; + // rc[K - 1] = arp[K - 1] = reflectionCoeff; + + // // Levinson-Durbin + // for (int j = 0; j < (K - 1) >> 1; j++) + // { + // float arptmp = arp[j]; + // arp[j] -= reflectionCoeff * arp[K - 2 - j]; + // arp[K - 2 - j] -= reflectionCoeff * arptmp; + // } + // if (((K - 1) & 1) != 0) + // arp[(K - 1) >> 1] -= reflectionCoeff * arp[(K - 1) >> 1]; + + // for (int j = 0; j < frameSize - K; j++) + // { + // float f = F[j + K]; + // float b = B[j]; + // F[j + K] = f - reflectionCoeff * b; + // B[j] = b - reflectionCoeff * f; + // } + + // for (int j = 0; j < K; j++) + // lpcs[(K - 1) * lpc.MAX_LPC_ORDER + j] = (float)arp[j]; + //} + + switch (omethod) + { + case OrderMethod.Akaike: + //lpc_ctx.SortOrdersAkaike(frame.blocksize, m_settings.EstimationDepth, max_order, 7.1, 0.0); + lpc_ctx.SortOrdersAkaike(frame.blocksize, m_settings.EstimationDepth, min_order, max_order, 4.5, 0); + break; + default: + throw new Exception("unknown order method"); + } + + for (i = 0; i < m_settings.EstimationDepth && i < max_order; i++) + encode_residual_lpc_sub(frame, lpcs, iWindow, lpc_ctx.best_orders[i], ch); + } + + postprocess_coefs(frame, frame.subframes[ch].best, ch); + } + + // FIXED + if (predict == PredictionType.Fixed || + (predict == PredictionType.Search && pass != 1) || + //predict == PredictionType.Search || + //(pass == 2 && frame.subframes[ch].best.type == SubframeType.Fixed) || + (n > m_settings.MaxFixedOrder && n <= m_settings.MaxLPCOrder)) + { + int max_fixed_order = Math.Min(m_settings.MaxFixedOrder, 4); + int min_fixed_order = Math.Min(m_settings.MinFixedOrder, max_fixed_order); + + if (min_fixed_order == 0 && max_fixed_order == 4) + { + fixed (ulong* fixed_errors = frame.subframes[ch].best_fixed) + { + if ((frame.subframes[ch].done_fixed & (1U << 5)) == 0) + { + fixed_compute_best_predictor(smp + 4, (uint)n - 4, fixed_errors); + frame.subframes[ch].done_fixed |= (1U << 5); + } + i = fixed_compute_best_predictor_order(fixed_errors); + encode_residual_fixed_sub(frame, i, ch); + } + } + else + { + for (i = max_fixed_order; i >= min_fixed_order; i--) + encode_residual_fixed_sub(frame, i, ch); + } + } + + } + + unsafe void output_frame_header(FlacFrame frame, BitWriter bitwriter) + { + bitwriter.writebits(15, 0x7FFC); + bitwriter.writebits(1, eparams.variable_block_size > 0 ? 1 : 0); + bitwriter.writebits(4, frame.bs_code0); + bitwriter.writebits(4, sr_code0); + if (frame.ch_mode == ChannelMode.NotStereo) + bitwriter.writebits(4, ch_code); + else + bitwriter.writebits(4, (int) frame.ch_mode); + bitwriter.writebits(3, bps_code); + bitwriter.writebits(1, 0); + bitwriter.write_utf8(frame_count); + + // custom block size + if (frame.bs_code1 >= 0) + { + if (frame.bs_code1 < 256) + bitwriter.writebits(8, frame.bs_code1); + else + bitwriter.writebits(16, frame.bs_code1); + } + + // custom sample rate + if (sr_code1 > 0) + { + if (sr_code1 < 256) + bitwriter.writebits(8, sr_code1); + else + bitwriter.writebits(16, sr_code1); + } + + // CRC-8 of frame header + bitwriter.flush(); + byte crc = crc8.ComputeChecksum(frame_buffer, 0, bitwriter.Length); + bitwriter.writebits(8, crc); + } + + unsafe void output_residual(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub) + { + // rice-encoded block + bitwriter.writebits(2, sub.best.rc.coding_method); + + // partition order + int porder = sub.best.rc.porder; + int psize = frame.blocksize >> porder; + //assert(porder >= 0); + bitwriter.writebits(4, porder); + int res_cnt = psize - sub.best.order; + + int rice_len = 4 + sub.best.rc.coding_method; + // residual + int j = sub.best.order; + fixed (byte* fixbuf = &frame_buffer[0]) + for (int p = 0; p < (1 << porder); p++) + { + int k = sub.best.rc.rparams[p]; + bitwriter.writebits(rice_len, k); + if (p == 1) res_cnt = psize; + int cnt = Math.Min(res_cnt, frame.blocksize - j); + bitwriter.write_rice_block_signed(fixbuf, k, sub.best.residual + j, cnt); + j += cnt; + } + } + + unsafe void + output_subframe_constant(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub) + { + bitwriter.writebits_signed(sub.obits, sub.best.residual[0]); + } + + unsafe void + output_subframe_verbatim(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub) + { + int n = frame.blocksize; + for (int i = 0; i < n; i++) + bitwriter.writebits_signed(sub.obits, sub.samples[i]); + // Don't use residual here, because we don't copy samples to residual for verbatim frames. + } + + unsafe void + output_subframe_fixed(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub) + { + // warm-up samples + for (int i = 0; i < sub.best.order; i++) + bitwriter.writebits_signed(sub.obits, sub.best.residual[i]); + + // residual + output_residual(frame, bitwriter, sub); + } + + unsafe void + output_subframe_lpc(FlacFrame frame, BitWriter bitwriter, FlacSubframeInfo sub) + { + // warm-up samples + for (int i = 0; i < sub.best.order; i++) + bitwriter.writebits_signed(sub.obits, sub.best.residual[i]); + + // LPC coefficients + int cbits = 1; + for (int i = 0; i < sub.best.order; i++) + while (cbits < 16 && sub.best.coefs[i] != (sub.best.coefs[i] << (32 - cbits)) >> (32 - cbits)) + cbits++; + bitwriter.writebits(4, cbits - 1); + bitwriter.writebits_signed(5, sub.best.shift); + for (int i = 0; i < sub.best.order; i++) + bitwriter.writebits_signed(cbits, sub.best.coefs[i]); + + // residual + output_residual(frame, bitwriter, sub); + } + + unsafe void output_subframes(FlacFrame frame, BitWriter bitwriter) + { + for (int ch = 0; ch < channels; ch++) + { + FlacSubframeInfo sub = frame.subframes[ch]; + // subframe header + int type_code = (int) sub.best.type; + if (sub.best.type == SubframeType.Fixed) + type_code |= sub.best.order; + if (sub.best.type == SubframeType.LPC) + type_code |= sub.best.order - 1; + bitwriter.writebits(1, 0); + bitwriter.writebits(6, type_code); + bitwriter.writebits(1, sub.wbits != 0 ? 1 : 0); + if (sub.wbits > 0) + bitwriter.writebits((int)sub.wbits, 1); + + // subframe + switch (sub.best.type) + { + case SubframeType.Constant: + output_subframe_constant(frame, bitwriter, sub); + break; + case SubframeType.Verbatim: + output_subframe_verbatim(frame, bitwriter, sub); + break; + case SubframeType.Fixed: + output_subframe_fixed(frame, bitwriter, sub); + break; + case SubframeType.LPC: + output_subframe_lpc(frame, bitwriter, sub); + break; + } + } + } + + void output_frame_footer(BitWriter bitwriter) + { + bitwriter.flush(); + ushort crc = bitwriter.get_crc16(); + bitwriter.writebits(16, crc); + bitwriter.flush(); + } + + unsafe void encode_residual_pass1(FlacFrame frame, int ch, int windows_mask) + { + int max_prediction_order = m_settings.MaxLPCOrder; + //int max_fixed_order = m_settings.MaxFixedOrder; + //int min_fixed_order = m_settings.MinFixedOrder; + int lpc_min_precision_search = m_settings.MinPrecisionSearch; + int lpc_max_precision_search = m_settings.MaxPrecisionSearch; + int max_partition_order = m_settings.MaxPartitionOrder; + int estimation_depth = m_settings.EstimationDepth; + var development_mode = eparams.development_mode; + //m_settings.MinFixedOrder = 2; + //m_settings.MaxFixedOrder = 2; + m_settings.MinPrecisionSearch = m_settings.MaxPrecisionSearch; + m_settings.MaxLPCOrder = Math.Min(m_settings.MaxLPCOrder, Math.Max(m_settings.MinLPCOrder, 8)); + m_settings.EstimationDepth = 1; + eparams.development_mode = Math.Min(eparams.development_mode, -1); + encode_residual(frame, ch, m_settings.PredictionType, OrderMethod.Akaike, 1, windows_mask); + //m_settings.MinFixedOrder = min_fixed_order; + //m_settings.MaxFixedOrder = max_fixed_order; + m_settings.MaxLPCOrder = max_prediction_order; + m_settings.MinPrecisionSearch = lpc_min_precision_search; + m_settings.MaxPrecisionSearch = lpc_max_precision_search; + m_settings.MaxPartitionOrder = max_partition_order; + m_settings.EstimationDepth = estimation_depth; + eparams.development_mode = development_mode; + } + + unsafe void encode_residual_pass2(FlacFrame frame, int ch) + { + encode_residual(frame, ch, m_settings.PredictionType, eparams.order_method, 2, estimate_best_windows(frame, ch)); + } + + unsafe int estimate_best_windows_akaike(FlacFrame 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]++; + } + + float* err = stackalloc float[lpc.MAX_LPC_ORDER]; + for (int i = 0; i < _windowcount; i++) + { + LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[i]; + if (onePerType && windows_present[i] <= count) + { + err[i] = 0; + continue; + } + int estimate_order = 4; + fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, i, 0]) + lpc_ctx.GetReflection( + frame.subframes[ch].sf, estimate_order, frame.blocksize, + frame.subframes[ch].samples, frame.window_buffer + i * FlakeConstants.MAX_BLOCKSIZE * 2, sections); + lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, estimate_order, 4.5, 0.0); + //err[i] = (float)(lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0)); + //err[i] = (float)((frame.blocksize * lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1] / windowScale[i]) + lpc_ctx.best_orders[0] * 4.5); + //err[i] = (float)((frame.blocksize * lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1] / windowScale[i]) + lpc_ctx.best_orders[0] * frame.subframes[ch].obits); + + // realistic + //err[i] = (float)(frame.blocksize * Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1]) / Math.Log(2) / 2.5 + //- windowScale[i] / 2 + lpc_ctx.best_orders[0] * frame.subframes[ch].obits / 2); + + //err[i] = (float)(frame.blocksize * Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1]) / Math.Log(2) / 2.5 + //- frame.blocksize * Math.Log(lpc_ctx.autocorr_values[0]) / 2.1 + //+ Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5 / 2.5 / Math.Log(2)); + + // Akaike + //err[i] = (float)(frame.blocksize * (Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1])) + Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5); + + //err[i] = (float)(lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0) - frame.blocksize * (frame.subframes[ch].obits + Math.Log(windowScale[i] / frame.blocksize) / 2)); + + // tested good + 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; + } + + unsafe int estimate_best_windows(FlacFrame frame, int ch) + { + if (_windowcount == 1 || m_settings.PredictionType == PredictionType.Fixed) + return 1; + switch (m_settings.WindowMethod) + { + case WindowMethod.Estimate: + return estimate_best_windows_akaike(frame, ch, 1, false); + case WindowMethod.Estimate2: + return estimate_best_windows_akaike(frame, ch, 2, false); + case WindowMethod.Estimate3: + return estimate_best_windows_akaike(frame, ch, 3, false); + case WindowMethod.EstimateN: + return estimate_best_windows_akaike(frame, ch, 1, true); + case WindowMethod.Evaluate2: + encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 2, false)); + return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; + case WindowMethod.Evaluate3: + encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 3, false)); + return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; + case WindowMethod.EvaluateN: + encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 1, true)); +#if XXX + if (frame.subframes[ch].best.type == SubframeType.LPC && frame.subframes[ch].best.order <= 4) + { + LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[frame.subframes[ch].best.window]; + double err = lpc_ctx.prediction_error[frame.subframes[ch].best.order - 1] / lpc_ctx.autocorr_values[0]; + double est = frame.blocksize * (frame.subframes[ch].obits * (1 - err)); + double est1 = frame.blocksize * (frame.subframes[ch].obits * (err)); + if (est < 0 || est1 < 0) return -1; + } +#endif + return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; + case WindowMethod.Evaluate2N: + encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 2, true)); + return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; + case WindowMethod.Evaluate3N: + encode_residual_pass1(frame, ch, estimate_best_windows_akaike(frame, ch, 3, true)); + return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; + case WindowMethod.Evaluate: + encode_residual_pass1(frame, ch, -1); + return frame.subframes[ch].best.type == SubframeType.LPC ? 1 << frame.subframes[ch].best.window : 0; + case WindowMethod.Search: + return -1; + } + return -1; + } + + unsafe void estimate_frame(FlacFrame frame, bool do_midside) + { + int subframes = do_midside ? channels * 2 : channels; + + switch (m_settings.StereoMethod) + { + case StereoMethod.Estimate: + for (int ch = 0; ch < subframes; ch++) + { + LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[0]; + int estimate_order = 4; + int iWindow = 0; + fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, iWindow, 0]) + lpc_ctx.GetReflection( + frame.subframes[ch].sf, estimate_order, frame.blocksize, + frame.subframes[ch].samples, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2, sections); + lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, estimate_order, 4.5, 0.0); + frame.subframes[ch].best.size + = (uint)Math.Max(0, frame.blocksize * (Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1])) + Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5 + //= (uint)Math.Max(0, lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0) + //* 2.0 / Math.Log(windowScale[0] / frame.blocksize) + + 7.1 * frame.subframes[ch].obits * m_settings.MaxLPCOrder); + } + break; + case StereoMethod.EstimateFixed: + for (int ch = 0; ch < subframes; ch++) + { + fixed (ulong* fixed_errors = frame.subframes[ch].best_fixed) + { + if ((frame.subframes[ch].done_fixed & (1U << 5)) == 0) + { + fixed_compute_best_predictor(frame.subframes[ch].samples + 4, (uint)frame.blocksize - 4, fixed_errors); + frame.subframes[ch].done_fixed |= (1U << 5); + } + int best_order = fixed_compute_best_predictor_order(fixed_errors); + //residual_bits_per_sample[0] = (float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); + frame.subframes[ch].best.size = (uint)fixed_errors[best_order]; + } + } + break; + case StereoMethod.EstimateX: + for (int ch = 0; ch < subframes; ch++) + { + for (int iWindow = 0; iWindow < _windowcount; iWindow++) + { + LpcContext lpc_ctx = frame.subframes[ch].lpc_ctx[iWindow]; + int estimate_order = 4; + fixed (LpcWindowSection* sections = &windowSections[frame.nSeg, iWindow, 0]) + lpc_ctx.GetReflection( + frame.subframes[ch].sf, estimate_order, frame.blocksize, + frame.subframes[ch].samples, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2, sections); + lpc_ctx.SortOrdersAkaike(frame.blocksize, 1, 1, estimate_order, 4.5, 0.0); + uint estimate + = (uint)Math.Max(0, frame.blocksize * (Math.Log(lpc_ctx.prediction_error[lpc_ctx.best_orders[0] - 1])) + Math.Log(frame.blocksize) * lpc_ctx.best_orders[0] * 4.5 + //= (uint)Math.Max(0, lpc_ctx.Akaike(frame.blocksize, lpc_ctx.best_orders[0], 4.5, 0.0) + //* 2.0 / Math.Log(windowScale[0] / frame.blocksize) + + 7.1 * frame.subframes[ch].obits * m_settings.MaxLPCOrder); + if (iWindow == 0 || frame.subframes[ch].best.size > estimate) + frame.subframes[ch].best.size = estimate; + } + } + break; + case StereoMethod.Evaluate: + for (int ch = 0; ch < subframes; ch++) + encode_residual_pass1(frame, ch, 1); + break; + case StereoMethod.EvaluateX: + for (int ch = 0; ch < subframes; ch++) + encode_residual_pass1(frame, ch, + estimate_best_windows_akaike(frame, ch, 1, false)); + break; + case StereoMethod.Search: + for (int ch = 0; ch < subframes; ch++) + encode_residual_pass2(frame, ch); + break; + } + } + + unsafe uint measure_frame_size(FlacFrame frame, bool do_midside) + { + // crude estimation of header/footer size + uint total = (uint)(32 + ((BitReader.log2i(frame_count) + 4) / 5) * 8 + (eparams.variable_block_size != 0 ? 16 : 0) + 16); + + if (do_midside) + { + uint bitsBest = AudioSamples.UINT32_MAX; + ChannelMode modeBest = ChannelMode.LeftRight; + + if (bitsBest > frame.subframes[2].best.size + frame.subframes[3].best.size) + { + bitsBest = frame.subframes[2].best.size + frame.subframes[3].best.size; + modeBest = ChannelMode.MidSide; + } + if (bitsBest > frame.subframes[3].best.size + frame.subframes[1].best.size) + { + bitsBest = frame.subframes[3].best.size + frame.subframes[1].best.size; + modeBest = ChannelMode.RightSide; + } + if (bitsBest > frame.subframes[3].best.size + frame.subframes[0].best.size) + { + bitsBest = frame.subframes[3].best.size + frame.subframes[0].best.size; + modeBest = ChannelMode.LeftSide; + } + if (bitsBest > frame.subframes[0].best.size + frame.subframes[1].best.size) + { + bitsBest = frame.subframes[0].best.size + frame.subframes[1].best.size; + modeBest = ChannelMode.LeftRight; + } + frame.ch_mode = modeBest; + return total + bitsBest; + } + + for (int ch = 0; ch < channels; ch++) + total += frame.subframes[ch].best.size; + return total; + } + + unsafe void encode_estimated_frame(FlacFrame frame) + { + switch (m_settings.StereoMethod) + { + case StereoMethod.Estimate: + case StereoMethod.EstimateX: + case StereoMethod.EstimateFixed: + for (int ch = 0; ch < channels; ch++) + { + frame.subframes[ch].best.size = AudioSamples.UINT32_MAX; + encode_residual_pass2(frame, ch); + } + break; + case StereoMethod.Evaluate: + case StereoMethod.EvaluateX: + for (int ch = 0; ch < channels; ch++) + encode_residual_pass2(frame, ch); + break; + case StereoMethod.Search: + break; + } + } + + unsafe delegate void window_function(float* window, int size); + + unsafe void calculate_window(float* window, window_function func, WindowFunction flag) + { + if ((m_settings.WindowFunctions & flag) == 0 || _windowcount == lpc.MAX_LPC_WINDOWS) + return; + int sz = _windowsize; + float* pos1 = window + _windowcount * FlakeConstants.MAX_BLOCKSIZE * 2; + float* pos = pos1; + int nSeg = 0; + do + { + windowSections[nSeg, _windowcount, 0].setData(0, sz); + for (int j = 1; j < lpc.MAX_LPC_SECTIONS; j++) + windowSections[nSeg, _windowcount, j].setZero(sz, sz); + + fixed (LpcWindowSection* sections = &windowSections[nSeg, _windowcount, 0]) + func(pos, sz); + if ((sz & 1) != 0) + break; + nSeg++; + pos += sz; + sz >>= 1; + } while (sz >= 32); + double scale = 0.0; + for (int i = 0; i < _windowsize; i++) + scale += pos1[i] * pos1[i]; + windowScale[_windowcount] = scale; + windowType[_windowcount] = flag; + _windowcount++; + } + + class PunchoutTukeyVariant + { + public PunchoutTukeyVariant( + WindowFunction _type, + int _parts, double _overlap, double _p) + { + parts = _parts; + type = _type; + overlap = _overlap; + p = _p; + } + public WindowFunction type; + public int parts; + public double overlap; + public double p; + }; + + unsafe int encode_frame(out int size) + { + fixed (int* s = samplesBuffer, r = residualBuffer) + fixed (float* window = windowBuffer) + { + frame.InitSize(m_blockSize, eparams.variable_block_size != 0); + + if (frame.blocksize != _windowsize && frame.blocksize > 4 && m_settings.PredictionType != PredictionType.Fixed) + { + _windowsize = frame.blocksize; + _windowcount = 0; + calculate_window(window, lpc.window_welch, WindowFunction.Welch); + calculate_window(window, lpc.window_flattop, WindowFunction.Flattop); + calculate_window(window, lpc.window_hann, WindowFunction.Hann); + calculate_window(window, lpc.window_bartlett, WindowFunction.Bartlett); + var tukeys = new PunchoutTukeyVariant[] + { + new PunchoutTukeyVariant(WindowFunction.Tukey4, 4, 0, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey4A, 4, 0, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey4B, 4, 0, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey4X, 4, m_settings.TukeyOverlap, m_settings.TukeyP), + new PunchoutTukeyVariant(WindowFunction.Tukey3, 3, 1.0/3, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey3A, 3, 1.0/3, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey3B, 3, 1.0/3, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey3X, 3, m_settings.TukeyOverlap, m_settings.TukeyP), + new PunchoutTukeyVariant(WindowFunction.Tukey2, 2, 0.25, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey2A, 2, 0.25, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey2B, 2, 0.25, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey2X, 2, m_settings.TukeyOverlap, m_settings.TukeyP), + new PunchoutTukeyVariant(WindowFunction.Tukey, 1, 0.0, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey1A, 1, 0.0, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey1B, 1, 0.0, 0.03), + new PunchoutTukeyVariant(WindowFunction.Tukey1X, 1, m_settings.TukeyOverlap, m_settings.TukeyP), + }; + + foreach (var tukey in tukeys) + { + if (tukey.parts == 0 || (m_settings.WindowFunctions & tukey.type) == 0) continue; + if (tukey.parts == 1) + { + calculate_window(window, (w, wsz) => + { + lpc.window_tukey(w, wsz, tukey.p); + }, tukey.type); + continue; + } + double overlap = tukey.overlap; + 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, tukey.p, tukey.p, + m / (tukey.parts + overlap_units), + (m + 1 + overlap_units) / (tukey.parts + overlap_units)); + }, tukey.type); + } + + if (_windowcount == 0) + throw new Exception("invalid windowfunction"); + int nSeg = 0; + int sz = _windowsize; + float* window_segment = window; + do + { + fixed (LpcWindowSection* sections = &windowSections[nSeg, 0, 0]) + LpcWindowSection.Detect(_windowcount, window_segment, FlakeConstants.MAX_BLOCKSIZE * 2, sz, Settings.PCM.BitsPerSample, sections); + if ((sz & 1) != 0) + break; + window_segment += sz; + nSeg++; + sz >>= 1; + } while (sz >= 32); +#if NONONO + using (TextWriter tx = File.CreateText(@"H:\ubuntu\flac\w.txt")) + { +#if !NONONO + int totaltotal = 0; + for (int i = 0; i < _windowcount; i++) + { + int total = 0; + for (int sec = 0; sec < lpc.MAX_LPC_SECTIONS; sec++) + if (windowSections[0, i, sec].m_type != LpcWindowSection.SectionType.Zero || windowSections[0, i, sec].m_start != windowSections[0, i, sec].m_end) + { + tx.WriteLine("{0}\t{1}\t{2}\t{3}", windowSections[0, i, sec].m_start, windowSections[0, i, sec].m_end, windowSections[0, i, sec].m_type, windowSections[0, i, sec].m_id); + if (windowSections[0, i, sec].m_type != LpcWindowSection.SectionType.One) + total += windowSections[0, i, sec].m_end - windowSections[0, i, sec].m_start; + } + totaltotal += total; + tx.WriteLine("{0} total window data", total); + } + tx.WriteLine("{0} grand total window data", totaltotal); +#endif + for (int x = 0; x < frame.blocksize; x++) + { + tx.Write("{0}", x); + for (int i = 0; i < _windowcount; i++) + tx.Write("\t{0}", window[i * FlakeConstants.MAX_BLOCKSIZE * 2 + x]); + tx.WriteLine(); + } + } +#endif + } + + if (channels != 2 || frame.blocksize <= 32 || m_settings.StereoMethod == StereoMethod.Independent) + { + frame.window_buffer = window; + frame.nSeg = 0; + frame.current.residual = r + channels * FlakeConstants.MAX_BLOCKSIZE; + frame.ch_mode = channels != 2 ? ChannelMode.NotStereo : ChannelMode.LeftRight; + for (int ch = 0; ch < channels; ch++) + frame.subframes[ch].Init(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE, + Settings.PCM.BitsPerSample, get_wasted_bits(s + ch * FlakeConstants.MAX_BLOCKSIZE, frame.blocksize)); + + for (int ch = 0; ch < channels; ch++) + encode_residual_pass2(frame, ch); + } + else + { + //channel_decorrelation(s, s + FlakeConstants.MAX_BLOCKSIZE, s + 2 * FlakeConstants.MAX_BLOCKSIZE, s + 3 * FlakeConstants.MAX_BLOCKSIZE, frame.blocksize); + frame.window_buffer = window; + frame.nSeg = 0; + frame.current.residual = r + 4 * FlakeConstants.MAX_BLOCKSIZE; + for (int ch = 0; ch < 4; ch++) + frame.subframes[ch].Init(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE, + Settings.PCM.BitsPerSample + (ch == 3 ? 1 : 0), get_wasted_bits(s + ch * FlakeConstants.MAX_BLOCKSIZE, frame.blocksize)); + + //for (int ch = 0; ch < 4; ch++) + // for (int iWindow = 0; iWindow < _windowcount; iWindow++) + // frame.subframes[ch].lpc_ctx[iWindow].GetReflection(32, frame.subframes[ch].samples, frame.blocksize, frame.window_buffer + iWindow * FlakeConstants.MAX_BLOCKSIZE * 2); + + estimate_frame(frame, true); + uint fs = measure_frame_size(frame, true); + + if (0 != eparams.variable_block_size) + { + FlacFrame frame2 = new FlacFrame(channels * 2); + FlacFrame frame3 = new FlacFrame(channels * 2); + int tumbler = 1; + while ((frame.blocksize & 1) == 0 && frame.blocksize >= 1024) + { + frame2.InitSize(frame.blocksize / 2, true); + frame2.window_buffer = frame.window_buffer + frame.blocksize; + frame2.nSeg = frame.nSeg + 1; + frame2.current.residual = r + tumbler * 5 * FlakeConstants.MAX_BLOCKSIZE; + for (int ch = 0; ch < 4; ch++) + frame2.subframes[ch].Init(frame.subframes[ch].samples, frame2.current.residual + (ch + 1) * frame2.blocksize, + frame.subframes[ch].obits + frame.subframes[ch].wbits, frame.subframes[ch].wbits); + estimate_frame(frame2, true); + //measure_frame_size(frame2, true); + //frame2.ChooseSubframes(); + //encode_estimated_frame(frame2); + //uint fs2 = measure_frame_size(frame2, false); + uint fs2 = measure_frame_size(frame2, true); + uint fs3 = fs2; + if (eparams.variable_block_size == 2 || eparams.variable_block_size == 4) + { + frame3.InitSize(frame2.blocksize, true); + frame3.window_buffer = frame2.window_buffer; + frame3.nSeg = frame2.nSeg; + frame3.current.residual = frame2.current.residual + 5 * frame2.blocksize; + for (int ch = 0; ch < 4; ch++) + frame3.subframes[ch].Init(frame2.subframes[ch].samples + frame2.blocksize, frame3.current.residual + (ch + 1) * frame3.blocksize, + frame.subframes[ch].obits + frame.subframes[ch].wbits, frame.subframes[ch].wbits); + estimate_frame(frame3, true); + fs3 = measure_frame_size(frame3, true); + } + if (fs2 + fs3 > fs) + break; + FlacFrame tmp = frame; + frame = frame2; + frame2 = tmp; + fs = fs2; + if (eparams.variable_block_size <= 2) + break; + tumbler = 1 - tumbler; + } + } + + frame.ChooseSubframes(); + encode_estimated_frame(frame); + } + + BitWriter bitwriter = new BitWriter(frame_buffer, 0, max_frame_size); + + output_frame_header(frame, bitwriter); + output_subframes(frame, bitwriter); + output_frame_footer(bitwriter); + + if (bitwriter.Length >= max_frame_size) + throw new Exception("buffer overflow"); + + if (frame_buffer != null) + { + if (eparams.variable_block_size > 0) + frame_count += frame.blocksize; + else + frame_count++; + } + size = frame.blocksize; + return bitwriter.Length; + } + } + + unsafe int output_frame() + { + if (verify != null) + { + fixed (int* s = verifyBuffer, r = samplesBuffer) + for (int ch = 0; ch < channels; ch++) + AudioSamples.MemCpy(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE, m_blockSize); + } + + int fs, bs; + //if (0 != eparams.variable_block_size && 0 == (m_blockSize & 7) && m_blockSize >= 128) + // fs = encode_frame_vbs(); + //else + fs = encode_frame(out bs); + + if (seek_table != null && _IO.CanSeek) + { + for (int sp = 0; sp < seek_table.Length; sp++) + { + if (seek_table[sp].framesize != 0) + continue; + if (seek_table[sp].number > _position + bs) + break; + if (seek_table[sp].number >= _position) + { + seek_table[sp].number = _position; + seek_table[sp].offset = _IO.Position - first_frame_offset; + seek_table[sp].framesize = bs; + } + } + } + + _position += bs; + _IO.Write(frame_buffer, 0, fs); + _totalSize += fs; + + if (verify != null) + try + { + int decoded = verify.DecodeFrame(frame_buffer, 0, fs); + if (decoded != fs || verify.Remaining != bs) + throw new Exception(Properties.Resources.ExceptionValidationFailed); + fixed (int* s = verifyBuffer, r = verify.Samples) + { + for (int ch = 0; ch < channels; ch++) + if (AudioSamples.MemCmp(s + ch * FlakeConstants.MAX_BLOCKSIZE, r + ch * FlakeConstants.MAX_BLOCKSIZE, bs)) + throw new Exception(Properties.Resources.ExceptionValidationFailed); + } + } + catch (Exception ex) + { + //if (channels == 2) + //{ + // var sw = new WAVWriter(string.Format("verify_{0}.wav", this.frame_count), new WAVWriterSettings(this.Settings.PCM)); + // sw.FinalSampleCount = this.frame.blocksize; + // var ab = new AudioBuffer(this.Settings.PCM, this.frame.blocksize); + // ab.Prepare(this.frame.blocksize); + // fixed (int* abs = ab.Samples, s = verifyBuffer) + // AudioSamples.Interlace(abs, s, s + FlakeConstants.MAX_BLOCKSIZE, this.frame.blocksize); + // sw.Write(ab); + // sw.Close(); + //} else + throw ex; + } + + if (bs < m_blockSize) + { + for (int ch = 0; ch < (channels == 2 ? 4 : channels); ch++) + Buffer.BlockCopy(samplesBuffer, (bs + ch * FlakeConstants.MAX_BLOCKSIZE) * sizeof(int), samplesBuffer, ch * FlakeConstants.MAX_BLOCKSIZE * sizeof(int), (m_blockSize - bs) * sizeof(int)); + //fixed (int* s = samplesBuffer) + // for (int ch = 0; ch < channels; ch++) + // AudioSamples.MemCpy(s + ch * FlakeConstants.MAX_BLOCKSIZE, s + bs + ch * FlakeConstants.MAX_BLOCKSIZE, m_blockSize - bs); + } + + samplesInBuffer -= bs; + + return bs; + } + + public void Write(AudioBuffer buff) + { + if (!inited) + { + if (_IO == null) + _IO = new FileStream(_path, FileMode.Create, FileAccess.Write, FileShare.Read, 0x10000); + inited = true; + int header_size = flake_encode_init(); + _IO.Write(header, 0, header_size); + if (_IO.CanSeek) + first_frame_offset = _IO.Position; + } + + buff.Prepare(this); + + int pos = 0; + while (pos < buff.Length) + { + int block = Math.Min(buff.Length - pos, m_blockSize - samplesInBuffer); + + copy_samples(buff.Samples, pos, block); + + pos += block; + + while (samplesInBuffer >= m_blockSize) + output_frame(); + } + + if (md5 != null) + md5.TransformBlock(buff.Bytes, 0, buff.ByteLength, null, 0); + } + + public string Path { get { return _path; } } + + public static string Vendor + { + get + { + var version = typeof(AudioEncoder).Assembly.GetName().Version; + return vendor_string ?? "CUETools " + version.Major + "." + version.Minor + "." + version.Build; + } + set + { + vendor_string = value; + } + } + + static string vendor_string = null; + + int select_blocksize(int samplerate, int time_ms) + { + int blocksize = FlakeConstants.flac_blocksizes[1]; + int target = (samplerate * time_ms) / 1000; + if (eparams.variable_block_size > 0) + { + blocksize = 1024; + while (target >= blocksize) + blocksize <<= 1; + return blocksize >> 1; + } + + for (int i = 8; i < FlakeConstants.flac_blocksizes.Length - 1; i++) + if (target >= FlakeConstants.flac_blocksizes[i] && FlakeConstants.flac_blocksizes[i] > blocksize) + { + blocksize = FlakeConstants.flac_blocksizes[i]; + } + return blocksize; + } + + void write_streaminfo(byte[] header, int pos, int last) + { + Array.Clear(header, pos, 38); + BitWriter bitwriter = new BitWriter(header, pos, 38); + + // metadata header + bitwriter.writebits(1, last); + bitwriter.writebits(7, (int)MetadataType.StreamInfo); + bitwriter.writebits(24, 34); + + if (eparams.variable_block_size > 0) + bitwriter.writebits(16, 0); + else + bitwriter.writebits(16, m_blockSize); + + bitwriter.writebits(16, m_blockSize); + bitwriter.writebits(24, 0); + bitwriter.writebits(24, max_frame_size); + bitwriter.writebits(20, Settings.PCM.SampleRate); + bitwriter.writebits(3, channels - 1); + bitwriter.writebits(5, Settings.PCM.BitsPerSample - 1); + + // total samples + if (sample_count > 0) + { + bitwriter.writebits(4, 0); + bitwriter.writebits(32, sample_count); + } + else + { + bitwriter.writebits(4, 0); + bitwriter.writebits(32, 0); + } + bitwriter.flush(); + } + + /** + * Write vorbis comment metadata block to byte array. + * Just writes the vendor string for now. + */ + int write_vorbis_comment(byte[] comment, int pos, int len, int last) + { + BitWriter bitwriter = new BitWriter(comment, pos, len); + Encoding enc = new UTF8Encoding(); + byte[] str = enc.GetBytes(Vendor); + + // metadata header + bitwriter.writebits(1, last); + bitwriter.writebits(7, (int)MetadataType.VorbisComment); + int tagsLen = 0; + if (m_settings.Tags != null) + foreach (var t in m_settings.Tags) + tagsLen += 4 + enc.GetByteCount(t); + bitwriter.writebits(24, 8 + str.Length + tagsLen); + for (int i = 0; i < 4; i++) + bitwriter.writebits(8, (str.Length >> (i * 8)) & 0xff); + bitwriter.write(str); + int nTags = m_settings.Tags != null ? m_settings.Tags.Length : 0; + for (int i = 0; i < 4; i++) + bitwriter.writebits(8, (nTags >> (i * 8)) & 0xff); + if (m_settings.Tags != null) + foreach (var tag in m_settings.Tags) + { + str = enc.GetBytes(tag); + for (int i = 0; i < 4; i++) + bitwriter.writebits(8, (str.Length >> (i * 8)) & 0xff); + bitwriter.write(str); + } + bitwriter.flush(); + return bitwriter.Length; + } + + int write_seekpoints(byte[] header, int pos, int last) + { + seek_table_offset = pos + 4; + + BitWriter bitwriter = new BitWriter(header, pos, 4 + 18 * seek_table.Length); + + // metadata header + bitwriter.writebits(1, last); + bitwriter.writebits(7, (int)MetadataType.Seektable); + bitwriter.writebits(24, 18 * seek_table.Length); + for (int i = 0; i < seek_table.Length; i++) + { + bitwriter.writebits(FlakeConstants.FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN, (ulong)seek_table[i].number); + bitwriter.writebits(FlakeConstants.FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN, (ulong)seek_table[i].offset); + bitwriter.writebits(FlakeConstants.FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN, seek_table[i].framesize); + } + bitwriter.flush(); + return 4 + 18 * seek_table.Length; + } + + /** + * Write padding metadata block to byte array. + */ + int + write_padding(byte[] padding, int pos, int last, int padlen) + { + BitWriter bitwriter = new BitWriter(padding, pos, 4); + + // metadata header + bitwriter.writebits(1, last); + bitwriter.writebits(7, (int)MetadataType.Padding); + bitwriter.writebits(24, padlen); + + bitwriter.flush(); + return padlen + 4; + } + + int write_headers() + { + int header_size = 0; + int last = 0; + + // stream marker + header[0] = 0x66; + header[1] = 0x4C; + header[2] = 0x61; + header[3] = 0x43; + header_size += 4; + + // streaminfo + write_streaminfo(header, header_size, last); + header_size += 38; + + // seek table + if (_IO.CanSeek && seek_table != null) + header_size += write_seekpoints(header, header_size, last); + + // vorbis comments + if (m_settings.Padding == 0) last = 1; + header_size += write_vorbis_comment(header, header_size, header.Length - header_size, last); + + // padding + if (m_settings.Padding > 0) + { + last = 1; + header_size += write_padding(header, header_size, last, m_settings.Padding); + } + + return header_size; + } + + int flake_encode_init() + { + int i, header_len; + + //if(flake_validate_params(s) < 0) + + ch_code = channels - 1; + + // find samplerate in table + for (i = 1; i < 12; i++) + { + if (Settings.PCM.SampleRate == FlakeConstants.flac_samplerates[i]) + { + sr_code0 = i; + break; + } + } + + // if not in table, samplerate is non-standard + if (i == 12) + throw new Exception("non-standard samplerate"); + + for (i = 1; i < 8; i++) + { + if (Settings.PCM.BitsPerSample == FlakeConstants.flac_bitdepths[i]) + { + bps_code = i; + break; + } + } + if (i == 8) + throw new Exception("non-standard bps"); + + m_blockSize = m_settings.BlockSize != 0 ? m_settings.BlockSize : + select_blocksize(Settings.PCM.SampleRate, eparams.block_time_ms); + + // set maximum encoded frame size (if larger, re-encodes in verbatim mode) + if (channels == 2) + max_frame_size = 16 + ((m_blockSize * (Settings.PCM.BitsPerSample + Settings.PCM.BitsPerSample + 1) + 7) >> 3); + else + max_frame_size = 16 + ((m_blockSize * channels * Settings.PCM.BitsPerSample + 7) >> 3); + + if (_IO.CanSeek && eparams.do_seektable && sample_count > 0) + { + int seek_points_distance = Settings.PCM.SampleRate * 10; + int num_seek_points = 1 + sample_count / seek_points_distance; // 1 seek point per 10 seconds + if (sample_count % seek_points_distance == 0) + num_seek_points--; + seek_table = new SeekPoint[num_seek_points]; + for (int sp = 0; sp < num_seek_points; sp++) + { + seek_table[sp].framesize = 0; + seek_table[sp].offset = 0; + seek_table[sp].number = sp * seek_points_distance; + } + } + + // output header bytes + int tagsLen = 0; + Encoding enc = new UTF8Encoding(); + if (m_settings.Tags != null) + foreach (var t in m_settings.Tags) + tagsLen += 4 + enc.GetByteCount(t); + header = new byte[m_settings.Padding + 1024 + (seek_table == null ? 0 : seek_table.Length * 18) + tagsLen]; + header_len = write_headers(); + + // initialize CRC & MD5 + if (_IO.CanSeek && m_settings.DoMD5) + md5 = new MD5CryptoServiceProvider(); + + if (m_settings.DoVerify) + { + verify = new AudioDecoder(Settings.PCM); + verifyBuffer = new int[FlakeConstants.MAX_BLOCKSIZE * channels]; + } + + frame_buffer = new byte[max_frame_size]; + + return header_len; + } + } + + struct FlakeEncodeParams + { + // prediction order selection method + // set by user prior to calling flake_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; + + // block time in milliseconds + // set by the user prior to calling flake_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; + + // whether to use variable block sizes + // set by user prior to calling flake_encode_init + // 0 = fixed block size + // 1 = variable block size + public int variable_block_size; + + public bool do_seektable; + + public int development_mode; + + public int flake_set_defaults(EncoderSettings settings) + { + order_method = OrderMethod.Akaike; + block_time_ms = 105; + variable_block_size = 0; + do_seektable = true; + development_mode = -1; + + if (settings.GetEncoderModeIndex() == 11) + variable_block_size = 4; + + return 0; + } + } +} diff --git a/CUETools.Codecs.Flake/DecoderSettings.cs b/CUETools.Codecs.Flake/DecoderSettings.cs new file mode 100644 index 0000000..dea9e88 --- /dev/null +++ b/CUETools.Codecs.Flake/DecoderSettings.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace CUETools.Codecs.Flake +{ + [JsonObject(MemberSerialization.OptIn)] + public class DecoderSettings : IAudioDecoderSettings + { + #region IAudioDecoderSettings implementation + [Browsable(false)] + public string Extension => "flac"; + + [Browsable(false)] + public string Name => "cuetools"; + + [Browsable(false)] + public Type DecoderType => typeof(AudioDecoder); + + [Browsable(false)] + public int Priority => 2; + + public IAudioDecoderSettings Clone() + { + return MemberwiseClone() as IAudioDecoderSettings; + } + #endregion + + public DecoderSettings() + { + this.Init(); + } + } +} diff --git a/CUETools.Codecs.Flake/EncoderSettings.cs b/CUETools.Codecs.Flake/EncoderSettings.cs new file mode 100644 index 0000000..ba782b6 --- /dev/null +++ b/CUETools.Codecs.Flake/EncoderSettings.cs @@ -0,0 +1,271 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace CUETools.Codecs.Flake +{ + [JsonObject(MemberSerialization.OptIn)] + public class EncoderSettings : IAudioEncoderSettings + { + #region IAudioEncoderSettings implementation + [Browsable(false)] + public string Extension => "flac"; + + [Browsable(false)] + public string Name => "cuetools"; + + [Browsable(false)] + public Type EncoderType => typeof(AudioEncoder); + + [Browsable(false)] + public bool Lossless => true; + + [Browsable(false)] + public int Priority => 4; + + [Browsable(false)] + public string SupportedModes => this.AllowNonSubset || (this.PCM != null && this.PCM.SampleRate > 48000) ? "0 1 2 3 4 5 6 7 8 9 10 11" : "0 1 2 3 4 5 6 7 8"; + + [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 bool IsSubset() + { + return (BlockSize == 0 || (BlockSize <= 16384 && (PCM.SampleRate > 48000 || BlockSize <= 4608))) + && (PCM.SampleRate > 48000 || MaxLPCOrder <= 12) + && MaxPartitionOrder <= 8 + ; + //The blocksize bits in the frame header must be 0001-1110. The blocksize must be <=16384; if the sample rate is <= 48000Hz, the blocksize must be <=4608. + //The sample rate bits in the frame header must be 0001-1110. + //The bits-per-sample bits in the frame header must be 001-111. + //If the sample rate is <= 48000Hz, the filter order in LPC subframes must be less than or equal to 12, i.e. the subframe type bits in the subframe header may not be 101100-111111. + //The Rice partition order in a Rice-coded residual section must be less than or equal to 8. + } + + public void Validate() + { + if (this.GetEncoderModeIndex() < 0) + throw new Exception("unsupported encoder mode"); + this.SetDefaultValuesForMode(); + if (Padding < 0) + throw new Exception("unsupported padding value " + Padding.ToString()); + if (BlockSize != 0 && (BlockSize < 256 || BlockSize >= FlakeConstants.MAX_BLOCKSIZE)) + throw new Exception("unsupported block size " + BlockSize.ToString()); + if (MinLPCOrder > MaxLPCOrder || MaxLPCOrder > lpc.MAX_LPC_ORDER) + throw new Exception("invalid MaxLPCOrder " + MaxLPCOrder.ToString()); + if (MinFixedOrder < 0 || MinFixedOrder > 4) + throw new Exception("invalid MinFixedOrder " + MinFixedOrder.ToString()); + if (MaxFixedOrder < 0 || MaxFixedOrder > 4) + throw new Exception("invalid MaxFixedOrder " + MaxFixedOrder.ToString()); + if (MinPartitionOrder < 0) + throw new Exception("invalid MinPartitionOrder " + MinPartitionOrder.ToString()); + if (MinPartitionOrder > MaxPartitionOrder || MaxPartitionOrder > 8) + throw new Exception("invalid MaxPartitionOrder " + MaxPartitionOrder.ToString()); + if (PredictionType == PredictionType.None) + throw new Exception("invalid PredictionType " + PredictionType.ToString()); + if (PredictionType != PredictionType.Fixed) + { + if (WindowMethod == WindowMethod.Invalid) + throw new InvalidOperationException("invalid WindowMethod " + WindowMethod.ToString()); + if (WindowFunctions == WindowFunction.None) + throw new InvalidOperationException("invalid WindowFunctions " + WindowFunctions.ToString()); + if (EstimationDepth > 32 || EstimationDepth < 1) + throw new InvalidOperationException("invalid EstimationDepth " + EstimationDepth.ToString()); + if (MinPrecisionSearch < 0 || MinPrecisionSearch >= lpc.MAX_LPC_PRECISIONS) + throw new Exception("unsupported MinPrecisionSearch value"); + if (MaxPrecisionSearch < 0 || MaxPrecisionSearch >= lpc.MAX_LPC_PRECISIONS) + throw new Exception("unsupported MaxPrecisionSearch value"); + if (MaxPrecisionSearch < MinPrecisionSearch) + throw new Exception("unsupported MaxPrecisionSearch value"); + } + if (!AllowNonSubset && !IsSubset()) + throw new Exception("the encoding parameters specified do not conform to the FLAC Subset"); + } + + [DefaultValue(-1)] + [DefaultValueForMode(2, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0)] + [Browsable(false)] + [DisplayName("MinFixedOrder")] + [SRDescription(typeof(Properties.Resources), "MinFixedOrderDescription")] + public int MinFixedOrder { get; set; } + + [DefaultValue(-1)] + [DefaultValueForMode(2, 4, 4, 4, 2, 2, 4, 4, 4, 4, 4, 4)] + [Browsable(false)] + [DisplayName("MaxFixedOrder")] + [SRDescription(typeof(Properties.Resources), "MaxFixedOrderDescription")] + public int MaxFixedOrder { get; set; } + + [DefaultValue(1)] + [Browsable(false)] + [DisplayName("MinLPCOrder")] + [SRDescription(typeof(Properties.Resources), "MinLPCOrderDescription")] + public int MinLPCOrder { get; set; } + + [DefaultValue(-1)] + [DefaultValueForMode(8, 8, 8, 12, 12, 12, 12, 12, 12, 32, 32, 32)] + [Browsable(false)] + [DisplayName("MaxLPCOrder")] + [SRDescription(typeof(Properties.Resources), "MaxLPCOrderDescription")] + public int MaxLPCOrder { get; set; } + + [DefaultValue(0)] + [DisplayName("MinPartitionOrder")] + [Browsable(false)] + [SRDescription(typeof(Properties.Resources), "MinPartitionOrderDescription")] + public int MinPartitionOrder { get; set; } + + [DefaultValue(-1)] + [DefaultValueForMode(6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 8)] + [DisplayName("MaxPartitionOrder")] + [Browsable(false)] + [SRDescription(typeof(Properties.Resources), "MaxPartitionOrderDescription")] + public int MaxPartitionOrder { get; set; } + + [DefaultValue(false)] + [DisplayName("Verify")] + [SRDescription(typeof(Properties.Resources), "DoVerifyDescription")] + [JsonProperty] + public bool DoVerify { get; set; } + + [DefaultValue(true)] + [DisplayName("MD5")] + [SRDescription(typeof(Properties.Resources), "DoMD5Description")] + [JsonProperty] + public bool DoMD5 { get; set; } + + [DefaultValue(false)] + [DisplayName("Allow Non-subset")] + [SRDescription(typeof(Properties.Resources), "AllowNonSubsetDescription")] + [JsonProperty] + public bool AllowNonSubset { get; set; } + + [DefaultValue(StereoMethod.Invalid)] + [DefaultValueForMode( + /* 0 */ StereoMethod.Independent, + /* 1 */ StereoMethod.EstimateFixed, + /* 2 */ StereoMethod.Estimate, + /* 3 */ StereoMethod.Estimate, + /* 4 */ StereoMethod.Evaluate, + /* 5 */ StereoMethod.Evaluate, + /* 6 */ StereoMethod.Evaluate, + /* 7 */ StereoMethod.Evaluate, + /* 8 */ StereoMethod.Evaluate, + /* 9 */ StereoMethod.Evaluate, + /* 10 */ StereoMethod.Evaluate, + /* 11 */ StereoMethod.Evaluate)] + [Browsable(false)] + public StereoMethod StereoMethod { get; set; } + + [DefaultValue(PredictionType.None)] + [DefaultValueForMode( + /* 0 */ PredictionType.Fixed, + /* 1 */ PredictionType.Fixed, + /* 2 */ PredictionType.Levinson, + /* 3 */ PredictionType.Levinson, + /* 4 */ PredictionType.Search, + /* 5 */ PredictionType.Search, + /* 6 */ PredictionType.Search, + /* 7 */ PredictionType.Search, + /* 8 */ PredictionType.Search, + /* 9 */ PredictionType.Levinson, + /* 10 */ PredictionType.Search, + /* 11 */ PredictionType.Search)] + [Browsable(false)] + public PredictionType PredictionType { get; set; } + + [DefaultValue(WindowMethod.Invalid)] + [DefaultValueForMode( + /* 0 */ WindowMethod.Invalid, + /* 1 */ WindowMethod.Invalid, + /* 2 */ WindowMethod.Estimate, + /* 3 */ WindowMethod.Estimate, + /* 4 */ WindowMethod.Estimate, + /* 5 */ WindowMethod.EvaluateN, + /* 6 */ WindowMethod.EvaluateN, + /* 7 */ WindowMethod.EvaluateN, + /* 8 */ WindowMethod.EvaluateN, + /* 9 */ WindowMethod.EvaluateN, + /* 10 */ WindowMethod.EvaluateN, + /* 11 */ WindowMethod.EvaluateN)] + [Browsable(false)] + public WindowMethod WindowMethod { get; set; } + + [DefaultValue(WindowFunction.None)] + [DefaultValueForMode( + /* 0 */ WindowFunction.None, + /* 1 */ WindowFunction.None, + /* 2 */ WindowFunction.Tukey3, + /* 3 */ WindowFunction.Tukey4, + /* 4 */ WindowFunction.Tukey4, + /* 5 */ WindowFunction.Tukey4 | WindowFunction.Tukey3, + /* 6 */ WindowFunction.Tukey4 | WindowFunction.Tukey3 | WindowFunction.Tukey, + /* 7 */ WindowFunction.Tukey4 | WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey, + /* 8 */ WindowFunction.Tukey4 | WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey, + /* 9 */ WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey, + /* 10 */ WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey, + /* 11 */ WindowFunction.Tukey3 | WindowFunction.Tukey2 | WindowFunction.Tukey)] + [Browsable(false)] + [DisplayName("WindowFunctions")] + [SRDescription(typeof(Properties.Resources), "WindowFunctionsDescription")] + public WindowFunction WindowFunctions { get; set; } + + [DefaultValue(0)] + [DefaultValueForMode(0, 0, 1, 1, 1, 1, 1, 1, 3, 1, 1, 5)] + [Browsable(false)] + public int EstimationDepth { get; set; } + + [DefaultValue(-1)] + [DefaultValueForMode(1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1)] + [Browsable(false)] + public int MinPrecisionSearch { get; set; } + + [DefaultValue(-1)] + [DefaultValueForMode(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)] + [Browsable(false)] + public int MaxPrecisionSearch { get; set; } + + [DefaultValue(0)] + [Browsable(false)] + public int TukeyParts { get; set; } + + [DefaultValue(1.0)] + [Browsable(false)] + public double TukeyOverlap { get; set; } + + [DefaultValue(1.0)] + [Browsable(false)] + public double TukeyP { get; set; } + + [Browsable(false)] + public string[] Tags { get; set; } + } +} diff --git a/CUETools.Codecs.HDCD/HDCDDotNet.cs b/CUETools.Codecs.HDCD/HDCDDotNet.cs index 06a174f..9ad0203 100644 --- a/CUETools.Codecs.HDCD/HDCDDotNet.cs +++ b/CUETools.Codecs.HDCD/HDCDDotNet.cs @@ -66,7 +66,7 @@ namespace CUETools.Codecs.HDCD get { throw new Exception("unsupported"); } } - public AudioEncoderSettings Settings + public IAudioEncoderSettings Settings { get { throw new Exception("unsupported"); } set { throw new Exception("unsupported"); } diff --git a/CUETools.Codecs.Icecast/IcecastWriter.cs b/CUETools.Codecs.Icecast/IcecastWriter.cs index 0e7cc3f..7a4abfe 100644 --- a/CUETools.Codecs.Icecast/IcecastWriter.cs +++ b/CUETools.Codecs.Icecast/IcecastWriter.cs @@ -11,7 +11,7 @@ namespace CUETools.Codecs.Icecast public class IcecastWriter: IAudioDest { private long _sampleOffset = 0; - private AudioEncoderSettings m_settings; + private Codecs.WAV.EncoderSettings m_settings; private libmp3lame.AudioEncoder encoder = null; private HttpWebRequest req = null; private HttpWebResponse resp = null; @@ -28,7 +28,7 @@ namespace CUETools.Codecs.Icecast public IcecastWriter(AudioPCMConfig pcm, IcecastSettingsData settings) { - this.m_settings = new AudioEncoderSettings(pcm); + this.m_settings = new Codecs.WAV.EncoderSettings(pcm); this.settings = settings; } @@ -194,28 +194,16 @@ namespace CUETools.Codecs.Icecast encoder.Write(tmp); } - public long Position - { - get - { - return _sampleOffset; - } - } + public long Position => _sampleOffset; public long FinalSampleCount { set { ; } } - public AudioEncoderSettings Settings - { - get - { - return m_settings; - } - } + public IAudioEncoderSettings Settings => m_settings; - public string Path { get { return null; } } + public string Path => null; #endregion public long BytesWritten diff --git a/CUETools.Codecs.LossyWAV/LossyWAVReader.cs b/CUETools.Codecs.LossyWAV/LossyWAVReader.cs index d628a39..9929e1b 100644 --- a/CUETools.Codecs.LossyWAV/LossyWAVReader.cs +++ b/CUETools.Codecs.LossyWAV/LossyWAVReader.cs @@ -10,7 +10,7 @@ namespace CUETools.Codecs.LossyWAV private AudioBuffer lwcdfBuffer; private double scaling_factor; - public AudioDecoderSettings Settings { get { return null; } } + public IAudioDecoderSettings Settings => null; public long Length { diff --git a/CUETools.Codecs.LossyWAV/LossyWAVWriter.cs b/CUETools.Codecs.LossyWAV/LossyWAVWriter.cs index 3cf9009..a788818 100644 --- a/CUETools.Codecs.LossyWAV/LossyWAVWriter.cs +++ b/CUETools.Codecs.LossyWAV/LossyWAVWriter.cs @@ -10,7 +10,7 @@ namespace CUETools.Codecs.LossyWAV public const string version_string = "1.1.1#"; private IAudioDest _audioDest, _lwcdfDest; - private AudioEncoderSettings m_settings; + private Codecs.WAV.EncoderSettings m_settings; private AudioBuffer _audioBuffer; private short[] fft_bit_length; private float[] frequency_limits; @@ -71,13 +71,7 @@ namespace CUETools.Codecs.LossyWAV } } - public AudioEncoderSettings Settings - { - get - { - return m_settings; - } - } + public IAudioEncoderSettings Settings => m_settings; public int OverallBitsRemoved { @@ -154,7 +148,7 @@ namespace CUETools.Codecs.LossyWAV #region Constructor - public LossyWAVWriter(IAudioDest audioDest, IAudioDest lwcdfDest, double quality, AudioEncoderSettings settings) + public LossyWAVWriter(IAudioDest audioDest, IAudioDest lwcdfDest, double quality, Codecs.WAV.EncoderSettings settings) { _audioDest = audioDest; _lwcdfDest = lwcdfDest; diff --git a/CUETools.Codecs.TTA/CUETools.Codecs.TTA.cpp b/CUETools.Codecs.TTA/CUETools.Codecs.TTA.cpp index 12b69c2..3314117 100644 --- a/CUETools.Codecs.TTA/CUETools.Codecs.TTA.cpp +++ b/CUETools.Codecs.TTA/CUETools.Codecs.TTA.cpp @@ -29,32 +29,41 @@ namespace TTA { ref class AudioDecoder; - public ref class DecoderSettings : public AudioDecoderSettings + [Newtonsoft::Json::JsonObject(Newtonsoft::Json::MemberSerialization::OptIn)] + public ref class DecoderSettings : public IAudioDecoderSettings { public: DecoderSettings() - : AudioDecoderSettings() { } + [System::ComponentModel::Browsable(false)] virtual property String^ Name { - String^ get() override { return "ttalib"; } + String^ get() { return "ttalib"; } } + [System::ComponentModel::Browsable(false)] virtual property String^ Extension { - String^ get() override { return "tta"; } + String^ get() { return "tta"; } } + [System::ComponentModel::Browsable(false)] virtual property Type^ DecoderType { - Type^ get() override { return AudioDecoder::typeid; } + Type^ get() { return AudioDecoder::typeid; } } + [System::ComponentModel::Browsable(false)] virtual property int Priority { - int get() override { return 1; } + int get() { return 1; } + } + + virtual IAudioDecoderSettings^ Clone() + { + return (IAudioDecoderSettings^)MemberwiseClone(); } }; @@ -218,8 +227,8 @@ namespace TTA { return buff->Length; } - virtual property AudioDecoderSettings^ Settings { - AudioDecoderSettings^ get(void) { + virtual property IAudioDecoderSettings^ Settings { + IAudioDecoderSettings^ get(void) { return m_settings; } } @@ -245,37 +254,95 @@ namespace TTA { ref class AudioEncoder; - public ref class EncoderSettings : public AudioEncoderSettings + [Newtonsoft::Json::JsonObject(Newtonsoft::Json::MemberSerialization::OptIn)] + public ref class EncoderSettings : public IAudioEncoderSettings { - public: - EncoderSettings() : AudioEncoderSettings() - { - } - + public: + [System::ComponentModel::Browsable(false)] virtual property String^ Name { - String^ get() override { return "ttalib"; } + String^ get() { return "ttalib"; } } + [System::ComponentModel::Browsable(false)] virtual property String^ Extension { - String^ get() override { return "tta"; } + String^ get() { return "tta"; } } + [System::ComponentModel::Browsable(false)] virtual property Type^ EncoderType { - Type^ get() override { return AudioEncoder::typeid; } + Type^ get() { return AudioEncoder::typeid; } } + [System::ComponentModel::Browsable(false)] virtual property bool Lossless { - bool get() override { return true; } + bool get() { return true; } } + [System::ComponentModel::Browsable(false)] virtual property int Priority { - int get() override { return 1; } + int get() { return 1; } } + + [System::ComponentModel::Browsable(false)] + virtual property String^ SupportedModes + { + String^ get() { return ""; } + } + + [System::ComponentModel::Browsable(false)] + virtual property String^ DefaultMode + { + String^ get() { return ""; } + } + + [System::ComponentModel::Browsable(false)] + virtual property String^ EncoderMode + { + String^ get() { return encoderMode; } + void set(String^ value) { encoderMode = value; } + } + + [System::ComponentModel::Browsable(false)] + virtual property AudioPCMConfig^ PCM + { + AudioPCMConfig^ get() { return pcm; } + void set(AudioPCMConfig^ value) { pcm = value; } + } + + [System::ComponentModel::Browsable(false)] + virtual property int BlockSize + { + int get() { return blockSize; } + void set(int value) { blockSize = value; } + } + + [System::ComponentModel::Browsable(false)] + virtual property int Padding + { + int get() { return padding; } + void set(int value) { padding = value; } + } + + virtual IAudioEncoderSettings^ Clone() + { + return (IAudioEncoderSettings^)MemberwiseClone(); + } + + EncoderSettings() + { + IAudioEncoderSettingsExtensions::Init(this, nullptr); + } + + private: + String ^ encoderMode; + AudioPCMConfig^ pcm; + int blockSize; + int padding; }; public ref class AudioEncoder : public IAudioDest @@ -373,9 +440,9 @@ namespace TTA { _samplesWritten += sampleBuffer->Length; } - virtual property AudioEncoderSettings^ Settings + virtual property IAudioEncoderSettings^ Settings { - AudioEncoderSettings^ get() + IAudioEncoderSettings^ get() { return _settings; } @@ -388,7 +455,7 @@ namespace TTA { bool _initialized; String^ _path; Int64 _finalSampleCount, _samplesWritten; - AudioEncoderSettings^ _settings; + EncoderSettings^ _settings; void Initialize() { diff --git a/CUETools.Codecs.TTA/CUETools.Codecs.TTA.vcxproj b/CUETools.Codecs.TTA/CUETools.Codecs.TTA.vcxproj index 0b5a161..5c124f4 100644 --- a/CUETools.Codecs.TTA/CUETools.Codecs.TTA.vcxproj +++ b/CUETools.Codecs.TTA/CUETools.Codecs.TTA.vcxproj @@ -178,6 +178,12 @@ + + ..\bin\Release\net40\Newtonsoft.Json.dll + false + false + true + true true diff --git a/CUETools.Codecs.WMA/WMAReader.cs b/CUETools.Codecs.WMA/AudioDecoder.cs similarity index 94% rename from CUETools.Codecs.WMA/WMAReader.cs rename to CUETools.Codecs.WMA/AudioDecoder.cs index de4d480..7ce86e6 100644 --- a/CUETools.Codecs.WMA/WMAReader.cs +++ b/CUETools.Codecs.WMA/AudioDecoder.cs @@ -1,389 +1,375 @@ -/** - * CUETools.WMA: WMA audio decoder - * Copyright (c) 20139 Grigory Chudov - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; -using WindowsMediaLib; -using WindowsMediaLib.Defs; -using System.Runtime.InteropServices; - -namespace CUETools.Codecs.WMA -{ - public class DecoderSettings : AudioDecoderSettings - { - public override string Extension => "wma"; - - public override string Name => "windows"; - - public override Type DecoderType => typeof(AudioDecoder); - - public override int Priority => 2; - - public DecoderSettings() : base() { } - } - - public class AudioDecoder : IAudioSource - { - IWMSyncReader m_syncReader; - INSSBuffer m_pSample; - int m_pSampleOffset = 0, m_pSampleSize = 0; - short m_wStreamNum = -1; - int m_dwAudioOutputNum = -1; - - AudioPCMConfig pcm; - - long m_sampleCount = -1, m_sampleOffset = 0; - - string m_path; - Stream m_IO; - StreamWrapper m_streamWrapper; - - public AudioDecoder(string path, Stream IO) - { - m_path = path; - isValid(path); - bool pfIsProtected; - WMUtils.WMIsContentProtected(path, out pfIsProtected); - if (pfIsProtected) - throw new Exception("DRM present"); - WMUtils.WMCreateSyncReader(IntPtr.Zero, Rights.None, out m_syncReader); - - if (path == null) - { - m_IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000); - m_streamWrapper = new StreamWrapper(m_IO); - m_syncReader.OpenStream(m_streamWrapper); - } - else - { - m_syncReader.Open(path); - } - var pProfile = (m_syncReader as IWMProfile); - int dwStreamCount; - pProfile.GetStreamCount(out dwStreamCount); - for (int dwIndex = 0; dwIndex < dwStreamCount; dwIndex++) - { - IWMStreamConfig pConfig = null; - pProfile.GetStream(dwIndex, out pConfig); - try - { - Guid guid; - pConfig.GetStreamType(out guid); - if (MediaType.Audio != guid) - continue; - short wStreamNum; - pConfig.GetStreamNumber(out wStreamNum); - int dwBitrate = -1; - pConfig.GetBitrate(out dwBitrate); - var pIWMMediaProps = pConfig as IWMMediaProps; - int cbType = 0; - pIWMMediaProps.GetMediaType(null, ref cbType); - var pMediaType = new AMMediaType(); - pMediaType.formatSize = cbType; - pIWMMediaProps.GetMediaType(pMediaType, ref cbType); - if (pMediaType.formatType != FormatType.WaveEx) - continue; - if (pMediaType.subType != MediaSubType.WMAudio_Lossless) - continue; - m_wStreamNum = wStreamNum; - pcm = WaveFormatExtensible.FromMediaType(pMediaType).GetConfig(); - break; - } - finally - { - Marshal.ReleaseComObject(pConfig); - } - } - if (m_wStreamNum == -1) - throw new Exception("No WMA lossless streams found"); - - m_syncReader.SetReadStreamSamples(m_wStreamNum, false); - bool pfCompressed; - m_syncReader.GetReadStreamSamples(m_wStreamNum, out pfCompressed); - if (pfCompressed) - throw new Exception("doesn't decompress"); - m_syncReader.GetOutputNumberForStream(m_wStreamNum, out m_dwAudioOutputNum); - IWMOutputMediaProps pProps; - m_syncReader.GetOutputProps(m_dwAudioOutputNum, out pProps); - - try - { - StringBuilder sName = null; - AMMediaType pMediaType = null; - int cbType = 0; - - cbType = 0; - pMediaType = null; - pProps.GetMediaType(pMediaType, ref cbType); - - // Get the name of the output we'll be using - sName = null; - short iName = 0; - pProps.GetConnectionName(sName, ref iName); - - sName = new StringBuilder(iName); - pProps.GetConnectionName(sName, ref iName); - - if (pcm.ChannelCount > 2) - { - m_syncReader.SetOutputSetting(m_dwAudioOutputNum, Constants.g_wszEnableDiscreteOutput, AttrDataType.BOOL, new byte[] { 1, 0, 0, 0 }, 4); - m_syncReader.SetOutputSetting(m_dwAudioOutputNum, Constants.g_wszSpeakerConfig, AttrDataType.DWORD, new byte[] { 0, 0, 0, 0 }, 4); - } - - pMediaType = new AMMediaType(); - pMediaType.formatSize = cbType - Marshal.SizeOf(typeof(AMMediaType)); - - // - // Get the value for MediaType - // - pProps.GetMediaType(pMediaType, ref cbType); - - try - { - if (MediaType.Audio != pMediaType.majorType) - throw new Exception("not Audio"); - if (FormatType.WaveEx != pMediaType.formatType) - throw new Exception("not WaveEx"); - var wfe = new WaveFormatExtensible(pcm); - Marshal.FreeCoTaskMem(pMediaType.formatPtr); - pMediaType.formatPtr = IntPtr.Zero; - pMediaType.formatSize = 0; - pMediaType.formatPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(wfe)); - pMediaType.formatSize = Marshal.SizeOf(wfe); - Marshal.StructureToPtr(wfe, pMediaType.formatPtr, false); - pProps.SetMediaType(pMediaType); - m_syncReader.SetOutputProps(m_dwAudioOutputNum, pProps); - } - finally - { - WMUtils.FreeWMMediaType(pMediaType); - } - } - finally - { - Marshal.ReleaseComObject(pProps); - } - - //try - //{ - // AttrDataType wmtType; - // short cbLength = 0; - // short wAnyStream = 0; - // var pHeaderInfo = m_syncReader as IWMHeaderInfo; - // pHeaderInfo.GetAttributeByName(ref wAnyStream, Constants.g_wszWMDuration, out wmtType, null, ref cbLength); - // var pbValue = new byte[cbLength]; - // pHeaderInfo.GetAttributeByName(ref wAnyStream, Constants.g_wszWMDuration, out wmtType, pbValue, ref cbLength); - // var m_cnsFileDuration = BitConverter.ToInt64(pbValue, 0); - // _sampleCount = m_cnsFileDuration * m_pWfx.nSamplesPerSec / 10000000; - // // NOT ACCURATE ENOUGH (~1ms precision observed) - //} - //catch (COMException) - //{ - //} - - //try - //{ - // var pHeaderInfo = m_syncReader as IWMHeaderInfo2; - // int nCodec; - // pHeaderInfo.GetCodecInfoCount(out nCodec); - // for (int wIndex = 0; wIndex < nCodec; wIndex++) - // { - // CodecInfoType enumCodecType; - // short cchName = 0; - // short cchDescription = 0; - // short cbCodecInfo = 0; - // pHeaderInfo.GetCodecInfo(wIndex, ref cchName, null, - // ref cchDescription, null, out enumCodecType, - // ref cbCodecInfo, null); - // var pwszName = new StringBuilder(cchName); - // var pwszDescription = new StringBuilder(cchDescription); - // var pbCodecInfo = new byte[cbCodecInfo]; - // pHeaderInfo.GetCodecInfo(wIndex, ref cchName, pwszName, - // ref cchDescription, pwszDescription, out enumCodecType, - // ref cbCodecInfo, pbCodecInfo); - // if (enumCodecType == CodecInfoType.Audio) - // { - // // pbCodecInfo = {99,1} ??/ - // } - // } - //} - //catch (COMException) - //{ - //} - - //int cbMax; - //m_syncReader.GetMaxOutputSampleSize(m_dwAudioOutputNum, out cbMax); - } - - public AudioDecoderSettings Settings { get { return null; } } - - public void isValid(string filename) - { - int pdwDataSize = 0; - WMUtils.WMValidateData(null, ref pdwDataSize); - byte[] data = new byte[pdwDataSize]; - using (FileStream s = new FileStream(filename, FileMode.Open, FileAccess.Read)) - { - if (s.Read(data, 0, pdwDataSize) < pdwDataSize) - throw new Exception("partial read"); // TODO - } - WMUtils.WMValidateData(data, ref pdwDataSize); - } - - public void Close() - { - //if (m_streamWrapper != null) - // m_streamWrapper.Close(); - if (m_IO != null) - m_IO.Close(); - if (m_pSample != null) - Marshal.ReleaseComObject(m_pSample); - if (m_syncReader != null) - { - m_syncReader.Close(); - Marshal.ReleaseComObject(m_syncReader); - } - m_IO = null; - m_pSample = null; - m_syncReader = null; - } - - public long Length - { - get - { - return m_sampleCount; - } - } - - public long Remaining - { - get - { - return Length - Position; - } - } - - public long Position - { - get - { - return m_sampleOffset / PCM.BlockAlign; - } - set - { - if (m_sampleCount < 0 || value > m_sampleCount) - throw new Exception("seeking past end of stream"); - if (value < Position) - throw new NotSupportedException(); - if (value < Position) - throw new Exception("cannot seek backwards"); - var buff = new AudioBuffer(this, 0x10000); - while (value > Position && Read(buff, (int)Math.Min(Int32.MaxValue, value - Position)) != 0) - ; - } - } - - public AudioPCMConfig PCM - { - get - { - return pcm; - } - } - - public string Path - { - get - { - return m_path; - } - } - - public int Read(AudioBuffer buff, int maxLength) - { - buff.Prepare(this, maxLength); - - int buff_offset = 0; - int buff_size = buff.ByteLength; - - while (m_pSampleSize < buff_size) - { - if (m_pSampleSize > 0) - { - IntPtr pdwBuffer; - m_pSample.GetBuffer(out pdwBuffer); - Marshal.Copy((IntPtr)(pdwBuffer.ToInt64() + m_pSampleOffset), buff.Bytes, buff_offset, m_pSampleSize); - buff_size -= m_pSampleSize; - buff_offset += m_pSampleSize; - m_sampleOffset += m_pSampleSize; - m_pSampleSize = 0; - Marshal.ReleaseComObject(m_pSample); - m_pSample = null; - } - - long cnsSampleTime; - long cnsDuration; - SampleFlag flags; - int dwOutputNum; - short wStreamNum; - try - { - m_syncReader.GetNextSample(m_wStreamNum, out m_pSample, out cnsSampleTime, out cnsDuration, out flags, out dwOutputNum, out wStreamNum); - } - catch (COMException ex) - { - // EOF - if (ex.ErrorCode == NSResults.E_NO_MORE_SAMPLES) - { - if ((m_sampleOffset % PCM.BlockAlign) != 0) - throw new Exception("(m_sampleOffset % PCM.BlockAlign) != 0"); - m_sampleCount = m_sampleOffset / PCM.BlockAlign; - if ((buff_offset % PCM.BlockAlign) != 0) - throw new Exception("(buff_offset % PCM.BlockAlign) != 0"); - return buff.Length = buff_offset / PCM.BlockAlign; - } - throw ex; - } - //if (dwOutputNum != m_dwAudioOutputNum || wStreamNum != m_wStreamNum) - //{ - //} - m_pSampleOffset = 0; - m_pSample.GetLength(out m_pSampleSize); - } - - if (buff_size > 0) - { - IntPtr pdwBuffer; - m_pSample.GetBuffer(out pdwBuffer); - Marshal.Copy((IntPtr)(pdwBuffer.ToInt64() + m_pSampleOffset), buff.Bytes, buff_offset, buff_size); - m_pSampleOffset += buff_size; - m_pSampleSize -= buff_size; - m_sampleOffset += buff_size; - buff_offset += buff_size; - buff_size = 0; - } - if ((buff_offset % PCM.BlockAlign) != 0) - throw new Exception("(buff_offset % PCM.BlockAlign) != 0"); - return buff.Length = buff_offset / PCM.BlockAlign; - } - } -} +/** + * CUETools.WMA: WMA audio decoder + * Copyright (c) 20139 Grigory Chudov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using WindowsMediaLib; +using WindowsMediaLib.Defs; + +namespace CUETools.Codecs.WMA +{ + public class AudioDecoder : IAudioSource + { + IWMSyncReader m_syncReader; + INSSBuffer m_pSample; + int m_pSampleOffset = 0, m_pSampleSize = 0; + short m_wStreamNum = -1; + int m_dwAudioOutputNum = -1; + + AudioPCMConfig pcm; + + long m_sampleCount = -1, m_sampleOffset = 0; + + string m_path; + Stream m_IO; + StreamWrapper m_streamWrapper; + + public AudioDecoder(string path, Stream IO) + { + m_path = path; + isValid(path); + bool pfIsProtected; + WMUtils.WMIsContentProtected(path, out pfIsProtected); + if (pfIsProtected) + throw new Exception("DRM present"); + WMUtils.WMCreateSyncReader(IntPtr.Zero, Rights.None, out m_syncReader); + + if (path == null) + { + m_IO = IO != null ? IO : new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000); + m_streamWrapper = new StreamWrapper(m_IO); + m_syncReader.OpenStream(m_streamWrapper); + } + else + { + m_syncReader.Open(path); + } + var pProfile = (m_syncReader as IWMProfile); + int dwStreamCount; + pProfile.GetStreamCount(out dwStreamCount); + for (int dwIndex = 0; dwIndex < dwStreamCount; dwIndex++) + { + IWMStreamConfig pConfig = null; + pProfile.GetStream(dwIndex, out pConfig); + try + { + Guid guid; + pConfig.GetStreamType(out guid); + if (MediaType.Audio != guid) + continue; + short wStreamNum; + pConfig.GetStreamNumber(out wStreamNum); + int dwBitrate = -1; + pConfig.GetBitrate(out dwBitrate); + var pIWMMediaProps = pConfig as IWMMediaProps; + int cbType = 0; + pIWMMediaProps.GetMediaType(null, ref cbType); + var pMediaType = new AMMediaType(); + pMediaType.formatSize = cbType; + pIWMMediaProps.GetMediaType(pMediaType, ref cbType); + if (pMediaType.formatType != FormatType.WaveEx) + continue; + if (pMediaType.subType != MediaSubType.WMAudio_Lossless) + continue; + m_wStreamNum = wStreamNum; + pcm = WaveFormatExtensible.FromMediaType(pMediaType).GetConfig(); + break; + } + finally + { + Marshal.ReleaseComObject(pConfig); + } + } + if (m_wStreamNum == -1) + throw new Exception("No WMA lossless streams found"); + + m_syncReader.SetReadStreamSamples(m_wStreamNum, false); + bool pfCompressed; + m_syncReader.GetReadStreamSamples(m_wStreamNum, out pfCompressed); + if (pfCompressed) + throw new Exception("doesn't decompress"); + m_syncReader.GetOutputNumberForStream(m_wStreamNum, out m_dwAudioOutputNum); + IWMOutputMediaProps pProps; + m_syncReader.GetOutputProps(m_dwAudioOutputNum, out pProps); + + try + { + StringBuilder sName = null; + AMMediaType pMediaType = null; + int cbType = 0; + + cbType = 0; + pMediaType = null; + pProps.GetMediaType(pMediaType, ref cbType); + + // Get the name of the output we'll be using + sName = null; + short iName = 0; + pProps.GetConnectionName(sName, ref iName); + + sName = new StringBuilder(iName); + pProps.GetConnectionName(sName, ref iName); + + if (pcm.ChannelCount > 2) + { + m_syncReader.SetOutputSetting(m_dwAudioOutputNum, Constants.g_wszEnableDiscreteOutput, AttrDataType.BOOL, new byte[] { 1, 0, 0, 0 }, 4); + m_syncReader.SetOutputSetting(m_dwAudioOutputNum, Constants.g_wszSpeakerConfig, AttrDataType.DWORD, new byte[] { 0, 0, 0, 0 }, 4); + } + + pMediaType = new AMMediaType(); + pMediaType.formatSize = cbType - Marshal.SizeOf(typeof(AMMediaType)); + + // + // Get the value for MediaType + // + pProps.GetMediaType(pMediaType, ref cbType); + + try + { + if (MediaType.Audio != pMediaType.majorType) + throw new Exception("not Audio"); + if (FormatType.WaveEx != pMediaType.formatType) + throw new Exception("not WaveEx"); + var wfe = new WaveFormatExtensible(pcm); + Marshal.FreeCoTaskMem(pMediaType.formatPtr); + pMediaType.formatPtr = IntPtr.Zero; + pMediaType.formatSize = 0; + pMediaType.formatPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(wfe)); + pMediaType.formatSize = Marshal.SizeOf(wfe); + Marshal.StructureToPtr(wfe, pMediaType.formatPtr, false); + pProps.SetMediaType(pMediaType); + m_syncReader.SetOutputProps(m_dwAudioOutputNum, pProps); + } + finally + { + WMUtils.FreeWMMediaType(pMediaType); + } + } + finally + { + Marshal.ReleaseComObject(pProps); + } + + //try + //{ + // AttrDataType wmtType; + // short cbLength = 0; + // short wAnyStream = 0; + // var pHeaderInfo = m_syncReader as IWMHeaderInfo; + // pHeaderInfo.GetAttributeByName(ref wAnyStream, Constants.g_wszWMDuration, out wmtType, null, ref cbLength); + // var pbValue = new byte[cbLength]; + // pHeaderInfo.GetAttributeByName(ref wAnyStream, Constants.g_wszWMDuration, out wmtType, pbValue, ref cbLength); + // var m_cnsFileDuration = BitConverter.ToInt64(pbValue, 0); + // _sampleCount = m_cnsFileDuration * m_pWfx.nSamplesPerSec / 10000000; + // // NOT ACCURATE ENOUGH (~1ms precision observed) + //} + //catch (COMException) + //{ + //} + + //try + //{ + // var pHeaderInfo = m_syncReader as IWMHeaderInfo2; + // int nCodec; + // pHeaderInfo.GetCodecInfoCount(out nCodec); + // for (int wIndex = 0; wIndex < nCodec; wIndex++) + // { + // CodecInfoType enumCodecType; + // short cchName = 0; + // short cchDescription = 0; + // short cbCodecInfo = 0; + // pHeaderInfo.GetCodecInfo(wIndex, ref cchName, null, + // ref cchDescription, null, out enumCodecType, + // ref cbCodecInfo, null); + // var pwszName = new StringBuilder(cchName); + // var pwszDescription = new StringBuilder(cchDescription); + // var pbCodecInfo = new byte[cbCodecInfo]; + // pHeaderInfo.GetCodecInfo(wIndex, ref cchName, pwszName, + // ref cchDescription, pwszDescription, out enumCodecType, + // ref cbCodecInfo, pbCodecInfo); + // if (enumCodecType == CodecInfoType.Audio) + // { + // // pbCodecInfo = {99,1} ??/ + // } + // } + //} + //catch (COMException) + //{ + //} + + //int cbMax; + //m_syncReader.GetMaxOutputSampleSize(m_dwAudioOutputNum, out cbMax); + } + + public IAudioDecoderSettings Settings => null; + + public void isValid(string filename) + { + int pdwDataSize = 0; + WMUtils.WMValidateData(null, ref pdwDataSize); + byte[] data = new byte[pdwDataSize]; + using (FileStream s = new FileStream(filename, FileMode.Open, FileAccess.Read)) + { + if (s.Read(data, 0, pdwDataSize) < pdwDataSize) + throw new Exception("partial read"); // TODO + } + WMUtils.WMValidateData(data, ref pdwDataSize); + } + + public void Close() + { + //if (m_streamWrapper != null) + // m_streamWrapper.Close(); + if (m_IO != null) + m_IO.Close(); + if (m_pSample != null) + Marshal.ReleaseComObject(m_pSample); + if (m_syncReader != null) + { + m_syncReader.Close(); + Marshal.ReleaseComObject(m_syncReader); + } + m_IO = null; + m_pSample = null; + m_syncReader = null; + } + + public long Length + { + get + { + return m_sampleCount; + } + } + + public long Remaining + { + get + { + return Length - Position; + } + } + + public long Position + { + get + { + return m_sampleOffset / PCM.BlockAlign; + } + set + { + if (m_sampleCount < 0 || value > m_sampleCount) + throw new Exception("seeking past end of stream"); + if (value < Position) + throw new NotSupportedException(); + if (value < Position) + throw new Exception("cannot seek backwards"); + var buff = new AudioBuffer(this, 0x10000); + while (value > Position && Read(buff, (int)Math.Min(Int32.MaxValue, value - Position)) != 0) + ; + } + } + + public AudioPCMConfig PCM + { + get + { + return pcm; + } + } + + public string Path + { + get + { + return m_path; + } + } + + public int Read(AudioBuffer buff, int maxLength) + { + buff.Prepare(this, maxLength); + + int buff_offset = 0; + int buff_size = buff.ByteLength; + + while (m_pSampleSize < buff_size) + { + if (m_pSampleSize > 0) + { + IntPtr pdwBuffer; + m_pSample.GetBuffer(out pdwBuffer); + Marshal.Copy((IntPtr)(pdwBuffer.ToInt64() + m_pSampleOffset), buff.Bytes, buff_offset, m_pSampleSize); + buff_size -= m_pSampleSize; + buff_offset += m_pSampleSize; + m_sampleOffset += m_pSampleSize; + m_pSampleSize = 0; + Marshal.ReleaseComObject(m_pSample); + m_pSample = null; + } + + long cnsSampleTime; + long cnsDuration; + SampleFlag flags; + int dwOutputNum; + short wStreamNum; + try + { + m_syncReader.GetNextSample(m_wStreamNum, out m_pSample, out cnsSampleTime, out cnsDuration, out flags, out dwOutputNum, out wStreamNum); + } + catch (COMException ex) + { + // EOF + if (ex.ErrorCode == NSResults.E_NO_MORE_SAMPLES) + { + if ((m_sampleOffset % PCM.BlockAlign) != 0) + throw new Exception("(m_sampleOffset % PCM.BlockAlign) != 0"); + m_sampleCount = m_sampleOffset / PCM.BlockAlign; + if ((buff_offset % PCM.BlockAlign) != 0) + throw new Exception("(buff_offset % PCM.BlockAlign) != 0"); + return buff.Length = buff_offset / PCM.BlockAlign; + } + throw ex; + } + //if (dwOutputNum != m_dwAudioOutputNum || wStreamNum != m_wStreamNum) + //{ + //} + m_pSampleOffset = 0; + m_pSample.GetLength(out m_pSampleSize); + } + + if (buff_size > 0) + { + IntPtr pdwBuffer; + m_pSample.GetBuffer(out pdwBuffer); + Marshal.Copy((IntPtr)(pdwBuffer.ToInt64() + m_pSampleOffset), buff.Bytes, buff_offset, buff_size); + m_pSampleOffset += buff_size; + m_pSampleSize -= buff_size; + m_sampleOffset += buff_size; + buff_offset += buff_size; + buff_size = 0; + } + if ((buff_offset % PCM.BlockAlign) != 0) + throw new Exception("(buff_offset % PCM.BlockAlign) != 0"); + return buff.Length = buff_offset / PCM.BlockAlign; + } + } +} diff --git a/CUETools.Codecs.WMA/AudioEncoder.cs b/CUETools.Codecs.WMA/AudioEncoder.cs new file mode 100644 index 0000000..a0dfdc6 --- /dev/null +++ b/CUETools.Codecs.WMA/AudioEncoder.cs @@ -0,0 +1,160 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using WindowsMediaLib; +using WindowsMediaLib.Defs; + +namespace CUETools.Codecs.WMA +{ + public class AudioEncoder : IAudioDest + { + IWMWriter m_pEncoder; + private string outputPath; + private bool closed = false; + private bool fileCreated = false; + private bool writingBegan = false; + private long sampleCount, finalSampleCount; + + public long FinalSampleCount + { + set + { + this.finalSampleCount = value; + } + } + + public string Path + { + get { return this.outputPath; } + } + + EncoderSettings m_settings; + + public IAudioEncoderSettings Settings => m_settings; + + public AudioEncoder(string path, EncoderSettings settings) + { + this.m_settings = settings; + this.outputPath = path; + + try + { + m_pEncoder = settings.GetWriter(); + int cInputs; + m_pEncoder.GetInputCount(out cInputs); + if (cInputs < 1) throw new InvalidOperationException(); + IWMInputMediaProps pInput; + m_pEncoder.GetInputProps(0, out pInput); + try + { + int cbType = 0; + AMMediaType pMediaType = null; + pInput.GetMediaType(pMediaType, ref cbType); + pMediaType = new AMMediaType(); + pMediaType.formatSize = cbType - Marshal.SizeOf(typeof(AMMediaType)); + pInput.GetMediaType(pMediaType, ref cbType); + try + { + var wfe = new WaveFormatExtensible(m_settings.PCM); + Marshal.FreeCoTaskMem(pMediaType.formatPtr); + pMediaType.formatPtr = IntPtr.Zero; + pMediaType.formatSize = 0; + pMediaType.formatPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(wfe)); + pMediaType.formatSize = Marshal.SizeOf(wfe); + Marshal.StructureToPtr(wfe, pMediaType.formatPtr, false); + pInput.SetMediaType(pMediaType); + m_pEncoder.SetInputProps(0, pInput); + } + finally + { + WMUtils.FreeWMMediaType(pMediaType); + } + } + finally + { + Marshal.ReleaseComObject(pInput); + } + } + catch (Exception ex) + { + if (m_pEncoder != null) + { + Marshal.ReleaseComObject(m_pEncoder); + m_pEncoder = null; + } + throw ex; + } + } + + public void Close() + { + if (!this.closed) + { + try + { + if (this.writingBegan) + { + m_pEncoder.EndWriting(); + this.writingBegan = false; + } + } + finally + { + if (m_pEncoder != null) + { + Marshal.ReleaseComObject(m_pEncoder); + m_pEncoder = null; + } + } + + this.closed = true; + } + } + + public void Delete() + { + if (this.outputPath == null) + throw new InvalidOperationException("This writer was not created from file."); + + if (!this.closed) + { + this.Close(); + + if (this.fileCreated) + { + File.Delete(this.outputPath); + this.fileCreated = false; + } + } + } + + public void Write(AudioBuffer buffer) + { + if (this.closed) + throw new InvalidOperationException("Writer already closed."); + + if (!this.fileCreated) + { + this.m_pEncoder.SetOutputFilename(outputPath); + this.fileCreated = true; + } + if (!this.writingBegan) + { + this.m_pEncoder.BeginWriting(); + this.writingBegan = true; + } + + buffer.Prepare(this); + INSSBuffer pSample; + m_pEncoder.AllocateSample(buffer.ByteLength, out pSample); + IntPtr pdwBuffer; + pSample.GetBuffer(out pdwBuffer); + pSample.SetLength(buffer.ByteLength); + Marshal.Copy(buffer.Bytes, 0, pdwBuffer, buffer.ByteLength); + long cnsSampleTime = sampleCount * 10000000L / Settings.PCM.SampleRate; + m_pEncoder.WriteSample(0, cnsSampleTime, SampleFlag.CleanPoint, pSample); + Marshal.ReleaseComObject(pSample); + sampleCount += buffer.Length; + } + } +} diff --git a/CUETools.Codecs.WMA/DecoderSettings.cs b/CUETools.Codecs.WMA/DecoderSettings.cs new file mode 100644 index 0000000..01751b4 --- /dev/null +++ b/CUETools.Codecs.WMA/DecoderSettings.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json; +using System; +using System.ComponentModel; + +namespace CUETools.Codecs.WMA +{ + [JsonObject(MemberSerialization.OptIn)] + public class DecoderSettings : IAudioDecoderSettings + { + #region IAudioDecoderSettings implementation + [Browsable(false)] + public string Extension => "wma"; + + [Browsable(false)] + public string Name => "windows"; + + [Browsable(false)] + public Type DecoderType => typeof(AudioDecoder); + + [Browsable(false)] + public int Priority => 2; + + public IAudioDecoderSettings Clone() + { + return MemberwiseClone() as IAudioDecoderSettings; + } + #endregion + + public DecoderSettings() + { + this.Init(); + } + } +} diff --git a/CUETools.Codecs.WMA/WMAWriter.cs b/CUETools.Codecs.WMA/EncoderSettings.cs similarity index 63% rename from CUETools.Codecs.WMA/WMAWriter.cs rename to CUETools.Codecs.WMA/EncoderSettings.cs index 69c8162..fac5a06 100644 --- a/CUETools.Codecs.WMA/WMAWriter.cs +++ b/CUETools.Codecs.WMA/EncoderSettings.cs @@ -1,436 +1,305 @@ -using System; -using System.ComponentModel; -using System.Runtime.InteropServices; -using CUETools.Codecs; -using System.IO; -using System.Text; -using WindowsMediaLib; -using WindowsMediaLib.Defs; -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace CUETools.Codecs.WMA -{ - public abstract class EncoderSettings : AudioEncoderSettings - { - public override Type EncoderType => typeof(AudioEncoder); - - public override bool Lossless => true; - - public EncoderSettings() - : base() - { - } - - protected Guid m_subType; - protected bool m_vbr = true; - - public IWMWriter GetWriter() - { - IWMProfileManager pProfileManager = null; - try - { - WMUtils.WMCreateProfileManager(out pProfileManager); - var pCodecInfo3 = pProfileManager as IWMCodecInfo3; - // We have to use the same pProfileManager for enumeration, - // because it calls SetCodecEnumerationSetting, so chosenFormat.format - // would not point to the same format for a pProfileManager - // with different (default) settings, and GetCodecFormat - // would return the wrong stream config. - var formats = GetFormats(pProfileManager); - if (this.EncoderMode != "") - formats.RemoveAll(fmt => fmt.modeName != this.EncoderMode); - if (formats.Count < 1) - throw new NotSupportedException("codec/format not found"); - if (formats.Count > 1) - throw new NotSupportedException("codec/format ambiguous"); - var chosenFormat = formats[0]; - IWMStreamConfig pStreamConfig1; - pCodecInfo3.GetCodecFormat(MediaType.Audio, chosenFormat.codec, chosenFormat.format, out pStreamConfig1); - try - { - pStreamConfig1.SetStreamNumber(1); - IWMProfile pProfile; - pProfileManager.CreateEmptyProfile(WMVersion.V9_0, out pProfile); - try - { - pProfile.AddStream(pStreamConfig1); - IWMWriter pWriter; - WMUtils.WMCreateWriter(IntPtr.Zero, out pWriter); - try - { - pWriter.SetProfile(pProfile); - } - catch (Exception ex) - { - Marshal.ReleaseComObject(pWriter); - throw ex; - } - return pWriter; - } - finally - { - Marshal.ReleaseComObject(pProfile); - } - } - finally - { - Marshal.ReleaseComObject(pStreamConfig1); - } - } - finally - { - Marshal.ReleaseComObject(pProfileManager); - } - } - - internal IEnumerable EnumerateFormatInfo(IWMProfileManager pProfileManager2) - { - IWMProfileManager pProfileManager = null; - try - { - if (pProfileManager2 == null) - WMUtils.WMCreateProfileManager(out pProfileManager); - var pCodecInfo3 = (pProfileManager2 ?? pProfileManager) as IWMCodecInfo3; - int cCodecs; - pCodecInfo3.GetCodecInfoCount(MediaType.Audio, out cCodecs); - for (int iCodec = 0; iCodec < cCodecs; iCodec++) - { - int szCodecName = 0; - pCodecInfo3.GetCodecName(MediaType.Audio, iCodec, null, ref szCodecName); - var codecName = new StringBuilder(szCodecName); - pCodecInfo3.GetCodecName(MediaType.Audio, iCodec, codecName, ref szCodecName); - var attrDataType = new AttrDataType(); - int dwAttrSize = 0; - byte[] pAttrValue = new byte[4]; - pCodecInfo3.GetCodecProp(MediaType.Audio, iCodec, Constants.g_wszIsVBRSupported, out attrDataType, pAttrValue, ref dwAttrSize); - if (pAttrValue[0] != 1) - continue; - if (m_vbr) - { - pCodecInfo3.SetCodecEnumerationSetting(MediaType.Audio, iCodec, Constants.g_wszVBREnabled, AttrDataType.BOOL, new byte[] { 1, 0, 0, 0 }, 4); - pCodecInfo3.SetCodecEnumerationSetting(MediaType.Audio, iCodec, Constants.g_wszNumPasses, AttrDataType.DWORD, new byte[] { 1, 0, 0, 0 }, 4); - } - else - { - pCodecInfo3.SetCodecEnumerationSetting(MediaType.Audio, iCodec, Constants.g_wszVBREnabled, AttrDataType.BOOL, new byte[] { 0, 0, 0, 0 }, 4); - } - - int cFormat; - pCodecInfo3.GetCodecFormatCount(MediaType.Audio, iCodec, out cFormat); - for (int iFormat = 0; iFormat < cFormat; iFormat++) - { - IWMStreamConfig pStreamConfig; - int cchDesc = 1024; - StringBuilder szDesc = new StringBuilder(cchDesc); - pCodecInfo3.GetCodecFormatDesc(MediaType.Audio, iCodec, iFormat, out pStreamConfig, szDesc, ref cchDesc); - if (szDesc.ToString().Contains("(A/V)")) - continue; - try - { - var pProps = pStreamConfig as IWMMediaProps; - int cbType = 0; - AMMediaType pMediaType = null; - pProps.GetMediaType(pMediaType, ref cbType); - pMediaType = new AMMediaType(); - pMediaType.formatSize = cbType - Marshal.SizeOf(typeof(AMMediaType)); - pProps.GetMediaType(pMediaType, ref cbType); - try - { - if (pMediaType.majorType == MediaType.Audio && pMediaType.formatType == FormatType.WaveEx && pMediaType.subType == m_subType) - { - var pcm = WaveFormatExtensible.FromMediaType(pMediaType).GetConfig(); - if (PCM == null || (pcm.ChannelCount == PCM.ChannelCount && pcm.SampleRate == PCM.SampleRate && pcm.BitsPerSample >= PCM.BitsPerSample)) - yield return new WMAFormatInfo() - { - codec = iCodec, - codecName = codecName.ToString(), - format = iFormat, - formatName = szDesc.ToString(), - subType = pMediaType.subType, - pcm = pcm - }; - } - } - finally - { - WMUtils.FreeWMMediaType(pMediaType); - } - } - finally - { - Marshal.ReleaseComObject(pStreamConfig); - } - } - } - } - finally - { - if (pProfileManager != null) - Marshal.ReleaseComObject(pProfileManager); - } - } - - internal List GetFormats(IWMProfileManager pProfileManager) - { - var formats = new List(this.EnumerateFormatInfo(pProfileManager)); - formats.RemoveAll(fmt => formats.Exists(fmt2 => fmt2.pcm.BitsPerSample < fmt.pcm.BitsPerSample && fmt2.pcm.ChannelCount == fmt.pcm.ChannelCount && fmt2.pcm.SampleRate == fmt.pcm.SampleRate)); - if (formats.Count < 2) return formats; - int prefixLen = 0, suffixLen = 0; - while (formats.TrueForAll(s => s.formatName.Length > prefixLen && - s.formatName.Substring(0, prefixLen + 1) == - formats[0].formatName.Substring(0, prefixLen + 1))) - prefixLen++; - while (formats.TrueForAll(s => s.formatName.Length > suffixLen && - s.formatName.Substring(s.formatName.Length - suffixLen - 1) == - formats[0].formatName.Substring(formats[0].formatName.Length - suffixLen - 1))) - suffixLen++; - formats.ForEach(s => s.modeName = s.formatName.Substring(prefixLen, s.formatName.Length - suffixLen - prefixLen).Trim().Replace(' ', '_')); - int ix, iy; - formats.Sort((Comparison)((x, y) => int.TryParse(x.modeName, out ix) && int.TryParse(y.modeName, out iy) ? ix - iy : x.modeName.CompareTo(y.modeName))); - return formats; - } - - public override string GetSupportedModes(out string defaultMode) - { - var fmts = GetFormats(null); - defaultMode = fmts.Count > 0 ? fmts[fmts.Count - 1].modeName : ""; - return string.Join(" ", fmts.ConvertAll(s => s.modeName).ToArray()); - } - } - - internal class WMAFormatInfo - { - public int codec; - public string codecName; - public int format; - public string formatName; - public string modeName; - public Guid subType; - public AudioPCMConfig pcm; - } - - public class LosslessEncoderSettings : EncoderSettings - { - public override string Extension => "wma"; - - public override string Name => "wma lossless"; - - public override int Priority => 1; - - public override bool Lossless => true; - - public LosslessEncoderSettings() - : base() - { - this.m_subType = MediaSubType.WMAudio_Lossless; - } - } - - public class LossyEncoderSettings : EncoderSettings - { - public override string Extension => "wma"; - - public override string Name => "wma lossy"; - - public override int Priority => 1; - - public LossyEncoderSettings() - : base() - { - this.m_subType = MediaSubType.WMAudioV9; - } - - public enum Codec - { - WMA9, - WMA10Pro - } - - [DefaultValue(Codec.WMA10Pro)] - [JsonProperty] - public Codec Version - { - get - { - return this.m_subType == MediaSubType.WMAudioV9 ? Codec.WMA10Pro : Codec.WMA9; - } - set - { - this.m_subType = value == Codec.WMA10Pro ? MediaSubType.WMAudioV9 : MediaSubType.WMAudioV8; - } - } - - [DefaultValue(true)] - [JsonProperty] - public bool VBR - { - get - { - return this.m_vbr; - } - set - { - this.m_vbr = value; - } - } - } - - public class AudioEncoder : IAudioDest - { - IWMWriter m_pEncoder; - private string outputPath; - private bool closed = false; - private bool fileCreated = false; - private bool writingBegan = false; - private long sampleCount, finalSampleCount; - - public long FinalSampleCount - { - set - { - this.finalSampleCount = value; - } - } - - public string Path - { - get { return this.outputPath; } - } - - EncoderSettings m_settings; - - public virtual AudioEncoderSettings Settings - { - get - { - return m_settings; - } - } - - public AudioEncoder(string path, EncoderSettings settings) - { - this.m_settings = settings; - this.outputPath = path; - - try - { - m_pEncoder = settings.GetWriter(); - int cInputs; - m_pEncoder.GetInputCount(out cInputs); - if (cInputs < 1) throw new InvalidOperationException(); - IWMInputMediaProps pInput; - m_pEncoder.GetInputProps(0, out pInput); - try - { - int cbType = 0; - AMMediaType pMediaType = null; - pInput.GetMediaType(pMediaType, ref cbType); - pMediaType = new AMMediaType(); - pMediaType.formatSize = cbType - Marshal.SizeOf(typeof(AMMediaType)); - pInput.GetMediaType(pMediaType, ref cbType); - try - { - var wfe = new WaveFormatExtensible(m_settings.PCM); - Marshal.FreeCoTaskMem(pMediaType.formatPtr); - pMediaType.formatPtr = IntPtr.Zero; - pMediaType.formatSize = 0; - pMediaType.formatPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(wfe)); - pMediaType.formatSize = Marshal.SizeOf(wfe); - Marshal.StructureToPtr(wfe, pMediaType.formatPtr, false); - pInput.SetMediaType(pMediaType); - m_pEncoder.SetInputProps(0, pInput); - } - finally - { - WMUtils.FreeWMMediaType(pMediaType); - } - } - finally - { - Marshal.ReleaseComObject(pInput); - } - } - catch (Exception ex) - { - if (m_pEncoder != null) - { - Marshal.ReleaseComObject(m_pEncoder); - m_pEncoder = null; - } - throw ex; - } - } - - public void Close() - { - if (!this.closed) - { - try - { - if (this.writingBegan) - { - m_pEncoder.EndWriting(); - this.writingBegan = false; - } - } - finally - { - if (m_pEncoder != null) - { - Marshal.ReleaseComObject(m_pEncoder); - m_pEncoder = null; - } - } - - this.closed = true; - } - } - - public void Delete() - { - if (this.outputPath == null) - throw new InvalidOperationException("This writer was not created from file."); - - if (!this.closed) - { - this.Close(); - - if (this.fileCreated) - { - File.Delete(this.outputPath); - this.fileCreated = false; - } - } - } - - public void Write(AudioBuffer buffer) - { - if (this.closed) - throw new InvalidOperationException("Writer already closed."); - - if (!this.fileCreated) - { - this.m_pEncoder.SetOutputFilename(outputPath); - this.fileCreated = true; - } - if (!this.writingBegan) - { - this.m_pEncoder.BeginWriting(); - this.writingBegan = true; - } - - buffer.Prepare(this); - INSSBuffer pSample; - m_pEncoder.AllocateSample(buffer.ByteLength, out pSample); - IntPtr pdwBuffer; - pSample.GetBuffer(out pdwBuffer); - pSample.SetLength(buffer.ByteLength); - Marshal.Copy(buffer.Bytes, 0, pdwBuffer, buffer.ByteLength); - long cnsSampleTime = sampleCount * 10000000L / Settings.PCM.SampleRate; - m_pEncoder.WriteSample(0, cnsSampleTime, SampleFlag.CleanPoint, pSample); - Marshal.ReleaseComObject(pSample); - sampleCount += buffer.Length; - } - } -} +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Text; +using WindowsMediaLib; +using WindowsMediaLib.Defs; + +namespace CUETools.Codecs.WMA +{ + [JsonObject(MemberSerialization.OptIn)] + public abstract class EncoderSettings : IAudioEncoderSettings + { + #region IAudioEncoderSettings implementation + [Browsable(false)] + public string Extension => "wma"; + + [Browsable(false)] + public abstract string Name { get; } + + [Browsable(false)] + public Type EncoderType => typeof(AudioEncoder); + + [Browsable(false)] + public abstract bool Lossless { get; } + + [Browsable(false)] + public int Priority => 1; + + [Browsable(false)] + public string SupportedModes => + string.Join(" ", GetFormats(null).ConvertAll(s => s.modeName).ToArray()); + + [Browsable(false)] + public string DefaultMode => + GetFormats(null).ConvertAll(s => s.modeName).FindLast(x => true) ?? ""; + + [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(); + } + + protected Guid m_subType; + protected bool m_vbr = true; + + public IWMWriter GetWriter() + { + IWMProfileManager pProfileManager = null; + try + { + WMUtils.WMCreateProfileManager(out pProfileManager); + var pCodecInfo3 = pProfileManager as IWMCodecInfo3; + // We have to use the same pProfileManager for enumeration, + // because it calls SetCodecEnumerationSetting, so chosenFormat.format + // would not point to the same format for a pProfileManager + // with different (default) settings, and GetCodecFormat + // would return the wrong stream config. + var formats = GetFormats(pProfileManager); + if (this.EncoderMode != "") + formats.RemoveAll(fmt => fmt.modeName != this.EncoderMode); + if (formats.Count < 1) + throw new NotSupportedException("codec/format not found"); + if (formats.Count > 1) + throw new NotSupportedException("codec/format ambiguous"); + var chosenFormat = formats[0]; + IWMStreamConfig pStreamConfig1; + pCodecInfo3.GetCodecFormat(MediaType.Audio, chosenFormat.codec, chosenFormat.format, out pStreamConfig1); + try + { + pStreamConfig1.SetStreamNumber(1); + IWMProfile pProfile; + pProfileManager.CreateEmptyProfile(WMVersion.V9_0, out pProfile); + try + { + pProfile.AddStream(pStreamConfig1); + IWMWriter pWriter; + WMUtils.WMCreateWriter(IntPtr.Zero, out pWriter); + try + { + pWriter.SetProfile(pProfile); + } + catch (Exception ex) + { + Marshal.ReleaseComObject(pWriter); + throw ex; + } + return pWriter; + } + finally + { + Marshal.ReleaseComObject(pProfile); + } + } + finally + { + Marshal.ReleaseComObject(pStreamConfig1); + } + } + finally + { + Marshal.ReleaseComObject(pProfileManager); + } + } + + internal IEnumerable EnumerateFormatInfo(IWMProfileManager pProfileManager2) + { + IWMProfileManager pProfileManager = null; + try + { + if (pProfileManager2 == null) + WMUtils.WMCreateProfileManager(out pProfileManager); + var pCodecInfo3 = (pProfileManager2 ?? pProfileManager) as IWMCodecInfo3; + int cCodecs; + pCodecInfo3.GetCodecInfoCount(MediaType.Audio, out cCodecs); + for (int iCodec = 0; iCodec < cCodecs; iCodec++) + { + int szCodecName = 0; + pCodecInfo3.GetCodecName(MediaType.Audio, iCodec, null, ref szCodecName); + var codecName = new StringBuilder(szCodecName); + pCodecInfo3.GetCodecName(MediaType.Audio, iCodec, codecName, ref szCodecName); + var attrDataType = new AttrDataType(); + int dwAttrSize = 0; + byte[] pAttrValue = new byte[4]; + pCodecInfo3.GetCodecProp(MediaType.Audio, iCodec, Constants.g_wszIsVBRSupported, out attrDataType, pAttrValue, ref dwAttrSize); + if (pAttrValue[0] != 1) + continue; + if (m_vbr) + { + pCodecInfo3.SetCodecEnumerationSetting(MediaType.Audio, iCodec, Constants.g_wszVBREnabled, AttrDataType.BOOL, new byte[] { 1, 0, 0, 0 }, 4); + pCodecInfo3.SetCodecEnumerationSetting(MediaType.Audio, iCodec, Constants.g_wszNumPasses, AttrDataType.DWORD, new byte[] { 1, 0, 0, 0 }, 4); + } + else + { + pCodecInfo3.SetCodecEnumerationSetting(MediaType.Audio, iCodec, Constants.g_wszVBREnabled, AttrDataType.BOOL, new byte[] { 0, 0, 0, 0 }, 4); + } + + int cFormat; + pCodecInfo3.GetCodecFormatCount(MediaType.Audio, iCodec, out cFormat); + for (int iFormat = 0; iFormat < cFormat; iFormat++) + { + IWMStreamConfig pStreamConfig; + int cchDesc = 1024; + StringBuilder szDesc = new StringBuilder(cchDesc); + pCodecInfo3.GetCodecFormatDesc(MediaType.Audio, iCodec, iFormat, out pStreamConfig, szDesc, ref cchDesc); + if (szDesc.ToString().Contains("(A/V)")) + continue; + try + { + var pProps = pStreamConfig as IWMMediaProps; + int cbType = 0; + AMMediaType pMediaType = null; + pProps.GetMediaType(pMediaType, ref cbType); + pMediaType = new AMMediaType(); + pMediaType.formatSize = cbType - Marshal.SizeOf(typeof(AMMediaType)); + pProps.GetMediaType(pMediaType, ref cbType); + try + { + if (pMediaType.majorType == MediaType.Audio && pMediaType.formatType == FormatType.WaveEx && pMediaType.subType == m_subType) + { + var pcm = WaveFormatExtensible.FromMediaType(pMediaType).GetConfig(); + if (PCM == null || (pcm.ChannelCount == PCM.ChannelCount && pcm.SampleRate == PCM.SampleRate && pcm.BitsPerSample >= PCM.BitsPerSample)) + yield return new WMAFormatInfo() + { + codec = iCodec, + codecName = codecName.ToString(), + format = iFormat, + formatName = szDesc.ToString(), + subType = pMediaType.subType, + pcm = pcm + }; + } + } + finally + { + WMUtils.FreeWMMediaType(pMediaType); + } + } + finally + { + Marshal.ReleaseComObject(pStreamConfig); + } + } + } + } + finally + { + if (pProfileManager != null) + Marshal.ReleaseComObject(pProfileManager); + } + } + + internal List GetFormats(IWMProfileManager pProfileManager) + { + var formats = new List(this.EnumerateFormatInfo(pProfileManager)); + formats.RemoveAll(fmt => formats.Exists(fmt2 => fmt2.pcm.BitsPerSample < fmt.pcm.BitsPerSample && fmt2.pcm.ChannelCount == fmt.pcm.ChannelCount && fmt2.pcm.SampleRate == fmt.pcm.SampleRate)); + if (formats.Count < 2) return formats; + int prefixLen = 0, suffixLen = 0; + while (formats.TrueForAll(s => s.formatName.Length > prefixLen && + s.formatName.Substring(0, prefixLen + 1) == + formats[0].formatName.Substring(0, prefixLen + 1))) + prefixLen++; + while (formats.TrueForAll(s => s.formatName.Length > suffixLen && + s.formatName.Substring(s.formatName.Length - suffixLen - 1) == + formats[0].formatName.Substring(formats[0].formatName.Length - suffixLen - 1))) + suffixLen++; + formats.ForEach(s => s.modeName = s.formatName.Substring(prefixLen, s.formatName.Length - suffixLen - prefixLen).Trim().Replace(' ', '_')); + int ix, iy; + formats.Sort((Comparison)((x, y) => int.TryParse(x.modeName, out ix) && int.TryParse(y.modeName, out iy) ? ix - iy : x.modeName.CompareTo(y.modeName))); + return formats; + } + } + + internal class WMAFormatInfo + { + public int codec; + public string codecName; + public int format; + public string formatName; + public string modeName; + public Guid subType; + public AudioPCMConfig pcm; + } + + public class LosslessEncoderSettings : EncoderSettings + { + public override string Name => "wma lossless"; + + public override bool Lossless => true; + + public LosslessEncoderSettings() + : base() + { + this.m_subType = MediaSubType.WMAudio_Lossless; + } + } + + public class LossyEncoderSettings : EncoderSettings + { + public override string Name => "wma lossy"; + + public override bool Lossless => false; + + public LossyEncoderSettings() + : base() + { + this.m_subType = MediaSubType.WMAudioV9; + } + + public enum Codec + { + WMA9, + WMA10Pro + } + + [DefaultValue(Codec.WMA10Pro)] + [JsonProperty] + public Codec Version + { + get + { + return this.m_subType == MediaSubType.WMAudioV9 ? Codec.WMA10Pro : Codec.WMA9; + } + set + { + this.m_subType = value == Codec.WMA10Pro ? MediaSubType.WMAudioV9 : MediaSubType.WMAudioV8; + } + } + + [DefaultValue(true)] + [JsonProperty] + public bool VBR + { + get + { + return this.m_vbr; + } + set + { + this.m_vbr = value; + } + } + } +} diff --git a/CUETools.Codecs.libFLAC/Reader.cs b/CUETools.Codecs.libFLAC/Reader.cs index e6aae27..ab49281 100644 --- a/CUETools.Codecs.libFLAC/Reader.cs +++ b/CUETools.Codecs.libFLAC/Reader.cs @@ -5,20 +5,36 @@ using System.IO; using System.Runtime.InteropServices; using System.Text; using CUETools.Codecs; +using Newtonsoft.Json; namespace CUETools.Codecs.libFLAC { - public class Settings : AudioDecoderSettings + [JsonObject(MemberSerialization.OptIn)] + public class DecoderSettings : IAudioDecoderSettings { - public override string Extension => "flac"; + #region IAudioDecoderSettings implementation + [Browsable(false)] + public string Extension => "flac"; - public override string Name => "libFLAC"; + [Browsable(false)] + public string Name => "libFLAC"; - public override Type DecoderType => typeof(Reader); + [Browsable(false)] + public Type DecoderType => typeof(Reader); - public override int Priority => 1; + [Browsable(false)] + public int Priority => 1; - public Settings() : base() { } + public IAudioDecoderSettings Clone() + { + return MemberwiseClone() as IAudioDecoderSettings; + } + #endregion + + public DecoderSettings() + { + this.Init(); + } } public unsafe class Reader : IAudioSource @@ -282,7 +298,7 @@ namespace CUETools.Codecs.libFLAC return m_stream.Position == m_stream.Length ? 1 : 0; } - public AudioDecoderSettings Settings => null; + public IAudioDecoderSettings Settings => null; public AudioPCMConfig PCM => m_pcm; diff --git a/CUETools.Codecs.libFLAC/Writer.cs b/CUETools.Codecs.libFLAC/Writer.cs index 1e7fa61..f29f52b 100644 --- a/CUETools.Codecs.libFLAC/Writer.cs +++ b/CUETools.Codecs.libFLAC/Writer.cs @@ -10,21 +10,54 @@ using Newtonsoft.Json; namespace CUETools.Codecs.libFLAC { [JsonObject(MemberSerialization.OptIn)] - public class EncoderSettings : AudioEncoderSettings + public class EncoderSettings : IAudioEncoderSettings { - public override string Extension => "flac"; + #region IAudioEncoderSettings implementation + [Browsable(false)] + public string Extension => "flac"; - public override string Name => "libFLAC"; + [Browsable(false)] + public string Name => "libFLAC"; - public override Type EncoderType => typeof(Encoder); + [Browsable(false)] + public Type EncoderType => typeof(Encoder); - public override int Priority => 2; + [Browsable(false)] + public bool Lossless => true; - public override bool Lossless => true; + [Browsable(false)] + public int Priority => 2; + + [Browsable(false)] + public string SupportedModes => "0 1 2 3 4 5 6 7 8"; + + [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() - : base("0 1 2 3 4 5 6 7 8", "5") { + this.Init(); } [DefaultValue(false)] @@ -74,7 +107,7 @@ namespace CUETools.Codecs.libFLAC { } - public AudioEncoderSettings Settings => m_settings; + public IAudioEncoderSettings Settings => m_settings; public string Path { get => m_path; } @@ -239,7 +272,7 @@ namespace CUETools.Codecs.libFLAC FLACDLL.FLAC__stream_encoder_set_metadata(m_encoder, metadata, metadataCount); FLACDLL.FLAC__stream_encoder_set_verify(m_encoder, m_settings.Verify ? 1 : 0); FLACDLL.FLAC__stream_encoder_set_do_md5(m_encoder, m_settings.MD5Sum ? 1 : 0); - FLACDLL.FLAC__stream_encoder_set_compression_level(m_encoder, m_settings.EncoderModeIndex); + FLACDLL.FLAC__stream_encoder_set_compression_level(m_encoder, m_settings.GetEncoderModeIndex()); if (m_finalSampleCount != 0) FLACDLL.FLAC__stream_encoder_set_total_samples_estimate(m_encoder, m_finalSampleCount); if (m_settings.BlockSize > 0) diff --git a/CUETools.Codecs.libmp3lame/LameWriter.cs b/CUETools.Codecs.libmp3lame/LameWriter.cs index 688ea74..8d75af3 100644 --- a/CUETools.Codecs.libmp3lame/LameWriter.cs +++ b/CUETools.Codecs.libmp3lame/LameWriter.cs @@ -35,7 +35,7 @@ namespace CUETools.Codecs.libmp3lame private LameEncoderSettings m_settings; - public virtual AudioEncoderSettings Settings + public IAudioEncoderSettings Settings { get { diff --git a/CUETools.Codecs.libmp3lame/LameWriterCBRSettings.cs b/CUETools.Codecs.libmp3lame/LameWriterCBRSettings.cs index 3d16011..46f1926 100644 --- a/CUETools.Codecs.libmp3lame/LameWriterCBRSettings.cs +++ b/CUETools.Codecs.libmp3lame/LameWriterCBRSettings.cs @@ -9,12 +9,14 @@ namespace CUETools.Codecs.libmp3lame [JsonObject(MemberSerialization.OptIn)] public class CBREncoderSettings : LameEncoderSettings { - public override string Extension => "mp3"; - public override string Name => "libmp3lame-CBR"; public override int Priority => 1; + public override string SupportedModes => "96 128 192 256 320"; + + public override string DefaultMode => "256"; + public static readonly int[] bps_table = new int[] { 96, 128, 192, 256, 320 }; [JsonProperty] @@ -22,14 +24,14 @@ namespace CUETools.Codecs.libmp3lame public LameQuality Quality { get; set; } public CBREncoderSettings() - : base("96 128 192 256 320", "256") + : base() { } public override void Apply(IntPtr lame) { libmp3lamedll.lame_set_VBR(lame, (int)LameVbrMode.Off); - libmp3lamedll.lame_set_brate(lame, CBREncoderSettings.bps_table[this.EncoderModeIndex]); + libmp3lamedll.lame_set_brate(lame, CBREncoderSettings.bps_table[this.GetEncoderModeIndex()]); libmp3lamedll.lame_set_quality(lame, (int)this.Quality); } } diff --git a/CUETools.Codecs.libmp3lame/LameWriterSettings.cs b/CUETools.Codecs.libmp3lame/LameWriterSettings.cs index c2bc082..f84527b 100644 --- a/CUETools.Codecs.libmp3lame/LameWriterSettings.cs +++ b/CUETools.Codecs.libmp3lame/LameWriterSettings.cs @@ -1,21 +1,61 @@ -using System; +using Newtonsoft.Json; +using System; using System.Collections.Generic; +using System.ComponentModel; using System.Text; namespace CUETools.Codecs.libmp3lame { - public class LameEncoderSettings : AudioEncoderSettings + public abstract class LameEncoderSettings : IAudioEncoderSettings { - public override Type EncoderType => typeof(AudioEncoder); + #region IAudioEncoderSettings implementation + [Browsable(false)] + public string Extension => "mp3"; - public LameEncoderSettings(string modes, string defaultMode) - : base(modes, defaultMode) + [Browsable(false)] + public abstract string Name { get; } + + [Browsable(false)] + public Type EncoderType => typeof(AudioEncoder); + + [Browsable(false)] + public bool Lossless => false; + + [Browsable(false)] + public abstract int Priority { get; } + + [Browsable(false)] + public abstract string SupportedModes { get; } + + [Browsable(false)] + public abstract string DefaultMode { get; } + + [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 LameEncoderSettings() + { + this.Init(); } - public virtual void Apply(IntPtr lame) - { - throw new MethodAccessException(); - } + public abstract void Apply(IntPtr lame); } } diff --git a/CUETools.Codecs.libmp3lame/LameWriterVBRSettings.cs b/CUETools.Codecs.libmp3lame/LameWriterVBRSettings.cs index caabc7e..e390a19 100644 --- a/CUETools.Codecs.libmp3lame/LameWriterVBRSettings.cs +++ b/CUETools.Codecs.libmp3lame/LameWriterVBRSettings.cs @@ -9,25 +9,27 @@ namespace CUETools.Codecs.libmp3lame [JsonObject(MemberSerialization.OptIn)] public class VBREncoderSettings : LameEncoderSettings { - public override string Extension => "mp3"; - public override string Name => "libmp3lame-VBR"; public override int Priority => 2; + public override string SupportedModes => "V9 V8 V7 V6 V5 V4 V3 V2 V1 V0"; + + public override string DefaultMode => "V2"; + [JsonProperty] [DefaultValue(LameQuality.High)] public LameQuality Quality { get; set; } public VBREncoderSettings() - : base("V9 V8 V7 V6 V5 V4 V3 V2 V1 V0", "V2") + : base() { } public override void Apply(IntPtr lame) { libmp3lamedll.lame_set_VBR(lame, (int)LameVbrMode.Default); - libmp3lamedll.lame_set_VBR_quality(lame, 9 - this.EncoderModeIndex); + libmp3lamedll.lame_set_VBR_quality(lame, 9 - this.GetEncoderModeIndex()); libmp3lamedll.lame_set_quality(lame, (int)this.Quality); } } diff --git a/CUETools.Codecs.libwavpack/Reader.cs b/CUETools.Codecs.libwavpack/Reader.cs index 501ff49..2ddf59f 100644 --- a/CUETools.Codecs.libwavpack/Reader.cs +++ b/CUETools.Codecs.libwavpack/Reader.cs @@ -5,20 +5,36 @@ using System.IO; using System.Runtime.InteropServices; using System.Text; using CUETools.Codecs; +using Newtonsoft.Json; namespace CUETools.Codecs.libwavpack { - public class DecoderSettings : AudioDecoderSettings + [JsonObject(MemberSerialization.OptIn)] + public class DecoderSettings : IAudioDecoderSettings { - public override string Extension => "wv"; + #region IAudioDecoderSettings implementation + [Browsable(false)] + public string Extension => "wv"; - public override string Name => "libwavpack"; + [Browsable(false)] + public string Name => "libwavpack"; - public override Type DecoderType => typeof(AudioDecoder); + [Browsable(false)] + public Type DecoderType => typeof(AudioDecoder); - public override int Priority => 1; + [Browsable(false)] + public int Priority => 1; - public DecoderSettings() : base() { } + public IAudioDecoderSettings Clone() + { + return MemberwiseClone() as IAudioDecoderSettings; + } + #endregion + + public DecoderSettings() + { + this.Init(); + } } public unsafe class AudioDecoder : IAudioSource @@ -77,7 +93,7 @@ namespace CUETools.Codecs.libwavpack private DecoderSettings m_settings; - public AudioDecoderSettings Settings => m_settings; + public IAudioDecoderSettings Settings => m_settings; public AudioPCMConfig PCM => pcm; diff --git a/CUETools.Codecs.libwavpack/Writer.cs b/CUETools.Codecs.libwavpack/Writer.cs index 7f92b20..c55e54d 100644 --- a/CUETools.Codecs.libwavpack/Writer.cs +++ b/CUETools.Codecs.libwavpack/Writer.cs @@ -11,21 +11,54 @@ using Newtonsoft.Json; namespace CUETools.Codecs.libwavpack { [JsonObject(MemberSerialization.OptIn)] - public class EncoderSettings : AudioEncoderSettings + public class EncoderSettings : IAudioEncoderSettings { - public override string Extension => "wv"; + #region IAudioEncoderSettings implementation + [Browsable(false)] + public string Extension => "wv"; - public override string Name => "libwavpack"; + [Browsable(false)] + public string Name => "libwavpack"; - public override Type EncoderType => typeof(AudioEncoder); + [Browsable(false)] + public Type EncoderType => typeof(AudioEncoder); - public override int Priority => 1; + [Browsable(false)] + public bool Lossless => true; - public override bool Lossless => true; + [Browsable(false)] + public int Priority => 1; + + [Browsable(false)] + public string SupportedModes => "fast normal high high+"; + + [Browsable(false)] + public string DefaultMode => "normal"; + + [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() - : base("fast normal high high+", "normal") { + this.Init(); } [DefaultValue(0)] @@ -70,7 +103,7 @@ namespace CUETools.Codecs.libwavpack throw new Exception("bits per sample must be 16..24"); } - public AudioEncoderSettings Settings => m_settings; + public IAudioEncoderSettings Settings => m_settings; public string Path { get => m_path; } @@ -186,7 +219,7 @@ namespace CUETools.Codecs.libwavpack config.channel_mask = (int)m_settings.PCM.ChannelMask; config.sample_rate = m_settings.PCM.SampleRate; config.flags = ConfigFlags.CONFIG_COMPATIBLE_WRITE; - Int32 _compressionMode = m_settings.EncoderModeIndex; + Int32 _compressionMode = m_settings.GetEncoderModeIndex(); if (_compressionMode == 0) config.flags |= ConfigFlags.CONFIG_FAST_FLAG; if (_compressionMode == 2) config.flags |= ConfigFlags.CONFIG_HIGH_FLAG; if (_compressionMode == 3) config.flags |= ConfigFlags.CONFIG_HIGH_FLAG | ConfigFlags.CONFIG_VERY_HIGH_FLAG; diff --git a/CUETools.Codecs/AudioDecoderSettings.cs b/CUETools.Codecs/AudioDecoderSettings.cs index c612cfc..8b9189b 100644 --- a/CUETools.Codecs/AudioDecoderSettings.cs +++ b/CUETools.Codecs/AudioDecoderSettings.cs @@ -15,45 +15,17 @@ namespace CUETools.Codecs Type DecoderType { get; } - bool Lossless { get; } - int Priority { get; } + + IAudioDecoderSettings Clone(); } - [JsonObject(MemberSerialization.OptIn)] - public class AudioDecoderSettings: IAudioDecoderSettings + public static class IAudioDecoderSettingsExtensions { - [Browsable(false)] - public virtual string Name => null; - - [Browsable(false)] - public virtual string Extension => null; - - [Browsable(false)] - public virtual Type DecoderType => null; - - [Browsable(false)] - public virtual bool Lossless => true; - - [Browsable(false)] - public virtual int Priority => 0; - - public AudioDecoderSettings() - { - // Iterate through each property and call ResetValue() - foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this)) - property.ResetValue(this); - } - - public AudioDecoderSettings Clone() - { - return this.MemberwiseClone() as AudioDecoderSettings; - } - - public bool HasBrowsableAttributes() + public static bool HasBrowsableAttributes(this IAudioDecoderSettings settings) { bool hasBrowsable = false; - foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this)) + foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(settings)) { bool isBrowsable = true; foreach (var attribute in property.Attributes) @@ -65,5 +37,12 @@ namespace CUETools.Codecs } return hasBrowsable; } + + public static void Init(this IAudioDecoderSettings settings) + { + // Iterate through each property and call ResetValue() + foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(settings)) + property.ResetValue(settings); + } } } diff --git a/CUETools.Codecs/AudioEncoderSettings.cs b/CUETools.Codecs/AudioEncoderSettings.cs index 99c5e3c..6d86eb3 100644 --- a/CUETools.Codecs/AudioEncoderSettings.cs +++ b/CUETools.Codecs/AudioEncoderSettings.cs @@ -4,6 +4,15 @@ using System.Collections.Generic; using System.Text; using Newtonsoft.Json; +#if NET20 + namespace System.Runtime.CompilerServices + { + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class + | AttributeTargets.Method)] + public sealed class ExtensionAttribute : Attribute { } + } +#endif + namespace CUETools.Codecs { public interface IAudioEncoderSettings @@ -17,67 +26,28 @@ namespace CUETools.Codecs bool Lossless { get; } int Priority { get; } + + string SupportedModes { get; } + + string DefaultMode { get; } + + string EncoderMode { get; set; } + + AudioPCMConfig PCM { get; set; } + + int BlockSize { get; set; } + + int Padding { get; set; } + + IAudioEncoderSettings Clone(); } - [JsonObject(MemberSerialization.OptIn)] - public class AudioEncoderSettings : IAudioEncoderSettings + public static class IAudioEncoderSettingsExtensions { - [Browsable(false)] - public virtual string Name => null; - - [Browsable(false)] - public virtual string Extension => null; - - [Browsable(false)] - public virtual Type EncoderType => null; - - [Browsable(false)] - public virtual bool Lossless => false; - - [Browsable(false)] - public virtual int Priority => 0; - - public AudioEncoderSettings() - : this("", "") - { - } - - public AudioEncoderSettings(AudioPCMConfig pcm) - : this("", "") - { - this.PCM = pcm; - } - - public AudioEncoderSettings(string supported_modes, string default_mode) - { - // Iterate through each property and call ResetValue() - foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this)) - property.ResetValue(this); - this.m_supported_modes = supported_modes; - this.m_default_mode = default_mode; - if (default_mode == "") - GetSupportedModes(out default_mode); - this.EncoderMode = default_mode; - } - - protected string m_supported_modes; - protected string m_default_mode; - - public virtual string GetSupportedModes(out string defaultMode) - { - defaultMode = m_default_mode; - return this.m_supported_modes; - } - - public AudioEncoderSettings Clone() - { - return this.MemberwiseClone() as AudioEncoderSettings; - } - - public bool HasBrowsableAttributes() + public static bool HasBrowsableAttributes(this IAudioEncoderSettings settings) { bool hasBrowsable = false; - foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this)) + foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(settings)) { bool isBrowsable = true; foreach (var attribute in property.Attributes) @@ -90,101 +60,65 @@ namespace CUETools.Codecs return hasBrowsable; } - protected void SetDefaultValuesForMode() + public static int GetEncoderModeIndex(this IAudioEncoderSettings settings) { - foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this)) - if (!property.CanResetValue(this)) + return new List(settings.SupportedModes.Split(' ')).FindIndex(m => m == settings.EncoderMode); + } + + public static void SetEncoderModeIndex(this IAudioEncoderSettings settings, int value) + { + string[] modes = settings.SupportedModes.Split(' '); + if (modes.Length == 0 && value < 0) + return; + if (value < 0 || value >= modes.Length) + throw new IndexOutOfRangeException(); + settings.EncoderMode = modes[value]; + } + + public static void SetDefaultValuesForMode(this IAudioEncoderSettings settings) + { + foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(settings)) + if (!property.CanResetValue(settings)) foreach (var attribute in property.Attributes) if (attribute is DefaultValueForModeAttribute) { var defaultValueForMode = attribute as DefaultValueForModeAttribute; - property.SetValue(this, defaultValueForMode.m_values[EncoderModeIndex]); + property.SetValue(settings, defaultValueForMode.m_values[settings.GetEncoderModeIndex()]); } } - public bool HasDefaultValuesForMode(int index) + public static bool HasDefaultValuesForMode(this IAudioEncoderSettings settings, int index) { bool res = true; - foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this)) + foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(settings)) foreach (var attribute in property.Attributes) if (attribute is DefaultValueForModeAttribute) { var defaultValueForMode = attribute as DefaultValueForModeAttribute; - res &= property.GetValue(this).Equals(defaultValueForMode.m_values[index]); + res &= property.GetValue(settings).Equals(defaultValueForMode.m_values[index]); } return res; } - public int GuessEncoderMode() + public static int GuessEncoderMode(this IAudioEncoderSettings settings) { - string defaultMode; - string[] modes = this.GetSupportedModes(out defaultMode).Split(' '); + // return new List(settings.SupportedModes.Split(' ')).FindIndex(m => settings.HasDefaultValuesForMode(m)); + string[] modes = settings.SupportedModes.Split(' '); if (modes == null || modes.Length < 1) return -1; for (int i = 0; i < modes.Length; i++) - if (HasDefaultValuesForMode(i)) + if (settings.HasDefaultValuesForMode(i)) return i; return -1; } - [Browsable(false)] - public AudioPCMConfig PCM + public static void Init(this IAudioEncoderSettings settings, AudioPCMConfig pcm = null) { - get; - set; - } - - - [Browsable(false)] - [DefaultValue(0)] - public int BlockSize - { - get; - set; - } - - [Browsable(false)] - [DefaultValue(4096)] - public int Padding - { - get; - set; - } - - [Browsable(false)] - [DefaultValue("")] - [JsonProperty] - public string EncoderMode - { - get; - set; - } - - [Browsable(false)] - public int EncoderModeIndex - { - get - { - string defaultMode; - string[] modes = this.GetSupportedModes(out defaultMode).Split(' '); - if (modes == null || modes.Length < 1) - return -1; - for (int i = 0; i < modes.Length; i++) - if (modes[i] == this.EncoderMode) - return i; - return -1; - } - - set - { - string defaultMode; - string[] modes = this.GetSupportedModes(out defaultMode).Split(' '); - if (modes.Length == 0 && value < 0) - return; - if (value < 0 || value >= modes.Length) - throw new IndexOutOfRangeException(); - this.EncoderMode = modes[value]; - } + // Iterate through each property and call ResetValue() + foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(settings)) + property.ResetValue(settings); + settings.EncoderMode = settings.DefaultMode; + settings.PCM = pcm; } } } diff --git a/CUETools.Codecs/AudioPipe.cs b/CUETools.Codecs/AudioPipe.cs index ed871e6..1545b9d 100644 --- a/CUETools.Codecs/AudioPipe.cs +++ b/CUETools.Codecs/AudioPipe.cs @@ -18,7 +18,7 @@ namespace CUETools.Codecs private bool own; private ThreadPriority priority; - public AudioDecoderSettings Settings { get { return null; } } + public IAudioDecoderSettings Settings => null; public long Position { diff --git a/CUETools.Codecs/CUEToolsCodecsConfig.cs b/CUETools.Codecs/CUEToolsCodecsConfig.cs index 51d2d30..aeef7b9 100644 --- a/CUETools.Codecs/CUEToolsCodecsConfig.cs +++ b/CUETools.Codecs/CUEToolsCodecsConfig.cs @@ -14,8 +14,8 @@ namespace CUETools.Codecs { [JsonIgnore] public Dictionary formats; - public List encoders; - public List decoders; + public List encoders; + public List decoders; [JsonIgnore] public EncoderListViewModel encodersViewModel; [JsonIgnore] @@ -23,8 +23,8 @@ namespace CUETools.Codecs public CUEToolsCodecsConfig() { - encoders = new List(); - decoders = new List(); + encoders = new List(); + decoders = new List(); encodersViewModel = new EncoderListViewModel(encoders); decodersViewModel = new DecoderListViewModel(decoders); formats = new Dictionary(); @@ -32,8 +32,8 @@ namespace CUETools.Codecs public CUEToolsCodecsConfig(CUEToolsCodecsConfig src) { - encoders = new List(); - decoders = new List(); + encoders = new List(); + decoders = new List(); src.encoders.ForEach(item => encoders.Add(item.Clone())); src.decoders.ForEach(item => decoders.Add(item.Clone())); encodersViewModel = new EncoderListViewModel(encoders); @@ -43,10 +43,10 @@ namespace CUETools.Codecs formats.Add(fmt.Key, fmt.Value.Clone(this)); } - public void Init(List src_encoders, List src_decoders) + public void Init(List src_encoders, List src_decoders) { - encoders = new List(); - decoders = new List(); + encoders = new List(); + decoders = new List(); src_encoders.ForEach(item => encoders.Add(item.Clone())); src_decoders.ForEach(item => decoders.Add(item.Clone())); @@ -74,16 +74,16 @@ namespace CUETools.Codecs decodersViewModel = new DecoderListViewModel(decoders); formats = new Dictionary(); - formats.Add("flac", new CUEToolsFormat("flac", CUEToolsTagger.TagLibSharp, true, false, true, true, encodersViewModel.GetDefault("flac", true), null, decodersViewModel.GetDefault("flac", true))); - formats.Add("wv", new CUEToolsFormat("wv", CUEToolsTagger.TagLibSharp, true, false, true, true, encodersViewModel.GetDefault("wv", true), null, decodersViewModel.GetDefault("wv", true))); - formats.Add("ape", new CUEToolsFormat("ape", CUEToolsTagger.TagLibSharp, true, false, true, true, encodersViewModel.GetDefault("ape", true), null, decodersViewModel.GetDefault("ape", true))); - formats.Add("tta", new CUEToolsFormat("tta", CUEToolsTagger.APEv2, true, false, false, true, encodersViewModel.GetDefault("tta", true), null, decodersViewModel.GetDefault("tta", true))); - formats.Add("m2ts", new CUEToolsFormat("m2ts", CUEToolsTagger.APEv2, true, false, false, true, null, null, decodersViewModel.GetDefault("m2ts", true))); - formats.Add("mpls", new CUEToolsFormat("mpls", CUEToolsTagger.APEv2, true, false, false, true, null, null, decodersViewModel.GetDefault("mpls", true))); - formats.Add("wav", new CUEToolsFormat("wav", CUEToolsTagger.TagLibSharp, true, false, false, true, encodersViewModel.GetDefault("wav", true), null, decodersViewModel.GetDefault("wav", true))); - formats.Add("m4a", new CUEToolsFormat("m4a", CUEToolsTagger.TagLibSharp, true, true, false, true, encodersViewModel.GetDefault("m4a", true), encodersViewModel.GetDefault("m4a", false), decodersViewModel.GetDefault("m4a", true))); - formats.Add("tak", new CUEToolsFormat("tak", CUEToolsTagger.APEv2, true, false, true, true, encodersViewModel.GetDefault("tak", true), null, decodersViewModel.GetDefault("tak", true))); - formats.Add("wma", new CUEToolsFormat("wma", CUEToolsTagger.TagLibSharp, true, true, false, true, encodersViewModel.GetDefault("wma", true), encodersViewModel.GetDefault("wma", false), decodersViewModel.GetDefault("wma", true))); + formats.Add("flac", new CUEToolsFormat("flac", CUEToolsTagger.TagLibSharp, true, false, true, true, encodersViewModel.GetDefault("flac", true), null, decodersViewModel.GetDefault("flac"))); + formats.Add("wv", new CUEToolsFormat("wv", CUEToolsTagger.TagLibSharp, true, false, true, true, encodersViewModel.GetDefault("wv", true), null, decodersViewModel.GetDefault("wv"))); + formats.Add("ape", new CUEToolsFormat("ape", CUEToolsTagger.TagLibSharp, true, false, true, true, encodersViewModel.GetDefault("ape", true), null, decodersViewModel.GetDefault("ape"))); + formats.Add("tta", new CUEToolsFormat("tta", CUEToolsTagger.APEv2, true, false, false, true, encodersViewModel.GetDefault("tta", true), null, decodersViewModel.GetDefault("tta"))); + formats.Add("m2ts", new CUEToolsFormat("m2ts", CUEToolsTagger.APEv2, true, false, false, true, null, null, decodersViewModel.GetDefault("m2ts"))); + formats.Add("mpls", new CUEToolsFormat("mpls", CUEToolsTagger.APEv2, true, false, false, true, null, null, decodersViewModel.GetDefault("mpls"))); + formats.Add("wav", new CUEToolsFormat("wav", CUEToolsTagger.TagLibSharp, true, false, false, true, encodersViewModel.GetDefault("wav", true), null, decodersViewModel.GetDefault("wav"))); + formats.Add("m4a", new CUEToolsFormat("m4a", CUEToolsTagger.TagLibSharp, true, true, false, true, encodersViewModel.GetDefault("m4a", true), encodersViewModel.GetDefault("m4a", false), decodersViewModel.GetDefault("m4a"))); + formats.Add("tak", new CUEToolsFormat("tak", CUEToolsTagger.APEv2, true, false, true, true, encodersViewModel.GetDefault("tak", true), null, decodersViewModel.GetDefault("tak"))); + formats.Add("wma", new CUEToolsFormat("wma", CUEToolsTagger.TagLibSharp, true, true, false, true, encodersViewModel.GetDefault("wma", true), encodersViewModel.GetDefault("wma", false), decodersViewModel.GetDefault("wma"))); formats.Add("mp3", new CUEToolsFormat("mp3", CUEToolsTagger.TagLibSharp, false, true, false, true, null, encodersViewModel.GetDefault("mp3", false), null)); formats.Add("ogg", new CUEToolsFormat("ogg", CUEToolsTagger.TagLibSharp, false, true, false, true, null, encodersViewModel.GetDefault("ogg", false), null)); formats.Add("opus", new CUEToolsFormat("opus", CUEToolsTagger.TagLibSharp, false, true, false, true, null, encodersViewModel.GetDefault("opus", false), null)); diff --git a/CUETools.Codecs/CUEToolsFormat.cs b/CUETools.Codecs/CUEToolsFormat.cs index 9686053..cbb9706 100644 --- a/CUETools.Codecs/CUEToolsFormat.cs +++ b/CUETools.Codecs/CUEToolsFormat.cs @@ -34,7 +34,7 @@ public CUEToolsFormat Clone(CUEToolsCodecsConfig cfg) { var res = this.MemberwiseClone() as CUEToolsFormat; - if (decoder != null) cfg.decodersViewModel.TryGetValue(decoder.Settings.Extension, decoder.Lossless, decoder.Settings.Name, out res.decoder); + if (decoder != null) cfg.decodersViewModel.TryGetValue(decoder.Settings.Extension, decoder.Settings.Name, out res.decoder); if (encoderLossy != null) cfg.encodersViewModel.TryGetValue(encoderLossy.Settings.Extension, encoderLossy.Lossless, encoderLossy.Settings.Name, out res.encoderLossy); if (encoderLossless != null) cfg.encodersViewModel.TryGetValue(encoderLossless.Settings.Extension, encoderLossless.Lossless, encoderLossless.Settings.Name, out res.encoderLossless); return res; diff --git a/CUETools.Codecs/CommandLine/AudioDecoder.cs b/CUETools.Codecs/CommandLine/AudioDecoder.cs index d49637b..40e63ca 100644 --- a/CUETools.Codecs/CommandLine/AudioDecoder.cs +++ b/CUETools.Codecs/CommandLine/AudioDecoder.cs @@ -11,7 +11,7 @@ namespace CUETools.Codecs.CommandLine WAV.AudioDecoder rdr; private DecoderSettings m_settings; - public AudioDecoderSettings Settings => m_settings; + public IAudioDecoderSettings Settings => m_settings; public long Position { diff --git a/CUETools.Codecs/CommandLine/AudioEncoder.cs b/CUETools.Codecs/CommandLine/AudioEncoder.cs index a14e5d0..24d23ad 100644 --- a/CUETools.Codecs/CommandLine/AudioEncoder.cs +++ b/CUETools.Codecs/CommandLine/AudioEncoder.cs @@ -30,7 +30,7 @@ namespace CUETools.Codecs.CommandLine // !!!! Must not start the process in constructor, so that we can set CompressionLevel via Settings! private EncoderSettings m_settings; - public AudioEncoderSettings Settings => m_settings; + public IAudioEncoderSettings Settings => m_settings; public string Path { get { return _path; } } diff --git a/CUETools.Codecs/CommandLine/DecoderSettings.cs b/CUETools.Codecs/CommandLine/DecoderSettings.cs index 14d58bf..9bdba0d 100644 --- a/CUETools.Codecs/CommandLine/DecoderSettings.cs +++ b/CUETools.Codecs/CommandLine/DecoderSettings.cs @@ -7,37 +7,48 @@ using Newtonsoft.Json; namespace CUETools.Codecs.CommandLine { [JsonObject(MemberSerialization.OptIn)] - public class DecoderSettings : AudioDecoderSettings + public class DecoderSettings : IAudioDecoderSettings { - public override string Name => name; + #region IAudioDecoderSettings implementation + [DefaultValue("")] + [JsonProperty] + public string Name { get; set; } - public override string Extension => extension; + [DefaultValue("")] + [JsonProperty] + public string Extension { get; set; } + + [Browsable(false)] + public Type DecoderType => typeof(AudioDecoder); + + [Browsable(false)] + public int Priority => 2; + + public IAudioDecoderSettings Clone() + { + return MemberwiseClone() as IAudioDecoderSettings; + } + #endregion public DecoderSettings() - : base() { + this.Init(); } public DecoderSettings( - string _name, - string _extension, - string _path, - string _parameters) + string name, + string extension, + string path, + string parameters) : base() { - name = _name; - extension = _extension; - Path = _path; - Parameters = _parameters; + Name = name; + Extension = extension; + Path = path; + Parameters = parameters; } - [JsonProperty] - public string name; - - [JsonProperty] - public string extension; - - [DefaultValue(null)] + [DefaultValue("")] [JsonProperty] public string Path { @@ -45,7 +56,7 @@ namespace CUETools.Codecs.CommandLine set; } - [DefaultValue(null)] + [DefaultValue("")] [JsonProperty] public string Parameters { diff --git a/CUETools.Codecs/CommandLine/EncoderSettings.cs b/CUETools.Codecs/CommandLine/EncoderSettings.cs index c1d334f..004e7bf 100644 --- a/CUETools.Codecs/CommandLine/EncoderSettings.cs +++ b/CUETools.Codecs/CommandLine/EncoderSettings.cs @@ -7,50 +7,79 @@ using Newtonsoft.Json; namespace CUETools.Codecs.CommandLine { [JsonObject(MemberSerialization.OptIn)] - public class EncoderSettings : AudioEncoderSettings + public class EncoderSettings : IAudioEncoderSettings { - public override string Name => name; + #region IAudioEncoderSettings implementation + [DefaultValue("")] + [JsonProperty] + public string Name { get; set; } - public override string Extension => extension; + [DefaultValue("")] + [JsonProperty] + public string Extension { get; set; } - public override Type EncoderType => typeof(AudioEncoder); + [Browsable(false)] + public Type EncoderType => typeof(AudioEncoder); - public override bool Lossless => lossless; + [JsonProperty] + public bool Lossless { get; set; } + + [Browsable(false)] + public int Priority => 0; + + [DefaultValue("")] + [JsonProperty] + public string SupportedModes { get; set; } + + public string DefaultMode => EncoderMode; + + [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() - : base() { + this.Init(); } public EncoderSettings( - string _name, - string _extension, - bool _lossless, - string _supported_modes, - string _default_mode, - string _path, - string _parameters + string name, + string extension, + bool lossless, + string supportedModes, + string defaultMode, + string path, + string parameters ) { - name = _name; - extension = _extension; - lossless = _lossless; - SupportedModes = _supported_modes; - EncoderMode = _default_mode; - Path = _path; - Parameters = _parameters; + this.Init(); + Name = name; + Extension = extension; + Lossless = lossless; + SupportedModes = supportedModes; + Path = path; + EncoderMode = defaultMode; + Parameters = parameters; } - [JsonProperty] - public string name; - - [JsonProperty] - public string extension; - - [JsonProperty] - public bool lossless; - - [DefaultValue(null)] + [DefaultValue("")] [JsonProperty] public string Path { @@ -58,25 +87,12 @@ namespace CUETools.Codecs.CommandLine set; } - [DefaultValue(null)] + [DefaultValue("")] [JsonProperty] public string Parameters { get; set; } - - [JsonProperty] - public string SupportedModes - { - get - { - return m_supported_modes; - } - set - { - m_supported_modes = value; - } - } } } diff --git a/CUETools.Codecs/IAudioDest.cs b/CUETools.Codecs/IAudioDest.cs index c0394a5..46b8f74 100644 --- a/CUETools.Codecs/IAudioDest.cs +++ b/CUETools.Codecs/IAudioDest.cs @@ -2,7 +2,7 @@ { public interface IAudioDest { - AudioEncoderSettings Settings { get; } + IAudioEncoderSettings Settings { get; } string Path { get; } long FinalSampleCount { set; } diff --git a/CUETools.Codecs/IAudioSource.cs b/CUETools.Codecs/IAudioSource.cs index 3d7d337..3dbb2f0 100644 --- a/CUETools.Codecs/IAudioSource.cs +++ b/CUETools.Codecs/IAudioSource.cs @@ -2,7 +2,7 @@ { public interface IAudioSource { - AudioDecoderSettings Settings { get; } + IAudioDecoderSettings Settings { get; } AudioPCMConfig PCM { get; } string Path { get; } diff --git a/CUETools.Codecs/NULL/AudioDecoder.cs b/CUETools.Codecs/NULL/AudioDecoder.cs index 2944c06..b4b78ff 100644 --- a/CUETools.Codecs/NULL/AudioDecoder.cs +++ b/CUETools.Codecs/NULL/AudioDecoder.cs @@ -6,7 +6,7 @@ private AudioPCMConfig pcm; private int _sampleVal; - public AudioDecoderSettings Settings { get { return null; } } + public IAudioDecoderSettings Settings => null; public long Length { diff --git a/CUETools.Codecs/NULL/AudioEncoder.cs b/CUETools.Codecs/NULL/AudioEncoder.cs index b4d3960..fbeb367 100644 --- a/CUETools.Codecs/NULL/AudioEncoder.cs +++ b/CUETools.Codecs/NULL/AudioEncoder.cs @@ -4,9 +4,9 @@ namespace CUETools.Codecs.NULL { public class AudioEncoder : IAudioDest { - AudioEncoderSettings m_settings; + IAudioEncoderSettings m_settings; - public AudioEncoder(string path, AudioEncoderSettings settings) + public AudioEncoder(string path, IAudioEncoderSettings settings) { m_settings = settings; } @@ -24,7 +24,7 @@ namespace CUETools.Codecs.NULL set { } } - public AudioEncoderSettings Settings => m_settings; + public IAudioEncoderSettings Settings => m_settings; public void Write(AudioBuffer buff) { diff --git a/CUETools.Codecs/ViewModel/AudioDecoderSettingsViewModel.cs b/CUETools.Codecs/ViewModel/AudioDecoderSettingsViewModel.cs index 95d3eab..a58d23e 100644 --- a/CUETools.Codecs/ViewModel/AudioDecoderSettingsViewModel.cs +++ b/CUETools.Codecs/ViewModel/AudioDecoderSettingsViewModel.cs @@ -8,7 +8,7 @@ namespace CUETools.Codecs public class AudioDecoderSettingsViewModel : INotifyPropertyChanged { [JsonProperty] - public AudioDecoderSettings Settings = null; + public IAudioDecoderSettings Settings = null; public event PropertyChangedEventHandler PropertyChanged; @@ -17,7 +17,7 @@ namespace CUETools.Codecs { } - public AudioDecoderSettingsViewModel(AudioDecoderSettings settings) + public AudioDecoderSettingsViewModel(IAudioDecoderSettings settings) { this.Settings = settings; } @@ -62,21 +62,13 @@ namespace CUETools.Codecs } } - public bool Lossless - { - get => true; - set { - throw new InvalidOperationException(); - } - } - public string Name { get => Settings.Name; set { if (Settings is CommandLine.DecoderSettings) - (Settings as CommandLine.DecoderSettings).name = value; + (Settings as CommandLine.DecoderSettings).Name = value; else throw new InvalidOperationException(); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name")); } @@ -88,7 +80,7 @@ namespace CUETools.Codecs set { if (Settings is CommandLine.DecoderSettings) - (Settings as CommandLine.DecoderSettings).extension = value; + (Settings as CommandLine.DecoderSettings).Extension = value; else throw new InvalidOperationException(); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Extension")); } diff --git a/CUETools.Codecs/ViewModel/AudioEncoderSettingsViewModel.cs b/CUETools.Codecs/ViewModel/AudioEncoderSettingsViewModel.cs index 0e4a752..a9e0f10 100644 --- a/CUETools.Codecs/ViewModel/AudioEncoderSettingsViewModel.cs +++ b/CUETools.Codecs/ViewModel/AudioEncoderSettingsViewModel.cs @@ -8,7 +8,7 @@ namespace CUETools.Codecs public class AudioEncoderSettingsViewModel : INotifyPropertyChanged { [JsonProperty] - public AudioEncoderSettings Settings = null; + public IAudioEncoderSettings Settings = null; public event PropertyChangedEventHandler PropertyChanged; @@ -17,7 +17,7 @@ namespace CUETools.Codecs { } - public AudioEncoderSettingsViewModel(AudioEncoderSettings settings) + public AudioEncoderSettingsViewModel(IAudioEncoderSettings settings) { this.Settings = settings; } @@ -70,7 +70,7 @@ namespace CUETools.Codecs { var settings = this.Settings as CommandLine.EncoderSettings; if (settings == null) throw new InvalidOperationException(); - settings.lossless = value; + settings.Lossless = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Lossless")); } } @@ -83,7 +83,7 @@ namespace CUETools.Codecs { var settings = this.Settings as CommandLine.EncoderSettings; if (settings == null) throw new InvalidOperationException(); - settings.name = value; + settings.Name = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name")); } } @@ -95,36 +95,30 @@ namespace CUETools.Codecs { var settings = this.Settings as CommandLine.EncoderSettings; if (settings == null) throw new InvalidOperationException(); - settings.extension = value; + settings.Extension = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Extension")); } } public string DotExtension => "." + Extension; - public string SupportedModesStr + public string SupportedModes { - get - { - string defaultMode; - return this.Settings.GetSupportedModes(out defaultMode); - } + get => Settings.SupportedModes; set { var settings = this.Settings as CommandLine.EncoderSettings; if (settings == null) throw new InvalidOperationException(); settings.SupportedModes = value; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SupportedModesStr")); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SupportedModes")); } } - public string[] SupportedModes => this.SupportedModesStr.Split(' '); - public int EncoderModeIndex { get { - string[] modes = this.SupportedModes; + string[] modes = this.SupportedModes.Split(' '); if (modes == null || modes.Length < 2) return -1; for (int i = 0; i < modes.Length; i++) diff --git a/CUETools.Codecs/ViewModel/DecoderListViewModel.cs b/CUETools.Codecs/ViewModel/DecoderListViewModel.cs index 7b20dcb..0a790cf 100644 --- a/CUETools.Codecs/ViewModel/DecoderListViewModel.cs +++ b/CUETools.Codecs/ViewModel/DecoderListViewModel.cs @@ -6,9 +6,9 @@ namespace CUETools.Codecs { public class DecoderListViewModel : BindingList { - private List model; + private List model; - public DecoderListViewModel(List model) + public DecoderListViewModel(List model) : base() { this.model = model; @@ -23,11 +23,11 @@ namespace CUETools.Codecs e.NewObject = new AudioDecoderSettingsViewModel(item); } - public bool TryGetValue(string extension, bool lossless, string name, out AudioDecoderSettingsViewModel result) + public bool TryGetValue(string extension, string name, out AudioDecoderSettingsViewModel result) { foreach (AudioDecoderSettingsViewModel udc in this) { - if (udc.Settings.Extension == extension && udc.Settings.Lossless == lossless && udc.Settings.Name == name) + if (udc.Settings.Extension == extension && udc.Settings.Name == name) { result = udc; return true; @@ -37,12 +37,12 @@ namespace CUETools.Codecs return false; } - public AudioDecoderSettingsViewModel GetDefault(string extension, bool lossless) + public AudioDecoderSettingsViewModel GetDefault(string extension) { AudioDecoderSettingsViewModel result = null; foreach (AudioDecoderSettingsViewModel udc in this) { - if (udc.Settings.Extension == extension && udc.Settings.Lossless == lossless && (result == null || result.Settings.Priority < udc.Settings.Priority)) + if (udc.Settings.Extension == extension && (result == null || result.Settings.Priority < udc.Settings.Priority)) { result = udc; } diff --git a/CUETools.Codecs/ViewModel/EncoderListViewModel.cs b/CUETools.Codecs/ViewModel/EncoderListViewModel.cs index 6111d4f..1c5ba2c 100644 --- a/CUETools.Codecs/ViewModel/EncoderListViewModel.cs +++ b/CUETools.Codecs/ViewModel/EncoderListViewModel.cs @@ -6,9 +6,9 @@ namespace CUETools.Codecs { public class EncoderListViewModel : BindingList { - private List model; + private List model; - public EncoderListViewModel(List model) + public EncoderListViewModel(List model) : base() { this.model = model; diff --git a/CUETools.Codecs/WAV/AudioDecoder.cs b/CUETools.Codecs/WAV/AudioDecoder.cs index ba88519..d3df5e7 100644 --- a/CUETools.Codecs/WAV/AudioDecoder.cs +++ b/CUETools.Codecs/WAV/AudioDecoder.cs @@ -14,7 +14,7 @@ namespace CUETools.Codecs.WAV string _path; private DecoderSettings m_settings; - public AudioDecoderSettings Settings => m_settings; + public IAudioDecoderSettings Settings => m_settings; public long Position { diff --git a/CUETools.Codecs/WAV/AudioEncoder.cs b/CUETools.Codecs/WAV/AudioEncoder.cs index 1f2c619..3f32eb0 100644 --- a/CUETools.Codecs/WAV/AudioEncoder.cs +++ b/CUETools.Codecs/WAV/AudioEncoder.cs @@ -30,7 +30,7 @@ namespace CUETools.Codecs.WAV } private EncoderSettings m_settings; - public AudioEncoderSettings Settings => m_settings; + public IAudioEncoderSettings Settings => m_settings; public string Path { get { return _path; } } diff --git a/CUETools.Codecs/WAV/DecoderSettings.cs b/CUETools.Codecs/WAV/DecoderSettings.cs index fd5af34..1163647 100644 --- a/CUETools.Codecs/WAV/DecoderSettings.cs +++ b/CUETools.Codecs/WAV/DecoderSettings.cs @@ -1,19 +1,36 @@ -using System; +using Newtonsoft.Json; +using System; +using System.ComponentModel; namespace CUETools.Codecs.WAV { - public class DecoderSettings : AudioDecoderSettings + [JsonObject(MemberSerialization.OptIn)] + public class DecoderSettings : IAudioDecoderSettings { - public override string Name => "cuetools"; + #region IAudioDecoderSettings implementation + [Browsable(false)] + public string Extension => "wav"; - public override string Extension => "wav"; + [Browsable(false)] + public string Name => "cuetools"; - public override Type DecoderType => typeof(AudioDecoder); + [Browsable(false)] + public Type DecoderType => typeof(AudioDecoder); - public override int Priority => 2; + [Browsable(false)] + public int Priority => 2; - public DecoderSettings() : base() { } + public IAudioDecoderSettings Clone() + { + return MemberwiseClone() as IAudioDecoderSettings; + } + #endregion - public bool IgnoreChunkSizes; + public DecoderSettings() + { + this.Init(); + } + + public bool IgnoreChunkSizes { get; set; } } } diff --git a/CUETools.Codecs/WAV/EncoderSettings.cs b/CUETools.Codecs/WAV/EncoderSettings.cs index f9661ad..2a55a0f 100644 --- a/CUETools.Codecs/WAV/EncoderSettings.cs +++ b/CUETools.Codecs/WAV/EncoderSettings.cs @@ -1,29 +1,64 @@ -using System; +using Newtonsoft.Json; +using System; using System.Collections.Generic; +using System.ComponentModel; using System.Text; namespace CUETools.Codecs.WAV { - public class EncoderSettings : AudioEncoderSettings + [JsonObject(MemberSerialization.OptIn)] + public class EncoderSettings : IAudioEncoderSettings { - public override string Extension => "wav"; + #region IAudioEncoderSettings implementation + [Browsable(false)] + public string Extension => "wav"; - public override string Name => "cuetools"; + [Browsable(false)] + public string Name => "cuetools"; - public override Type EncoderType => typeof(WAV.AudioEncoder); + [Browsable(false)] + public Type EncoderType => typeof(WAV.AudioEncoder); - public override int Priority => 10; + [Browsable(false)] + public bool Lossless => true; - public override bool Lossless => true; + [Browsable(false)] + public int Priority => 10; + + [Browsable(false)] + public string SupportedModes => ""; + + [Browsable(false)] + public string DefaultMode => ""; + + [Browsable(false)] + [DefaultValue("")] + 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(null) { + this.Init(); } public EncoderSettings(AudioPCMConfig pcm) - : base(pcm) { + this.Init(pcm); } } } diff --git a/CUETools.Converter/Program.cs b/CUETools.Converter/Program.cs index 55d3c49..18b8b79 100644 --- a/CUETools.Converter/Program.cs +++ b/CUETools.Converter/Program.cs @@ -47,7 +47,7 @@ namespace CUETools.Converter throw new Exception("Unsupported audio type: " + path); var decoder = fmt.decoder; - if (chosenDecoder != null && !config.decodersViewModel.TryGetValue(fmt.extension, true, chosenDecoder, out decoder)) + if (chosenDecoder != null && !config.decodersViewModel.TryGetValue(fmt.extension, chosenDecoder, out decoder)) throw new Exception("Unknown audio decoder " + chosenDecoder + " or unsupported audio type " + fmt.extension); if (decoder == null) throw new Exception("Unsupported audio type: " + path); diff --git a/CUETools.DSP.Mixer/MixingSource.cs b/CUETools.DSP.Mixer/MixingSource.cs index 01da099..4ed6f7d 100644 --- a/CUETools.DSP.Mixer/MixingSource.cs +++ b/CUETools.DSP.Mixer/MixingSource.cs @@ -17,7 +17,7 @@ namespace CUETools.DSP.Mixer private int mixoffs = 0; private int current = 0; - public AudioDecoderSettings Settings { get { return null; } } + public IAudioDecoderSettings Settings => null; public void Close() { diff --git a/CUETools.DSP.Mixer/MixingWriter.cs b/CUETools.DSP.Mixer/MixingWriter.cs index b4d3e26..0a4c95e 100644 --- a/CUETools.DSP.Mixer/MixingWriter.cs +++ b/CUETools.DSP.Mixer/MixingWriter.cs @@ -10,7 +10,7 @@ namespace CUETools.DSP.Mixer private long samplePos; private MixingBuffer mixbuff; private float volume; - private AudioEncoderSettings m_settings; + private Codecs.WAV.EncoderSettings m_settings; public long Position { @@ -23,13 +23,7 @@ namespace CUETools.DSP.Mixer set { throw new NotSupportedException(); } } - public AudioEncoderSettings Settings - { - get - { - return m_settings; - } - } + public IAudioEncoderSettings Settings => m_settings; public float Volume { @@ -41,7 +35,7 @@ namespace CUETools.DSP.Mixer public MixingWriter(MixingSource mixer, int iSource) { - this.m_settings = new AudioEncoderSettings(mixer.PCM); + this.m_settings = new Codecs.WAV.EncoderSettings(mixer.PCM); this.mixer = mixer; this.iSource = iSource; this.samplePos = 0; diff --git a/CUETools.FLACCL.cmd/CUETools.FLACL.cmd.csproj b/CUETools.FLACCL.cmd/CUETools.FLACL.cmd.csproj index 2587a9b..28a4384 100644 --- a/CUETools.FLACCL.cmd/CUETools.FLACL.cmd.csproj +++ b/CUETools.FLACCL.cmd/CUETools.FLACL.cmd.csproj @@ -67,9 +67,9 @@ - + {082D6B9E-326E-4D15-9798-EDAE9EDE70A6} - CUETools.Codecs.FLAKE + CUETools.Codecs.Flake False diff --git a/CUETools.FLACCL.cmd/Program.cs b/CUETools.FLACCL.cmd/Program.cs index 96781ee..65c5e88 100644 --- a/CUETools.FLACCL.cmd/Program.cs +++ b/CUETools.FLACCL.cmd/Program.cs @@ -22,7 +22,7 @@ using System.Collections.Generic; using System.Text; using System.IO; using CUETools.Codecs; -using CUETools.Codecs.FLAKE; +using CUETools.Codecs.Flake; using CUETools.Codecs.FLACCL; namespace CUETools.FLACCL.cmd @@ -209,7 +209,7 @@ namespace CUETools.FLACCL.cmd else if (args[arg] != "-" && args[arg][0] == '-' && int.TryParse(args[arg].Substring(1), out level)) { ok = level >= 0 && level <= 11; - settings.EncoderModeIndex = level; + settings.SetEncoderModeIndex(level); } else if ((args[arg][0] != '-' || args[arg] == "-") && input_file == null) input_file = args[arg]; diff --git a/CUETools.FlaCudaExe/CUETools.FlaCuda.csproj b/CUETools.FlaCudaExe/CUETools.FlaCuda.csproj index 3b7439a..5eaa780 100644 --- a/CUETools.FlaCudaExe/CUETools.FlaCuda.csproj +++ b/CUETools.FlaCudaExe/CUETools.FlaCuda.csproj @@ -62,9 +62,9 @@ - + {082D6B9E-326E-4D15-9798-EDAE9EDE70A6} - CUETools.Codecs.FLAKE + CUETools.Codecs.Flake False diff --git a/CUETools.Flake/CUETools.Flake.csproj b/CUETools.Flake/CUETools.Flake.csproj index 6164ae2..bc03b33 100644 --- a/CUETools.Flake/CUETools.Flake.csproj +++ b/CUETools.Flake/CUETools.Flake.csproj @@ -24,7 +24,7 @@ - + diff --git a/CUETools.Flake/Program.cs b/CUETools.Flake/Program.cs index 1d05141..eff6e47 100644 --- a/CUETools.Flake/Program.cs +++ b/CUETools.Flake/Program.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Text; using System.IO; using CUETools.Codecs; -using CUETools.Codecs.FLAKE; +using CUETools.Codecs.Flake; namespace CUETools.FlakeExe { @@ -144,7 +144,7 @@ namespace CUETools.FlakeExe bool do_seektable = true; bool buffered = false; string coeffs = null; - var settings = new Codecs.FLAKE.EncoderSettings() { AllowNonSubset = true }; + var settings = new Codecs.Flake.EncoderSettings() { AllowNonSubset = true }; bool allowNonSubset = false; bool ignore_chunk_sizes = false; bool force = false; @@ -272,7 +272,7 @@ namespace CUETools.FlakeExe else if (args[arg] != "-" && args[arg][0] == '-' && int.TryParse(args[arg].Substring(1), out intarg)) { ok = intarg >= 0 && intarg <= 11; - settings.EncoderModeIndex = intarg; + settings.SetEncoderModeIndex(intarg); } else if ((args[arg][0] != '-' || args[arg] == "-") && input_file == null) input_file = args[arg]; @@ -357,7 +357,7 @@ namespace CUETools.FlakeExe else if (File.Exists(input_file) && Path.GetExtension(input_file) == ".wav") audioSource = new Codecs.WAV.AudioDecoder(new Codecs.WAV.DecoderSettings(), input_file); else if (File.Exists(input_file) && Path.GetExtension(input_file) == ".flac") - audioSource = new Codecs.FLAKE.AudioDecoder(new Codecs.FLAKE.DecoderSettings(), input_file); + audioSource = new Codecs.Flake.AudioDecoder(new Codecs.Flake.DecoderSettings(), input_file); else { Usage(); @@ -495,7 +495,7 @@ namespace CUETools.FlakeExe if (debug) { - settings = flake.Settings as Codecs.FLAKE.EncoderSettings; + settings = flake.Settings as Codecs.Flake.EncoderSettings; Console.SetOut(stdout); Console.Out.WriteLine("{17}\t{0}\t{1:0.000}\t{2}\t{3}\t{4}\t{5}\t{6}..{7}\t{8}..{9}\t{10}..{11}\t{12}..{13}\t{14}\t{15}\t{16}\t{18}", flake.TotalSize, diff --git a/CUETools.LossyWAV/Program.cs b/CUETools.LossyWAV/Program.cs index bd8bdcd..1548350 100644 --- a/CUETools.LossyWAV/Program.cs +++ b/CUETools.LossyWAV/Program.cs @@ -96,7 +96,7 @@ namespace CUETools.LossyWAVSharp AudioPCMConfig pcm = outputBPS == 0 ? audioSource.PCM : new AudioPCMConfig(outputBPS, audioSource.PCM.ChannelCount, audioSource.PCM.SampleRate, audioSource.PCM.ChannelMask); Codecs.WAV.AudioEncoder audioDest = new Codecs.WAV.AudioEncoder(new Codecs.WAV.EncoderSettings(pcm), Path.ChangeExtension(sourceFile, ".lossy.wav"), toStdout ? Console.OpenStandardOutput() : null); Codecs.WAV.AudioEncoder lwcdfDest = createCorrection ? new Codecs.WAV.AudioEncoder(new Codecs.WAV.EncoderSettings(audioSource.PCM), Path.ChangeExtension(sourceFile, ".lwcdf.wav")) : null; - LossyWAVWriter lossyWAV = new LossyWAVWriter(audioDest, lwcdfDest, quality, new AudioEncoderSettings(audioSource.PCM)); + LossyWAVWriter lossyWAV = new LossyWAVWriter(audioDest, lwcdfDest, quality, new Codecs.WAV.EncoderSettings(audioSource.PCM)); AudioBuffer buff = new AudioBuffer(audioSource, 0x10000); Console.WriteLine("Filename : {0}", sourceFile); diff --git a/CUETools.Processor/AudioReadWrite.cs b/CUETools.Processor/AudioReadWrite.cs index 338010b..7278fcf 100644 --- a/CUETools.Processor/AudioReadWrite.cs +++ b/CUETools.Processor/AudioReadWrite.cs @@ -51,7 +51,7 @@ namespace CUETools.Processor { IAudioDest dest; if (audioEncoderType == AudioEncoderType.NoAudio || extension == ".dummy") - return new Codecs.NULL.AudioEncoder(path, new AudioEncoderSettings(pcm)) { FinalSampleCount = finalSampleCount }; + return new Codecs.NULL.AudioEncoder(path, new Codecs.WAV.EncoderSettings(pcm)) { FinalSampleCount = finalSampleCount }; CUEToolsFormat fmt; if (!extension.StartsWith(".") || !config.formats.TryGetValue(extension.Substring(1), out fmt)) throw new Exception("Unsupported audio type: " + path); diff --git a/CUETools.Processor/CUEConfig.cs b/CUETools.Processor/CUEConfig.cs index 731cc50..875f3f8 100644 --- a/CUETools.Processor/CUEConfig.cs +++ b/CUETools.Processor/CUEConfig.cs @@ -422,7 +422,7 @@ namespace CUETools.Processor .FindAll(y => y.Extension == x.Extension && y.Lossless == x.Lossless && y.Name == x.Name).Count == 0) .ToList().ForEach(x => advanced.encoders.Add(x)); backup.decoders.Where(x => advanced.decoders - .FindAll(y => y.Extension == x.Extension && y.Lossless == x.Lossless && y.Name == x.Name).Count == 0) + .FindAll(y => y.Extension == x.Extension && y.Name == x.Name).Count == 0) .ToList().ForEach(x => advanced.decoders.Add(x)); // Reset the ViewModel @@ -439,8 +439,8 @@ namespace CUETools.Processor encoderLossless = Encoders.GetDefault(extension, true); if (format.encoderLossy == null || !Encoders.TryGetValue(extension, false, format.encoderLossy.Name, out encoderLossy)) encoderLossy = Encoders.GetDefault(extension, false); - if (format.decoder == null || !Decoders.TryGetValue(extension, true, format.decoder.Name, out decoder)) - decoder = Decoders.GetDefault(extension, true); + if (format.decoder == null || !Decoders.TryGetValue(extension, format.decoder.Name, out decoder)) + decoder = Decoders.GetDefault(extension); format.encoderLossless = encoderLossless; format.encoderLossy = encoderLossy; format.decoder = decoder; @@ -471,8 +471,8 @@ namespace CUETools.Processor udcLossless = Encoders.GetDefault(extension, true); if (encoderLossy == "" || !Encoders.TryGetValue(extension, false, encoderLossy, out udcLossy)) udcLossy = Encoders.GetDefault(extension, false); - if (decoder == "" || !Decoders.TryGetValue(extension, true, decoder, out udcDecoder)) - udcDecoder = Decoders.GetDefault(extension, true); + if (decoder == "" || !Decoders.TryGetValue(extension, decoder, out udcDecoder)) + udcDecoder = Decoders.GetDefault(extension); if (!formats.TryGetValue(extension, out format)) formats.Add(extension, new CUEToolsFormat(extension, tagger, allowLossless, allowLossy, allowEmbed, false, udcLossless, udcLossy, udcDecoder)); else diff --git a/CUETools.Processor/CUEProcessorPlugins.cs b/CUETools.Processor/CUEProcessorPlugins.cs index 37af73f..3512a02 100644 --- a/CUETools.Processor/CUEProcessorPlugins.cs +++ b/CUETools.Processor/CUEProcessorPlugins.cs @@ -11,8 +11,8 @@ namespace CUETools.Processor { public static class CUEProcessorPlugins { - public static List encs; - public static List decs; + public static List encs; + public static List decs; public static List arcp; public static List arcp_fmt; public static Type hdcd; @@ -20,8 +20,8 @@ namespace CUETools.Processor static CUEProcessorPlugins() { - encs = new List(); - decs = new List(); + encs = new List(); + decs = new List(); arcp = new List(); arcp_fmt = new List(); @@ -68,13 +68,13 @@ namespace CUETools.Processor try { if (!type.IsClass || type.IsAbstract) continue; - if (type.GetInterface(typeof(IAudioDecoderSettings).Name) != null && type != typeof(AudioDecoderSettings)) + if (type.GetInterface(typeof(IAudioDecoderSettings).Name) != null) { - decs.Add(Activator.CreateInstance(type) as AudioDecoderSettings); + decs.Add(Activator.CreateInstance(type) as IAudioDecoderSettings); } - if (type.GetInterface(typeof(IAudioEncoderSettings).Name) != null && type != typeof(AudioEncoderSettings)) + if (type.GetInterface(typeof(IAudioEncoderSettings).Name) != null) { - encs.Add(Activator.CreateInstance(type) as AudioEncoderSettings); + encs.Add(Activator.CreateInstance(type) as IAudioEncoderSettings); } CompressionProviderClass archclass = Attribute.GetCustomAttribute(type, typeof(CompressionProviderClass)) as CompressionProviderClass; if (archclass != null) diff --git a/CUETools.Processor/CUESheetAudio.cs b/CUETools.Processor/CUESheetAudio.cs index 4d3edd0..2f9af32 100644 --- a/CUETools.Processor/CUESheetAudio.cs +++ b/CUETools.Processor/CUESheetAudio.cs @@ -11,7 +11,7 @@ namespace CUETools.Processor private long nextPos; private long _samplePos, _sampleLen; - public AudioDecoderSettings Settings { get { return null; } } + public IAudioDecoderSettings Settings => null; public long Length { diff --git a/CUETools.Ripper.SCSI/SCSIDrive.cs b/CUETools.Ripper.SCSI/SCSIDrive.cs index e5e6d99..3554829 100644 --- a/CUETools.Ripper.SCSI/SCSIDrive.cs +++ b/CUETools.Ripper.SCSI/SCSIDrive.cs @@ -77,7 +77,7 @@ namespace CUETools.Ripper.SCSI private ReadProgressArgs progressArgs = new ReadProgressArgs(); public event EventHandler ReadProgress; - public AudioDecoderSettings Settings { get { return null; } } + public IAudioDecoderSettings Settings => null; public CDImageLayout TOC { diff --git a/CUETools.TestHelpers/NoiseAndErrorsGenerator.cs b/CUETools.TestHelpers/NoiseAndErrorsGenerator.cs index 23d31fe..da89c79 100644 --- a/CUETools.TestHelpers/NoiseAndErrorsGenerator.cs +++ b/CUETools.TestHelpers/NoiseAndErrorsGenerator.cs @@ -49,7 +49,7 @@ namespace CUETools.TestHelpers this.nextError = 0; } - public AudioDecoderSettings Settings { get { return null; } } + public IAudioDecoderSettings Settings => null; public NoiseAndErrorsGenerator(long sampleCount) : this(AudioPCMConfig.RedBook, sampleCount, 0, 0, 0) diff --git a/CUETools.eac3ui/BLUTools.csproj b/CUETools.eac3ui/BLUTools.csproj index 0b2ac88..659beed 100644 --- a/CUETools.eac3ui/BLUTools.csproj +++ b/CUETools.eac3ui/BLUTools.csproj @@ -110,9 +110,9 @@ CUETools.Codecs.BDLPCM False - + {082d6b9e-326e-4d15-9798-edae9ede70a6} - CUETools.Codecs.FLAKE + CUETools.Codecs.Flake False diff --git a/CUETools.eac3ui/MainWindow.xaml.cs b/CUETools.eac3ui/MainWindow.xaml.cs index 6377244..95558a2 100644 --- a/CUETools.eac3ui/MainWindow.xaml.cs +++ b/CUETools.eac3ui/MainWindow.xaml.cs @@ -19,7 +19,7 @@ using CUETools.CTDB; using System.ComponentModel; using Krystalware.UploadHelper; using CUETools.Codecs; -using CUETools.Codecs.FLAKE; +//using CUETools.Codecs.Flake; using CUETools.Processor; using System.Collections.ObjectModel; //using Microsoft.Win32; @@ -247,7 +247,7 @@ namespace BluTools if (File.Exists(outputCuePath)) throw new Exception(string.Format("File \"{0}\" already exists", outputCuePath)); if (File.Exists(outputAudioPath)) throw new Exception(string.Format("File \"{0}\" already exists", outputAudioPath)); AudioBuffer buff = new AudioBuffer(reader, 0x10000); - EncoderSettings settings = new EncoderSettings() + CUETools.Codecs.Flake.EncoderSettings settings = new CUETools.Codecs.Flake.EncoderSettings() { PCM = reader.PCM, Padding = 16536, @@ -297,7 +297,7 @@ namespace BluTools } var start = DateTime.Now; TimeSpan lastPrint = TimeSpan.FromMilliseconds(0); - var writer = new AudioEncoder(settings, outputAudioPath); + var writer = new CUETools.Codecs.Flake.AudioEncoder(settings, outputAudioPath); try { while (reader.Read(buff, -1) != 0) diff --git a/CUETools/CUETools.TestCodecs/CUETools.TestCodecs.csproj b/CUETools/CUETools.TestCodecs/CUETools.TestCodecs.csproj index 87a2aa4..9aac177 100644 --- a/CUETools/CUETools.TestCodecs/CUETools.TestCodecs.csproj +++ b/CUETools/CUETools.TestCodecs/CUETools.TestCodecs.csproj @@ -91,9 +91,9 @@ CUETools.Codecs.ALAC True - + {082D6B9E-326E-4D15-9798-EDAE9EDE70A6} - CUETools.Codecs.FLAKE + CUETools.Codecs.Flake True diff --git a/CUETools/CUETools.TestCodecs/FlakeWriterTest.cs b/CUETools/CUETools.TestCodecs/FlakeWriterTest.cs index 55273fe..3bfebf0 100644 --- a/CUETools/CUETools.TestCodecs/FlakeWriterTest.cs +++ b/CUETools/CUETools.TestCodecs/FlakeWriterTest.cs @@ -6,7 +6,7 @@ using System.Text; using System.Collections.Generic; using System.IO; using CUETools.Codecs; -using CUETools.Codecs.FLAKE; +using CUETools.Codecs.Flake; namespace CUETools.TestCodecs { /// diff --git a/CUETools/CUETools.sln b/CUETools/CUETools.sln index b41885f..b870834 100644 --- a/CUETools/CUETools.sln +++ b/CUETools/CUETools.sln @@ -103,7 +103,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CUETools.Converter", "..\CU EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CUEControls", "..\CUEControls\CUEControls.csproj", "{CA4D64E6-6544-4A29-8BA5-7DB08D50D072}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CUETools.Codecs.FLAKE", "..\CUETools.Codecs.FLAKE\CUETools.Codecs.FLAKE.csproj", "{082D6B9E-326E-4D15-9798-EDAE9EDE70A6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CUETools.Codecs.Flake", "..\CUETools.Codecs.Flake\CUETools.Codecs.Flake.csproj", "{082D6B9E-326E-4D15-9798-EDAE9EDE70A6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CUETools.Flake", "..\CUETools.Flake\CUETools.Flake.csproj", "{2379BAAF-A406-4477-BF53-2D6A326C24C8}" EndProject diff --git a/CUETools/frmCUETools.cs b/CUETools/frmCUETools.cs index 634852f..35381f0 100644 --- a/CUETools/frmCUETools.cs +++ b/CUETools/frmCUETools.cs @@ -2411,7 +2411,7 @@ namespace JDP // TODO: something cleverer than this hack... encoder.Settings.PCM = AudioPCMConfig.RedBook; buttonEncoderSettings.Visible = encoder.Settings.HasBrowsableAttributes(); - string[] modes = encoder.SupportedModes; + string[] modes = encoder.SupportedModes.Split(' '); if (modes == null || modes.Length < 2) { trackBarEncoderMode.Visible = false; @@ -2423,9 +2423,7 @@ namespace JDP { if (encoder.EncoderModeIndex == -1) { - string defaultMode; - encoder.Settings.GetSupportedModes(out defaultMode); - encoder.Settings.EncoderMode = defaultMode; + encoder.Settings.EncoderMode = encoder.Settings.DefaultMode; } trackBarEncoderMode.Maximum = modes.Length - 1; trackBarEncoderMode.Value = encoder.EncoderModeIndex == -1 ? modes.Length - 1 : encoder.EncoderModeIndex; @@ -2460,7 +2458,7 @@ namespace JDP private void trackBarEncoderMode_Scroll(object sender, EventArgs e) { var encoder = comboBoxEncoder.SelectedItem as AudioEncoderSettingsViewModel; - string[] modes = encoder.SupportedModes; + string[] modes = encoder.SupportedModes.Split(' '); encoder.Settings.EncoderMode = modes[trackBarEncoderMode.Value]; labelEncoderMode.Text = encoder.Settings.EncoderMode; } diff --git a/CUETools/frmSettings.Designer.cs b/CUETools/frmSettings.Designer.cs index fecf618..5105341 100644 --- a/CUETools/frmSettings.Designer.cs +++ b/CUETools/frmSettings.Designer.cs @@ -513,7 +513,7 @@ namespace JDP // textBoxEncoderModes // resources.ApplyResources(this.textBoxEncoderModes, "textBoxEncoderModes"); - this.textBoxEncoderModes.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.encodersBindingSource, "SupportedModesStr", true)); + this.textBoxEncoderModes.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.encodersBindingSource, "SupportedModes", true)); this.textBoxEncoderModes.Name = "textBoxEncoderModes"; this.toolTip1.SetToolTip(this.textBoxEncoderModes, resources.GetString("textBoxEncoderModes.ToolTip")); //