Skip to content

Commit

Permalink
Support Sony ES series
Browse files Browse the repository at this point in the history
  • Loading branch information
VoidXH committed Sep 22, 2024
1 parent a335227 commit adb28a1
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 39 deletions.
49 changes: 36 additions & 13 deletions Cavern.QuickEQ.Format/FilterSet/BaseClasses/FilterSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Cavern.Channels;
using Cavern.Filters;
using Cavern.Format.Common;
using Cavern.Utilities;

namespace Cavern.Format.FilterSet {
/// <summary>
Expand Down Expand Up @@ -62,6 +63,19 @@ public abstract class ChannelData {
/// </summary>
protected FilterSet(int sampleRate) => SampleRate = sampleRate;

/// <summary>
/// Convert a double to string with its maximum decimal places dependent on the base 10 logarithm.
/// </summary>
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);
}
}

/// <inheritdoc/>
public abstract void Export(string path);

Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -160,6 +176,23 @@ public static FilterSet Create(FilterSetTarget device, ReferenceChannel[] channe
/// </summary>
protected virtual string GetLabel(int channel) => Channels[channel].name ?? "CH" + (channel + 1);

/// <summary>
/// Insert channel header and basic information to a root file.
/// </summary>
/// <returns>Any information was exported.</returns>
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;
}

/// <summary>
/// Add extra information for a channel that can't be part of the filter files to be written in the root file.
/// </summary>
Expand Down Expand Up @@ -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} <channel>.{filterFileExtension}\" files as EQ." :
$"Set up levels by this file. Load \"{fileNameBase} <channel>.{filterFileExtension}\" files as EQ.") +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ public enum FilterSetTarget {
/// </summary>
MonolithHTP1,
/// <summary>
/// Sony ES-series AVRs.
/// </summary>
SonyES,
/// <summary>
/// StormAudio ISP processors.
/// </summary>
StormAudio,
Expand Down Expand Up @@ -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",
Expand Down
26 changes: 2 additions & 24 deletions Cavern.QuickEQ.Format/FilterSet/BaseClasses/IIRFilterSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,6 @@ public bool Equals(IIRChannelData other) => filters.Equals(other.filters) && gai
/// </summary>
public IIRFilterSet(ReferenceChannel[] channels, int sampleRate) : base(sampleRate) => Initialize<IIRChannelData>(channels);

/// <summary>
/// Convert a double to string with its maximum decimal places dependent on the base 10 logarithm.
/// </summary>
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);
}
}

/// <summary>
/// If the filter set's band count is dependent on which channel is selected, use this function instead of <see cref="Bands"/>.
/// </summary>
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
/// <summary>
/// A fixed set of bands to sample from an <see cref="Equalizer"/> 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 <see cref="SonyESSeriesFilterSet"/>.
/// </summary>
public abstract class LimitedEqualizerFilterSet : EqualizerFilterSet {
/// <summary>
/// All frequency bands that need to be set.
/// </summary>
protected abstract float[] Frequencies { get; }

/// <summary>
/// Frequency bands for the LFE channel.
/// </summary>
protected abstract float[] LFEFrequencies { get; }

/// <summary>
/// How much smoothing in octaves shall be applied on the results to have a precise enough averaged value at each used frequency.
/// </summary>
protected abstract float Smoothing { get; }

/// <summary>
/// A fixed set of bands to sample from an <see cref="Equalizer"/> for export into a single file.
/// </summary>
protected LimitedEqualizerFilterSet(int sampleRate) : base(sampleRate) { }

/// <summary>
/// A fixed set of bands to sample from an <see cref="Equalizer"/> for export into a single file.
/// </summary>
protected LimitedEqualizerFilterSet(int channels, int sampleRate) : base(channels, sampleRate) { }

/// <summary>
/// A fixed set of bands to sample from an <see cref="Equalizer"/> for export into a single file.
/// </summary>
protected LimitedEqualizerFilterSet(ReferenceChannel[] channels, int sampleRate) : base(channels, sampleRate) { }

/// <summary>
/// Save the results of each channel to a single file.
/// </summary>
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());
}
}
}
38 changes: 38 additions & 0 deletions Cavern.QuickEQ.Format/FilterSet/SonyESSeriesFilterSet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Cavern.Channels;
using Cavern.Format.FilterSet.BaseClasses;

namespace Cavern.Format.FilterSet {
/// <summary>
/// Banded filter set for Sony ES-series receivers.
/// </summary>
public class SonyESSeriesFilterSet : LimitedEqualizerFilterSet {
/// <inheritdoc/>
protected override float[] Frequencies => frequencies;

/// <inheritdoc/>
protected override float[] LFEFrequencies => lfeFrequencies;

/// <inheritdoc/>
protected override float Smoothing => 1;

/// <summary>
/// Banded filter set for Sony ES-series receivers.
/// </summary>
public SonyESSeriesFilterSet(int channels, int sampleRate) : base(channels, sampleRate) { }

/// <summary>
/// Banded filter set for Sony ES-series receivers.
/// </summary>
public SonyESSeriesFilterSet(ReferenceChannel[] channels, int sampleRate) : base(channels, sampleRate) { }

/// <summary>
/// All frequency bands that need to be set.
/// </summary>
static readonly float[] frequencies = { 47, 230, 470, 840, 1300, 2300, 3800, 5800, 9000, 14000 };

/// <summary>
/// All LFE frequency bands that need to be set.
/// </summary>
static readonly float[] lfeFrequencies = { 40, 60, 80, 90, 100, 120 };
}
}
5 changes: 5 additions & 0 deletions Cavern.QuickEQ/Equalization/Equalizer.Transform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ public void Normalize(double startFreq, double endFreq) {
Offset(total / (first - last));
}

/// <summary>
/// Set the RMS gain of the curve to 0 dB between frequency limits.
/// </summary>
public void NormalizeRMS(double startFreq, double endFreq) => Offset(-GetAverageLevel(startFreq, endFreq));

/// <summary>
/// Change the frequencies contained in this <see cref="Equalizer"/>.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/NuGet Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit adb28a1

Please sign in to comment.