/// <inheritdoc/> public override RecognizerResults Recognize( AudioRecording audioRecording, Config genericConfig, TimeSpan segmentStartOffset, Lazy <IndexCalculateResult[]> getSpectralIndexes, DirectoryInfo outputDirectory, int?imageWidth) { var configuration = (GenericRecognizerConfig)genericConfig; if (configuration.Profiles.NotNull() && configuration.Profiles.Count == 0) { throw new ConfigFileException( "The generic recognizer needs at least one profile set. 0 were found."); } int count = configuration.Profiles.Count; var message = $"Found {count} analysis profile(s): " + configuration.Profiles.Keys.Join(", "); Log.Info(message); var allResults = new RecognizerResults() { Events = new List <AcousticEvent>(), Hits = null, ScoreTrack = null, Plots = new List <Plot>(), Sonogram = null, }; // Now process each of the profiles foreach (var(profileName, profileConfig) in configuration.Profiles) { Log.Info("Processing profile: " + profileName); List <AcousticEvent> acousticEvents; var plots = new List <Plot>(); SpectrogramStandard sonogram; Log.Debug($"Using the {profileName} algorithm... "); if (profileConfig is CommonParameters parameters) { if (profileConfig is BlobParameters || profileConfig is OscillationParameters || profileConfig is WhistleParameters || profileConfig is HarmonicParameters) { sonogram = new SpectrogramStandard(ParametersToSonogramConfig(parameters), audioRecording.WavReader); if (profileConfig is OscillationParameters op) { Oscillations2012.Execute( sonogram, op.MinHertz.Value, op.MaxHertz.Value, op.DctDuration, op.MinOscillationFrequency, op.MaxOscillationFrequency, op.DctThreshold, op.EventThreshold, op.MinDuration.Value, op.MaxDuration.Value, out var scores, out acousticEvents, out var hits, segmentStartOffset); //plots.Add(new Plot($"{profileName} (:OscillationScore)", scores, op.EventThreshold)); var plot = PreparePlot(scores, $"{profileName} (:OscillationScore)", op.EventThreshold); plots.Add(plot); } else if (profileConfig is BlobParameters bp) { //get the array of intensity values minus intensity in side/buffer bands. //i.e. require silence in side-bands. Otherwise might simply be getting part of a broader band acoustic event. var decibelArray = SNR.CalculateFreqBandAvIntensityMinusBufferIntensity( sonogram.Data, bp.MinHertz.Value, bp.MaxHertz.Value, bp.BottomHertzBuffer.Value, bp.TopHertzBuffer.Value, sonogram.NyquistFrequency); // prepare plot of resultant blob decibel array. var plot = PreparePlot(decibelArray, $"{profileName} (Blob:db Intensity)", bp.DecibelThreshold.Value); plots.Add(plot); // iii: CONVERT blob decibel SCORES TO ACOUSTIC EVENTS. // Note: This method does NOT do prior smoothing of the dB array. acousticEvents = AcousticEvent.GetEventsAroundMaxima( decibelArray, segmentStartOffset, bp.MinHertz.Value, bp.MaxHertz.Value, bp.DecibelThreshold.Value, TimeSpan.FromSeconds(bp.MinDuration.Value), TimeSpan.FromSeconds(bp.MaxDuration.Value), sonogram.FramesPerSecond, sonogram.FBinWidth); } else if (profileConfig is WhistleParameters wp) { //get the array of intensity values minus intensity in side/buffer bands. double[] decibelArray; (acousticEvents, decibelArray) = WhistleParameters.GetWhistles( sonogram, wp.MinHertz.Value, wp.MaxHertz.Value, sonogram.NyquistFrequency, wp.DecibelThreshold.Value, wp.MinDuration.Value, wp.MaxDuration.Value, segmentStartOffset); var plot = PreparePlot(decibelArray, $"{profileName} (Whistle:dB Intensity)", wp.DecibelThreshold.Value); plots.Add(plot); } else if (profileConfig is HarmonicParameters hp) { //get the array of intensity values minus intensity in side/buffer bands. double[] scoreArray; (acousticEvents, scoreArray) = HarmonicParameters.GetComponentsWithHarmonics( sonogram, hp.MinHertz.Value, hp.MaxHertz.Value, sonogram.NyquistFrequency, hp.DecibelThreshold.Value, hp.DctThreshold.Value, hp.MinDuration.Value, hp.MaxDuration.Value, hp.MinFormantGap.Value, hp.MaxFormantGap.Value, segmentStartOffset); var plot = PreparePlot(scoreArray, $"{profileName} (Harmonics:dB Intensity)", hp.DecibelThreshold.Value); plots.Add(plot); } else { throw new InvalidOperationException(); } } else { throw new InvalidOperationException(); } //iV add additional info to the acoustic events acousticEvents.ForEach(ae => { ae.FileName = audioRecording.BaseName; ae.SpeciesName = parameters.SpeciesName; ae.Name = parameters.ComponentName; ae.Profile = profileName; ae.SegmentDurationSeconds = audioRecording.Duration.TotalSeconds; ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds; ae.SetTimeAndFreqScales(sonogram.FrameStep, sonogram.FrameDuration, sonogram.FBinWidth); }); } else if (profileConfig is Aed.AedConfiguration ac) { var config = new SonogramConfig { NoiseReductionType = ac.NoiseReductionType, NoiseReductionParameter = ac.NoiseReductionParameter, }; sonogram = new SpectrogramStandard(config, audioRecording.WavReader); acousticEvents = Aed.CallAed(sonogram, ac, segmentStartOffset, audioRecording.Duration).ToList(); } else { throw new InvalidOperationException(); } // combine the results i.e. add the events list of call events. allResults.Events.AddRange(acousticEvents); allResults.Plots.AddRange(plots); // effectively keeps only the *last* sonogram produced allResults.Sonogram = sonogram; Log.Debug($"{profileName} event count = {acousticEvents.Count}"); // DEBUG PURPOSES COMMENT NEXT LINE //SaveDebugSpectrogram(allResults, genericConfig, outputDirectory, "name"); } return(allResults); }
/// <summary> /// Do your analysis. This method is called once per segment (typically one-minute segments). /// </summary> /// <param name="recording"></param> /// <param name="configuration"></param> /// <param name="segmentStartOffset"></param> /// <param name="getSpectralIndexes"></param> /// <param name="outputDirectory"></param> /// <param name="imageWidth"></param> /// <returns></returns> public override RecognizerResults Recognize(AudioRecording recording, Config configuration, TimeSpan segmentStartOffset, Lazy <IndexCalculateResult[]> getSpectralIndexes, DirectoryInfo outputDirectory, int?imageWidth) { string speciesName = configuration[AnalysisKeys.SpeciesName] ?? "<no species>"; string abbreviatedSpeciesName = configuration[AnalysisKeys.AbbreviatedSpeciesName] ?? "<no.sp>"; int minHz = configuration.GetInt(AnalysisKeys.MinHz); int maxHz = configuration.GetInt(AnalysisKeys.MaxHz); // BETTER TO CALCULATE THIS. IGNORE USER! // double frameOverlap = Double.Parse(configDict[Keys.FRAME_OVERLAP]); // duration of DCT in seconds double dctDuration = configuration.GetDouble(AnalysisKeys.DctDuration); // minimum acceptable value of a DCT coefficient double dctThreshold = configuration.GetDouble(AnalysisKeys.DctThreshold); // ignore oscillations below this threshold freq int minOscilFreq = configuration.GetInt(AnalysisKeys.MinOscilFreq); // ignore oscillations above this threshold freq int maxOscilFreq = configuration.GetInt(AnalysisKeys.MaxOscilFreq); // min duration of event in seconds double minDuration = configuration.GetDouble(AnalysisKeys.MinDuration); // max duration of event in seconds double maxDuration = configuration.GetDouble(AnalysisKeys.MaxDuration); // min score for an acceptable event double eventThreshold = configuration.GetDouble(AnalysisKeys.EventThreshold); if (recording.WavReader.SampleRate != 22050) { throw new InvalidOperationException("Requires a 22050Hz file"); } // The default was 512 for Canetoad. // Set longer Framesize for calls having longer pulse periodicity. const int FrameSize = 128; double windowOverlap = Oscillations2012.CalculateRequiredFrameOverlap( recording.SampleRate, FrameSize, maxOscilFreq); //windowOverlap = 0.75; // previous default // i: MAKE SONOGRAM var sonoConfig = new SonogramConfig { SourceFName = recording.BaseName, WindowSize = FrameSize, WindowOverlap = windowOverlap, //NoiseReductionType = NoiseReductionType.NONE, NoiseReductionType = NoiseReductionType.Standard, NoiseReductionParameter = 0.1, }; // sonoConfig.NoiseReductionType = SNR.Key2NoiseReductionType("STANDARD"); TimeSpan recordingDuration = recording.Duration; int sr = recording.SampleRate; double freqBinWidth = sr / (double)sonoConfig.WindowSize; BaseSonogram sonogram = new SpectrogramStandard(sonoConfig, recording.WavReader); int rowCount = sonogram.Data.GetLength(0); int colCount = sonogram.Data.GetLength(1); // double[,] subMatrix = MatrixTools.Submatrix(sonogram.Data, 0, minBin, (rowCount - 1), maxbin); // ###################################################################### // ii: DO THE ANALYSIS AND RECOVER SCORES OR WHATEVER // This window is used to smooth the score array before extracting events. // A short window (e.g. 3) preserves sharper score edges to define events but also keeps noise. int scoreSmoothingWindow = 13; double[] scores; // predefinition of score array List <AcousticEvent> acousticEvents; double[,] hits; Oscillations2012.Execute( (SpectrogramStandard)sonogram, minHz, maxHz, dctDuration, minOscilFreq, maxOscilFreq, dctThreshold, eventThreshold, minDuration, maxDuration, scoreSmoothingWindow, out scores, out acousticEvents, out hits, segmentStartOffset); acousticEvents.ForEach(ae => { ae.SpeciesName = speciesName; ae.SegmentDurationSeconds = recordingDuration.TotalSeconds; ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds; ae.Name = abbreviatedSpeciesName; }); var plot = new Plot(this.DisplayName, scores, eventThreshold); var plots = new List <Plot> { plot }; this.WriteDebugImage(recording, outputDirectory, sonogram, acousticEvents, plots, hits); return(new RecognizerResults() { Sonogram = sonogram, Hits = hits, Plots = plots, Events = acousticEvents, }); }
/// <summary> /// Do your analysis. This method is called once per segment (typically one-minute segments). /// </summary> public override RecognizerResults Recognize( AudioRecording recording, Config configuration, TimeSpan segmentStartOffset, Lazy <IndexCalculateResult[]> getSpectralIndexes, DirectoryInfo outputDirectory, int?imageWidth) { //string speciesName = (string)configuration[AnalysisKeys.SpeciesName] ?? "<no species>"; //string abbreviatedSpeciesName = (string)configuration[AnalysisKeys.AbbreviatedSpeciesName] ?? "<no.sp>"; // this default framesize seems to work for Lewin's Rail const int frameSize = 512; // DO NOT SET windowOverlap. Calculate it below. if (imageWidth == null) { throw new ArgumentNullException(nameof(imageWidth)); } // check the sample rate. Must be 22050 if (recording.WavReader.SampleRate != 22050) { throw new InvalidOperationException("Requires a 22050Hz file"); } TimeSpan recordingDuration = recording.WavReader.Time; // check for the profiles in the config file bool hasProfiles = ConfigFile.HasProfiles(configuration); if (!hasProfiles) { throw new ConfigFileException("The Config file for L.pectoralis must contain a profiles object."); } // get the profile names string[] profileNames = ConfigFile.GetProfileNames(configuration); var recognizerConfig = new LewinsRailConfig(); var prunedEvents = new List <AcousticEvent>(); var plots = new List <Plot>(); BaseSonogram sonogram = null; // cycle through the profiles and analyse recording using each of them foreach (var name in profileNames) { Log.Debug($"Reading profile <{name}>."); recognizerConfig.ReadConfigFile(configuration, name); // ignore oscillations above this threshold freq int maxOscilRate = (int)Math.Ceiling(1 / recognizerConfig.MinPeriod); // calculate frame overlap and ignor any user inut. double windowOverlap = Oscillations2012.CalculateRequiredFrameOverlap( recording.SampleRate, frameSize, maxOscilRate); // i: MAKE SONOGRAM var sonoConfig = new SonogramConfig { SourceFName = recording.BaseName, //set default values - ignore those set by user WindowSize = frameSize, WindowOverlap = windowOverlap, // the default window is HAMMING //WindowFunction = WindowFunctions.HANNING.ToString(), //WindowFunction = WindowFunctions.NONE.ToString(), // if do not use noise reduction can get a more sensitive recogniser. //NoiseReductionType = NoiseReductionType.NONE, NoiseReductionType = SNR.KeyToNoiseReductionType("STANDARD"), }; //############################################################################################################################################# //DO THE ANALYSIS AND RECOVER SCORES OR WHATEVER var results = Analysis(recording, sonoConfig, recognizerConfig, this.ReturnDebugImage, segmentStartOffset); // ###################################################################### if (results == null) { return(null); //nothing to process } sonogram = results.Item1; //var hits = results.Item2; var scoreArray = results.Item3; var predictedEvents = results.Item4; var debugImage = results.Item5; //############################################################################################################################################# if (debugImage == null) { Log.Debug("DebugImage is null, not writing file"); } else if (MainEntry.InDEBUG) { var imageName = AnalysisResultName(recording.BaseName, this.SpeciesName, "png", "DebugSpectrogram"); var debugPath = outputDirectory.Combine(imageName); //debugImage.Save(debugPath.FullName); } foreach (var ae in predictedEvents) { // add additional info if (!(ae.Score > recognizerConfig.EventThreshold)) { continue; } ae.Name = recognizerConfig.AbbreviatedSpeciesName; ae.SpeciesName = recognizerConfig.SpeciesName; ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds; ae.SegmentDurationSeconds = recordingDuration.TotalSeconds; prunedEvents.Add(ae); } // do a recognizer TEST. if (false) { var testDir = new DirectoryInfo(outputDirectory.Parent.Parent.FullName); TestTools.RecognizerScoresTest(recording.BaseName, testDir, recognizerConfig.AnalysisName, scoreArray); AcousticEvent.TestToCompareEvents(recording.BaseName, testDir, recognizerConfig.AnalysisName, predictedEvents); } // increase very low scores for (int j = 0; j < scoreArray.Length; j++) { scoreArray[j] *= 4; if (scoreArray[j] > 1.0) { scoreArray[j] = 1.0; } } var plot = new Plot(this.DisplayName, scoreArray, recognizerConfig.EventThreshold); plots.Add(plot); } return(new RecognizerResults() { Sonogram = sonogram, Hits = null, Plots = plots, Events = prunedEvents, }); }
/// <summary> /// Do your analysis. This method is called once per segment (typically one-minute segments). /// </summary> /// <param name="recording"></param> /// <param name="configuration"></param> /// <param name="segmentStartOffset"></param> /// <param name="getSpectralIndexes"></param> /// <param name="outputDirectory"></param> /// <param name="imageWidth"></param> /// <returns></returns> public override RecognizerResults Recognize(AudioRecording recording, Config configuration, TimeSpan segmentStartOffset, Lazy <IndexCalculateResult[]> getSpectralIndexes, DirectoryInfo outputDirectory, int?imageWidth) { // WARNING: TODO TODO TODO = this method simply duplicates the CANETOAD analyser!!!!!!!!!!!!!!!!!!!!! ################### string speciesName = configuration[AnalysisKeys.SpeciesName] ?? "<no species>"; string abbreviatedSpeciesName = configuration[AnalysisKeys.AbbreviatedSpeciesName] ?? "<no.sp>"; int minHz = configuration.GetInt(AnalysisKeys.MinHz); int maxHz = configuration.GetInt(AnalysisKeys.MaxHz); // BETTER TO CALCULATE THIS. IGNORE USER! // double frameOverlap = Double.Parse(configDict[Keys.FRAME_OVERLAP]); // duration of DCT in seconds double dctDuration = configuration.GetDouble(AnalysisKeys.DctDuration); // minimum acceptable value of a DCT coefficient double dctThreshold = configuration.GetDouble(AnalysisKeys.DctThreshold); // ignore oscillations below this threshold freq int minOscilFreq = configuration.GetInt(AnalysisKeys.MinOscilFreq); // ignore oscillations above this threshold freq int maxOscilFreq = configuration.GetInt(AnalysisKeys.MaxOscilFreq); // min duration of event in seconds double minDuration = configuration.GetDouble(AnalysisKeys.MinDuration); // max duration of event in seconds double maxDuration = configuration.GetDouble(AnalysisKeys.MaxDuration); // min score for an acceptable event double eventThreshold = configuration.GetDouble(AnalysisKeys.EventThreshold); // The default was 512 for Canetoad. // Framesize = 128 seems to work for Littoria fallax. // frame size int frameSize = configuration.GetInt(AnalysisKeys.KeyFrameSize); if (recording.WavReader.SampleRate != 22050) { throw new InvalidOperationException("Requires a 22050Hz file"); } double windowOverlap = Oscillations2012.CalculateRequiredFrameOverlap( recording.SampleRate, frameSize, maxOscilFreq); //windowOverlap = 0.75; // previous default // i: MAKE SONOGRAM var sonoConfig = new SonogramConfig { SourceFName = recording.BaseName, WindowSize = frameSize, WindowOverlap = windowOverlap, NoiseReductionType = NoiseReductionType.None, }; // sonoConfig.NoiseReductionType = SNR.Key2NoiseReductionType("STANDARD"); TimeSpan recordingDuration = recording.Duration; int sr = recording.SampleRate; double freqBinWidth = sr / (double)sonoConfig.WindowSize; /* ############################################################################################################################################# * window sr frameDuration frames/sec hz/bin 64frameDuration hz/64bins hz/128bins * 1024 22050 46.4ms 21.5 21.5 2944ms 1376hz 2752hz * 1024 17640 58.0ms 17.2 17.2 3715ms 1100hz 2200hz * 2048 17640 116.1ms 8.6 8.6 7430ms 551hz 1100hz */ // int minBin = (int)Math.Round(minHz / freqBinWidth) + 1; // int maxbin = minBin + numberOfBins - 1; BaseSonogram sonogram = new SpectrogramStandard(sonoConfig, recording.WavReader); int rowCount = sonogram.Data.GetLength(0); int colCount = sonogram.Data.GetLength(1); // double[,] subMatrix = MatrixTools.Submatrix(sonogram.Data, 0, minBin, (rowCount - 1), maxbin); // ###################################################################### // ii: DO THE ANALYSIS AND RECOVER SCORES OR WHATEVER //minDuration = 1.0; Oscillations2012.Execute( (SpectrogramStandard)sonogram, minHz, maxHz, dctDuration, minOscilFreq, maxOscilFreq, dctThreshold, eventThreshold, minDuration, maxDuration, out var scores, out var acousticEvents, out var hits, segmentStartOffset); acousticEvents.ForEach(ae => { ae.SpeciesName = speciesName; ae.SegmentDurationSeconds = recordingDuration.TotalSeconds; ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds; ae.Name = abbreviatedSpeciesName; }); var plot = new Plot(this.DisplayName, scores, eventThreshold); return(new RecognizerResults() { Sonogram = sonogram, Hits = hits, Plots = plot.AsList(), Events = acousticEvents, }); }
/// <summary> /// Do your analysis. This method is called once per segment (typically one-minute segments). /// </summary> /// <param name="recording"></param> /// <param name="configuration"></param> /// <param name="segmentStartOffset"></param> /// <param name="getSpectralIndexes"></param> /// <param name="outputDirectory"></param> /// <param name="imageWidth"></param> /// <returns></returns> public override RecognizerResults Recognize(AudioRecording recording, Config configuration, TimeSpan segmentStartOffset, Lazy <IndexCalculateResult[]> getSpectralIndexes, DirectoryInfo outputDirectory, int?imageWidth) { // common properties var speciesName = configuration[AnalysisKeys.SpeciesName] ?? "<no species>"; var abbreviatedSpeciesName = configuration[AnalysisKeys.AbbreviatedSpeciesName] ?? "<no.sp>"; int minHz = configuration.GetInt(AnalysisKeys.MinHz); int maxHz = configuration.GetInt(AnalysisKeys.MaxHz); // BETTER TO CALCULATE THIS. IGNORE USER! // double frameOverlap = Double.Parse(configDict[Keys.FRAME_OVERLAP]); // duration of DCT in seconds double dctDuration = configuration.GetDouble(AnalysisKeys.DctDuration); // minimum acceptable value of a DCT coefficient double dctThreshold = configuration.GetDouble(AnalysisKeys.DctThreshold); // ignore oscillations below this threshold freq int minOscilFreq = configuration.GetInt(AnalysisKeys.MinOscilFreq); // ignore oscillations above this threshold freq int maxOscilFreq = configuration.GetInt(AnalysisKeys.MaxOscilFreq); // min duration of event in seconds double minDuration = configuration.GetDouble(AnalysisKeys.MinDuration); // max duration of event in seconds double maxDuration = configuration.GetDouble(AnalysisKeys.MaxDuration); // min score for an acceptable event double eventThreshold = configuration.GetDouble(AnalysisKeys.EventThreshold); // this default framesize seems to work for Canetoad const int frameSize = 512; double windowOverlap = Oscillations2012.CalculateRequiredFrameOverlap( recording.SampleRate, frameSize, maxOscilFreq); //windowOverlap = 0.75; // previous default // DEBUG: Following line used to search for where indeterminism creeps into the spectrogram values which vary from run to run. //FileTools.AddArrayAdjacentToExistingArrays(Path.Combine(outputDirectory.FullName, recording.BaseName+"_RecordingSamples.csv"), recording.WavReader.GetChannel(0)); // i: MAKE SONOGRAM var sonoConfig = new SonogramConfig { SourceFName = recording.BaseName, WindowSize = frameSize, WindowOverlap = windowOverlap, // the default window is HAMMING //WindowFunction = WindowFunctions.HANNING.ToString(), //WindowFunction = WindowFunctions.NONE.ToString(), // if do not use noise reduction can get a more sensitive recogniser. NoiseReductionType = NoiseReductionType.None, }; // sonoConfig.NoiseReductionType = SNR.Key2NoiseReductionType("STANDARD"); TimeSpan recordingDuration = recording.Duration; //int sr = recording.SampleRate; //double freqBinWidth = sr / (double)sonoConfig.WindowSize; /* ############################################################################################################################################# * window sr frameDuration frames/sec hz/bin 64frameDuration hz/64bins hz/128bins * 1024 22050 46.4ms 21.5 21.5 2944ms 1376hz 2752hz * 1024 17640 58.0ms 17.2 17.2 3715ms 1100hz 2200hz * 2048 17640 116.1ms 8.6 8.6 7430ms 551hz 1100hz */ // int minBin = (int)Math.Round(minHz / freqBinWidth) + 1; // int maxbin = minBin + numberOfBins - 1; BaseSonogram sonogram = new SpectrogramStandard(sonoConfig, recording.WavReader); //int rowCount = sonogram.Data.GetLength(0); //int colCount = sonogram.Data.GetLength(1); // DEBUG: Following lines used to search for where indeterminism creeps into the spectrogram values which vary from run to run. //double[] array = DataTools.Matrix2Array(sonogram.Data); //FileTools.AddArrayAdjacentToExistingArrays(Path.Combine(outputDirectory.FullName, recording.BaseName+".csv"), array); // ###################################################################### // ii: DO THE ANALYSIS AND RECOVER SCORES OR WHATEVER double minDurationOfAdvertCall = minDuration; // this boundary duration should = 5.0 seconds as of 4 June 2015. //double minDurationOfReleaseCall = 1.0; double[] scores; // predefinition of score array List <AcousticEvent> events; double[,] hits; Oscillations2012.Execute( (SpectrogramStandard)sonogram, minHz, maxHz, dctDuration, minOscilFreq, maxOscilFreq, dctThreshold, eventThreshold, minDurationOfAdvertCall, maxDuration, out scores, out events, out hits, segmentStartOffset); // DEBUG: Following line used to search for where indeterminism creeps into the event detection //FileTools.AddArrayAdjacentToExistingArrays(Path.Combine(outputDirectory.FullName, recording.BaseName+"_ScoreArray.csv"), scores); var prunedEvents = new List <AcousticEvent>(); foreach (AcousticEvent ae in events) { //if (ae.Duration < minDurationOfReleaseCall) { continue; } if (ae.EventDurationSeconds < minDurationOfAdvertCall) { continue; } if (ae.EventDurationSeconds > maxDuration) { continue; } // add additional info ae.SpeciesName = speciesName; ae.Name = abbreviatedSpeciesName; ae.SegmentDurationSeconds = recordingDuration.TotalSeconds; ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds; prunedEvents.Add(ae); //if (ae.Duration >= minDurationOfAdvertCall) //{ // ae.Name = abbreviatedSpeciesName; // + ".AdvertCall"; // prunedEvents.Add(ae); // continue; //} } // do a recognizer test. if (false) { if (MainEntry.InDEBUG) { RecognizerTest(scores, new FileInfo(recording.FilePath)); RecognizerTest(prunedEvents, new FileInfo(recording.FilePath)); } } var plot = new Plot(this.DisplayName, scores, eventThreshold); return(new RecognizerResults() { Sonogram = sonogram, Hits = hits, Plots = plot.AsList(), Events = prunedEvents, //Events = events }); }