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"));
//