} //Analysis() /// <summary> /// returns some indices relevant to rain and cicadas from a short (10seconds) chunk of audio /// </summary> /// <param name="signal">signal envelope of a 10s chunk of audio</param> /// <param name="spectrogram">spectrogram of a 10s chunk of audio</param> /// <param name="lowFreqBound"></param> /// <param name="midFreqBound"></param> /// <param name="binWidth"></param> /// <returns></returns> public static RainStruct Get10SecondIndices(double[] signal, double[,] spectrogram, int lowFreqBound, int midFreqBound, TimeSpan frameDuration, double binWidth) { // i: FRAME ENERGIES - double StandardDeviationCount = 0.1; var results3 = SNR.SubtractBackgroundNoiseFromWaveform_dB(SNR.Signal2Decibels(signal), StandardDeviationCount); //use Lamel et al. var dBarray = SNR.TruncateNegativeValues2Zero(results3.NoiseReducedSignal); bool[] activeFrames = new bool[dBarray.Length]; //record frames with activity >= threshold dB above background and count for (int i = 0; i < dBarray.Length; i++) { if (dBarray[i] >= ActivityAndCover.DefaultActivityThresholdDb) { activeFrames[i] = true; } } //int activeFrameCount = dBarray.Count((x) => (x >= AcousticIndices.DEFAULT_activityThreshold_dB)); int activeFrameCount = DataTools.CountTrues(activeFrames); double spikeThreshold = 0.05; double spikeIndex = CalculateSpikeIndex(signal, spikeThreshold); //Console.WriteLine("spikeIndex=" + spikeIndex); //DataTools.writeBarGraph(signal); RainStruct rainIndices; // struct in which to store all indices rainIndices.activity = activeFrameCount / (double)dBarray.Length; //fraction of frames having acoustic activity rainIndices.bgNoise = results3.NoiseMode; //bg noise in dB rainIndices.snr = results3.Snr; //snr rainIndices.avSig_dB = 20 * Math.Log10(signal.Average()); //10 times log of amplitude squared rainIndices.temporalEntropy = DataTools.EntropyNormalised(DataTools.SquareValues(signal)); //ENTROPY of ENERGY ENVELOPE rainIndices.spikes = spikeIndex; // ii: calculate the bin id of boundary between mid and low frequency spectrum int lowBinBound = (int)Math.Ceiling(lowFreqBound / binWidth); var midbandSpectrogram = MatrixTools.Submatrix(spectrogram, 0, lowBinBound, spectrogram.GetLength(0) - 1, spectrogram.GetLength(1) - 1); // iii: ENTROPY OF AVERAGE SPECTRUM and VARIANCE SPECTRUM - at this point the spectrogram is still an amplitude spectrogram var tuple = SpectrogramTools.CalculateAvgSpectrumAndVarianceSpectrumFromAmplitudeSpectrogram(midbandSpectrogram); rainIndices.spectralEntropy = DataTools.EntropyNormalised(tuple.Item1); //ENTROPY of spectral averages if (double.IsNaN(rainIndices.spectralEntropy)) { rainIndices.spectralEntropy = 1.0; } // iv: CALCULATE Acoustic Complexity Index on the AMPLITUDE SPECTRUM var aciArray = AcousticComplexityIndex.CalculateAci(midbandSpectrogram); rainIndices.ACI = aciArray.Average(); //v: remove background noise from the spectrogram double spectralBgThreshold = 0.015; // SPECTRAL AMPLITUDE THRESHOLD for smoothing background //double[] modalValues = SNR.CalculateModalValues(spectrogram); //calculate modal value for each freq bin. //modalValues = DataTools.filterMovingAverage(modalValues, 7); //smooth the modal profile //spectrogram = SNR.SubtractBgNoiseFromSpectrogramAndTruncate(spectrogram, modalValues); //spectrogram = SNR.RemoveNeighbourhoodBackgroundNoise(spectrogram, spectralBgThreshold); //vi: SPECTROGRAM ANALYSIS - SPECTRAL COVER. NOTE: spectrogram is still a noise reduced amplitude spectrogram SpectralActivity sa = ActivityAndCover.CalculateSpectralEvents(spectrogram, spectralBgThreshold, frameDuration, lowFreqBound, midFreqBound, binWidth); rainIndices.lowFreqCover = sa.LowFreqBandCover; rainIndices.midFreqCover = sa.MidFreqBandCover; rainIndices.hiFreqCover = sa.HighFreqBandCover; //double[] coverSpectrum = sa.coverSpectrum; //double[] eventSpectrum = sa.eventSpectrum; return(rainIndices); }
/// <summary> /// Counts the number of spectral tracks or harmonics in the passed ferquency band. /// Also calculates the average amplitude of the peaks to each succeeding trough. /// </summary> /// <param name="values">Spectral values in the frequency band.</param> /// <param name="row">This argument is NOT used. Is included only for debugging purposes.</param> public static Tuple <double, int, bool[]> CountHarmonicTracks(double[] values, int expectedHarmonicCount) { int L = values.Length; int expectedPeriod = L / expectedHarmonicCount; int midPeriod = expectedPeriod / 2; //double[] smooth = DataTools.filterMovingAverage(values, 3); double[] smooth = values; bool[] peaks = DataTools.GetPeaks(smooth); int peakCount = DataTools.CountTrues(peaks); //return if too far outside limits int lowerLimit = expectedHarmonicCount / 2; int upperLimit = expectedHarmonicCount * 2; if (peakCount <= lowerLimit) { return(Tuple.Create(0.0, 0, peaks)); } else if (peakCount >= upperLimit) { return(Tuple.Create(0.0, peakCount, peaks)); } // Store peak locations. var peakLocations = new List <int>(); for (int i = 0; i < values.Length; i++) { if (peaks[i]) { peakLocations.Add(i); } } //// If have too many peaks (local maxima), remove the lowest of them //if (peakCount > (expectedHarmonicCount + 1)) //{ // var peakValues = new double[peakCount]; // for (int i = 0; i < peakCount; i++) peakValues[i] = values[peakLocations[i]]; // IEnumerable<double> ordered = peakValues.OrderByDescending(d => d); // double avValue = ordered.Take(expectedHarmonicCount).Average(); // double min = ordered.Last(); // double threshold = min + ((avValue - min) / 2); // // apply threshold to remove low peaks // for (int i = 0; i < L; i++) // { // if ((peaks[i]) && (values[i] < threshold)) peaks[i] = false; // } // // recalculate the number of peaks // peakCount = -1; // for (int i = 0; i < L; i++) // { // if (peaks[i]) // { // peakCount++; // peakLocations[peakCount] = i; // } // } //} //if (peakCount <= 1) return Tuple.Create(0.0, 0, peaks); double amplitude = 0.0; for (int i = 0; i < peakLocations.Count; i++) { int troughIndex = peakLocations[i] + midPeriod; if (troughIndex >= L) { troughIndex = peakLocations[i] - midPeriod; } double delta = smooth[peakLocations[i]] - smooth[troughIndex]; if (delta > 1.0) { amplitude += delta; // dB threshold - required a minimum perceptible difference } } double avAmplitude = amplitude / peakCount; return(Tuple.Create(avAmplitude, peakCount, peaks)); }