-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Parametric room curves and treble suppressing for everything except flat
- Loading branch information
Showing
10 changed files
with
268 additions
and
199 deletions.
There are no files selected for viewing
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
using System; | ||
|
||
namespace Cavern.QuickEQ.EQCurves { | ||
/// <summary> | ||
/// A curve with a linear bass rise and linear treble suppression. | ||
/// </summary> | ||
public class RoomCurveLikeCurve : TrebleSuppressingCurve { | ||
/// <summary> | ||
/// The frequency from where the bass rise starts. | ||
/// </summary> | ||
readonly double kneeFrequency; | ||
|
||
/// <summary> | ||
/// Cached low knee position on logarithmic scale. | ||
/// </summary> | ||
readonly double kneePosition; | ||
|
||
/// <summary> | ||
/// Bass rise at 20 Hz in decibels. | ||
/// </summary> | ||
readonly float rise; | ||
|
||
/// <summary> | ||
/// The multiplier for getting the gain for a given frequency in the index operator. | ||
/// </summary> | ||
readonly double singleFreqHelper; | ||
|
||
/// <summary> | ||
/// A curve with a linear bass rise and linear treble suppression. | ||
/// </summary> | ||
/// <param name="kneeFrequency">The frequency from where the bass rise starts</param> | ||
/// <param name="rise">Bass rise at 20 Hz in decibels</param> | ||
public RoomCurveLikeCurve(double kneeFrequency, float rise) { | ||
this.kneeFrequency = kneeFrequency; | ||
kneePosition = Math.Log10(kneeFrequency); | ||
this.rise = rise; | ||
singleFreqHelper = rise / (Math.Log10(kneeFrequency) - log10_20); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public sealed override double this[double frequency] { | ||
get { | ||
if (frequency < kneeFrequency) { | ||
return singleFreqHelper * (kneePosition - Math.Log10(frequency)); | ||
} | ||
return base[frequency]; | ||
} | ||
} | ||
|
||
/// <inheritdoc/> | ||
public sealed override float[] GenerateLinearCurve(int sampleRate, int length, float gain) { | ||
float[] curve = base.GenerateLinearCurve(sampleRate, length, gain); | ||
int knee = (int)(2 * kneeFrequency * curve.Length / sampleRate + .5f); | ||
if (knee > curve.Length) { | ||
knee = curve.Length; | ||
} | ||
|
||
float positioner = sampleRate * .5f / curve.Length, | ||
kneeFreq = (float)kneeFrequency, | ||
scale = -rise / (kneeFreq - 20); | ||
for (int i = 0; i < knee; i++) { | ||
curve[i] = scale * (i * positioner - kneeFreq) + gain; | ||
} | ||
return curve; | ||
} | ||
|
||
/// <inheritdoc/> | ||
public sealed override float[] GenerateLogCurve(double startFreq, double endFreq, int length, float gain) { | ||
float[] curve = base.GenerateLogCurve(startFreq, endFreq, length, gain); | ||
double powerMin = Math.Log10(startFreq), powerRange = curve.Length / (Math.Log10(endFreq) - powerMin); | ||
int knee = (int)((Math.Log10(kneeFrequency) - powerMin) * powerRange); | ||
if (knee < 0) { | ||
return curve; | ||
} else if (knee > curve.Length) { | ||
knee = curve.Length; | ||
} | ||
|
||
float positioner = (float)(1.0 / (knee - (log10_20 - powerMin) * powerRange)); | ||
for (int i = 0; i < knee; i++) { | ||
curve[i] = rise * (knee - i) * positioner + gain; | ||
} | ||
return curve; | ||
} | ||
|
||
/// <summary> | ||
/// Hardcoded log10(20) (low extension position on log scale). | ||
/// </summary> | ||
const double log10_20 = 1.30102999566f; | ||
} | ||
} |
77 changes: 77 additions & 0 deletions
77
Cavern.QuickEQ/EQCurves/BaseClasses/TrebleSuppressingCurve.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
using System; | ||
|
||
namespace Cavern.QuickEQ.EQCurves { | ||
/// <summary> | ||
/// Applies the top part of the <see cref="RoomCurve"/>, to be reused in other curves not to let them be flat on the top end. | ||
/// The exact curve is linearly decreasing from 1 kHz to 20 kHz by a set value. | ||
/// </summary> | ||
public class TrebleSuppressingCurve : EQCurve { | ||
/// <summary> | ||
/// Linear treble decrease from 1 kHz to 20 kHz in decibels. | ||
/// </summary> | ||
public float trebleSuppression = 3; | ||
|
||
/// <inheritdoc/> | ||
public override double this[double frequency] { | ||
get { | ||
if (frequency < 1000) { | ||
return 0; | ||
} | ||
return -3 * (Math.Log10(frequency) - log10_1000) * highMul; | ||
} | ||
} | ||
|
||
/// <inheritdoc/> | ||
public override float[] GenerateLinearCurve(int sampleRate, int length, float gain) { | ||
float[] curve = new float[length]; | ||
if (trebleSuppression == 0 || sampleRate < 2000) { // Knee would start from 1 kHz | ||
return curve; | ||
} | ||
int knee = 2 * 1000 * length / sampleRate; | ||
Array.Fill(curve, gain, 0, knee); | ||
|
||
float positioner = sampleRate * .5f / length, | ||
scale = -trebleSuppression / 19000; | ||
for (int i = knee; i < length; i++) { | ||
curve[i] = scale * (i * positioner - 1000) + gain; | ||
} | ||
return curve; | ||
} | ||
|
||
/// <inheritdoc/> | ||
public override float[] GenerateLogCurve(double startFreq, double endFreq, int length, float gain) { | ||
float[] curve = new float[length]; | ||
if (trebleSuppression == 0) { | ||
return curve; | ||
} | ||
double powerMin = Math.Log10(startFreq), powerRange = length / (Math.Log10(endFreq) - powerMin); | ||
int knee = (int)((log10_1000 - powerMin) * powerRange); | ||
if (knee > length) { | ||
knee = length; | ||
} | ||
Array.Fill(curve, gain, 0, knee); | ||
|
||
float positioner = (float)(1.0 / ((log10_20000 - powerMin) * powerRange - knee)), | ||
scale = -trebleSuppression; | ||
for (int pos = knee; pos < length; pos++) { | ||
curve[pos] = scale * (pos - knee) * positioner + gain; | ||
} | ||
return curve; | ||
} | ||
|
||
/// <summary> | ||
/// Hardcoded log10(1000) (high knee position on log scale). | ||
/// </summary> | ||
const double log10_1000 = 3; | ||
|
||
/// <summary> | ||
/// Hardcoded log10(20000) (high extension position on log scale). | ||
/// </summary> | ||
const double log10_20000 = 4.30102999566f; | ||
|
||
/// <summary> | ||
/// Hardcoded 1 / (log10(20000) - log10(1000)) for high slope division. | ||
/// </summary> | ||
const double highMul = 0.76862178684; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,68 +1,13 @@ | ||
using System; | ||
|
||
namespace Cavern.QuickEQ.EQCurves { | ||
namespace Cavern.QuickEQ.EQCurves { | ||
/// <summary> | ||
/// EQ curve with a sub-bass slope for depth emphasis. | ||
/// EQ curve with a sub-bass slope for depth emphasis. Linearly rises by 12 dB down from 60 Hz to 20 Hz, | ||
/// and optionally linearly decreases from 1 kHz to 20 kHz by 3 dB. | ||
/// </summary> | ||
public class Depth : EQCurve { | ||
public class Depth : RoomCurveLikeCurve { | ||
/// <summary> | ||
/// Hardcoded log10(60), as C# compilers don't optimize this. | ||
/// EQ curve with a sub-bass slope for depth emphasis. Linearly rises by 12 dB down from 60 Hz to 20 Hz, | ||
/// and optionally linearly decreases from 1 kHz to 20 kHz by 3 dB. | ||
/// </summary> | ||
const float log10_60 = 1.77815125038f; | ||
|
||
/// <summary> | ||
/// Get the curve's gain in decibels at a given frequency. | ||
/// </summary> | ||
public override double this[double frequency] { | ||
get { | ||
if (frequency > 60) { | ||
return 0; | ||
} | ||
return 12.0 / 60 * (60 - frequency); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Generate a linear curve for correction generators. | ||
/// </summary> | ||
/// <param name="length">Curve length</param> | ||
/// <param name="sampleRate">Sample rate of the measurement that the generated curve will be used for</param> | ||
/// <param name="gain">Curve reference level</param> | ||
public override float[] GenerateLinearCurve(int sampleRate, int length, float gain) { | ||
float[] curve = new float[length]; | ||
float positioner = 12f / 60 * sampleRate * .5f / length; | ||
int at60 = (int)(length * 120f / sampleRate); | ||
if (at60 > length) { | ||
at60 = length; | ||
} | ||
gain += 12; | ||
for (int pos = 0; pos < at60; pos++) { | ||
curve[pos] = gain - pos * positioner; | ||
} | ||
Array.Fill(curve, gain, at60, length - at60); | ||
return curve; | ||
} | ||
|
||
/// <summary> | ||
/// Generate a logarithmic curve for correction generators. | ||
/// </summary> | ||
/// <param name="length">Curve length</param> | ||
/// <param name="startFreq">Frequency at the beginning of the curve</param> | ||
/// <param name="endFreq">Frequency at the end of the curve</param> | ||
/// <param name="gain">Curve reference level</param> | ||
public override float[] GenerateLogCurve(double startFreq, double endFreq, int length, float gain) { | ||
float[] curve = new float[length]; | ||
double powerMin = Math.Log10(startFreq), powerRange = (Math.Log10(endFreq) - powerMin) / length; | ||
int at60 = (int)((log10_60 - powerMin) / powerRange); | ||
if (at60 > length) { | ||
at60 = length; | ||
} | ||
double startGain = this[startFreq], positioner = startGain / at60; | ||
for (int pos = 0; pos < at60; pos++) { | ||
curve[pos] = (float)(startGain - pos * positioner + gain); | ||
} | ||
Array.Fill(curve, gain, at60, length - at60); | ||
return curve; | ||
} | ||
public Depth() : base(60, 12) { } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.