/// <summary> /// Analyze one or more file segments using the same analysis and settings. /// Note each segment could be sourced from separate original audio files! /// If using a remote source preparer the segments could even be downloaded from a remote source!. /// </summary> /// <param name="segments"> /// The file Segments. /// </param> /// <param name="analysis"> /// The analysis. /// </param> /// <param name="settings"> /// The settings. /// </param> /// <returns> /// The analysis results. /// </returns> public AnalysisResult2[] Run <TSource>( ISegment <TSource>[] segments, IAnalyser2 analysis, AnalysisSettings settings) { Contract.Requires(settings != null, "Settings must not be null."); Contract.Requires(analysis != null, "Analysis must not be null."); Contract.Requires(segments != null, "File Segments must not be null."); // do not allow the program to continue // if there are no possible segments to process because the original file // is too short. var tooShort = segments .FirstOrDefault(segment => segment.SourceMetadata.Duration < settings.AnalysisMinSegmentDuration); if (tooShort != null) { Log.Fatal("Provided audio recording is too short too analyze!"); throw new AudioRecordingTooShortException( "{0} is too short to analyze with current analysisSettings.AnalysisMinSegmentDuration ({1})" .Format2(tooShort.Source, settings.AnalysisMinSegmentDuration)); } // ensure output directory exists Contract.Requires( settings.AnalysisOutputDirectory.TryCreate(), $"Attempt to create AnalysisOutputDirectory failed: {settings.AnalysisOutputDirectory}"); // try and create temp directory (returns true if already exists) if (!settings.IsAnalysisTempDirectoryValid) { // ensure a temp directory is always set Log.Warn( "No temporary directory provided, using random directory: " + settings.AnalysisTempDirectoryFallback); } // calculate the sub-segments of the given file segments that match what the analysis expects. var analysisSegments = PrepareAnalysisSegments(this.SourcePreparer, segments, settings); // Execute a pre analyzer hook Log.Info("Executing BeforeAnalyze"); analysis.BeforeAnalyze(settings); Log.Debug("Completed BeforeAnalyze"); AnalysisResult2[] results; // clone analysis settings for parallelism concerns: // - as each iteration modifies settings. This causes hard to track down bugs // clones are made for sequential runs to to ensure consistency var settingsForThisItem = (AnalysisSettings)settings.Clone(); Log.Info($"Analysis started in {(this.IsParallel ? "parallel" : "sequence")}."); var stopwatch = new Stopwatch(); stopwatch.Start(); // Analyze the sub-segments in parallel or sequentially (IsParallel property), // Create and delete directories and/or files as indicated by properties // DeleteFinished and UniqueDirectoryPerSegment if (this.IsParallel) { results = this.RunParallel(analysisSegments, analysis, settingsForThisItem); Array.Sort(results); } else { // sequential results = this.RunSequential(analysisSegments, analysis, settingsForThisItem); } stopwatch.Stop(); Log.Info($"Analysis complete, took {stopwatch.Elapsed}."); // TODO: execute SummariseResults hook here eventually // delete temp directories // only delete directory if we are using one that was created specifically for this analysis settings.AnalysisTempDirectoryFallback.TryDelete(true, $"Item {settings.InstanceId}"); return(results); }
/// <summary> /// This entrypoint should be used for testing short files (less than 2 minutes) /// </summary> public static void Execute(Arguments arguments) { MainEntry.WarnIfDevleoperEntryUsed("EventRecognizer entry does not do any audio maniuplation."); Log.Info("Running event recognizer"); var sourceAudio = arguments.Source; var configFile = arguments.Config.ToFileInfo(); var outputDirectory = arguments.Output; if (configFile == null) { throw new FileNotFoundException("No config file argument provided"); } else if (!configFile.Exists) { Log.Warn($"Config file {configFile.FullName} not found... attempting to resolve config file"); configFile = ConfigFile.Resolve(configFile.Name, Directory.GetCurrentDirectory().ToDirectoryInfo()); } LoggedConsole.WriteLine("# Recording file: " + sourceAudio.FullName); LoggedConsole.WriteLine("# Configuration file: " + configFile); LoggedConsole.WriteLine("# Output folder: " + outputDirectory); // find an appropriate event IAnalyzer IAnalyser2 recognizer = AnalyseLongRecording.FindAndCheckAnalyser <IEventRecognizer>( arguments.AnalysisIdentifier, configFile.Name); Log.Info("Attempting to run recognizer: " + recognizer.Identifier); Log.Info("Reading configuration file"); Config configuration = ConfigFile.Deserialize <RecognizerBase.RecognizerConfig>(configFile); // get default settings AnalysisSettings analysisSettings = recognizer.DefaultSettings; // convert arguments to analysis settings analysisSettings = arguments.ToAnalysisSettings( analysisSettings, outputIntermediate: true, resultSubDirectory: recognizer.Identifier, configuration: configuration); // Enable this if you want the Config file ResampleRate parameter to work. // Generally however the ResampleRate should remain at 22050Hz for all recognizers. //analysisSettings.AnalysisTargetSampleRate = (int) configuration[AnalysisKeys.ResampleRate]; // get transform input audio file - if needed Log.Info("Querying source audio file"); var audioUtilityRequest = new AudioUtilityRequest() { TargetSampleRate = analysisSettings.AnalysisTargetSampleRate, }; var preparedFile = AudioFilePreparer.PrepareFile( outputDirectory, sourceAudio, MediaTypes.MediaTypeWav, audioUtilityRequest, outputDirectory); var source = preparedFile.SourceInfo.ToSegment(); var prepared = preparedFile.TargetInfo.ToSegment(FileSegment.FileDateBehavior.None); var segmentSettings = new SegmentSettings <FileInfo>( analysisSettings, source, (analysisSettings.AnalysisOutputDirectory, analysisSettings.AnalysisTempDirectory), prepared); if (preparedFile.TargetInfo.SampleRate.Value != analysisSettings.AnalysisTargetSampleRate) { Log.Warn("Input audio sample rate does not match target sample rate"); } // Execute a pre analyzer hook recognizer.BeforeAnalyze(analysisSettings); // execute actual analysis - output data will be written Log.Info("Running recognizer: " + recognizer.Identifier); AnalysisResult2 results = recognizer.Analyze(analysisSettings, segmentSettings); // run summarize code - output data can be written Log.Info("Running recognizer summary: " + recognizer.Identifier); recognizer.SummariseResults( analysisSettings, source, results.Events, results.SummaryIndices, results.SpectralIndices, new[] { results }); //Log.Info("Recognizer run, saving extra results"); // TODO: Michael, output anything else as you wish. Log.Debug("Clean up temporary files"); if (source.Source.FullName != prepared.Source.FullName) { prepared.Source.Delete(); } int eventCount = results?.Events?.Length ?? 0; Log.Info($"Number of detected events: {eventCount}"); Log.Success(recognizer.Identifier + " recognizer has completed"); }