From 1598a40b0b3c668b3fb9bc01191514c4378be1ba Mon Sep 17 00:00:00 2001 From: towsey Date: Tue, 4 Aug 2020 16:26:04 +1000 Subject: [PATCH] Make Octave Scale methods more efficient Issue #332 --- src/AudioAnalysisTools/DSP/OctaveFreqScale.cs | 102 ++++++++---------- .../SpectrogramOctaveScale.cs | 26 ++--- 2 files changed, 50 insertions(+), 78 deletions(-) diff --git a/src/AudioAnalysisTools/DSP/OctaveFreqScale.cs b/src/AudioAnalysisTools/DSP/OctaveFreqScale.cs index 28ba18f86..01a6b7820 100644 --- a/src/AudioAnalysisTools/DSP/OctaveFreqScale.cs +++ b/src/AudioAnalysisTools/DSP/OctaveFreqScale.cs @@ -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; @@ -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; @@ -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); } @@ -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; } @@ -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 @@ -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 @@ -417,25 +397,26 @@ public static double[] OctaveSpectrum(int[,] octaveBinBounds, double[] linearSpe /// /// Returns the index bounds for a split herz scale - bottom part linear, top part octave scaled. /// - 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++) { @@ -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); @@ -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 @@ -580,6 +561,11 @@ public static double[] GetFractionalOctaveBands(double minFreq, double maxFreq, continue; } + if (toneFloor > maxFreq) + { + break; + } + list.Add(toneFloor); } } diff --git a/src/AudioAnalysisTools/StandardSpectrograms/SpectrogramOctaveScale.cs b/src/AudioAnalysisTools/StandardSpectrograms/SpectrogramOctaveScale.cs index 328654124..f6d69fdf1 100644 --- a/src/AudioAnalysisTools/StandardSpectrograms/SpectrogramOctaveScale.cs +++ b/src/AudioAnalysisTools/StandardSpectrograms/SpectrogramOctaveScale.cs @@ -44,10 +44,12 @@ public SpectrogramOctaveScale(AmplitudeSonogram sg) /// Matrix of amplitude values. 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); @@ -56,21 +58,5 @@ public override void Make(double[,] amplitudeM) //store the full bandwidth modal noise profile this.ModalNoiseProfile = tuple.Item2; } - - //################################################################################################################################## - - /// - /// 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.OctaveDataReduction); - //var freqScale = new FrequencyScale(FreqScaleType.Linear125Octaves6Tones30Nyquist11025); - var freqScale = new FrequencyScale(FreqScaleType.Linear62Octaves7Tones31Nyquist11025); - - double[,] m = OctaveFreqScale.ConvertAmplitudeSpectrogramToDecibelOctaveScale(matrix, freqScale); - return m; - } } } \ No newline at end of file