public static SpectralTrack GetVerticalTrack(double[,] peaks, int startRow, int startBin, int maxBin, double threshold) { var track = new SpectralTrack(SpectralTrackType.VerticalTrack, startRow, startBin, peaks[startRow, startBin]); // set the start point in peaks matrix to zero to prevent return to this point. peaks[startRow, startBin] = 0.0; int row = startRow; for (int bin = startBin; bin < maxBin - 1; bin++) { // Avoid row edge effects. if (row < 2 || row > peaks.GetLength(0) - 3) { // arrived back at start of recording or end of recording. // The track has come to end return(track); } if (bin >= maxBin) { // arrived at top of the requested frequency band - track has come to end return(track); } // explore options for track vertical double optionStraight = Math.Max(peaks[row, bin] + peaks[row, bin + 1], peaks[row, bin] + peaks[row - 1, bin + 1]); optionStraight = Math.Max(optionStraight, peaks[row, bin] + peaks[row + 1, bin + 1]); // option for track with negative slope i.e. return to previous row/frame. double optionDown = Math.Max(peaks[row - 1, bin] + peaks[row - 1, bin + 1], peaks[row - 1, bin] + peaks[row - 2, bin + 1]); optionDown = Math.Max(optionDown, peaks[row - 1, bin] + peaks[row - 1, bin + 1]); // option for track with positive slope double optionUp = Math.Max(peaks[row + 1, bin] + peaks[row + 1, bin + 1], peaks[row + 1, bin] + peaks[row + 2, bin + 1]); optionUp = Math.Max(optionUp, peaks[row + 1, bin] + peaks[row + 2, bin + 1]); // get max of the three next possible steps double[] options = { optionStraight, optionDown, optionUp }; var maxId = DataTools.GetMaxIndex(options); // Check if track has come to an end - average value of the two values is less than threshold. var maxValue = options[maxId] / 2; if (maxValue < threshold) { return(track); } // else set visited values to zero so as not to revisit..... peaks[row - 1, bin] = 0.0; peaks[row, bin] = 0.0; peaks[row + 1, bin] = 0.0; // and go to the new time frame if (maxId == 1) { row--; } else if (maxId == 2) { row++; } track.SetPoint(row, bin, maxValue); } return(track); }
//Analyze() /// <summary> /// ################ THE KEY ANALYSIS METHOD /// Returns a DataTable /// </summary> /// <param name="fiSegmentOfSourceFile"></param> /// <param name="analysisSettings"></param> /// <param name="originalSampleRate"></param> /// <param name="segmentStartOffset"></param> /// <param name="configDict"></param> /// <param name="diOutputDir"></param> public static Tuple <BaseSonogram, double[, ], List <Plot>, List <AcousticEvent>, TimeSpan> Analysis(FileInfo fiSegmentOfSourceFile, AnalysisSettings analysisSettings, int originalSampleRate, TimeSpan segmentStartOffset) { Dictionary <string, string> configDict = analysisSettings.ConfigDict; int originalAudioNyquist = originalSampleRate / 2; // original sample rate can be anything 11.0-44.1 kHz. //set default values - ignore those set by user int frameSize = 32; double windowOverlap = 0.3; int xCorrelationLength = 256; //for Xcorrelation - 256 frames @801 = 320ms, almost 1/3 second. //int xCorrelationLength = 128; //for Xcorrelation - 128 frames @801 = 160ms, almost 1/6 second. //int xCorrelationLength = 64; //for Xcorrelation - 64 frames @128 = 232ms, almost 1/4 second. //int xCorrelationLength = 16; //for Xcorrelation - 16 frames @128 = 232ms, almost 1/4 second. double dBThreshold = 12.0; // read frog data to datatable var dt = CsvTools.ReadCSVToTable(configDict[key_FROG_DATA], true); // read file contining parameters of frog calls to a table double intensityThreshold = double.Parse(configDict[AnalysisKeys.IntensityThreshold]); //in 0-1 double minDuration = double.Parse(configDict[AnalysisKeys.MinDuration]); // seconds double maxDuration = double.Parse(configDict[AnalysisKeys.MaxDuration]); // seconds double minPeriod = double.Parse(configDict[AnalysisKeys.MinPeriodicity]); // seconds double maxPeriod = double.Parse(configDict[AnalysisKeys.MaxPeriodicity]); // seconds AudioRecording recording = new AudioRecording(fiSegmentOfSourceFile.FullName); if (recording == null) { LoggedConsole.WriteLine("AudioRecording == null. Analysis not possible."); return(null); } //i: MAKE SONOGRAM SonogramConfig sonoConfig = new SonogramConfig(); //default values config sonoConfig.SourceFName = recording.BaseName; sonoConfig.WindowSize = frameSize; sonoConfig.WindowOverlap = windowOverlap; //sonoConfig.NoiseReductionType = SNR.Key2NoiseReductionType("NONE"); sonoConfig.NoiseReductionType = SNR.KeyToNoiseReductionType("STANDARD"); //must do noise removal TimeSpan tsRecordingtDuration = recording.Duration; int sr = recording.SampleRate; double freqBinWidth = sr / (double)sonoConfig.WindowSize; double frameOffset = sonoConfig.GetFrameOffset(sr); double framesPerSecond = 1 / frameOffset; BaseSonogram sonogram = new SpectrogramStandard(sonoConfig, recording.WavReader); //iii: GET TRACKS int nhLimit = 3; //limit of neighbourhood around maximum var peaks = DataTools.GetPeakValues(sonogram.DecibelsPerFrame); var tuple = SpectralTrack.GetSpectralMaxima(sonogram.DecibelsPerFrame, sonogram.Data, dBThreshold, nhLimit); var maxFreqArray = tuple.Item1; //array (one element per frame) indicating which freq bin has max amplitude. var hitsMatrix = tuple.Item2; int herzOffset = 0; int maxFreq = 6000; var tracks = SpectralTrack.GetSpectralTracks(maxFreqArray, framesPerSecond, freqBinWidth, herzOffset, SpectralTrack.MIN_TRACK_DURATION, SpectralTrack.MAX_INTRASYLLABLE_GAP, maxFreq); double severity = 0.5; double dynamicRange = 60; // deciBels above background noise. BG noise has already been removed from each bin. // convert sonogram to a list of frequency bin arrays var listOfFrequencyBins = SpectrogramTools.Sonogram2ListOfFreqBinArrays(sonogram, dynamicRange); int minFrameLength = SpectralTrack.FrameCountEquivalent(SpectralTrack.MIN_TRACK_DURATION, framesPerSecond); for (int i = tracks.Count - 1; i >= 0; i--) { tracks[i].CropTrack(listOfFrequencyBins, severity); if (tracks[i].Length < minFrameLength) { tracks.Remove(tracks[i]); } } // foreach track foreach (SpectralTrack track in tracks) // find any periodicity in the track and calculate its score. { SpectralTrack.DetectTrackPeriodicity(track, xCorrelationLength, listOfFrequencyBins, sonogram.FramesPerSecond); } // foreach track int rowCount = sonogram.Data.GetLength(0); int MAX_FREQ_BOUND = 6000; int topBin = (int)Math.Round(MAX_FREQ_BOUND / freqBinWidth); var plots = CreateScorePlots(tracks, rowCount, topBin); //iv: CONVERT TRACKS TO ACOUSTIC EVENTS List <AcousticEvent> frogEvents = SpectralTrack.ConvertTracks2Events(tracks, segmentStartOffset); // v: GET FROG IDs //var frogEvents = new List<AcousticEvent>(); foreach (AcousticEvent ae in frogEvents) { double oscRate = 1 / ae.Periodicity; // ae.DominantFreq // ae.Score // ae.Duration //ClassifyFrogEvent(ae); string[] names = ClassifyFrogEvent(ae.DominantFreq, oscRate, dt); ae.Name = names[0]; ae.Name2 = names[1]; } return(Tuple.Create(sonogram, hitsMatrix, plots, frogEvents, tsRecordingtDuration)); } //Analysis()
public static SpectralTrack GetTrack(double[,] peaks, int startRow, int startBin, double threshold) { var track = new SpectralTrack(SpectralTrackType.HorizontalTrack, startRow, startBin, peaks[startRow, startBin]); // set the start point in peaks matrix to zero to prevent return to this point. peaks[startRow, startBin] = 0.0; int bin = startBin; for (int row = startRow + 1; row < peaks.GetLength(0) - 2; row++) { //cannot take bin value less than 3 because of edge effects. if (bin < 3) { bin = 3; } if (bin > peaks.GetLength(1) - 4) { bin = peaks.GetLength(1) - 4; } // explore options for track ahead double optionStraight = Math.Max(peaks[row, bin] + peaks[row + 1, bin], peaks[row, bin] + peaks[row + 1, bin - 1]); optionStraight = Math.Max(optionStraight, peaks[row, bin] + peaks[row + 1, bin + 1]); // option for track descent double optionDown = Math.Max(peaks[row, bin - 1] + peaks[row + 1, bin - 1], peaks[row, bin - 1] + peaks[row + 1, bin - 2]); optionDown = Math.Max(optionDown, peaks[row, bin - 1] + peaks[row + 1, bin]); // need this option for a steep track descent double optionTwoDown = Math.Max(peaks[row, bin - 2] + peaks[row + 1, bin - 2], peaks[row, bin - 2] + peaks[row + 1, bin - 1]); optionTwoDown = Math.Max(optionTwoDown, peaks[row, bin - 2] + peaks[row + 1, bin - 3]); // option for track asscent double optionUp = Math.Max(peaks[row, bin + 1] + peaks[row + 1, bin + 1], peaks[row, bin + 1] + peaks[row + 1, bin]); optionUp = Math.Max(optionUp, peaks[row, bin + 1] + peaks[row + 1, bin + 2]); // need this option for a steep track asscent double optionTwoUp = Math.Max(peaks[row, bin + 2] + peaks[row + 1, bin + 2], peaks[row, bin + 2] + peaks[row + 1, bin + 1]); optionTwoUp = Math.Max(optionTwoUp, peaks[row, bin + 2] + peaks[row + 1, bin + 3]); // get max of the five next possible steps double[] options = { optionStraight, optionDown, optionUp, optionTwoDown, optionTwoUp }; //double[] options = { optionStraight, optionDown, optionUp, 0.0, 0.0 }; var maxId = DataTools.GetMaxIndex(options); // if track has come to an end var maxValue = options[maxId] / 2; if (maxValue < threshold) { break; } // else set bins to zero so as not to revisit..... for (int col = -2; col <= 2; col++) { peaks[row, bin + col] = 0.0; } // and go to the new frequency bin if (maxId == 1) { bin--; } else if (maxId == 2) { bin++; } else if (maxId == 3) { bin -= 2; } else if (maxId == 4) { bin += 2; } track.SetPoint(row, bin, maxValue); } return(track); }