Skip to content

Commit

Permalink
More work on Octave scales
Browse files Browse the repository at this point in the history
Issue #332
  • Loading branch information
towsey committed Aug 2, 2020
1 parent a7ca52f commit c20e4ca
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 33 deletions.
3 changes: 2 additions & 1 deletion src/AudioAnalysisTools/DSP/FrequencyScale.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,9 @@ public FrequencyScale(FreqScaleType fst)

/// <summary>
/// Gets or sets number of octave to appear above the linear part of scale.
/// Can have a fraction of an octave.
/// </summary>
public int OctaveCount { get; set; }
public double OctaveCount { get; set; }

/// <summary>
/// Gets or sets number of bands or tones per octave.
Expand Down
117 changes: 85 additions & 32 deletions src/AudioAnalysisTools/DSP/OctaveFreqScale.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,30 @@ public static void GetOctaveScale(FrequencyScale scale)

// NOTE: octaveDivisions = the number of fractional Hz steps within one octave. Piano octave contains 12 steps per octave.

FreqScaleType fst = scale.ScaleType;
var fst = scale.ScaleType;

switch (fst)
{
case FreqScaleType.LinearOctaveStandard:
// The number of octaveDivsions/tones (T) is set equal to number of linear bins.
// The remainder of the spectrum will be reduced over four T-tone octaves
scale.LinearBound = 500;
//This is a split linear-octave frequency scale.
// Valid values for linearUpperBound are 125, 250, 500, 1000.
int linearUpperBound = 250;
scale = GetStandardOctaveScale(scale, linearUpperBound);
return;

case FreqScaleType.OctaveDataReduction:
// This data conversion is for data reduction purposes.
// The remainder of the spectrum will be reduced over four 6-tone octaves
sr = 22050;
scale.Nyquist = sr / 2;
frameSize = 512;
var binWidth = sr / (double)frameSize;
var linearBinCount = (int)Math.Floor(scale.LinearBound / binWidth);
octaveDivisions = linearBinCount;
scale.OctaveCount = 1; //#################### CHECK THIS - APPEARS NOT TO BE USED
//finalBinCount = linearBinCount + 79;
finalBinCount = linearBinCount + 51;
finalBinCount = 45;
scale.OctaveCount = 4;
octaveDivisions = 6; // tone steps within one octave.
scale.LinearBound = 1000;
scale.Nyquist = 11025;
break;

case FreqScaleType.Linear62Octaves7Tones31Nyquist11025:
// constants required for split linear-octave scale when sr = 22050
sr = 22050;
frameSize = 8192;
scale.OctaveCount = 7;
Expand All @@ -64,18 +67,6 @@ public static void GetOctaveScale(FrequencyScale scale)
scale.Nyquist = 11025;
break;

case FreqScaleType.OctaveDataReduction:
// This data conversion is for data reduction purposes.
// The remainder of the spectrum will be reduced over four 6-tone octaves
sr = 22050;
frameSize = 512;
finalBinCount = 45;
scale.OctaveCount = 4;
octaveDivisions = 6; // tone steps within one octave.
scale.LinearBound = 1000;
scale.Nyquist = 11025;
break;

case FreqScaleType.Octaves24Nyquist32000:
//// constants required for full octave scale when sr = 64000
sr = 64000;
Expand All @@ -97,7 +88,7 @@ public static void GetOctaveScale(FrequencyScale scale)
break;

default:
LoggedConsole.WriteErrorLine("WARNING: UNKNOWN OCTAVE SCALE.");
LoggedConsole.WriteErrorLine("WARNING: GetOctaveScale() was passed UNKNOWN OCTAVE SCALE.");
return;
}

Expand All @@ -108,6 +99,68 @@ public static void GetOctaveScale(FrequencyScale scale)
scale.GridLineLocations = GetGridLineLocations(fst, scale.BinBounds);
}

/// <summary>
/// Calculates the parameters for a mixed linear-octave frequency scale.
/// Works only for "standard" recordings, i.e. sr = 22050 and frame = 512.
/// The number of octaveDivsions/tones (T) is set equal to number of linear bins.
/// The remainder of the spectrum will be reduced over T-tone octaves.
/// Valid values for linearUpperBound are 125, 250, 500, 1000.
/// Note that when linearUpperBound = 500, the resulting spectrogram is very similar to the default MelScale.
/// The default MelScale has 64 frequency bins and Linear500-octave has 66 frequency bands.
/// </summary>
/// <param name="linearUpperBound">The upper limit of the linear frqeuency band in Hertz.</param>
public static FrequencyScale GetStandardOctaveScale(FrequencyScale scale, int linearUpperBound)
{
int sr = 22050;
int frameSize = 512;
scale.WindowSize = frameSize;
scale.Nyquist = sr / 2;
scale.LinearBound = linearUpperBound;
var binWidth = sr / (double)frameSize;

// init tone steps within one octave. Note: piano = 12 steps per octave.
int linearBinCount, finalBinCount;

switch (linearUpperBound)
{
case 125:
linearBinCount = (int)Math.Round(scale.LinearBound / binWidth);
scale.ToneCount = linearBinCount;
scale.OctaveCount = 7.7;
finalBinCount = (int)Math.Floor(scale.OctaveCount * scale.ToneCount);
break;

case 250:
linearBinCount = (int)Math.Round(scale.LinearBound / binWidth);
scale.ToneCount = linearBinCount;
scale.OctaveCount = 6.7;
finalBinCount = (int)Math.Floor(scale.OctaveCount * scale.ToneCount);
break;

case 500:
linearBinCount = (int)Math.Round(scale.LinearBound / binWidth);
scale.ToneCount = linearBinCount;
scale.OctaveCount = 5.5;
finalBinCount = (int)Math.Floor(scale.OctaveCount * scale.ToneCount);
break;

case 1000:
linearBinCount = (int)Math.Round(scale.LinearBound / binWidth);
scale.ToneCount = linearBinCount;
scale.OctaveCount = 4.5;
finalBinCount = (int)Math.Floor(scale.OctaveCount * scale.ToneCount);
break;

default:
throw new ArgumentException("WARNING: Illegal parameter passed to method GetStandardOctaveScale(int linearUpperBound).");
}

scale.FinalBinCount = finalBinCount;
scale.BinBounds = LinearToSplitLinearOctaveScale(sr, frameSize, finalBinCount, scale.LinearBound, scale.Nyquist, scale.ToneCount);
scale.GridLineLocations = GetGridLineLocations(FreqScaleType.LinearOctaveStandard, scale.BinBounds);
return scale;
}

public static double[,] ConvertAmplitudeSpectrogramToDecibelOctaveScale(double[,] inputSpgram, FrequencyScale freqScale)
{
//square the values to produce power spectrogram
Expand Down Expand Up @@ -365,14 +418,14 @@ public static double[] OctaveSpectrum(int[,] octaveBinBounds, double[] linearSpe
/// </summary>
public static int[,] LinearToSplitLinearOctaveScale(int sr, int frameSize, int finalBinCount, int lowerFreqBound, int upperFreqBound, int octaveDivisions)
{
var bandBounds = GetFractionalOctaveBands(lowerFreqBound, upperFreqBound, octaveDivisions);
var octaveBandsLowerBounds = GetFractionalOctaveBands(lowerFreqBound, upperFreqBound, octaveDivisions);
int nyquist = sr / 2;
int binCount = frameSize / 2;
var linearFreqScale = GetLinearFreqScale(nyquist, binCount);
int spectrumBinCount = frameSize / 2;
var linearFreqScale = GetLinearFreqScale(nyquist, spectrumBinCount);

var splitLinearOctaveIndexBounds = new int[finalBinCount, 2];
double freqStep = nyquist / (double)binCount;
int topLinearIndex = (int)Math.Round(lowerFreqBound / freqStep) + 1;
double freqStep = nyquist / (double)spectrumBinCount;
int topLinearIndex = (int)Math.Round(lowerFreqBound / freqStep);

// fill in the linear part of the freq scale
for (int i = 0; i < topLinearIndex; i++)
Expand All @@ -385,7 +438,7 @@ public static double[] OctaveSpectrum(int[,] octaveBinBounds, double[] linearSpe
{
for (int j = 0; j < linearFreqScale.Length; j++)
{
if (linearFreqScale[j] > bandBounds[i - topLinearIndex])
if (linearFreqScale[j] > octaveBandsLowerBounds[i - topLinearIndex])
{
splitLinearOctaveIndexBounds[i, 0] = j;
splitLinearOctaveIndexBounds[i, 1] = (int)Math.Round(linearFreqScale[j]);
Expand All @@ -394,7 +447,7 @@ public static double[] OctaveSpectrum(int[,] octaveBinBounds, double[] linearSpe
}
}

// make sure last index has values
// make sure last index extends to last bin of the linear spectrum.
splitLinearOctaveIndexBounds[finalBinCount - 1, 0] = linearFreqScale.Length - 1;
splitLinearOctaveIndexBounds[finalBinCount - 1, 1] = (int)Math.Round(linearFreqScale[linearFreqScale.Length - 1]);

Expand Down

0 comments on commit c20e4ca

Please sign in to comment.