Skip to content

Commit

Permalink
Shift methods to better locations
Browse files Browse the repository at this point in the history
Issue #332 Shift filterBank methods to Spectrum tools because they are now generalised for all frequency scale transformations.
  • Loading branch information
towsey committed Aug 20, 2020
1 parent 93c2703 commit 6acfa88
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 232 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -487,19 +487,6 @@ public static Image<Rgb24> GetRibbonSpectrograms(
AudioRecording recording,
string sourceRecordingName)
{
//var type = FreqScaleType.OctaveDataReduction;
//var freqScale = new FrequencyScale(type);

//// ensure that the freq scale and the spectrogram config are consistent.
//sgConfig.WindowSize = freqScale.WindowSize;
//freqScale.WindowStep = sgConfig.WindowStep;
//sgConfig.WindowOverlap = SonogramConfig.CalculateFrameOverlap(freqScale.WindowSize, freqScale.WindowStep);

//// TODO at present noise reduction type must be set = Standard.
//sgConfig.NoiseReductionType = NoiseReductionType.Standard;
//sgConfig.NoiseReductionParameter = 3.0;

//var octaveScaleGram = new SpectrogramOctaveScale(sgConfig, freqScale, recording.WavReader);
var octaveScaleGram = GetOctaveReducedSpectrogram(sgConfig, recording);
var image1 = octaveScaleGram.GetImage();

Expand All @@ -511,13 +498,6 @@ public static Image<Rgb24> GetRibbonSpectrograms(

var combinedImage = ImageTools.CombineImagesVertically(imageList);
var title = "RIBBON SPECTROGRAMS-Linear32 & Octave20: " + sourceRecordingName;

//var titleBar = BaseSonogram.DrawTitleBarOfGrayScaleSpectrogram(title, image.Width, ImageTags[OctaveScaleSpectrogram]);
//var startTime = TimeSpan.Zero;
//var xAxisTicInterval = TimeSpan.FromSeconds(1);
//TimeSpan xAxisPixelDuration = TimeSpan.FromSeconds(sgConfig.WindowStep / (double)sgConfig.SampleRate);
//var labelInterval = TimeSpan.FromSeconds(5);
//image = BaseSonogram.FrameSonogram(image, titleBar, startTime, xAxisTicInterval, xAxisPixelDuration, labelInterval);
var image = octaveScaleGram.GetImageFullyAnnotated(combinedImage, title, null);
return image;
}
Expand Down
34 changes: 28 additions & 6 deletions src/AudioAnalysisTools/DSP/FrequencyScale.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@
namespace AudioAnalysisTools.DSP
{
using System;
using System.IO;
using Acoustics.Shared.ImageSharp;
using AudioAnalysisTools.StandardSpectrograms;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using TowseyLibrary;
using Path = System.IO.Path;

// IMPORTANT NOTE: If you are converting Hertz scale from LINEAR to MEL or OCTAVE, this conversion MUST be done BEFORE noise reduction

Expand All @@ -34,7 +31,7 @@ public class FrequencyScale
/// <summary>
/// Initializes a new instance of the <see cref="FrequencyScale"/> class.
/// CONSTRUCTOR
/// Calling this constructor assumes a full-scale linear freq scale is required.
/// Calling this constructor assumes the standard linear 0-nyquist freq scale is required.
/// </summary>
public FrequencyScale(int nyquist, int frameSize, int hertzGridInterval)
{
Expand All @@ -51,7 +48,7 @@ public FrequencyScale(int nyquist, int frameSize, int hertzGridInterval)
/// <summary>
/// Initializes a new instance of the <see cref="FrequencyScale"/> class.
/// CONSTRUCTOR
/// Call this constructor when want to change freq scale but keep linear.
/// Call this constructor when want to change freq scale but keep it linear.
/// </summary>
public FrequencyScale(int nyquist, int frameSize, int finalBinCount, int hertzGridInterval)
{
Expand Down Expand Up @@ -289,6 +286,31 @@ public int GetBinIdInReducedSpectrogramForHerzValue(int herzValue)
return binBounds;
}

/// <summary>
/// THis method assumes that the frameSize will be power of 2
/// FOR DEBUG PURPOSES, when sr = 22050 and frame size = 8192, the following Hz are located at index:
/// Hz Index
/// 15 6
/// 31 12
/// 62 23
/// 125 46
/// 250 93
/// 500 186
/// 1000 372.
/// </summary>
public static double[] GetLinearFreqScale(int nyquist, int binCount)
{
double freqStep = nyquist / (double)binCount;
double[] linearFreqScale = new double[binCount];

for (int i = 0; i < binCount; i++)
{
linearFreqScale[i] = freqStep * i;
}

return linearFreqScale;
}

/// <summary>
/// T.
/// </summary>
Expand Down Expand Up @@ -395,6 +417,6 @@ public static void DrawFrequencyLinesOnImage(Image<Rgb24> bmp, int[,] gridLineLo
}
}
});
} //end AddHzGridLines()
}
}
}
139 changes: 18 additions & 121 deletions src/AudioAnalysisTools/DSP/OctaveFreqScale.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace AudioAnalysisTools.DSP
{
using System;
using System.Collections.Generic;
using AudioAnalysisTools.StandardSpectrograms;
using MathNet.Numerics;
using TowseyLibrary;

Expand Down Expand Up @@ -128,7 +129,8 @@ public static FrequencyScale GetStandardOctaveScale(FrequencyScale scale)
var linearSpectrum = MatrixTools.GetRow(inputSpgram, row);

// convert the spectrum to its octave form
var octaveSpectrum = ConvertLinearSpectrumToOctaveScale(octaveBinBounds, linearSpectrum);
//var octaveSpectrum = ConvertLinearSpectrumToOctaveScale(octaveBinBounds, linearSpectrum);
var octaveSpectrum = SpectrogramTools.RescaleSpectrumUsingFilterbank(octaveBinBounds, linearSpectrum);

//return the spectrum to output spectrogram.
MatrixTools.SetRow(octaveSpectrogram, row, octaveSpectrum);
Expand Down Expand Up @@ -235,43 +237,6 @@ public static FrequencyScale GetStandardOctaveScale(FrequencyScale scale)
return returnMatrix;
}

/// <summary>
/// Converts a single linear spectrum to octave scale spectrum.
/// </summary>
public static double[] ConvertLinearSpectrumToOctaveScale(int[,] transformMatrix, double[] linearSpectrum)
{
int length = transformMatrix.GetLength(0);
var opOctaveSpectrum = new double[length];

// Fill in the first value of the octave spectrum
int lowIndex1 = transformMatrix[0, 0];
int centreIndex1 = transformMatrix[0, 0];
int highIndex1 = transformMatrix[1, 0];
opOctaveSpectrum[0] = FilterbankIntegral(linearSpectrum, lowIndex1, centreIndex1, highIndex1);

// fill in remainder except last
for (int i = 1; i < length - 1; i++)
{
int lowIndex = transformMatrix[i - 1, 0];
int centreIndex = transformMatrix[i, 0];
int highIndex = transformMatrix[i + 1, 0];
if (highIndex >= linearSpectrum.Length)
{
highIndex = linearSpectrum.Length - 1;
}

opOctaveSpectrum[i] = FilterbankIntegral(linearSpectrum, lowIndex, centreIndex, highIndex);
}

// now fill in the last value of the octave spectrum
int lowIndex2 = transformMatrix[length - 2, 0];
int centreIndex2 = transformMatrix[length - 1, 0];
int highIndex2 = transformMatrix[length - 1, 0];
opOctaveSpectrum[length - 1] = FilterbankIntegral(linearSpectrum, lowIndex2, centreIndex2, highIndex2);

return opOctaveSpectrum;
}

/// <summary>
/// Returns a matrix that is used to transform a spectrum having linear Hz scale into a spectrum having an octave freq scale.
/// The returned matrix is size N x 2, where N = the length of the output spectrum.
Expand All @@ -283,7 +248,7 @@ public static double[] ConvertLinearSpectrumToOctaveScale(int[,] transformMatrix
{
// Get the linear freq scale.
int inputSpectrumSize = frameSize / 2;
var linearFreqScale = GetLinearFreqScale(nyquist, inputSpectrumSize);
var linearFreqScale = FrequencyScale.GetLinearFreqScale(nyquist, inputSpectrumSize);

// Get the octave freq scale.
var octaveBandsLowerBounds = GetFractionalOctaveBands(lowerFreqBound, upperFreqBound, octaveDivisions);
Expand Down Expand Up @@ -325,7 +290,7 @@ public static double[] ConvertLinearSpectrumToOctaveScale(int[,] transformMatrix
/// <summary>
/// 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 linear part is from 0-2 kHz with reduction by factor of 8.
/// The octave part is obtained by setting octave divisions or tone count = 5.
/// </summary>
/// <returns>a frequency scale for spectral-data reduction purposes.</returns>
Expand All @@ -338,31 +303,34 @@ public static FrequencyScale GetDataReductionScale(FrequencyScale scale)

// linear reduction of the lower spectrum from 0 - 2 kHz.
scale.LinearBound = 2000;
int linearReductionFactor = 6;
int linearReductionFactor = 8;

// 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);
int spectrumBinCount = frameSize / 2;
var linearFreqScale = GetLinearFreqScale(scale.Nyquist, spectrumBinCount);
var linearFreqScale = FrequencyScale.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(octaveCount * scale.ToneCount);

// calculate number of bins in linear portion. +1 because going to finish up at end of linear portion.
int linearReducedBinCount = (topLinearIndex / linearReductionFactor) + 1;
int finalBinCount = linearReducedBinCount + octaveBandsLowerBounds.Length;
var splitLinearOctaveIndexBounds = new int[finalBinCount, 2];

// fill in the linear part of the freq scale
for (int i = 0; i < linearReducedBinCount; i++)
int z = 1;
while (splitLinearOctaveIndexBounds[z - 1, 1] < scale.LinearBound)
{
splitLinearOctaveIndexBounds[i, 0] = i;
splitLinearOctaveIndexBounds[i, 1] = (int)Math.Round(linearFreqScale[i * linearReductionFactor]);
splitLinearOctaveIndexBounds[z, 0] = z * linearReductionFactor;
splitLinearOctaveIndexBounds[z, 1] = (int)Math.Round(linearFreqScale[z * linearReductionFactor]);
z++;
}

// fill in the octave part of the freq scale
for (int i = linearReducedBinCount; i < finalBinCount; i++)
for (int i = linearReducedBinCount + 1; i < finalBinCount; i++)
{
for (int j = 0; j < linearFreqScale.Length; j++)
{
Expand Down Expand Up @@ -397,7 +365,7 @@ public static FrequencyScale GetDataReductionScale(FrequencyScale scale)
var bandBounds = GetFractionalOctaveBands(lowerFreqBound, upperFreqBound, octaveDivisions);
int nyquist = sr / 2;
int binCount = frameSize / 2;
var linearFreqScale = GetLinearFreqScale(nyquist, binCount);
var linearFreqScale = FrequencyScale.GetLinearFreqScale(nyquist, binCount);

var octaveIndexBounds = new int[finalBinCount, 2];

Expand Down Expand Up @@ -483,76 +451,5 @@ public static double[] GetFractionalOctaveBands(double lowerBound, int subbandCo

return fractionalOctaveBands;
}

/// <summary>
/// THis method assumes that the frameSize will be power of 2
/// FOR DEBUG PURPOSES, when sr = 22050 and frame size = 8192, the following Hz are located at index:
/// Hz Index
/// 15 6
/// 31 12
/// 62 23
/// 125 46
/// 250 93
/// 500 186
/// 1000 372.
/// </summary>
public static double[] GetLinearFreqScale(int nyquist, int binCount)
{
double freqStep = nyquist / (double)binCount;
double[] linearFreqScale = new double[binCount];

for (int i = 0; i < binCount; i++)
{
linearFreqScale[i] = freqStep * i;
}

return linearFreqScale;
}

public static double FilterbankIntegral(double[] spectrum, int lowIndex, int centreIndex, int highIndex)
{
// let k = index into spectral vector.
// for all k < lowIndex, filterBank[k] = 0;
// for all k > highIndex, filterBank[k] = 0;

// for all k in range (lowIndex <= k < centreIndex), filterBank[k] = (k-lowIndex) /(centreIndex - lowIndex)
// for all k in range (centreIndex <= k <= highIndex), filterBank[k] = (highIndex-k)/(highIndex - centreIndex)

double area = 0.0;
double integral = 0.0;
int delta = centreIndex - lowIndex;
if (delta > 0)
{
for (int k = lowIndex; k < centreIndex; k++)
{
double weight = (k - lowIndex) / (double)delta;
integral += weight * spectrum[k];
area += weight;
}
}

integral += spectrum[centreIndex];
area += 1.0;

delta = highIndex - centreIndex;
if (delta > 0)
{
for (int k = centreIndex + 1; k <= highIndex; k++)
{
if (delta == 0)
{
continue;
}

double weight = (highIndex - k) / (double)delta;
integral += weight * spectrum[k];
area += weight;
}
}

// NormaliseMatrixValues to area of the triangular filter
integral /= area;
return integral;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ private void InitialiseSpectrogram(WavReader wav)
{
//get each frame or spectrum in turn and rescale.
var linearSpectrum = MatrixTools.GetRow(inputSpgram, row);
var rescaledSpectrum = SpectrogramStandard.RescaleSpectrumUsingFilterbank(binBounds, linearSpectrum);
var rescaledSpectrum = SpectrogramTools.RescaleSpectrumUsingFilterbank(binBounds, linearSpectrum);

//return the spectrum to output spectrogram.
MatrixTools.SetRow(opM, row, rescaledSpectrum);
Expand Down
Loading

0 comments on commit 6acfa88

Please sign in to comment.