diff --git a/Cavern.QuickEQ.Format/FilterSet/BaseClasses/FilterSet.cs b/Cavern.QuickEQ.Format/FilterSet/BaseClasses/FilterSet.cs
index aa8472f..a2cca4d 100644
--- a/Cavern.QuickEQ.Format/FilterSet/BaseClasses/FilterSet.cs
+++ b/Cavern.QuickEQ.Format/FilterSet/BaseClasses/FilterSet.cs
@@ -7,6 +7,7 @@
using Cavern.Channels;
using Cavern.Filters;
using Cavern.Format.Common;
+using Cavern.Utilities;
namespace Cavern.Format.FilterSet {
///
@@ -62,6 +63,19 @@ public abstract class ChannelData {
///
protected FilterSet(int sampleRate) => SampleRate = sampleRate;
+ ///
+ /// Convert a double to string with its maximum decimal places dependent on the base 10 logarithm.
+ ///
+ protected static string RangeDependentDecimals(double value) {
+ if (value < 100) {
+ return QMath.ToStringLimitDecimals(value, 2);
+ } else if (value < 1000) {
+ return QMath.ToStringLimitDecimals(value, 1);
+ } else {
+ return QMath.ToStringLimitDecimals(value, 0);
+ }
+ }
+
///
public abstract void Export(string path);
@@ -91,6 +105,7 @@ public static FilterSet Create(FilterSetTarget device, int channels, int sampleR
FilterSetTarget.AcurusMuse => new AcurusMuseFilterSet(channels, sampleRate),
FilterSetTarget.Emotiva => new EmotivaFilterSet(channels, sampleRate),
FilterSetTarget.MonolithHTP1 => new MonolithHTP1FilterSet(channels, sampleRate),
+ FilterSetTarget.SonyES => new SonyESSeriesFilterSet(channels, sampleRate),
FilterSetTarget.StormAudio => new StormAudioFilterSet(channels, sampleRate),
FilterSetTarget.TonewinnerAT => new TonewinnerATFilterSet(channels, sampleRate),
FilterSetTarget.BehringerNX => new BehringerNXFilterSet(channels, sampleRate),
@@ -131,6 +146,7 @@ public static FilterSet Create(FilterSetTarget device, ReferenceChannel[] channe
FilterSetTarget.AcurusMuse => new AcurusMuseFilterSet(channels, sampleRate),
FilterSetTarget.Emotiva => new EmotivaFilterSet(channels, sampleRate),
FilterSetTarget.MonolithHTP1 => new MonolithHTP1FilterSet(channels, sampleRate),
+ FilterSetTarget.SonyES => new SonyESSeriesFilterSet(channels, sampleRate),
FilterSetTarget.StormAudio => new StormAudioFilterSet(channels, sampleRate),
FilterSetTarget.TonewinnerAT => new TonewinnerATFilterSet(channels, sampleRate),
FilterSetTarget.BehringerNX => new BehringerNXFilterSet(channels, sampleRate),
@@ -160,6 +176,23 @@ public static FilterSet Create(FilterSetTarget device, ReferenceChannel[] channe
///
protected virtual string GetLabel(int channel) => Channels[channel].name ?? "CH" + (channel + 1);
+ ///
+ /// Insert channel header and basic information to a root file.
+ ///
+ /// Any information was exported.
+ protected bool RootFileChannelHeader(int channel, StringBuilder result) {
+ result.AppendLine(string.Empty);
+ string chName = GetLabel(channel);
+ result.AppendLine(chName);
+ result.AppendLine(new string('=', chName.Length));
+ RootFileExtension(channel, result);
+ if (Channels[channel].delaySamples != 0) {
+ result.AppendLine("Delay: " + QMath.ToStringLimitDecimals(GetDelay(channel), 2));
+ return true;
+ }
+ return false;
+ }
+
///
/// Add extra information for a channel that can't be part of the filter files to be written in the root file.
///
@@ -204,21 +237,11 @@ protected void CreateRootFile(string path, string filterFileExtension) {
string fileNameBase = Path.GetFileName(path);
fileNameBase = fileNameBase[..fileNameBase.LastIndexOf('.')];
StringBuilder result = new StringBuilder();
- bool hasAnything = false,
- hasDelays = false;
+ bool hasDelays = false;
for (int i = 0, c = Channels.Length; i < c; i++) {
- result.AppendLine(string.Empty);
- result.AppendLine("Channel: " + GetLabel(i));
- if (Channels[i].delaySamples != 0) {
- result.AppendLine("Delay: " + GetDelay(i).ToString("0.0 ms"));
- hasAnything = true;
- hasDelays = true;
- }
- int before = result.Length;
- RootFileExtension(i, result);
- hasAnything |= result.Length != before;
+ hasDelays |= RootFileChannelHeader(i, result);
}
- if (hasAnything) {
+ if (result.Length != 0) {
File.WriteAllText(path, (hasDelays ?
$"Set up levels and delays by this file. Load \"{fileNameBase} .{filterFileExtension}\" files as EQ." :
$"Set up levels by this file. Load \"{fileNameBase} .{filterFileExtension}\" files as EQ.") +
diff --git a/Cavern.QuickEQ.Format/FilterSet/BaseClasses/FilterSetTarget.cs b/Cavern.QuickEQ.Format/FilterSet/BaseClasses/FilterSetTarget.cs
index a1abf03..1ce141c 100644
--- a/Cavern.QuickEQ.Format/FilterSet/BaseClasses/FilterSetTarget.cs
+++ b/Cavern.QuickEQ.Format/FilterSet/BaseClasses/FilterSetTarget.cs
@@ -84,6 +84,10 @@ public enum FilterSetTarget {
///
MonolithHTP1,
///
+ /// Sony ES-series AVRs.
+ ///
+ SonyES,
+ ///
/// StormAudio ISP processors.
///
StormAudio,
@@ -183,6 +187,7 @@ public static class FilterSetTargetExtensions {
FilterSetTarget.AcurusMuse => "Acurus Muse",
FilterSetTarget.Emotiva => "Emotiva",
FilterSetTarget.MonolithHTP1 => "Monoprice Monolith HTP-1",
+ FilterSetTarget.SonyES => "Sony ES series",
FilterSetTarget.StormAudio => "StormAudio",
FilterSetTarget.TonewinnerAT => "Tonewinner AT series",
FilterSetTarget.BehringerNX => "Behringer NX series",
diff --git a/Cavern.QuickEQ.Format/FilterSet/BaseClasses/IIRFilterSet.cs b/Cavern.QuickEQ.Format/FilterSet/BaseClasses/IIRFilterSet.cs
index 106e7aa..0212f30 100644
--- a/Cavern.QuickEQ.Format/FilterSet/BaseClasses/IIRFilterSet.cs
+++ b/Cavern.QuickEQ.Format/FilterSet/BaseClasses/IIRFilterSet.cs
@@ -78,19 +78,6 @@ public bool Equals(IIRChannelData other) => filters.Equals(other.filters) && gai
///
public IIRFilterSet(ReferenceChannel[] channels, int sampleRate) : base(sampleRate) => Initialize(channels);
- ///
- /// Convert a double to string with its maximum decimal places dependent on the base 10 logarithm.
- ///
- static string RangeDependentDecimals(double value) {
- if (value < 100) {
- return QMath.ToStringLimitDecimals(value, 2);
- } else if (value < 1000) {
- return QMath.ToStringLimitDecimals(value, 1);
- } else {
- return QMath.ToStringLimitDecimals(value, 0);
- }
- }
-
///
/// If the filter set's band count is dependent on which channel is selected, use this function instead of .
///
@@ -201,17 +188,8 @@ public override void Export(string path) {
protected virtual string Export(bool gainOnly) {
StringBuilder result = new StringBuilder("Set up the channels according to this configuration.").AppendLine();
for (int i = 0; i < Channels.Length; i++) {
- IIRChannelData channelRef = (IIRChannelData)Channels[i];
- result.AppendLine(string.Empty);
- string chName = GetLabel(i);
- result.AppendLine(chName);
- result.AppendLine(new string('=', chName.Length));
- RootFileExtension(i, result);
- if (channelRef.delaySamples != 0) {
- result.AppendLine("Delay: " + QMath.ToStringLimitDecimals(GetDelay(i), 2));
- }
-
- BiquadFilter[] bands = channelRef.filters;
+ RootFileChannelHeader(i, result);
+ BiquadFilter[] bands = ((IIRChannelData)Channels[i]).filters;
if (gainOnly) {
for (int j = 0; j < bands.Length; j++) {
string gain = QMath.ToStringLimitDecimals(bands[j].Gain, 2);
diff --git a/Cavern.QuickEQ.Format/FilterSet/BaseClasses/LimitedEqualizerFilterSet.cs b/Cavern.QuickEQ.Format/FilterSet/BaseClasses/LimitedEqualizerFilterSet.cs
new file mode 100644
index 0000000..e2368e2
--- /dev/null
+++ b/Cavern.QuickEQ.Format/FilterSet/BaseClasses/LimitedEqualizerFilterSet.cs
@@ -0,0 +1,62 @@
+using System.IO;
+using System.Text;
+
+using Cavern.Channels;
+using Cavern.QuickEQ.Equalization;
+using Cavern.Utilities;
+
+namespace Cavern.Format.FilterSet.BaseClasses {
+ ///
+ /// A fixed set of bands to sample from an for export into a single file. This is the recommended and fastest
+ /// approach of getting a filter set for incoherent fixed EQ bands, such as the .
+ ///
+ public abstract class LimitedEqualizerFilterSet : EqualizerFilterSet {
+ ///
+ /// All frequency bands that need to be set.
+ ///
+ protected abstract float[] Frequencies { get; }
+
+ ///
+ /// Frequency bands for the LFE channel.
+ ///
+ protected abstract float[] LFEFrequencies { get; }
+
+ ///
+ /// How much smoothing in octaves shall be applied on the results to have a precise enough averaged value at each used frequency.
+ ///
+ protected abstract float Smoothing { get; }
+
+ ///
+ /// A fixed set of bands to sample from an for export into a single file.
+ ///
+ protected LimitedEqualizerFilterSet(int sampleRate) : base(sampleRate) { }
+
+ ///
+ /// A fixed set of bands to sample from an for export into a single file.
+ ///
+ protected LimitedEqualizerFilterSet(int channels, int sampleRate) : base(channels, sampleRate) { }
+
+ ///
+ /// A fixed set of bands to sample from an for export into a single file.
+ ///
+ protected LimitedEqualizerFilterSet(ReferenceChannel[] channels, int sampleRate) : base(channels, sampleRate) { }
+
+ ///
+ /// Save the results of each channel to a single file.
+ ///
+ public override void Export(string path) {
+ StringBuilder result = new StringBuilder("Set up the channels according to this configuration.").AppendLine();
+ for (int i = 0; i < Channels.Length; i++) {
+ RootFileChannelHeader(i, result);
+ Equalizer curve = (Equalizer)((EqualizerChannelData)Channels[i]).curve.Clone();
+ curve.Smooth(Smoothing);
+ float[] freqs = Channels[i].reference != ReferenceChannel.ScreenLFE ? Frequencies : LFEFrequencies;
+ for (int j = 0; j < freqs.Length; j++) {
+ string gain = QMath.ToStringLimitDecimals(curve[freqs[j]], 2);
+ result.AppendLine($"{RangeDependentDecimals(freqs[j])} Hz:\t{gain} dB");
+ }
+ }
+ File.WriteAllText(path, result.ToString());
+ }
+ }
+}
\ No newline at end of file
diff --git a/Cavern.QuickEQ.Format/FilterSet/SonyESSeriesFilterSet.cs b/Cavern.QuickEQ.Format/FilterSet/SonyESSeriesFilterSet.cs
new file mode 100644
index 0000000..27f241e
--- /dev/null
+++ b/Cavern.QuickEQ.Format/FilterSet/SonyESSeriesFilterSet.cs
@@ -0,0 +1,38 @@
+using Cavern.Channels;
+using Cavern.Format.FilterSet.BaseClasses;
+
+namespace Cavern.Format.FilterSet {
+ ///
+ /// Banded filter set for Sony ES-series receivers.
+ ///
+ public class SonyESSeriesFilterSet : LimitedEqualizerFilterSet {
+ ///
+ protected override float[] Frequencies => frequencies;
+
+ ///
+ protected override float[] LFEFrequencies => lfeFrequencies;
+
+ ///
+ protected override float Smoothing => 1;
+
+ ///
+ /// Banded filter set for Sony ES-series receivers.
+ ///
+ public SonyESSeriesFilterSet(int channels, int sampleRate) : base(channels, sampleRate) { }
+
+ ///
+ /// Banded filter set for Sony ES-series receivers.
+ ///
+ public SonyESSeriesFilterSet(ReferenceChannel[] channels, int sampleRate) : base(channels, sampleRate) { }
+
+ ///
+ /// All frequency bands that need to be set.
+ ///
+ static readonly float[] frequencies = { 47, 230, 470, 840, 1300, 2300, 3800, 5800, 9000, 14000 };
+
+ ///
+ /// All LFE frequency bands that need to be set.
+ ///
+ static readonly float[] lfeFrequencies = { 40, 60, 80, 90, 100, 120 };
+ }
+}
\ No newline at end of file
diff --git a/Cavern.QuickEQ/Equalization/Equalizer.Transform.cs b/Cavern.QuickEQ/Equalization/Equalizer.Transform.cs
index 7d5023b..0800d50 100644
--- a/Cavern.QuickEQ/Equalization/Equalizer.Transform.cs
+++ b/Cavern.QuickEQ/Equalization/Equalizer.Transform.cs
@@ -214,6 +214,11 @@ public void Normalize(double startFreq, double endFreq) {
Offset(total / (first - last));
}
+ ///
+ /// Set the RMS gain of the curve to 0 dB between frequency limits.
+ ///
+ public void NormalizeRMS(double startFreq, double endFreq) => Offset(-GetAverageLevel(startFreq, endFreq));
+
///
/// Change the frequencies contained in this .
///
diff --git a/README.md b/README.md
index c0eb1b4..c822c57 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@ Cavernize.
* Supported software/hardware for EQ/filter set export:
* PC: Equalizer APO, CamillaDSP
* DSP: MiniDSP 2x4 Advanced, MiniDSP 2x4 HD, MiniDSP DDRC-88A
- * Processors: Acurus Muse, Emotiva, Monolith HTP-1, StormAudio, Tonewinner AT series
+ * Processors: Acurus Muse, Emotiva, Monolith HTP-1, Sony ES series, StormAudio, Tonewinner AT series
* Amplifiers: Behringer NX series
* Others: Audyssey MultEQ-X, Dirac Live, YPAO
* Direction and distance virtualization for headphones
diff --git a/docs/NuGet Readme.md b/docs/NuGet Readme.md
index 4b1aba6..0c96b5f 100644
--- a/docs/NuGet Readme.md
+++ b/docs/NuGet Readme.md
@@ -23,7 +23,7 @@ self-calibration libraries built on the Cavern engine are also available.
* Supported software/hardware for EQ/filter set export:
* PC: Equalizer APO, CamillaDSP
* DSP: MiniDSP 2x4 Advanced, MiniDSP 2x4 HD, MiniDSP DDRC-88A
- * Processors: Acurus Muse, Emotiva, Monolith HTP-1, StormAudio, Tonewinner AT series
+ * Processors: Acurus Muse, Emotiva, Monolith HTP-1, Sony ES series, StormAudio, Tonewinner AT series
* Amplifiers: Behringer NX series
* Others: Audyssey MultEQ-X, Dirac Live, YPAO
* Direction and distance virtualization for headphones