public override AnalysisResult2 Analyze <T>(AnalysisSettings analysisSettings, SegmentSettings <T> segmentSettings) { var recording = new AudioRecording(segmentSettings.SegmentAudioFile.FullName); // get indices configuration - extracted in BeforeAnalyze var acousticIndicesConfig = (RecognizerConfig)analysisSettings.Configuration; // get a lazily calculated indices function - if you never get the lazy value, the indices will never be calculated var lazyIndices = this.GetLazyIndices( recording, analysisSettings, segmentSettings, acousticIndicesConfig.HighResolutionIndices); // determine imageWidth for output images int imageWidth = (int)Math.Floor( recording.Duration.TotalSeconds / acousticIndicesConfig.HighResolutionIndices.IndexCalculationDuration); // execute actual analysis RecognizerResults results = this.Recognize( recording, analysisSettings.Configuration, segmentSettings.SegmentStartOffset, lazyIndices, segmentSettings.SegmentOutputDirectory, imageWidth); var analysisResults = new AnalysisResult2(analysisSettings, segmentSettings, recording.Duration); BaseSonogram sonogram = results.Sonogram; double[,] hits = results.Hits; var predictedEvents = results.GetAllEvents(); // double check all the events have the right offset in case it was missed foreach (var predictedEvent in predictedEvents) { predictedEvent.SegmentStartSeconds = segmentSettings.SegmentStartOffset.TotalSeconds; predictedEvent.SegmentDurationSeconds = recording.Duration.TotalSeconds; } analysisResults.Events = predictedEvents.ToArray(); // compress high resolution indices - and save them. // IF they aren't used, empty values are returned. if (lazyIndices.IsValueCreated) { this.SummarizeHighResolutionIndices( analysisResults, lazyIndices.Value, acousticIndicesConfig.HighResolutionIndices); } // write intermediate output if necessary if (analysisSettings.AnalysisDataSaveBehavior) { this.WriteEventsFile(segmentSettings.SegmentEventsFile, analysisResults.Events); analysisResults.EventsFile = segmentSettings.SegmentEventsFile; } if (analysisSettings.AnalysisDataSaveBehavior) { this.WriteSummaryIndicesFile(segmentSettings.SegmentSummaryIndicesFile, analysisResults.SummaryIndices); } if (analysisSettings.AnalysisDataSaveBehavior) { analysisResults.SpectraIndicesFiles = this.WriteSpectrumIndicesFiles( segmentSettings.SegmentSpectrumIndicesDirectory, segmentSettings.Segment.SourceMetadata.Identifier, analysisResults.SpectralIndices); } if (analysisSettings.AnalysisImageSaveBehavior.ShouldSave(analysisResults.Events.Length)) { string imagePath = segmentSettings.SegmentImageFile.FullName; const double EventThreshold = 0.1; var plots = results.Plots ?? new List <Plot>(); //TODO Remove this when we remove AcousticEvent. var convertedEvents = predictedEvents.Select(ec => ec is AcousticEvent ae ? EventConverters.ConvertAcousticEventToSpectralEvent(ae) : (EventCommon)ec).ToList(); Image image = this.DrawSonogram(sonogram, hits, plots, convertedEvents, EventThreshold); image.Save(imagePath); analysisResults.ImageFile = segmentSettings.SegmentImageFile; // draw a fancy high res index image // IF indices aren't used, no image is drawn. if (lazyIndices.IsValueCreated) { this.DrawLongDurationSpectrogram( segmentSettings.SegmentOutputDirectory, recording.BaseName, results.ScoreTrack, lazyIndices.Value, acousticIndicesConfig.HighResolutionIndices); } } return(analysisResults); }
public static (List <EventCommon> SpectralEvents, double[] AmplitudeArray, double[] HarmonicIntensityScores) GetComponentsWithHarmonics( SpectrogramStandard spectrogram, int minHz, int maxHz, int nyquist, double decibelThreshold, double dctThreshold, double minDuration, double maxDuration, int minFormantGap, int maxFormantGap, TimeSpan segmentStartOffset) { // Event threshold - Determines FP / FN trade-off for events. //double eventThreshold = 0.2; var sonogramData = spectrogram.Data; int frameCount = sonogramData.GetLength(0); int binCount = sonogramData.GetLength(1); double freqBinWidth = nyquist / (double)binCount; int minBin = (int)Math.Round(minHz / freqBinWidth); int maxBin = (int)Math.Round(maxHz / freqBinWidth); // extract the sub-band double[,] subMatrix = MatrixTools.Submatrix(spectrogram.Data, 0, minBin, frameCount - 1, maxBin); //ii: DETECT HARMONICS // now look for harmonics in search band using the Xcorrelation-DCT method. var results = CrossCorrelation.DetectHarmonicsInSpectrogramData(subMatrix, decibelThreshold); // set up score arrays double[] dBArray = results.Item1; double[] harmonicIntensityScores = results.Item2; //an array of formant intesnity int[] maxIndexArray = results.Item3; for (int r = 0; r < frameCount; r++) { if (harmonicIntensityScores[r] < dctThreshold) { continue; } //ignore locations with incorrect formant gap int maxId = maxIndexArray[r]; int bandBinCount = maxBin - minBin + 1; double freqBinGap = 2 * bandBinCount / (double)maxId; double formantGap = freqBinGap * freqBinWidth; if (formantGap < minFormantGap || formantGap > maxFormantGap) { harmonicIntensityScores[r] = 0.0; } } // smooth the harmonicIntensityScores array to allow for brief gaps. harmonicIntensityScores = DataTools.filterMovingAverageOdd(harmonicIntensityScores, 3); //extract the events based on length and threshhold. // Note: This method does NOT do prior smoothing of the score array. var harmonicEvents = AcousticEvent.ConvertScoreArray2Events( harmonicIntensityScores, minHz, maxHz, spectrogram.FramesPerSecond, spectrogram.FBinWidth, dctThreshold, minDuration, maxDuration, segmentStartOffset); var spectralEvents = new List <EventCommon>(); // add in temporary names to the events // These can be altered later. foreach (var he in harmonicEvents) { var se = EventConverters.ConvertAcousticEventToSpectralEvent(he); spectralEvents.Add(se); se.Name = "Harmonics"; //se.ComponentName = "Harmonics"; } return(spectralEvents, dBArray, harmonicIntensityScores); }