Skip to content

Commit

Permalink
Make Octave Scale methods more efficient
Browse files Browse the repository at this point in the history
Issue #332
  • Loading branch information
towsey committed Aug 4, 2020
1 parent 52556ac commit 1598a40
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 78 deletions.
102 changes: 44 additions & 58 deletions src/AudioAnalysisTools/DSP/OctaveFreqScale.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,28 @@ public static void GetOctaveScale(FrequencyScale scale)
//This is a split linear-octave frequency scale.
// Valid values for linearUpperBound are 125, 250, 500, 1000.
int linearUpperBound = 1000;
scale = GetStandardOctaveScale(scale, linearUpperBound);
GetStandardOctaveScale(scale, linearUpperBound);
return;

case FreqScaleType.OctaveDataReduction:
// This spectral conversion is for data reduction purposes.
// It is a split linear-octave frequency scale.
scale = GetDataReductionScale(scale);
GetDataReductionScale(scale);
return;

case FreqScaleType.Linear62Octaves7Tones31Nyquist11025:
case FreqScaleType.Linear62OctaveTones31Nyquist11025:
sr = 22050;
frameSize = 8192;
scale.LinearBound = 62;
scale.OctaveCount = 7;
scale.ToneCount = 31; // tone steps within one octave. Note: piano = 12 steps per octave.
scale.FinalBinCount = 253;
break;

case FreqScaleType.Linear125Octaves6Tones30Nyquist11025:
case FreqScaleType.Linear125OctaveTones30Nyquist11025:
// constants required for split linear-octave scale when sr = 22050
sr = 22050;
frameSize = 8192;
scale.LinearBound = 125;
scale.OctaveCount = 6;
scale.ToneCount = 32; // tone steps within one octave. Note: piano = 12 steps per octave.
scale.FinalBinCount = 255;
break;
Expand All @@ -65,17 +63,15 @@ public static void GetOctaveScale(FrequencyScale scale)
sr = 64000;
frameSize = 16384;
scale.LinearBound = 15;
scale.OctaveCount = 8;
scale.ToneCount = 24; // tone steps within one octave. Note: piano = 12 steps per octave.
scale.FinalBinCount = 253;
break;

case FreqScaleType.Linear125Octaves7Tones28Nyquist32000:
case FreqScaleType.Linear125OctaveTones28Nyquist32000:
// constants required for split linear-octave scale when sr = 64000
sr = 64000;
frameSize = 16384; // = 2*8192 or 4*4096;
scale.LinearBound = 125;
scale.OctaveCount = 7;
scale.ToneCount = 28; // tone steps within one octave. Note: piano = 12 steps per octave.
scale.FinalBinCount = 253;
break;
Expand All @@ -87,7 +83,7 @@ public static void GetOctaveScale(FrequencyScale scale)

scale.Nyquist = sr / 2;
scale.WindowSize = frameSize; // = 2*8192 or 4*4096
scale.BinBounds = LinearToSplitLinearOctaveScale(sr, frameSize, scale.FinalBinCount, scale.LinearBound, scale.Nyquist, scale.ToneCount);
scale.BinBounds = LinearToSplitLinearOctaveScale(sr, frameSize, scale.LinearBound, scale.Nyquist, scale.ToneCount);
scale.GridLineLocations = GetGridLineLocations(fst, scale.BinBounds);
}

Expand All @@ -110,46 +106,35 @@ public static FrequencyScale GetStandardOctaveScale(FrequencyScale scale, int li
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)
if (linearUpperBound < 64 || linearUpperBound > scale.Nyquist - 64)
{
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;
throw new ArgumentException("WARNING: Illegal parameter passed to method GetStandardOctaveScale(int linearUpperBound).");
}

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;
// init tone steps within one octave. Note: piano = 12 steps per octave.
scale.ToneCount = (int)Math.Round(scale.LinearBound / binWidth);
scale.BinBounds = LinearToSplitLinearOctaveScale(sr, frameSize, scale.LinearBound, scale.Nyquist, scale.ToneCount);
scale.FinalBinCount = scale.BinBounds.GetLength(0);

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;
// These only work for case where linearUpperScale = 1000 Hz
double freqStep = sr / frameSize;
int topLinearIndex = (int)Math.Round(linearUpperBound / freqStep);

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;
var gridLineLocations = new int[4, 2];
gridLineLocations[0, 0] = topLinearIndex;
gridLineLocations[0, 1] = 1000;
gridLineLocations[1, 0] = topLinearIndex * 2;
gridLineLocations[1, 1] = 2000;

default:
throw new ArgumentException("WARNING: Illegal parameter passed to method GetStandardOctaveScale(int linearUpperBound).");
if (linearUpperBound > 256)
{
gridLineLocations[2, 0] = topLinearIndex * 3;
gridLineLocations[2, 1] = 4000;
gridLineLocations[3, 0] = topLinearIndex * 4;
gridLineLocations[3, 1] = 8000;
}

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

Expand Down Expand Up @@ -299,12 +284,12 @@ public static FrequencyScale GetStandardOctaveScale(FrequencyScale scale, int li

switch (ost)
{
case FreqScaleType.Linear62Octaves7Tones31Nyquist11025:
case FreqScaleType.Linear62OctaveTones31Nyquist11025:
gridLineLocations = new int[8, 2];
LoggedConsole.WriteErrorLine("This Octave Scale does not currently have grid data provided.");
break;

case FreqScaleType.Linear125Octaves6Tones30Nyquist11025:
case FreqScaleType.Linear125OctaveTones30Nyquist11025:
gridLineLocations = new int[7, 2];
gridLineLocations[0, 0] = 46; // 125 Hz
gridLineLocations[1, 0] = 79; // 250
Expand All @@ -329,17 +314,12 @@ public static FrequencyScale GetStandardOctaveScale(FrequencyScale scale, int li
gridLineLocations = new int[6, 2];
break;

case FreqScaleType.LinearOctaveStandard:
gridLineLocations = new int[8, 2];
LoggedConsole.WriteErrorLine("This Octave Scale does not currently have grid data provided.");
break;

case FreqScaleType.Octaves24Nyquist32000:
gridLineLocations = new int[8, 2];
LoggedConsole.WriteErrorLine("This Octave Scale does not currently have grid data provided.");
break;

case FreqScaleType.Linear125Octaves7Tones28Nyquist32000:
case FreqScaleType.Linear125OctaveTones28Nyquist32000:
gridLineLocations = new int[9, 2];
gridLineLocations[0, 0] = 34; // 125 Hz
gridLineLocations[1, 0] = 62; // 250
Expand Down Expand Up @@ -417,25 +397,26 @@ public static double[] OctaveSpectrum(int[,] octaveBinBounds, double[] linearSpe
/// <summary>
/// Returns the index bounds for a split herz scale - bottom part linear, top part octave scaled.
/// </summary>
public static int[,] LinearToSplitLinearOctaveScale(int sr, int frameSize, int finalBinCount, int lowerFreqBound, int upperFreqBound, int octaveDivisions)
public static int[,] LinearToSplitLinearOctaveScale(int sr, int frameSize, int lowerFreqBound, int upperFreqBound, int octaveDivisions)
{
var octaveBandsLowerBounds = GetFractionalOctaveBands(lowerFreqBound, upperFreqBound, octaveDivisions);
int nyquist = sr / 2;
int spectrumBinCount = frameSize / 2;
var linearFreqScale = GetLinearFreqScale(nyquist, spectrumBinCount);

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

// fill in the linear part of the freq scale
for (int i = 0; i < topLinearIndex; i++)
for (int i = 0; i <= topLinearIndex; i++)
{
splitLinearOctaveIndexBounds[i, 0] = i;
splitLinearOctaveIndexBounds[i, 1] = (int)Math.Round(linearFreqScale[i]);
}

for (int i = topLinearIndex; i < finalBinCount; i++)
for (int i = topLinearIndex + 1; i < finalBinCount; i++)
{
for (int j = 0; j < linearFreqScale.Length; j++)
{
Expand Down Expand Up @@ -472,8 +453,8 @@ public static FrequencyScale GetDataReductionScale(FrequencyScale scale)
scale.LinearBound = 2000;
int linearReductionFactor = 6;

// REduction of upper spectrum 2-11 kHz: Octave count and tone steps within one octave.
scale.OctaveCount = 2.7;
// Reduction of upper spectrum 2-11 kHz: Octave count and tone steps within one octave.
double octaveCount = 2.7;
scale.ToneCount = 5;

var octaveBandsLowerBounds = GetFractionalOctaveBands(scale.LinearBound, scale.Nyquist, scale.ToneCount);
Expand All @@ -483,7 +464,7 @@ public static FrequencyScale GetDataReductionScale(FrequencyScale scale)
double linearBinWidth = scale.Nyquist / (double)spectrumBinCount;
int topLinearIndex = (int)Math.Round(scale.LinearBound / linearBinWidth);
int linearReducedBinCount = topLinearIndex / linearReductionFactor;
int finalBinCount = linearReducedBinCount + (int)Math.Floor(scale.OctaveCount * scale.ToneCount);
int finalBinCount = linearReducedBinCount + (int)Math.Floor(octaveCount * scale.ToneCount);
var splitLinearOctaveIndexBounds = new int[finalBinCount, 2];

// fill in the linear part of the freq scale
Expand Down Expand Up @@ -580,6 +561,11 @@ public static double[] GetFractionalOctaveBands(double minFreq, double maxFreq,
continue;
}

if (toneFloor > maxFreq)
{
break;
}

list.Add(toneFloor);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ public SpectrogramOctaveScale(AmplitudeSonogram sg)
/// <param name="amplitudeM">Matrix of amplitude values.</param>
public override void Make(double[,] amplitudeM)
{
// Make the octave scale spectrogram.
// Linear portion extends from 0 to H hertz where H can = 1000, 500, 250, 125.
int linearLimit = 1000;
var m = MakeOctaveScaleSpectrogram(this.Configuration, amplitudeM, this.SampleRate, linearLimit);
//var freqScale = new FrequencyScale(FreqScaleType.LinearOctaveStandard);
//var freqScale = new FrequencyScale(FreqScaleType.OctaveDataReduction);
//var freqScale = new FrequencyScale(FreqScaleType.Linear125Octaves6Tones30Nyquist11025);
var freqScale = new FrequencyScale(FreqScaleType.Linear62OctaveTones31Nyquist11025);

double[,] m = OctaveFreqScale.ConvertAmplitudeSpectrogramToDecibelOctaveScale(amplitudeM, freqScale);

// Do noise reduction
var tuple = SNR.NoiseReduce(m, this.Configuration.NoiseReductionType, this.Configuration.NoiseReductionParameter);
Expand All @@ -56,21 +58,5 @@ public override void Make(double[,] amplitudeM)
//store the full bandwidth modal noise profile
this.ModalNoiseProfile = tuple.Item2;
}

//##################################################################################################################################

/// <summary>
/// Converts amplitude spectrogram to octave scale using one of the possible octave scale types.
/// </summary>
public static double[,] MakeOctaveScaleSpectrogram(SonogramConfig config, double[,] matrix, int sampleRate, int linearLimit)
{
//var freqScale = new FrequencyScale(FreqScaleType.LinearOctaveStandard);
//var freqScale = new FrequencyScale(FreqScaleType.OctaveDataReduction);
//var freqScale = new FrequencyScale(FreqScaleType.Linear125Octaves6Tones30Nyquist11025);
var freqScale = new FrequencyScale(FreqScaleType.Linear62Octaves7Tones31Nyquist11025);

double[,] m = OctaveFreqScale.ConvertAmplitudeSpectrogramToDecibelOctaveScale(matrix, freqScale);
return m;
}
}
}

0 comments on commit 1598a40

Please sign in to comment.