/// <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");
        }