diff --git a/src/AudioAnalysisTools/DSP/OctaveFreqScale.cs b/src/AudioAnalysisTools/DSP/OctaveFreqScale.cs
index a3c31df20..28ba18f86 100644
--- a/src/AudioAnalysisTools/DSP/OctaveFreqScale.cs
+++ b/src/AudioAnalysisTools/DSP/OctaveFreqScale.cs
@@ -20,8 +20,7 @@ public static class OctaveFreqScale
///
public static void GetOctaveScale(FrequencyScale scale)
{
- int finalBinCount = 256;
- int sr, frameSize, octaveDivisions;
+ int sr, frameSize;
// NOTE: octaveDivisions = the number of fractional Hz steps within one octave. Piano octave contains 12 steps per octave.
@@ -32,59 +31,53 @@ public static void GetOctaveScale(FrequencyScale scale)
case FreqScaleType.LinearOctaveStandard:
//This is a split linear-octave frequency scale.
// Valid values for linearUpperBound are 125, 250, 500, 1000.
- int linearUpperBound = 250;
+ int linearUpperBound = 1000;
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;
- frameSize = 512;
- finalBinCount = 45;
- scale.OctaveCount = 4;
- octaveDivisions = 6; // tone steps within one octave.
- scale.LinearBound = 1000;
- scale.Nyquist = 11025;
- break;
+ // This spectral conversion is for data reduction purposes.
+ // It is a split linear-octave frequency scale.
+ scale = GetDataReductionScale(scale);
+ return;
case FreqScaleType.Linear62Octaves7Tones31Nyquist11025:
sr = 22050;
frameSize = 8192;
- scale.OctaveCount = 7;
- octaveDivisions = 31; // tone steps within one octave. Note: piano = 12 steps per octave.
scale.LinearBound = 62;
- scale.Nyquist = 11025;
+ scale.OctaveCount = 7;
+ scale.ToneCount = 31; // tone steps within one octave. Note: piano = 12 steps per octave.
+ scale.FinalBinCount = 253;
break;
case FreqScaleType.Linear125Octaves6Tones30Nyquist11025:
// constants required for split linear-octave scale when sr = 22050
sr = 22050;
frameSize = 8192;
- scale.OctaveCount = 6;
- octaveDivisions = 32; // tone steps within one octave. Note: piano = 12 steps per octave.
scale.LinearBound = 125;
- scale.Nyquist = 11025;
+ scale.OctaveCount = 6;
+ scale.ToneCount = 32; // tone steps within one octave. Note: piano = 12 steps per octave.
+ scale.FinalBinCount = 255;
break;
case FreqScaleType.Octaves24Nyquist32000:
//// constants required for full octave scale when sr = 64000
sr = 64000;
frameSize = 16384;
- scale.OctaveCount = 8;
- octaveDivisions = 24; // tone steps within one octave. Note: piano = 12 steps per octave.
scale.LinearBound = 15;
- scale.Nyquist = 32000;
+ scale.OctaveCount = 8;
+ scale.ToneCount = 24; // tone steps within one octave. Note: piano = 12 steps per octave.
+ scale.FinalBinCount = 253;
break;
case FreqScaleType.Linear125Octaves7Tones28Nyquist32000:
// constants required for split linear-octave scale when sr = 64000
sr = 64000;
frameSize = 16384; // = 2*8192 or 4*4096;
- scale.OctaveCount = 7;
- octaveDivisions = 28; // tone steps within one octave. Note: piano = 12 steps per octave.
scale.LinearBound = 125;
- scale.Nyquist = 32000;
+ scale.OctaveCount = 7;
+ scale.ToneCount = 28; // tone steps within one octave. Note: piano = 12 steps per octave.
+ scale.FinalBinCount = 253;
break;
default:
@@ -92,10 +85,9 @@ public static void GetOctaveScale(FrequencyScale scale)
return;
}
+ scale.Nyquist = sr / 2;
scale.WindowSize = frameSize; // = 2*8192 or 4*4096
- scale.FinalBinCount = finalBinCount;
- scale.ToneCount = octaveDivisions;
- scale.BinBounds = LinearToSplitLinearOctaveScale(sr, frameSize, finalBinCount, scale.LinearBound, scale.Nyquist, scale.ToneCount);
+ scale.BinBounds = LinearToSplitLinearOctaveScale(sr, frameSize, scale.FinalBinCount, scale.LinearBound, scale.Nyquist, scale.ToneCount);
scale.GridLineLocations = GetGridLineLocations(fst, scale.BinBounds);
}
@@ -381,11 +373,21 @@ public static FrequencyScale GetStandardOctaveScale(FrequencyScale scale, int li
///
/// Converts a single linear spectrum to octave scale spectrum.
+ /// WARNING: THis method assumes that the ocatve spectrum is to be same length as linear spectrum.
+ /// Therefore the index values in the octaveBinBounds matrix should NOT exceed bounds of the linear spectrum.
///
public static double[] OctaveSpectrum(int[,] octaveBinBounds, double[] linearSpectrum)
{
int length = octaveBinBounds.GetLength(0);
var octaveSpectrum = new double[length];
+
+ // Fill in the first value of the octave spectrum
+ int lowIndex1 = octaveBinBounds[0, 0];
+ int centreIndex1 = octaveBinBounds[0, 0];
+ int highIndex1 = octaveBinBounds[1, 0];
+ octaveSpectrum[0] = FilterbankIntegral(linearSpectrum, lowIndex1, centreIndex1, highIndex1);
+
+ // fill in remainer except last
for (int i = 1; i < length - 1; i++)
{
int lowIndex = octaveBinBounds[i - 1, 0];
@@ -399,17 +401,16 @@ public static double[] OctaveSpectrum(int[,] octaveBinBounds, double[] linearSpe
octaveSpectrum[i] = FilterbankIntegral(linearSpectrum, lowIndex, centreIndex, highIndex);
}
- // now fill in the first value of the octave spectrum
- int lowIndex1 = octaveBinBounds[0, 0];
- int centreIndex1 = octaveBinBounds[0, 0];
- int highIndex1 = octaveBinBounds[1, 0];
- octaveSpectrum[0] = FilterbankIntegral(linearSpectrum, lowIndex1, centreIndex1, highIndex1);
-
// now fill in the last value of the octave spectrum
int lowIndex2 = octaveBinBounds[length - 2, 0];
int centreIndex2 = octaveBinBounds[length - 1, 0];
int highIndex2 = octaveBinBounds[length - 1, 0];
- octaveSpectrum[length - 1] = FilterbankIntegral(linearSpectrum, lowIndex2, centreIndex2, highIndex2);
+
+ if (highIndex2 != 0)
+ {
+ octaveSpectrum[length - 1] = FilterbankIntegral(linearSpectrum, lowIndex2, centreIndex2, highIndex2);
+ }
+
return octaveSpectrum;
}
@@ -451,14 +452,67 @@ public static double[] OctaveSpectrum(int[,] octaveBinBounds, double[] linearSpe
splitLinearOctaveIndexBounds[finalBinCount - 1, 0] = linearFreqScale.Length - 1;
splitLinearOctaveIndexBounds[finalBinCount - 1, 1] = (int)Math.Round(linearFreqScale[linearFreqScale.Length - 1]);
- // A HACK!!! Make sure second last index has values if they are zero
- if (splitLinearOctaveIndexBounds[finalBinCount - 2, 0] == 0)
+ return splitLinearOctaveIndexBounds;
+ }
+
+ ///
+ /// This method assumes that the linear spectrum is derived from a 512 frame with sr = 22050.
+ /// It is a split linear-octave scale.
+ /// The linear part is from 0-2 kHz with reduction by averaging every 6 frequency bins.
+ /// The octave part is obtained by setting octave divisions or tone count = 5.
+ ///
+ /// a frequency scale for spectral-data reduction purposes.
+ public static FrequencyScale GetDataReductionScale(FrequencyScale scale)
+ {
+ int sr = 22050;
+ int frameSize = 512;
+ scale.Nyquist = sr / 2;
+
+ // linear reduction of the lower spectrum from 0 - 2 kHz.
+ 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;
+ scale.ToneCount = 5;
+
+ var octaveBandsLowerBounds = GetFractionalOctaveBands(scale.LinearBound, scale.Nyquist, scale.ToneCount);
+ int spectrumBinCount = frameSize / 2;
+ var linearFreqScale = GetLinearFreqScale(scale.Nyquist, spectrumBinCount);
+
+ 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);
+ var splitLinearOctaveIndexBounds = new int[finalBinCount, 2];
+
+ // fill in the linear part of the freq scale
+ for (int i = 0; i < linearReducedBinCount; i++)
{
- splitLinearOctaveIndexBounds[finalBinCount - 2, 0] = linearFreqScale.Length - 1;
- splitLinearOctaveIndexBounds[finalBinCount - 2, 1] = (int)Math.Round(linearFreqScale[linearFreqScale.Length - 1]);
+ splitLinearOctaveIndexBounds[i, 0] = i;
+ splitLinearOctaveIndexBounds[i, 1] = (int)Math.Round(linearFreqScale[i * linearReductionFactor]);
}
- return splitLinearOctaveIndexBounds;
+ // fill in the octave part of the freq scale
+ for (int i = linearReducedBinCount; i < finalBinCount; i++)
+ {
+ for (int j = 0; j < linearFreqScale.Length; j++)
+ {
+ if (linearFreqScale[j] > octaveBandsLowerBounds[i - linearReducedBinCount])
+ {
+ splitLinearOctaveIndexBounds[i, 0] = j;
+ splitLinearOctaveIndexBounds[i, 1] = (int)Math.Round(linearFreqScale[j]);
+ break;
+ }
+ }
+ }
+
+ // 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]);
+
+ scale.BinBounds = splitLinearOctaveIndexBounds;
+ return scale;
}
///
@@ -627,64 +681,5 @@ public static double FilterbankIntegral(double[] spectrum, int lowIndex, int cen
integral /= area;
return integral;
}
-
- ///
- /// Returns a simple spectrogram for test purposes.
- /// Write code for simple test. Different spectra tried so far:
- /// (1) Uniform spectrum = 1.0
- /// (2) Ramp spectrum
- /// (3) SPike spectrum.
- ///
- public static double[] GetSimpleTestSpectrum(int sr, int frameSize)
- {
- // int nyquist = sr / 2;
- int binCount = frameSize / 2;
- double[] spectrum = new double[binCount];
-
- // return a linear frequency scale
- // double freqStep = nyquist / (double)binCount;
- for (int i = 0; i < binCount; i++)
- {
- // ramp spectrum
- //spectrum[i] = freqStep * i;
-
- //Uniform spectrum
- spectrum[i] = 1.0;
- }
-
- // Spike spectrum
- //spectrum[500] = 1.0;
-
- return spectrum;
- }
-
- public static void TestOctaveScale(FreqScaleType fst)
- {
- var freqScale = new FrequencyScale(fst);
- var octaveBinBounds = freqScale.BinBounds;
-
- // now test the octave scale using a test spectrum
- int sr = 22050;
- int frameSize = 8192; // default for sr = 22050
-
- if (fst == FreqScaleType.Octaves24Nyquist32000 || fst == FreqScaleType.Linear125Octaves7Tones28Nyquist32000)
- {
- sr = 64000;
- frameSize = 16384; // default for sr = 64000
- }
-
- // Get a simple test spectrum
- var linearSpectrum = GetSimpleTestSpectrum(sr, frameSize);
-
- //do the test
- var octaveSpectrum = OctaveSpectrum(octaveBinBounds, linearSpectrum);
-
- // write output
- int rowCount = octaveBinBounds.GetLength(0);
- for (int i = 0; i < rowCount; i++)
- {
- Console.WriteLine(i + " bin-" + octaveBinBounds[i, 0] + " " + octaveBinBounds[i, 1] + "Hz " + octaveSpectrum[i]);
- }
- }
}
}
\ No newline at end of file
diff --git a/src/AudioAnalysisTools/StandardSpectrograms/SpectrogramOctaveScale.cs b/src/AudioAnalysisTools/StandardSpectrograms/SpectrogramOctaveScale.cs
index 27fdd71df..328654124 100644
--- a/src/AudioAnalysisTools/StandardSpectrograms/SpectrogramOctaveScale.cs
+++ b/src/AudioAnalysisTools/StandardSpectrograms/SpectrogramOctaveScale.cs
@@ -60,52 +60,17 @@ public override void Make(double[,] amplitudeM)
//##################################################################################################################################
///
- /// NOTE!!!! The decibel array has been normalised in 0 - 1.
+ /// Converts amplitude spectrogram to octave scale using one of the possible octave scale types.
///
public static double[,] MakeOctaveScaleSpectrogram(SonogramConfig config, double[,] matrix, int sampleRate, int linearLimit)
{
- var freqScale = new FrequencyScale(FreqScaleType.LinearOctaveStandard);
+ //var freqScale = new FrequencyScale(FreqScaleType.LinearOctaveStandard);
//var freqScale = new FrequencyScale(FreqScaleType.OctaveDataReduction);
- //var freqScale = new FrequencyScale(FreqScaleType.Linear1000Octaves4Tones6Nyquist11025);
+ //var freqScale = new FrequencyScale(FreqScaleType.Linear125Octaves6Tones30Nyquist11025);
+ var freqScale = new FrequencyScale(FreqScaleType.Linear62Octaves7Tones31Nyquist11025);
- // THIS IS THE CRITICAL LINE.
- // TODO: SHOULD DEVELOP A SEPARATE UNIT TEST for this method
double[,] m = OctaveFreqScale.ConvertAmplitudeSpectrogramToDecibelOctaveScale(matrix, freqScale);
return m;
}
-
- ///
- /// This method takes an audio recording and returns an octave scale spectrogram.
- /// At the present time it only works for recordings with 64000 sample rate and returns a 256 bin sonogram.
- /// TODO: generalise this method for other recordings and octave scales.
- ///
- public static BaseSonogram ConvertRecordingToOctaveScaleSonogram(AudioRecording recording, FreqScaleType fst)
- {
- var freqScale = new FrequencyScale(fst);
- double windowOverlap = 0.75;
- var sonoConfig = new SonogramConfig
- {
- WindowSize = freqScale.WindowSize,
- WindowOverlap = windowOverlap,
- SourceFName = recording.BaseName,
- NoiseReductionType = NoiseReductionType.None,
- NoiseReductionParameter = 0.0,
- };
-
- // Generate amplitude sonogram and then conver to octave scale
- var sonogram = new AmplitudeSonogram(sonoConfig, recording.WavReader);
-
- // THIS IS THE CRITICAL LINE.
- // TODO: SHOULD DEVELOP A SEPARATE UNIT TEST for this method
- sonogram.Data = OctaveFreqScale.ConvertAmplitudeSpectrogramToDecibelOctaveScale(sonogram.Data, freqScale);
-
- // DO NOISE REDUCTION
- var dataMatrix = SNR.NoiseReduce_Standard(sonogram.Data);
- sonogram.Data = dataMatrix;
- int windowSize = freqScale.FinalBinCount * 2;
- sonogram.Configuration.WindowSize = windowSize;
- sonogram.Configuration.WindowStep = (int)Math.Round(windowSize * (1 - windowOverlap));
- return sonogram;
- }
- } // end class SpectrogramOctaveScale
+ }
}
\ No newline at end of file
diff --git a/tests/Acoustics.Test/AudioAnalysisTools/DSP/FrequencyScaleTests.cs b/tests/Acoustics.Test/AudioAnalysisTools/DSP/FrequencyScaleTests.cs
index 07cbc6869..88f180e57 100644
--- a/tests/Acoustics.Test/AudioAnalysisTools/DSP/FrequencyScaleTests.cs
+++ b/tests/Acoustics.Test/AudioAnalysisTools/DSP/FrequencyScaleTests.cs
@@ -180,6 +180,39 @@ public void LinearFrequencyScale()
Assert.AreEqual(1621, image.Width);
}
+ ///
+ /// Test of the default standard split LINEAR-Octave FREQ SCALE
+ /// Check it on pure tone spectrum
+ ///
+ [TestMethod]
+ public void TestSplitLinearOctaveFrequencyScale()
+ {
+ // Test default octave scale where default linear portion is 0-1000Hz.
+ //var fst = FreqScaleType.Linear125Octaves6Tones30Nyquist11025;
+ var fst = FreqScaleType.LinearOctaveStandard;
+ var freqScale = new FrequencyScale(fst);
+
+ int[,] octaveBinBounds = freqScale.BinBounds;
+
+ // generate pure tone spectrum.
+ double[] linearSpectrum = new double[256];
+ linearSpectrum[128] = 1.0;
+
+ double[] octaveSpectrum = OctaveFreqScale.OctaveSpectrum(octaveBinBounds, linearSpectrum);
+
+ Assert.AreEqual(103, octaveSpectrum.Length);
+ Assert.AreEqual(0.0, octaveSpectrum[78]);
+ Assert.AreEqual(0.125, octaveSpectrum[79]);
+ Assert.AreEqual(0.125, octaveSpectrum[80]);
+ Assert.AreEqual(0.0, octaveSpectrum[81]);
+
+ //var expectedBinBounds = new[,]
+ //{
+ //}
+
+ //Assert.That.MatricesAreEqual(expectedBinBounds, freqScale.BinBounds);
+ }
+
///
/// METHOD TO CHECK IF Octave FREQ SCALE IS WORKING
/// Check it on standard one minute recording, SR=22050.