public static void ConvertEventsToIndices(
            IAnalyser2 analyser,
            EventBase[] events,
            ref SummaryIndexBase[] indices,
            TimeSpan durationOfTheOriginalAudioFile,
            double scoreThreshold)
        {
            if (events == null && indices == null)
            {
                Log.Warn("No events or summary indices were produced, events cannot be made into indices");
            }
            else if (events == null && indices != null)
            {
                // no-op, no events to convert, but indices already calculated
                Log.Debug("No events received, indices already given, no further action");
            }
            else if (events != null && indices == null)
            {
                Log.InfoFormat("Converting Events to {0} minute Indices", IndexUnitTime.TotalMinutes);

                indices = analyser.ConvertEventsToSummaryIndices(
                    events,
                    IndexUnitTime,
                    durationOfTheOriginalAudioFile,
                    scoreThreshold);
            }
            else if (events != null && indices != null)
            {
                // no-op both values already present, just ensure they match
                Log.Info("Both events and indices already given, no event conversion done");
            }
        }
Example #2
0
        /// <summary>
        /// A WRAPPER AROUND THE analyser.Analyze(analysisSettings) METHOD
        /// To be called as an executable with command line arguments.
        /// </summary>
        public static void Execute(Arguments arguments)
        {
            Contract.Requires(arguments != null);

            var(analysisSettings, segmentSettings) = arguments.ToAnalysisSettings();
            TimeSpan offsetStart  = TimeSpan.FromSeconds(arguments.Start ?? 0);
            TimeSpan duration     = TimeSpan.FromSeconds(arguments.Duration ?? 0);
            int      resampleRate = ConfigDictionary.GetInt(AnalysisKeys.ResampleRate, analysisSettings.ConfigDict);

            // EXTRACT THE REQUIRED RECORDING SEGMENT
            FileInfo tempF = segmentSettings.SegmentAudioFile;

            if (tempF.Exists)
            {
                tempF.Delete();
            }

            if (duration == TimeSpan.Zero)
            {
                // Process entire file
                AudioFilePreparer.PrepareFile(arguments.Source, tempF, new AudioUtilityRequest {
                    TargetSampleRate = resampleRate
                }, analysisSettings.AnalysisTempDirectoryFallback);
                ////var fiSegment = AudioFilePreparer.PrepareFile(diOutputDir, fiSourceFile, , Human2.RESAMPLE_RATE);
            }
            else
            {
                AudioFilePreparer.PrepareFile(arguments.Source, tempF, new AudioUtilityRequest {
                    TargetSampleRate = resampleRate, OffsetStart = offsetStart, OffsetEnd = offsetStart.Add(duration)
                }, analysisSettings.AnalysisTempDirectoryFallback);
                ////var fiSegmentOfSourceFile = AudioFilePreparer.PrepareFile(diOutputDir, new FileInfo(recordingPath), MediaTypes.MediaTypeWav, TimeSpan.FromMinutes(2), TimeSpan.FromMinutes(3), RESAMPLE_RATE);
            }

            //DO THE ANALYSIS
            // #############################################################################################################################################
            // BROKEN!
            throw new NotImplementedException("Broken in code updates");
            IAnalyser2      analyser = null; //new Rain_OBSOLETE();
            AnalysisResult2 result   = analyser.Analyze <FileInfo>(analysisSettings, null /*broken */);

            /*DataTable dt = result.Data;
             * //#############################################################################################################################################
             *
             * // ADD IN ADDITIONAL INFO TO RESULTS TABLE
             * if (dt != null)
             * {
             *  int iter = 0; // dummy - iteration number would ordinarily be available at this point.
             *  int startMinute = (int)offsetStart.TotalMinutes;
             *  foreach (DataRow row in dt.Rows)
             *  {
             *      row[InitialiseIndexProperties.KEYRankOrder] = iter;
             *      row[InitialiseIndexProperties.KEYStartMinute] = startMinute;
             *      row[InitialiseIndexProperties.KEYSegmentDuration] = result.AudioDuration.TotalSeconds;
             *  }
             *
             *  CsvTools.DataTable2CSV(dt, segmentSettings.SegmentSummaryIndicesFile.FullName);
             *  //DataTableTools.WriteTable2Console(dt);
             * }*/
        }
        /// <summary>
        /// Gets a named output directory. For example, if <paramref name="baseDir"/> is "C:\Temp" and
        /// <paramref name="analyzer"/> is the indices analysis, the result will be "C:\Temp\Towsey.Acoustic".
        /// </summary>
        /// <param name="baseDir">The base output directory (either normal output or a temp directory).</param>
        /// <param name="analyzer">The <see cref="IAnalyser2"/> to extract the identifier from.</param>
        /// <param name="subFolders">An optional list of sub folders to append to the path.</param>
        /// <returns>A combined directory made up of all the path fragments.</returns>
        public static DirectoryInfo GetNamedDirectory(
            DirectoryInfo baseDir,
            IAnalyser2 analyzer,
            params string[] subFolders)
        {
            Contract.Requires(analyzer.NotNull(), "analyzer must be not null");
            Contract.Requires(!string.IsNullOrWhiteSpace(analyzer.Identifier), "analysisIdentifier must be set.");

            return(baseDir.Combine(subFolders.Prepend(analyzer.Identifier)));
        }
        public static DirectoryInfo SaveSpectralIndices(IAnalyser2 analyser2, string fileName, DirectoryInfo outputDirectory, IEnumerable <SpectralIndexBase> spectra)
        {
            if (spectra == null)
            {
                Log.Debug("No spectral indices returned... file not written");
                return(null);
            }

            analyser2.WriteSpectrumIndicesFiles(outputDirectory, fileName, spectra);
            return(outputDirectory);
        }
Example #5
0
        /// <summary>
        /// 2. Analyses long audio recording (mp3 or wav) as per passed config file. Outputs an events.csv file AND an
        /// indices.csv file
        /// Signed off: Michael Towsey 4th December 2012
        /// </summary>
        public static void Execute(Arguments arguments)
        {
            if (arguments == null)
            {
                throw new NoDeveloperMethodException();
            }

            LoggedConsole.WriteLine("# PROCESS LONG RECORDING");
            LoggedConsole.WriteLine("# DATE AND TIME: " + DateTime.Now);

            // 1. set up the necessary files
            var sourceAudio        = arguments.Source;
            var configFile         = arguments.Config.ToFileInfo();
            var outputDirectory    = arguments.Output;
            var tempFilesDirectory = arguments.TempDir;

            // if a temp dir is not given, use output dir as temp dir
            if (tempFilesDirectory == null)
            {
                Log.Warn("No temporary directory provided, using output directory");
                tempFilesDirectory = outputDirectory;
            }

            // try an automatically find the config file
            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");

                // we use .ToString() here to get the original input string - Using fullname always produces an absolute path wrt to pwd... we don't want to prematurely make asusmptions:
                // e.g. We require a missing absolute path to fail... that wouldn't work with .Name
                // e.g. We require a relative path to try and resolve, using .FullName would fail the first absolute check inside ResolveConfigFile
                configFile = ConfigFile.Resolve(configFile.ToString(), Directory.GetCurrentDirectory().ToDirectoryInfo());
            }

            if (arguments.StartOffset.HasValue ^ arguments.EndOffset.HasValue)
            {
                throw new InvalidStartOrEndException("If StartOffset or EndOffset is specified, then both must be specified");
            }

            if (arguments.StartOffset.HasValue && arguments.EndOffset.HasValue && arguments.EndOffset.Value <= arguments.StartOffset.Value)
            {
                throw new InvalidStartOrEndException("Start offset must be less than end offset.");
            }

            LoggedConsole.WriteLine("# Recording file:      " + sourceAudio.FullName);
            LoggedConsole.WriteLine("# Configuration file:  " + configFile);
            LoggedConsole.WriteLine("# Output folder:       " + outputDirectory);
            LoggedConsole.WriteLine("# Temp File Directory: " + tempFilesDirectory);

            // optionally copy logs / config to make results easier to understand
            // TODO: remove, see https://github.com/QutEcoacoustics/audio-analysis/issues/133
            if (arguments.WhenExitCopyConfig || arguments.WhenExitCopyLog)
            {
                AppDomain.CurrentDomain.ProcessExit += (sender, args) => { Cleanup(arguments, configFile); };
            }

            // 2. initialize the analyzer
            // we're changing the way resolving config files works. Ideally, we'd like to use statically typed config files
            // but we can't do that unless we know which type we have to load first! Currently analyzer to load is in
            // the config file so we can't know which analyzer we can use. Thus we will change to using the file name,
            // or an argument to resolve the analyzer to load.
            // Get analysis name:
            IAnalyser2 analyzer = FindAndCheckAnalyzer <IAnalyser2>(arguments.AnalysisIdentifier, configFile.Name);

            // 2. get the analysis config
            AnalyzerConfig configuration = analyzer.ParseConfig(configFile);

            SaveBehavior saveIntermediateWavFiles  = configuration.SaveIntermediateWavFiles;
            bool         saveIntermediateDataFiles = configuration.SaveIntermediateCsvFiles;
            SaveBehavior saveSonogramsImages       = configuration.SaveSonogramImages;

            bool filenameDate = configuration.RequireDateInFilename;

            if (configuration[AnalysisKeys.AnalysisName].IsNotWhitespace())
            {
                Log.Warn("Your config file has `AnalysisName` set - this property is deprecated and ignored");
            }

            // AT 2018-02: changed logic so default index properties loaded if not provided
            FileInfo indicesPropertiesConfig = IndexProperties.Find(configuration, configFile);

            if (indicesPropertiesConfig == null || !indicesPropertiesConfig.Exists)
            {
                Log.Warn("IndexProperties config can not be found! Loading a default");
                indicesPropertiesConfig = ConfigFile.Default <Dictionary <string, IndexProperties> >();
            }

            LoggedConsole.WriteLine("# IndexProperties Cfg: " + indicesPropertiesConfig.FullName);

            // min score for an acceptable event
            Log.Info("Minimum event threshold has been set to " + configuration.EventThreshold);

            FileSegment.FileDateBehavior defaultBehavior = FileSegment.FileDateBehavior.Try;
            if (filenameDate)
            {
                if (!FileDateHelpers.FileNameContainsDateTime(sourceAudio.Name))
                {
                    throw new InvalidFileDateException(
                              "When RequireDateInFilename option is set, the filename of the source audio file must contain "
                              + "a valid AND UNAMBIGUOUS date. Such a date was not able to be parsed.");
                }

                defaultBehavior = FileSegment.FileDateBehavior.Required;
            }

            // 3. initilize AnalysisCoordinator class that will do the analysis
            var analysisCoordinator = new AnalysisCoordinator(
                new LocalSourcePreparer(),
                saveIntermediateWavFiles,
                false,
                arguments.Parallel);

            // 4. get the segment of audio to be analysed
            // if tiling output, specify that FileSegment needs to be able to read the date
            var fileSegment         = new FileSegment(sourceAudio, arguments.AlignToMinute, null, defaultBehavior);
            var bothOffsetsProvided = arguments.StartOffset.HasValue && arguments.EndOffset.HasValue;

            if (bothOffsetsProvided)
            {
                fileSegment.SegmentStartOffset = TimeSpan.FromSeconds(arguments.StartOffset.Value);
                fileSegment.SegmentEndOffset   = TimeSpan.FromSeconds(arguments.EndOffset.Value);
            }
            else
            {
                Log.Debug("Neither start nor end segment offsets provided. Therefore both were ignored.");
            }

            // 6. initialize the analysis settings object
            var analysisSettings = analyzer.DefaultSettings;

            analysisSettings.ConfigFile                = configFile;
            analysisSettings.Configuration             = configuration;
            analysisSettings.AnalysisOutputDirectory   = outputDirectory;
            analysisSettings.AnalysisTempDirectory     = tempFilesDirectory;
            analysisSettings.AnalysisDataSaveBehavior  = saveIntermediateDataFiles;
            analysisSettings.AnalysisImageSaveBehavior = saveSonogramsImages;
            analysisSettings.AnalysisChannelSelection  = arguments.Channels;
            analysisSettings.AnalysisMixDownToMono     = arguments.MixDownToMono;

            var segmentDuration = configuration.SegmentDuration?.Seconds();

            if (!segmentDuration.HasValue)
            {
                segmentDuration = analysisSettings.AnalysisMaxSegmentDuration ?? TimeSpan.FromMinutes(1);
                Log.Warn(
                    $"Can't read `{nameof(AnalyzerConfig.SegmentDuration)}` from config file. "
                    + $"Default value of {segmentDuration} used)");
            }

            analysisSettings.AnalysisMaxSegmentDuration = segmentDuration.Value;

            var segmentOverlap = configuration.SegmentOverlap?.Seconds();

            if (!segmentOverlap.HasValue)
            {
                segmentOverlap = analysisSettings.SegmentOverlapDuration;
                Log.Warn(
                    $"Can't read `{nameof(AnalyzerConfig.SegmentOverlap)}` from config file. "
                    + $"Default value of {segmentOverlap} used)");
            }

            analysisSettings.SegmentOverlapDuration = segmentOverlap.Value;

            // set target sample rate
            var resampleRate = configuration.ResampleRate;

            if (!resampleRate.HasValue)
            {
                resampleRate = analysisSettings.AnalysisTargetSampleRate ?? AppConfigHelper.DefaultTargetSampleRate;
                Log.Warn(
                    $"Can't read {nameof(configuration.ResampleRate)} from config file. "
                    + $"Default value of {resampleRate} used)");
            }

            analysisSettings.AnalysisTargetSampleRate = resampleRate;

            Log.Info(
                $"{nameof(configuration.SegmentDuration)}={segmentDuration}, "
                + $"{nameof(configuration.SegmentOverlap)}={segmentOverlap}, "
                + $"{nameof(configuration.ResampleRate)}={resampleRate}");

            // 7. ####################################### DO THE ANALYSIS ###################################
            LoggedConsole.WriteLine("START ANALYSIS ...");
            var analyserResults = analysisCoordinator.Run(fileSegment, analyzer, analysisSettings);

            // ##############################################################################################
            // 8. PROCESS THE RESULTS
            LoggedConsole.WriteLine(string.Empty);
            LoggedConsole.WriteLine("START PROCESSING RESULTS ...");
            if (analyserResults == null)
            {
                LoggedConsole.WriteErrorLine("###################################################\n");
                LoggedConsole.WriteErrorLine("The Analysis Run Coordinator has returned a null result.");
                LoggedConsole.WriteErrorLine("###################################################\n");
                throw new AnalysisOptionDevilException();
            }

            // Merge and correct main result types
            EventBase[]         mergedEventResults         = ResultsTools.MergeResults(analyserResults, ar => ar.Events, ResultsTools.CorrectEvent);
            SummaryIndexBase[]  mergedIndicesResults       = ResultsTools.MergeResults(analyserResults, ar => ar.SummaryIndices, ResultsTools.CorrectSummaryIndex);
            SpectralIndexBase[] mergedSpectralIndexResults = ResultsTools.MergeResults(analyserResults, ar => ar.SpectralIndices, ResultsTools.CorrectSpectrumIndex);

            // not an exceptional state, do not throw exception
            if (mergedEventResults != null && mergedEventResults.Length == 0)
            {
                LoggedConsole.WriteWarnLine("The analysis produced no EVENTS (mergedResults had zero count)");
            }

            if (mergedIndicesResults != null && mergedIndicesResults.Length == 0)
            {
                LoggedConsole.WriteWarnLine("The analysis produced no Summary INDICES (mergedResults had zero count)");
            }

            if (mergedSpectralIndexResults != null && mergedSpectralIndexResults.Length == 0)
            {
                LoggedConsole.WriteWarnLine("The analysis produced no Spectral INDICES (merged results had zero count)");
            }

            // 9. CREATE SUMMARY INDICES IF NECESSARY (FROM EVENTS)
#if DEBUG
            // get the duration of the original source audio file - need this to convert Events datatable to Indices Datatable
            var audioUtility = new MasterAudioUtility(tempFilesDirectory);
            var mimeType     = MediaTypes.GetMediaType(sourceAudio.Extension);
            var sourceInfo   = audioUtility.Info(sourceAudio);

            // updated by reference all the way down in LocalSourcePreparer
            Debug.Assert(fileSegment.TargetFileDuration == sourceInfo.Duration);
#endif
            var duration = fileSegment.TargetFileDuration.Value;

            ResultsTools.ConvertEventsToIndices(
                analyzer,
                mergedEventResults,
                ref mergedIndicesResults,
                duration,
                configuration.EventThreshold);
            int eventsCount           = mergedEventResults?.Length ?? 0;
            int numberOfRowsOfIndices = mergedIndicesResults?.Length ?? 0;

            // 10. Allow analysers to post-process

            // TODO: remove results directory if possible
            var instanceOutputDirectory =
                AnalysisCoordinator.GetNamedDirectory(analysisSettings.AnalysisOutputDirectory, analyzer);

            // 11. IMPORTANT - this is where IAnalyser2's post processor gets called.
            // Produces all spectrograms and images of SPECTRAL INDICES.
            // Long duration spectrograms are drawn IFF analysis type is Towsey.Acoustic
            analyzer.SummariseResults(analysisSettings, fileSegment, mergedEventResults, mergedIndicesResults, mergedSpectralIndexResults, analyserResults);

            // 12. SAVE THE RESULTS
            string fileNameBase = Path.GetFileNameWithoutExtension(sourceAudio.Name);

            var eventsFile  = ResultsTools.SaveEvents(analyzer, fileNameBase, instanceOutputDirectory, mergedEventResults);
            var indicesFile = ResultsTools.SaveSummaryIndices(analyzer, fileNameBase, instanceOutputDirectory, mergedIndicesResults);
            var spectraFile = ResultsTools.SaveSpectralIndices(analyzer, fileNameBase, instanceOutputDirectory, mergedSpectralIndexResults);

            // 13. THIS IS WHERE SUMMARY INDICES ARE PROCESSED
            //     Convert summary indices to black and white tracks image
            if (mergedIndicesResults == null)
            {
                Log.Info("No summary indices produced");
            }
            else
            {
                if (indicesPropertiesConfig == null || !indicesPropertiesConfig.Exists)
                {
                    throw new InvalidOperationException("Cannot process indices without an index configuration file, the file could not be found!");
                }

                // this arbitrary amount of data.
                if (mergedIndicesResults.Length > 5000)
                {
                    Log.Warn("Summary Indices Image not able to be drawn - there are too many indices to render");
                }
                else
                {
                    var    basename   = Path.GetFileNameWithoutExtension(fileNameBase);
                    string imageTitle = $"SOURCE:{basename},   {Meta.OrganizationTag};  ";

                    // Draw Tracks-Image of Summary indices
                    // set time scale resolution for drawing of summary index tracks
                    TimeSpan timeScale   = TimeSpan.FromSeconds(0.1);
                    Bitmap   tracksImage =
                        IndexDisplay.DrawImageOfSummaryIndices(
                            IndexProperties.GetIndexProperties(indicesPropertiesConfig),
                            indicesFile,
                            imageTitle,
                            timeScale,
                            fileSegment.TargetFileStartDate);
                    var imagePath = FilenameHelpers.AnalysisResultPath(instanceOutputDirectory, basename, "SummaryIndices", ImageFileExt);
                    tracksImage.Save(imagePath);
                }
            }

            // 14. wrap up, write stats
            LoggedConsole.WriteLine("INDICES CSV file(s) = " + (indicesFile?.Name ?? "<<No indices result, no file!>>"));
            LoggedConsole.WriteLine("\tNumber of rows (i.e. minutes) in CSV file of indices = " + numberOfRowsOfIndices);
            LoggedConsole.WriteLine(string.Empty);

            if (eventsFile == null)
            {
                LoggedConsole.WriteLine("An Events CSV file was NOT returned.");
            }
            else
            {
                LoggedConsole.WriteLine("EVENTS CSV file(s) = " + eventsFile.Name);
                LoggedConsole.WriteLine("\tNumber of events = " + eventsCount);
            }

            Log.Success($"Analysis Complete.\nSource={sourceAudio.Name}\nOutput={instanceOutputDirectory.FullName}");
        }
        /// <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>
 /// Analyze one file segment using the analysis and settings.
 /// </summary>
 /// <param name="segment">
 /// The file Segment.
 /// </param>
 /// <param name="analysis">
 /// The analysis.
 /// </param>
 /// <param name="settings">
 /// The settings.
 /// </param>
 /// <returns>
 /// The analysis results.
 /// </returns>
 public AnalysisResult2[] Run <TSource>(ISegment <TSource> segment, IAnalyser2 analysis, AnalysisSettings settings)
 {
     return(this.Run(new[] { segment }, analysis, settings));
 }
        /// <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");
        }
Example #9
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="fiAudio"></param>
        /// <param name="fiConfig"></param>
        /// <param name="fiImage"></param>
        /// <returns></returns>
        public static Image GetImageFromAudioSegment(FileInfo fiAudio, FileInfo fiConfig, FileInfo fiImage, IAnalyser2 analyser)
        {
            var config = new ConfigDictionary(fiConfig.FullName); //read in config file

            bool doAnnotate = config.GetBoolean(AnalysisKeys.AnnotateSonogram);

            //bool doNoiseReduction = config.GetBoolean(Keys.NOISE_DO_REDUCTION);
            //double bgNoiseThreshold = config.GetDouble(Keys.NOISE_BG_REDUCTION);

            var diOutputDir = new DirectoryInfo(Path.GetDirectoryName(fiImage.FullName));

            //Image image = null;

            if (doAnnotate)
            {
                if (analyser == null)
                {
                    string analyisName = config.GetString(AnalysisKeys.AnalysisName);
                    LoggedConsole.WriteLine("\nWARNING: Could not construct annotated image because analysis name not recognized:");
                    LoggedConsole.WriteLine("\t " + analyisName);
                    return(null);
                }

                throw new NotSupportedException("Code intentionally broken because it is out of date and not used");

                /*
                 * Image image = null;
                 * var settings = new AnalysisSettings
                 * {
                 *  ConfigDict = config.GetDictionary(),
                 *  SegmentAudioFile = fiAudio,
                 *  ConfigFile = fiConfig,
                 *  SegmentImageFile = fiImage,
                 *  SegmentOutputDirectory = diOutputDir
                 * };
                 *
                 * // want to pass SampleRate of the original file.
                 * settings.SampleRateOfOriginalAudioFile = int.Parse(settings.ConfigDict[AnalysisKeys.ResampleRate]);
                 *
                 * analyser.BeforeAnalyze(settings);
                 *
                 * var results = analyser.Analyze(settings, new SegmentSettings<FileInfo>(se));
                 *
                 * image = results.ImageFile == null ? null : Image.FromFile(results.ImageFile.FullName);
                 *
                 * analyser = null;
                 * return image;*/
            }
            else
            {
                analyser = null;
                var          configDict = config.GetDictionary();
                BaseSonogram sonogram   = Audio2DecibelSonogram(fiAudio, configDict);
                var          mti        = Sonogram2MultiTrackImage(sonogram, configDict);
                var          image      = mti.GetImage();

                if (image != null)
                {
                    if (fiImage.Exists)
                    {
                        fiImage.Delete();
                    }

                    image.Save(fiImage.FullName, ImageFormat.Png);
                }

                return(image);
            }
        }
 public static FileInfo SaveSummaryIndices(IAnalyser2 analyser2, string fileName, DirectoryInfo outputDirectory, IEnumerable <SummaryIndexBase> indices)
 {
     return(SaveResults(outputDirectory, fileName, analyser2.Identifier + "." + FilenameHelpers.StandardIndicesSuffix, analyser2.WriteSummaryIndicesFile, indices));
 }
 public static FileInfo SaveEvents(IAnalyser2 analyser2, string fileName, DirectoryInfo outputDirectory, IEnumerable <EventBase> events)
 {
     return(SaveResults(outputDirectory, fileName, analyser2.Identifier + "." + FilenameHelpers.StandardEventsSuffix, analyser2.WriteEventsFile, events));
 }