Skip to content

Commit

Permalink
Start work on calculating the spectral centroid
Browse files Browse the repository at this point in the history
Issue #292 SC is to be used as a summary index.
  • Loading branch information
towsey authored and atruskie committed Jul 7, 2020
1 parent fb81a88 commit 178af3e
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 3 deletions.
73 changes: 73 additions & 0 deletions src/AudioAnalysisTools/SpectralCentroid.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Text;
using TowseyLibrary;

namespace AudioAnalysisTools
{
/// <summary>
/// Calculates the spectral centroid of a signal or part thereof.
/// The spectral centroid is a considred to be a reliable estimate of the brightness of a recording.
/// Bright recordings contain mre high frequency content. See following for good intro:
/// https://www.cs.cmu.edu/~music/icm/slides/05-algorithmic-composition.pdf
/// Also Wikipedia entry: https://en.wikipedia.org/wiki/Spectral_centroid
/// The spectral centroid is derived from the values in the amplitude spectrogram.
/// A single spectral centroid is calculated for each time frame.
/// If a summary value is required for a longer signal, i.e. one second or one minute, then the centroid values for each frame are averaged over the time period.
/// Note that the frequency value for a bin is located at the centre of the bin. For a typical bin width of 43 Hz, the centre will be at 21.5 Hz above bin minimum.
/// The steps in the calculation are:
/// 1: Normalise the spectrum: normalized_spectrum = spectrum / sum(spectrum) # like a probability mass function
/// 2: Normalise the frequency values in [0,1], where the nyquist freq = 1.0.
/// 3: spectral_centroid = sum(normalized_frequencies* normalized_spectrum)
/// Note: When calculated this way the Spectral centroid is a ratio. Multiply this value by the Nyquist (maximum frequency) to get the centroid in Hertz.
/// </summary>
public static class SpectralCentroid
{
/// <summary>
/// Calculates the spectral centroid of the given amplitude spectrum.
/// See notes above.
/// </summary>
/// <param name="spectrum">Amplitude spectrum.</param>
/// <returns>Centroid.</returns>
public static double CalculateSpectralCentroid(double[] spectrum, int nyquist)
{
var normalisedSpectrum = DataTools.Normalise2Probabilites(spectrum);

// normalise the frequency values
var length = spectrum.Length;
var normalisedFrequencyValues = new double[length];
var binWidthHz = nyquist / length;
var halfBinWidth = binWidthHz / 2;

for (int i = 0; i < length - 1; i++)
{
normalisedFrequencyValues[i] = ((i * binWidthHz) + halfBinWidth) / nyquist;
}

double spectralCentroid = DataTools.DotProduct(normalisedSpectrum, normalisedFrequencyValues);
return spectralCentroid;
}

/// <summary>
/// This method assumes that the rows of the passed matrix are spectra and the columns are frequency bins.
/// </summary>
/// <param name="spectra">As a matrix.</param>
/// <param name="nyquist">The maximum frequency.</param>
/// <returns>An array of spectral centroids.</returns>
public static double[] CalculateSpectralCentroids(double[,] spectra, int nyquist)
{
var frameCount = spectra.GetLength(0);
var freqBinCount = spectra.GetLength(1);

var centroidArray = new double[frameCount];

for (int i = 0; i < frameCount - 1; i++)
{
double[] spectrum = MatrixTools.GetRow(spectra, i);
centroidArray[i] = CalculateSpectralCentroid(spectrum, nyquist);
}

return centroidArray;
}
}
}
13 changes: 10 additions & 3 deletions src/TowseyLibrary/DataTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2736,12 +2736,19 @@ public static double[] Vector2Zscores(double[] V)
return returnV;
}

/// <summary>
/// This method requires that v1 and v2 have same dimensions.
/// </summary>
/// <returns>The dot product.</returns>
public static double DotProduct(double[] v1, double[] v2)
{
// assume v1 and v2 have same dimensions
int L = v1.Length;
if (v1.Length != v2.Length)
{
throw new DataMisalignedException($"Vectors must be of same length. {v1.Length} != {v2.Length}");
}

double sum = 0.0;
for (int i = 0; i < L; i++)
for (int i = 0; i < v1.Length; i++)
{
sum += v1[i] * v2[i];
}
Expand Down

0 comments on commit 178af3e

Please sign in to comment.