diff --git a/src/Acoustics.Shared/Extensions/EnumerableExtensions.cs b/src/Acoustics.Shared/Extensions/EnumerableExtensions.cs index e94607231..8053f13d8 100644 --- a/src/Acoustics.Shared/Extensions/EnumerableExtensions.cs +++ b/src/Acoustics.Shared/Extensions/EnumerableExtensions.cs @@ -7,16 +7,17 @@ // ReSharper disable once CheckNamespace namespace System { - using System; + using Acoustics.Shared; + using Acoustics.Shared.Contracts; using Collections.Generic; + using JetBrains.Annotations; using Linq; using Threading.Tasks; - using Acoustics.Shared; - using Acoustics.Shared.Contracts; public static class EnumerableExtensions { + [ContractAnnotation("items:null => true; items:notnull => false")] public static bool IsNullOrEmpty(this IEnumerable items) { return items == null || !items.Any(); @@ -311,4 +312,4 @@ public static IEnumerable Append(this IEnumerable items, T newItem) yield return newItem; } } -} \ No newline at end of file +} diff --git a/src/Acoustics.Shared/Extensions/FileInfoExtensions.cs b/src/Acoustics.Shared/Extensions/FileInfoExtensions.cs index 97b0eb4d7..0e2b2a2f9 100644 --- a/src/Acoustics.Shared/Extensions/FileInfoExtensions.cs +++ b/src/Acoustics.Shared/Extensions/FileInfoExtensions.cs @@ -149,7 +149,7 @@ public static bool TryCreate(this DirectoryInfo file) public static FileInfo Touch(this FileInfo info) { using (File.OpenWrite(info.FullName)) - { + { } info.Refresh(); @@ -163,6 +163,11 @@ public static T RefreshInfo(this T info) info.Refresh(); return info; } + + public static string BaseName(this FileInfo file) + { + return Path.GetFileNameWithoutExtension(file.Name); + } } public class FileInfoNameComparer : IComparer, IEqualityComparer diff --git a/src/Acoustics.Shared/Logging/LoggedConsole.cs b/src/Acoustics.Shared/Logging/LoggedConsole.cs index eece12edf..97513f9d0 100644 --- a/src/Acoustics.Shared/Logging/LoggedConsole.cs +++ b/src/Acoustics.Shared/Logging/LoggedConsole.cs @@ -100,6 +100,11 @@ public static void WriteFatalLine(string str, Exception exception) Log.Fatal(str, exception); } + public static void WriteFatalLine(string str) + { + Log.Fatal(str); + } + public static void WriteWaitingLine(Task task, string message = null) { WriteLine(message ?? "Waiting..."); @@ -121,6 +126,7 @@ public static string Prompt(string prompt, bool forPassword = false, TimeSpan? t { return ReadHiddenLine(); } + var line = System.Console.ReadLine(); return line; }); diff --git a/src/Acoustics.Shared/Meta.cs b/src/Acoustics.Shared/Meta.cs index 3699609ce..0a22781f0 100644 --- a/src/Acoustics.Shared/Meta.cs +++ b/src/Acoustics.Shared/Meta.cs @@ -11,7 +11,7 @@ namespace Acoustics.Shared public static class Meta { - public const string Description = "QUT Aacoustics Analysis Program - version "; + public const string Description = "QUT Ecoacoustics Analysis Programs"; public const string Name = "AnalysisPrograms.exe"; diff --git a/src/Acoustics.Shared/Yaml.cs b/src/Acoustics.Shared/Yaml.cs index b4bb07ea8..677e049bd 100644 --- a/src/Acoustics.Shared/Yaml.cs +++ b/src/Acoustics.Shared/Yaml.cs @@ -54,9 +54,9 @@ public static void Serialize(FileInfo file, T obj) } } - internal static (object, T) LoadAndDeserialize(FileInfo file) + internal static (object, T) LoadAndDeserialize(string path) { - using (var stream = file.OpenText()) + using (var stream = File.OpenText(path)) { // allow merging in yaml back references var parser = new MergingParser(new Parser(stream)); diff --git a/src/Acoustics.Tools/AudioUtilityRequest.cs b/src/Acoustics.Tools/AudioUtilityRequest.cs index 25d433b6f..b8dafdd2f 100644 --- a/src/Acoustics.Tools/AudioUtilityRequest.cs +++ b/src/Acoustics.Tools/AudioUtilityRequest.cs @@ -166,14 +166,21 @@ private bool DoValidation(bool throwExceptions) return false; } - if (this.Channels.NotNull() && (this.Channels.Length == 0 || this.Channels.Any(c => c < 1))) + if (this.Channels.NotNull()) { - if (throwExceptions) + if (this.Channels.Length == 0) { - throw new ChannelNotAvailableException("Channel number should be greater than 0."); + this.Channels = null; } + else if (this.Channels.Any(c => c < 1)) + { + if (throwExceptions) + { + throw new ChannelNotAvailableException("Channel number should be greater than 0."); + } - return false; + return false; + } } /*if (this.Channel.HasValue && this.MixDownToMono.HasValue && this.MixDownToMono.Value) diff --git a/src/AnalysisPrograms/AED.cs b/src/AnalysisPrograms/AED.cs index 81a419173..fdbb0171b 100644 --- a/src/AnalysisPrograms/AED.cs +++ b/src/AnalysisPrograms/AED.cs @@ -54,7 +54,7 @@ public class Aed : AbstractStrongAnalyser /// [Command( CommandName, - Description = "Acosutic event detection, for short files (~ 1min)")] + Description = "Acoustic event detection, for short files (~ 1min)")] public class Arguments : SourceConfigOutputDirArguments { public override Task Execute(CommandLineApplication app) @@ -270,7 +270,7 @@ public static void Execute(Arguments arguments) LoggedConsole.WriteLine(date); FileInfo recodingFile = arguments.Source; - var recodingBaseName = Path.GetFileNameWithoutExtension(arguments.Source.Name); + var recodingBaseName = recodingFile.BaseName(); DirectoryInfo outputDir = arguments.Output.Combine(EcosoundsAedIdentifier); outputDir.Create(); diff --git a/src/AnalysisPrograms/AnalyseLongRecordings/AnalyseLongRecording.cs b/src/AnalysisPrograms/AnalyseLongRecordings/AnalyseLongRecording.cs index e88f3f63a..9473e6cb1 100644 --- a/src/AnalysisPrograms/AnalyseLongRecordings/AnalyseLongRecording.cs +++ b/src/AnalysisPrograms/AnalyseLongRecordings/AnalyseLongRecording.cs @@ -11,6 +11,7 @@ namespace AnalysisPrograms.AnalyseLongRecordings { using System; using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; using System.Diagnostics; using System.Drawing; using System.IO; @@ -61,7 +62,7 @@ public static void Execute(Arguments arguments) // 1. set up the necessary files var sourceAudio = arguments.Source; - var configFile = arguments.Config; + var configFile = arguments.Config.ToFileInfo(); var outputDirectory = arguments.Output; var tempFilesDirectory = arguments.TempDir; @@ -69,7 +70,7 @@ public static void Execute(Arguments arguments) if (tempFilesDirectory == null) { Log.Warn("No temporary directory provided, using output directory"); - tempFilesDirectory = arguments.Output; + tempFilesDirectory = outputDirectory; } // try an automatically find the config file @@ -137,7 +138,7 @@ public static void Execute(Arguments arguments) } // AT 2018-02: changed logic so default index properties loaded if not provided - FileInfo indicesPropertiesConfig = IndexProperties.Find(configuration, arguments.Config); + FileInfo indicesPropertiesConfig = IndexProperties.Find(configuration, configFile); if (indicesPropertiesConfig == null || !indicesPropertiesConfig.Exists) { Log.Warn("IndexProperties config can not be found! Loading a default"); @@ -391,17 +392,12 @@ public static T FindAndCheckAnalyser(string analysisIdentifier, string partia T analyser = analysers.FirstOrDefault(a => a.Identifier == searchName); if (analyser == null) { - var error = $@"### - -We can not determine what analysis you want to run. We tried to search for ""{searchName}"" - -### -"; + var error = $"We can not determine what analysis you want to run. We tried to search for \"{searchName}\""; LoggedConsole.WriteError(error); - var knownAnalyzers = analysers.Aggregate(string.Empty, (a, i) => a + $"\t {i.Identifier}\n"); + var knownAnalyzers = analysers.Aggregate(string.Empty, (a, i) => a + $" {i.Identifier}\n"); LoggedConsole.WriteLine("Available analysers are:\n" + knownAnalyzers); - throw new CommandLineArgumentException("Cannot find a valid IAnalyser2"); + throw new ValidationException($"Cannot find an IAnalyser2 with the name `{searchName}`"); } return analyser; @@ -414,7 +410,7 @@ private static void Cleanup(Arguments args, FileInfo configFile) { if (args.WhenExitCopyConfig) { - configFile.CopyTo(Path.Combine(args.Output.FullName, args.Config.Name), true); + configFile.CopyTo(Path.Combine(args.Output.FullName, Path.GetFileName(args.Config)), true); } if (args.WhenExitCopyLog) diff --git a/src/AnalysisPrograms/AnalyseLongRecordings/AnalyseLongRecordings.Arguments.cs b/src/AnalysisPrograms/AnalyseLongRecordings/AnalyseLongRecordings.Arguments.cs index dbd55ef1a..639fa5ee9 100644 --- a/src/AnalysisPrograms/AnalyseLongRecordings/AnalyseLongRecordings.Arguments.cs +++ b/src/AnalysisPrograms/AnalyseLongRecordings/AnalyseLongRecordings.Arguments.cs @@ -44,44 +44,54 @@ public Arguments() #endif } - [Option("Sets the name of the analysis to run. If not set, analysis identifer is parsed from the config file name.")] + [Option(Description = "Sets the name of the analysis to run. If not set, analysis identifer is parsed from the config file name.")] public string AnalysisIdentifier { get; set; } [Option( - "A TEMP directory where cut files will be stored. Use this option for efficiency (e.g. write to a RAM Disk).", - ShortName = "t" - )] + Description = "A TEMP directory where cut files will be stored. Use this option for efficiency (e.g. write to a RAM Disk).", + ShortName = "t")] [DirectoryExistsOrCreate(createIfNotExists: true)] + [LegalFilePath] public DirectoryInfo TempDir { get; set; } - [Option("The start offset to start analyzing from (in seconds)")] + [Option( + CommandOptionType.SingleValue, + Description = "The start offset to start analyzing from (in seconds)")] [InRange(min: 0)] public double? StartOffset { get; set; } - [Option("The end offset to stop analyzing (in seconds)")] + [Option( + CommandOptionType.SingleValue, + Description = "The end offset to stop analyzing (in seconds)")] [InRange(min: 0)] public double? EndOffset { get; set; } - [Option("Allow advancing the start of the analysis to the nearest minute. A valid datetime must be available in the file name. Seed additional notes for options.")] + [Option( + Description = "Allow advancing the start of the analysis to the nearest minute. A valid datetime must be available in the file name. Seed additional notes for options.", + ShortName = "")] public TimeAlignment AlignToMinute { get; set; } = TimeAlignment.None; - [Option("An array of channels to select. Default is all channels.")] + [Option(Description = "An array of channels to select. Default is all channels.")] public int[] Channels { get; set; } = null; [Option( - "Mix all selected input channels down into one mono channel. Default is to mixdown.", - CommandOptionType.SingleValue)] + CommandOptionType.SingleValue, + Description = "Mix all selected input channels down into one mono channel. Default is to mixdown.")] public bool MixDownToMono { get; set; } = true; - [Option("Whether or not run this analysis in parallel - multiple segments can be analyzed at the same time")] + [Option( + Description = "Whether or not run this analysis in parallel - multiple segments can be analyzed at the same time", + ShortName = "p")] public bool Parallel { get; set; } = false; [Option( - "If true, attempts to copy the executable's log file to output directory. If it can't determine an output directory, it copies to the working directory.")] + Description = "If true, attempts to copy the executable's log file to output directory. If it can't determine an output directory, it copies to the working directory.", + ShortName = "")] public bool WhenExitCopyLog { get; set; } [Option( - "If true, attempts to copy the executable's config file to output directory. If it can't determine an output directory, it copies to the working directory. If it can't find a config file, nothing is copied")] + Description = "If true, attempts to copy the executable's config file to output directory. If it can't determine an output directory, it copies to the working directory. If it can't find a config file, nothing is copied", + ShortName = "")] public bool WhenExitCopyConfig { get; set; } public override Task Execute(CommandLineApplication app) diff --git a/src/AnalysisPrograms/AnalysesAvailable.cs b/src/AnalysisPrograms/AnalysesAvailable.cs index 9cfb82374..a9b46b552 100644 --- a/src/AnalysisPrograms/AnalysesAvailable.cs +++ b/src/AnalysisPrograms/AnalysesAvailable.cs @@ -19,7 +19,9 @@ namespace AnalysisPrograms using Recognizers.Base; using TowseyLibrary; - [Command("List available IAnalyzers available for use with audio2csv or eventRecognizer")] + [Command( + "AnalysesAvailable", + Description = "List available IAnalyzers available for use with audio2csv or eventRecognizer")] public class AnalysesAvailable : SubCommandBase { diff --git a/src/AnalysisPrograms/AnalysisPrograms.csproj b/src/AnalysisPrograms/AnalysisPrograms.csproj index 849b9311a..d8444cf2e 100644 --- a/src/AnalysisPrograms/AnalysisPrograms.csproj +++ b/src/AnalysisPrograms/AnalysisPrograms.csproj @@ -83,7 +83,7 @@ ..\..\packages\MathNet.Numerics.3.20.2\lib\net40\MathNet.Numerics.dll - ..\..\packages\McMaster.Extensions.CommandLineUtils.2.2.0-alpha.162\lib\net45\McMaster.Extensions.CommandLineUtils.dll + ..\..\packages\McMaster.Extensions.CommandLineUtils.1.0.0-CI00005\lib\net45\McMaster.Extensions.CommandLineUtils.dll @@ -281,7 +281,13 @@ + + + + + + diff --git a/src/AnalysisPrograms/Audio2InputForConvCNN.cs b/src/AnalysisPrograms/Audio2InputForConvCNN.cs index db88bd749..815e3254e 100644 --- a/src/AnalysisPrograms/Audio2InputForConvCNN.cs +++ b/src/AnalysisPrograms/Audio2InputForConvCNN.cs @@ -81,7 +81,7 @@ private static Arguments Dev() //Source = @"C:\SensorNetworks\WavFiles\ConvDNNData\ConvDNN_annotation_export_commonNameOnly_withPadding_20140829.processed.csv".ToFileInfo(), Source = @"Y:\Results\2014Aug29-000000 - Mangalam Data Export\Output\ConvDNN_annotation_export_commonNameOnly_withPadding_20140829.processed.csv".ToFileInfo(), - Config = @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\Mangalam.Sonogram.yml".ToFileInfo(), + Config = @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\Mangalam.Sonogram.yml", Output = (@"C:\SensorNetworks\Output\ConvDNN\" + datestamp).ToDirectoryInfo(), }; @@ -100,9 +100,9 @@ public static void Execute(Arguments arguments) } LoggedConsole.WriteLine("Generate ConvDNN images for single recording"); - LoggedConsole.WriteLine("# Input Audio file: " + arguments.Source.Name); - LoggedConsole.WriteLine("# Configuration file: " + arguments.Config.Name); - LoggedConsole.WriteLine("# Output directry: " + arguments.Output.Name); + LoggedConsole.WriteLine("# Input Audio file: " + arguments.Source); + LoggedConsole.WriteLine("# Configuration file: " + arguments.Config); + LoggedConsole.WriteLine("# Output directry: " + arguments.Output); // Verify target event information if (string.IsNullOrWhiteSpace(arguments.TargetEventBounds)) @@ -128,7 +128,7 @@ public static void Execute(Arguments arguments) } // Grab configuration - var configDict = GetConfigurationForConvCnn(arguments.Config); + var configDict = GetConfigurationForConvCnn(arguments.Config.ToFileInfo()); var result = AnalyseOneRecording(arguments.Source, configDict, localEventStart, localEventEnd, (int)minHz, (int)mazHz, arguments.Output); Log.InfoFormat("SpectrogramPath:" + result.SpectrogramFile); @@ -149,21 +149,21 @@ public static void Main(Arguments arguments) arguments = Dev(); } - if (!arguments.Output.Exists) + var output = arguments.Output; + if (!output.Exists) { - arguments.Output.Create(); + output.Create(); } Log.InfoFormat("# PRE-PROCESS SHORT AUDIO RECORDINGS FOR Convolutional DNN"); Log.InfoFormat("# DATE AND TIME: " + DateTime.Now); - Log.InfoFormat("# Input .csv file: " + arguments.Source.Name); - Log.InfoFormat("# Configure file: " + arguments.Config.Name); - Log.InfoFormat("# Output directry: " + arguments.Output.Name); + Log.InfoFormat("# Input .csv file: " + arguments.Source); + Log.InfoFormat("# Configure file: " + arguments.Config); + Log.InfoFormat("# Output directry: " + arguments.Output); // 1. set up the necessary files FileInfo csvFileInfo = arguments.Source; - FileInfo configFile = arguments.Config; - DirectoryInfo output = arguments.Output; + FileInfo configFile = arguments.Config.ToFileInfo(); // 2. get the config dictionary var configDict = GetConfigurationForConvCnn(configFile); diff --git a/src/AnalysisPrograms/Audio2Sonogram.cs b/src/AnalysisPrograms/Audio2Sonogram.cs index 0ac9d0e33..36a6b8c4e 100644 --- a/src/AnalysisPrograms/Audio2Sonogram.cs +++ b/src/AnalysisPrograms/Audio2Sonogram.cs @@ -56,11 +56,11 @@ public class Audio2Sonogram public class Arguments : SourceConfigOutputDirArguments { - [Option("The start offset to start analyzing from (in seconds)")] + [Option(Description = "The start offset to start analyzing from (in seconds)")] [InRange(min: 0)] public double? StartOffset { get; set; } - [Option("The end offset to stop analyzing (in seconds)")] + [Option(Description = "The end offset to stop analyzing (in seconds)")] [InRange(min: 0)] public double? EndOffset { get; set; } @@ -100,7 +100,7 @@ private static Arguments Dev() //Source = @"C:\SensorNetworks\WavFiles\ConvDNNData\Melaleuca_Middle_183_192469_20101123_013009_4.0__.wav".ToFileInfo(), //Source = @"C:\SensorNetworks\WavFiles\ConvDNNData\SE_399_188293_20101014_132950_4.0__.wav".ToFileInfo(), - Config = @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\Towsey.Sonogram.yml".ToFileInfo(), + Config = @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\Towsey.Sonogram.yml", //Config = @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\Mangalam.Sonogram.yml".ToFileInfo(), }; @@ -114,9 +114,14 @@ public static void Main(Arguments arguments) arguments = Dev(); } - if (!arguments.Output.Exists) + // 1. set up the necessary files + FileInfo sourceRecording = arguments.Source; + FileInfo configFile = arguments.Config.ToFileInfo(); + DirectoryInfo output = arguments.Output; + + if (!output.Exists) { - arguments.Output.Create(); + output.Create(); } if (arguments.StartOffset.HasValue ^ arguments.EndOffset.HasValue) @@ -139,17 +144,14 @@ public static void Main(Arguments arguments) string date = "# DATE AND TIME: " + DateTime.Now; LoggedConsole.WriteLine(title); LoggedConsole.WriteLine(date); - LoggedConsole.WriteLine("# Input audio file: " + arguments.Source.Name); + LoggedConsole.WriteLine("# Input audio file: " + sourceRecording.Name); + - // 1. set up the necessary files - FileInfo sourceRecording = arguments.Source; - FileInfo configFile = arguments.Config; - DirectoryInfo output = arguments.Output; // 2. get the config dictionary var configDict = GetConfigDictionary(configFile, false); - configDict[ConfigKeys.Recording.Key_RecordingCallName] = arguments.Source.FullName; - configDict[ConfigKeys.Recording.Key_RecordingFileName] = arguments.Source.Name; + configDict[ConfigKeys.Recording.Key_RecordingCallName] = sourceRecording.FullName; + configDict[ConfigKeys.Recording.Key_RecordingFileName] = sourceRecording.Name; // 3: GET TEMPORARY RECORDING int resampleRate = Convert.ToInt32(configDict[AnalysisKeys.ResampleRate]); diff --git a/src/AnalysisPrograms/AudioCutter.cs b/src/AnalysisPrograms/AudioCutter.cs index 0fc4a9c80..acbe32e3f 100644 --- a/src/AnalysisPrograms/AudioCutter.cs +++ b/src/AnalysisPrograms/AudioCutter.cs @@ -24,7 +24,7 @@ public class AudioCutter [Command( CommandName, Description = "Cuts audio into segments of desired length and format")] - public class Arguments :SubCommandBase + public class Arguments : SubCommandBase { //[ArgDescription("The directory containing audio files.")] //[Production.ArgExistingDirectory(createIfNotExists: false)] @@ -32,53 +32,69 @@ public class Arguments :SubCommandBase //[ArgPosition(1)] //public virtual DirectoryInfo Input { get; set; } - [Option("The audio file to segment.")] + [Argument( + 0, + Description = "The audio file to segment.")] [Required] [FileExists] - public FileInfo InputFile { get; set; } + [LegalFilePath] + public string InputFile { get; set; } - [Option("The directory to create segmented audio files.")] + [Argument( + 1, + Description = "The directory to create segmented audio files.")] [DirectoryExistsOrCreate(createIfNotExists: true)] [Required] - public DirectoryInfo OutputDir { get; set; } + [LegalFilePath] + public string OutputDir { get; set; } - [Option("The directory for temporary files.")] + [Option( + Description = "The directory for temporary files.", + ShortName = "t")] [DirectoryExistsOrCreate(createIfNotExists: true)] - public DirectoryInfo TemporaryFilesDir { get; set; } + [LegalFilePath] + public string TemporaryFilesDir { get; set; } //[ArgDescription("Whether to recurse into subdirectories.")] //[DefaultValue(false)] //public bool Recurse { get; set; } [Option( - "The offset to start creating segmented audio files (in seconds, defaults to start of original file).")] + Description = "The offset to start creating segmented audio files (in seconds, defaults to start of original file).")] [InRange(min: 0)] public double StartOffset { get; set; } = 0; - [Option("The offset to stop creating segmented audio files (in seconds, defaults to end of original file).")] + [Option(Description = "The offset to stop creating segmented audio files (in seconds, defaults to end of original file).")] [InRange(min: 0)] public double? EndOffset { get; set; } [Option( - "The minimum duration of a segmented audio file (in seconds, defaults to 10; must be within [0,3600]).")] + Description = "The minimum duration of a segmented audio file (in seconds, defaults to 10; must be within [0,3600]).", + ShortName = "")] [InRange(1, 3600)] public double SegmentDurationMinimum { get; set; } = 10; - [Option("The duration of a segmented audio file (in seconds, defaults to 60; must be within [0,3600]).")] + [Option( + Description = "The duration of a segmented audio file (in seconds, defaults to 60; must be within [0,3600]).", + ShortName = "d")] [InRange(1, 3600)] public double SegmentDuration { get; set; } = 60; - [Option("The duration of overlap between segmented audio files (in seconds, defaults to 0).")] + [Option( + Description = "The duration of overlap between segmented audio files (in seconds, defaults to 0).", + ShortName = "")] [InRange(0, 3600)] public double SegmentOverlap { get; set; } = 0; [Option( - "The file type (file extension) of segmented audio files (defaults to wav; some possible values are wav, mp3, ogg, webm).")] + Description = "The file type (file extension) of segmented audio files (defaults to wav; some possible values are wav, mp3, ogg, webm).", + ShortName = "")] [OneOfThese("wav", "mp3", "ogg", "webm")] public string SegmentFileExtension { get; set; } = "wav"; [Option( - "The sample rate for segmented audio files (in hertz, defaults to 22050; valid values are 17640, 22050, 44100).")] + Description = "The sample rate for segmented audio files (in hertz, defaults to 22050; valid values are 17640, 22050, 44100).", + ShortName = "r")] [InRange(8000, 96000)] public int SampleRate { get; set; } = 22050; @@ -93,8 +109,9 @@ public class Arguments :SubCommandBase [Option( CommandOptionType.SingleValue, - Description = "Whether to create segments in parallel or sequentially (defaults to true - parallel).")] - public bool RunParallel { get; set; } = true; + Description = "Whether to create segments in parallel or sequentially (defaults to true - parallel).", + ShortName = "p")] + public bool Parallel { get; set; } = true; public override Task Execute(CommandLineApplication app) { @@ -124,11 +141,11 @@ public static void Execute(Arguments arguments) AnalysisMinSegmentDuration = TimeSpan.FromSeconds(arguments.SegmentDurationMinimum), SegmentOverlapDuration = TimeSpan.FromSeconds(arguments.SegmentOverlap), AnalysisTargetSampleRate = arguments.SampleRate, - AnalysisTempDirectory = arguments.TemporaryFilesDir, + AnalysisTempDirectory = arguments.TemporaryFilesDir.ToDirectoryInfo(), }; // create segments from file - var fileSegment = new FileSegment(arguments.InputFile, TimeAlignment.None) + var fileSegment = new FileSegment(arguments.InputFile.ToFileInfo(), TimeAlignment.None) { SegmentStartOffset = TimeSpan.FromSeconds(arguments.StartOffset), }; @@ -143,10 +160,10 @@ public static void Execute(Arguments arguments) LoggedConsole.WriteLine( "Started segmenting at {0} {1}: {2}.", DateTime.Now, - arguments.RunParallel ? "in parallel" : "sequentially", + arguments.Parallel ? "in parallel" : "sequentially", arguments.InputFile); - if (arguments.RunParallel) + if (arguments.Parallel) { RunParallel(fileSegments, sourcePreparer, settings, arguments); } @@ -193,7 +210,7 @@ private static void CreateSegment( bool mixDownToMono) { var task = sourcePreparer.PrepareFile( - arguments.OutputDir, + arguments.OutputDir.ToDirectoryInfo(), fileSegment, settings.SegmentMediaType, settings.AnalysisTargetSampleRate, diff --git a/src/AnalysisPrograms/AudioFileCheck.cs b/src/AnalysisPrograms/AudioFileCheck.cs index 8686d539b..ad85d5dc4 100644 --- a/src/AnalysisPrograms/AudioFileCheck.cs +++ b/src/AnalysisPrograms/AudioFileCheck.cs @@ -27,20 +27,21 @@ public class AudioFileCheck ExtendedHelpText = "Note: Specify a directory to process or an input file containing file paths, but not both.")] public class Arguments : SubCommandBase { - [Option("Csv file to write audio file information to.")] + [Option(Description = "Csv file to write audio file information to.")] [LegalFilePath] - public FileInfo OutputFile { get; set; } + public string OutputFile { get; set; } - [Option("Directory containing audio files.")] + [Option(Description = "Directory containing audio files.")] [DirectoryExists] - public DirectoryInfo InputDirectory { get; set; } + public string InputDirectory { get; set; } - [Option("true to recurse into subdirectories (if processing directories).")] + [Option(Description = "true to recurse into subdirectories (if processing directories).")] public bool Recurse { get; set; } - [Option("A text file containing one path per line.")] + [Option(Description = "A text file containing one path per line.", ShortName = "")] [FileExists] - public FileInfo InputFile { get; set; } + [LegalFilePath] + public string InputFile { get; set; } public override Task Execute(CommandLineApplication app) { @@ -61,12 +62,12 @@ public static void Execute(Arguments args) if (args.InputDirectory != null) { var shouldRecurse = args.Recurse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; - files = args.InputDirectory.EnumerateFiles("*.*", shouldRecurse); + files = args.InputDirectory.ToDirectoryInfo().EnumerateFiles("*.*", shouldRecurse); } else { // skip the output file - files = File.ReadLines(args.InputFile.FullName) + files = File.ReadLines(args.InputFile) .Where(l => !string.IsNullOrWhiteSpace(l)) .Select(l => l.Trim(' ', '"')) .Distinct() @@ -89,7 +90,7 @@ public static void Execute(Arguments args) "SHA256 Hash", "Identifier") + "\""; - using (var fs = File.Open(args.OutputFile.FullName, FileMode.Create, FileAccess.Write, FileShare.Read)) + using (var fs = File.Open(args.OutputFile, FileMode.Create, FileAccess.Write, FileShare.Read)) using (var sw = new StreamWriter(fs)) { sw.WriteLine(headers); diff --git a/src/AnalysisPrograms/ConcatenateIndexFiles.cs b/src/AnalysisPrograms/ConcatenateIndexFiles.cs index 1434115dc..324f215d3 100644 --- a/src/AnalysisPrograms/ConcatenateIndexFiles.cs +++ b/src/AnalysisPrograms/ConcatenateIndexFiles.cs @@ -61,64 +61,88 @@ public static class ConcatenateIndexFiles Description = "Concatenates multiple consecutive index.csv files.")] public class Arguments : SubCommandBase { - [Option( - CommandOptionType.MultipleValue, + [Argument( + 0, Description = "One or more directories where the original csv files are located.")] public DirectoryInfo[] InputDataDirectories { get; set; } [Obsolete("Originally hack to get around powerargs limitation, can probably be removed soon")] [Option( - "One directory where the original csv files are located. This option exists as an alternative to input data directories")] + Description = "One directory where the original csv files are located. This option exists as an alternative to input data directories")] public DirectoryInfo InputDataDirectory { get; set; } - [Option("Directory where the output is to go.")] + [Option(Description = "Directory where the output is to go.")] [DirectoryExistsOrCreate(createIfNotExists: true)] + [LegalFilePath] public DirectoryInfo OutputDirectory { get; set; } - [Option("Used to get the required data.csv files, which are assumed to be in a matching dir or subdirectory. E.g. use name of audio file suffix e.g.: *.wav")] + [Option(Description = "Used to get the required data.csv files, which are assumed to be in a matching dir or subdirectory. E.g. use name of audio file suffix e.g.: *.wav")] public string DirectoryFilter { get; set; } - [Option("File stem name for output files.")] + [Option(Description = "File stem name for output files.")] public string FileStemName { get; set; } - [Option("DateTimeOffset (inclusive) at which concatenation begins. If null, then start with earliest available file. Can parse an ISO8601 date.")] + [Option( + CommandOptionType.SingleValue, + Description = "DateTimeOffset (inclusive) at which concatenation begins. If null, then start with earliest available file. Can parse an ISO8601 date.")] public DateTimeOffset? StartDate { get; set; } - [Option("DateTimeOffset (exclusive) at which concatenation ends. If null, then will be set = today's date or last available file. Can parse an ISO8601 date.")] + [Option( + CommandOptionType.SingleValue, + Description = "DateTimeOffset (exclusive) at which concatenation ends. If null, then will be set = today's date or last available file. Can parse an ISO8601 date.")] public DateTimeOffset? EndDate { get; set; } - [Option("TimeSpan offset hint required if file names do not contain time zone info. NO DEFAULT IS SET")] + [Option( + CommandOptionType.SingleValue, + Description = "TimeSpan offset hint required if file names do not contain time zone info. NO DEFAULT IS SET", + ShortName = "z")] public TimeSpan? TimeSpanOffsetHint { get; set; } [Option( CommandOptionType.SingleValue, - Description = "Draw false-colour spectrograms after concatenating index files")] + Description = "Draw false-colour spectrograms after concatenating index files", + ShortName = "")] public bool DrawImages { get; set; } = true; - [Option("The mapping of indices to colour channel in false-colour spectrogram 1")] + [Option( + Description = "The mapping of indices to colour channel in false-colour spectrogram 1", + ShortName = "")] public string ColorMap1 { get; set; } - [Option("The mapping of indices to colour channel in false-colour spectrogram 2")] + [Option( + Description = "The mapping of indices to colour channel in false-colour spectrogram 2", + ShortName = "")] public string ColorMap2 { get; set; } - [Option("User specified file containing a list of indices and their properties.")] + [Option( + Description = "User specified file containing a list of indices and their properties.", + ShortName = "ip")] [ExistingFile(Extension = ".yml")] - public FileInfo IndexPropertiesConfig { get; set; } + [LegalFilePath] + public string IndexPropertiesConfig { get; set; } - [Option("Config file for drawing the false colour spectrograms.")] + [Option( + Description = "Config file for drawing the false colour spectrograms.", + ShortName = "fcs")] [ExistingFile(Extension = ".yml")] - public FileInfo FalseColourSpectrogramConfig { get; set; } + [LegalFilePath] + public string FalseColourSpectrogramConfig { get; set; } - [Option("Set true only when concatenating more than 24-hours of data into one image")] + [Option( + Description = "Set true only when concatenating more than 24-hours of data into one image", + LongName = "concatenate-everything", + ShortName = "")] public bool ConcatenateEverythingYouCanLayYourHandsOn { get; set; } - [Option("How to render gaps in a recording. Valid options: `" + nameof(ConcatMode.TimedGaps) + "` (default), `" + nameof(ConcatMode.NoGaps) + "`, `"+ nameof(ConcatMode.EchoGaps) + "`")] + [Option(Description = "How to render gaps in a recording. Valid options: `" + nameof(ConcatMode.TimedGaps) + "` (default), `" + nameof(ConcatMode.NoGaps) + "`, `"+ nameof(ConcatMode.EchoGaps) + "`")] public ConcatMode GapRendering { get; set; } - [Option("One or more directories where the RECOGNIZER event scores are located in csv files. This is optional")] - public DirectoryInfo[] EventDataDirectories { get; set; } + [Option( + Description = "One or more directories where the RECOGNIZER event scores are located in csv files. This is optional", + ShortName = "")] + public string[] EventDataDirectories { get; set; } - [Option("Used only to get Event Recognizer files.")] + [Option(Description = "Used only to get Event Recognizer files.", ShortName = "")] public string EventFilePattern { get; set; } public override Task Execute(CommandLineApplication app) @@ -152,12 +176,14 @@ public static void Execute(Arguments arguments) LoggedConsole.WriteLine("# IT IS ASSUMED THESE WERE OBTAINED FROM MULTIPLE SHORT AUDIO RECORDINGs"); LoggedConsole.WriteLine(date); LoggedConsole.WriteLine("# Index.csv files are in directories:"); - foreach (DirectoryInfo dir in arguments.InputDataDirectories) + var inputDirs = arguments.InputDataDirectories; + foreach (var dir in inputDirs) { LoggedConsole.WriteLine(" {0}", dir.FullName); } - LoggedConsole.WriteLine("# Output directory: " + arguments.OutputDirectory.FullName); + var output = arguments.OutputDirectory; + LoggedConsole.WriteLine("# Output directory: " + output.FullName); LoggedConsole.WriteLine("# DIRECTORY FILTER = " + arguments.DirectoryFilter); LoggedConsole.WriteLine(); @@ -165,7 +191,7 @@ public static void Execute(Arguments arguments) // 1. PATTERN SEARCH FOR CORRECT SUBDIRECTORIES // Assumes that the required subdirectories have the given FILTER/SiteName somewhere in their path. - var subDirectories = LdSpectrogramStitching.GetSubDirectoriesForSiteData(arguments.InputDataDirectories, arguments.DirectoryFilter); + var subDirectories = LdSpectrogramStitching.GetSubDirectoriesForSiteData(inputDirs, arguments.DirectoryFilter); if (subDirectories.Length == 0) { LoggedConsole.WriteErrorLine("\n\n#WARNING from method ConcatenateIndexFiles.Execute():"); @@ -223,10 +249,10 @@ public static void Execute(Arguments arguments) LoggedConsole.WriteLine("# WARNING: A sunrise/sunset data file does not exist for time zone >> " + arguments.TimeSpanOffsetHint.ToString()); // create top level output directory if it does not exist. - DirectoryInfo opDir = arguments.OutputDirectory; + DirectoryInfo opDir = output; if (!opDir.Exists) { - arguments.OutputDirectory.Create(); + output.Create(); } string outputFileStem = arguments.FileStemName; @@ -256,13 +282,14 @@ public static void Execute(Arguments arguments) indexGenerationData.RecordingStartDate = startDate; } - indexPropertiesConfig = arguments.IndexPropertiesConfig; + indexPropertiesConfig = arguments.IndexPropertiesConfig.ToFileInfo(); // prepare the false-colour spgm config file // or set up a default config // WARNING: This default config is used when testing. If you alter these defaults, Unit Test results may be affected. - ldSpectrogramConfig = (arguments?.FalseColourSpectrogramConfig?.Exists ?? false) - ? LdSpectrogramConfig.ReadYamlToConfig(arguments.FalseColourSpectrogramConfig) + var colourSpectrogramConfig = arguments?.FalseColourSpectrogramConfig?.ToFileInfo(); + ldSpectrogramConfig = (colourSpectrogramConfig?.Exists ?? false) + ? LdSpectrogramConfig.ReadYamlToConfig(colourSpectrogramConfig) : new LdSpectrogramConfig(); // the user should have provided ColorMap arguments which we insert here @@ -478,23 +505,23 @@ public static void Execute(Arguments arguments) if (arguments.EventDataDirectories != null) { - var candidateFiles = IndexMatrices.GetFilesInDirectories(arguments.EventDataDirectories, arguments.EventFilePattern); + var candidateFiles = IndexMatrices.GetFilesInDirectories(arguments.EventDataDirectories.Select(FileInfoExtensions.ToDirectoryInfo).ToArray(), arguments.EventFilePattern); var sortedDictionaryOfEventFiles = FileDateHelpers.FilterFilesForDates(candidateFiles, arguments.TimeSpanOffsetHint); var eventFiles = LdSpectrogramStitching.GetFileArrayForOneDay(sortedDictionaryOfEventFiles, thisday); //int lineCount = 0; - var output = new List(); + var output2 = new List(); foreach (var file in eventFiles) { var lines = FileTools.ReadTextFile(file.FullName); lines.RemoveAt(0); // ignore header - output.AddRange(lines); + output2.AddRange(lines); //lineCount += lines.Count; //Console.WriteLine($" # events = {lines.Count}"); } - var indexArray = ConvertEventsToSummaryIndices(output); + var indexArray = ConvertEventsToSummaryIndices(output2); double[] normalisedScores; double normalisedThreshold; @@ -841,23 +868,16 @@ public static void TESTMETHOD_ConcatenateIndexFilesTest1() string drive = "G"; // top level directory - DirectoryInfo[] dataDirs = - { - new DirectoryInfo($"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\Indonesia_2\\"), - }; + var dataDirs = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\Indonesia_2\\"; - var outputDir = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Test3_Output".ToDirectoryInfo(); - if (!outputDir.Exists) - { - outputDir.Create(); - } + var outputDir = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Test3_Output"; - var falseColourSpgConfig = new FileInfo($"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatTest_SpectrogramFalseColourConfig.yml"); + var falseColourSpgConfig = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatTest_SpectrogramFalseColourConfig.yml"; var arguments = new Arguments { - InputDataDirectories = dataDirs, - OutputDirectory = outputDir, + InputDataDirectory = dataDirs.ToDirectoryInfo(), + OutputDirectory = outputDir.ToDirectoryInfo(), DirectoryFilter = "*.wav", FileStemName = "Indonesia2016", @@ -866,7 +886,7 @@ public static void TESTMETHOD_ConcatenateIndexFilesTest1() StartDate = new DateTimeOffset(2016, 07, 27, 0, 0, 0, TimeSpan.Zero), EndDate = new DateTimeOffset(2016, 07, 25, 0, 0, 0, TimeSpan.Zero), - IndexPropertiesConfig = new FileInfo($"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatTest_IndexPropertiesConfig.yml"), + IndexPropertiesConfig = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatTest_IndexPropertiesConfig.yml", FalseColourSpectrogramConfig = falseColourSpgConfig, ColorMap1 = SpectrogramConstants.RGBMap_ACI_ENT_EVN, ColorMap2 = "BGN-POW-SPT", // This color map dates pre-May 2017. @@ -897,26 +917,22 @@ public static void TESTMETHOD_ConcatenateIndexFilesTest2() string drive = "G"; // top level directory - DirectoryInfo[] dataDirs = { new DirectoryInfo($"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\Indonesia_2\\"), }; - var outputDir = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Test4_Output".ToDirectoryInfo(); - if (!outputDir.Exists) - { - outputDir.Create(); - } + var dataDirs = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\Indonesia_2\\"; + var outputDir = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Test4_Output"; // var falseColourSpgConfig = new FileInfo($"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatTest_SpectrogramFalseColourConfig.yml"); // if set null will use the default for testing. - FileInfo falseColourSpgConfig = null; + string falseColourSpgConfig = null; var arguments = new Arguments { - InputDataDirectories = dataDirs, - OutputDirectory = outputDir, + InputDataDirectory = dataDirs.ToDirectoryInfo(), + OutputDirectory = outputDir.ToDirectoryInfo(), DirectoryFilter = "*.wav", FileStemName = "Indonesia2016", StartDate = new DateTimeOffset(2016, 07, 25, 0, 0, 0, TimeSpan.Zero), EndDate = new DateTimeOffset(2016, 07, 25, 0, 0, 0, TimeSpan.Zero), - IndexPropertiesConfig = new FileInfo($"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatTest_IndexPropertiesConfig.yml"), + IndexPropertiesConfig = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatTest_IndexPropertiesConfig.yml", FalseColourSpectrogramConfig = falseColourSpgConfig, ColorMap1 = SpectrogramConstants.RGBMap_ACI_ENT_EVN, ColorMap2 = "BGN-POW-SPT", // This color map dates pre-May 2017. @@ -949,19 +965,19 @@ public static void TESTMETHOD_ConcatenateIndexFilesTest3() string drive = "G"; // top level directory - DirectoryInfo[] dataDirs = { new DirectoryInfo($"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\Indonesia_2\\"), }; - var outputDir = @"C:\SensorNetworks\SoftwareTests\TestConcatenation\Test5_Output".ToDirectoryInfo(); - var falseColourSpgConfig = new FileInfo($"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatTest_SpectrogramFalseColourConfig.yml"); + var dataDirs = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\Indonesia_2\\"; + var outputDir = @"C:\SensorNetworks\SoftwareTests\TestConcatenation\Test5_Output"; + var falseColourSpgConfig = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatTest_SpectrogramFalseColourConfig.yml"; var arguments = new Arguments { - InputDataDirectories = dataDirs, - OutputDirectory = outputDir, + InputDataDirectory = dataDirs.ToDirectoryInfo(), + OutputDirectory = outputDir.ToDirectoryInfo(), DirectoryFilter = "*.wav", FileStemName = "Indonesia2016", StartDate = null, EndDate = null, - IndexPropertiesConfig = new FileInfo($"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatTest_IndexPropertiesConfig.yml"), + IndexPropertiesConfig = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatTest_IndexPropertiesConfig.yml", FalseColourSpectrogramConfig = falseColourSpgConfig, ColorMap1 = SpectrogramConstants.RGBMap_ACI_ENT_EVN, ColorMap2 = "BGN-POW-SPT", // This color map dates pre-May 2017. @@ -995,21 +1011,21 @@ public static void TESTMETHOD_ConcatenateIndexFilesTest4() ZipFile.ExtractToDirectory(zipFile.FullName, dataDir.FullName); // top level directory - DirectoryInfo[] dataDirs = + var dataDirs = new[] { - new DirectoryInfo(dataDir.FullName + "\\Indonesia_2Reduced"), + dataDir.FullName + "\\Indonesia_2Reduced", //new DirectoryInfo($"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\Indonesia_2Reduced"), //new DirectoryInfo($"{drive}:\\Work\\GitHub\\audio-analysis\\Acoustics\\Acoustics.Test\\TestResources\\Concatenation\\Indonesia20160726"), }; - var outputDir = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Test7_Output".ToDirectoryInfo(); - var falseColourSpgConfig = new FileInfo($"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatTest_SpectrogramFalseColourConfig.yml"); + var outputDir = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Test7_Output"; + var falseColourSpgConfig = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatTest_SpectrogramFalseColourConfig.yml"; var arguments = new Arguments { - InputDataDirectories = dataDirs, - OutputDirectory = outputDir, + InputDataDirectories = dataDirs.Select(FileInfoExtensions.ToDirectoryInfo).ToArray(), + OutputDirectory = outputDir.ToDirectoryInfo(), DirectoryFilter = "*.wav", FileStemName = "Indonesia2016", @@ -1018,7 +1034,7 @@ public static void TESTMETHOD_ConcatenateIndexFilesTest4() StartDate = new DateTimeOffset(2016, 07, 27, 0, 0, 0, TimeSpan.Zero), EndDate = new DateTimeOffset(2016, 07, 22, 0, 0, 0, TimeSpan.Zero), - IndexPropertiesConfig = new FileInfo($"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatTest_IndexPropertiesConfig.yml"), + IndexPropertiesConfig = $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatTest_IndexPropertiesConfig.yml", FalseColourSpectrogramConfig = falseColourSpgConfig, ColorMap1 = SpectrogramConstants.RGBMap_ACI_ENT_EVN, ColorMap2 = "BGN-POW-SPT", // This color map dates pre-May 2017. diff --git a/src/AnalysisPrograms/Create4Sonograms.cs b/src/AnalysisPrograms/Create4Sonograms.cs index 6e9a23a4e..6e0583fa7 100644 --- a/src/AnalysisPrograms/Create4Sonograms.cs +++ b/src/AnalysisPrograms/Create4Sonograms.cs @@ -37,10 +37,11 @@ public static class Create4Sonograms Description = "Creates a set of four standard spectrograms derived using different algorithms. For short recordings only.")] public class Arguments : SourceAndConfigArguments { - [Option("A file path to write output to")] + [Option(Description = "A file path to write output to")] [NotExistingFile] [Required] - public FileInfo Output { get; set; } + [LegalFilePath] + public string Output { get; set; } public override Task Execute(CommandLineApplication app) { @@ -57,8 +58,8 @@ private static Arguments Dev() //Source = @"C:\SensorNetworks\WavFiles\LewinsRail\BAC1_20071008-081607.wav".ToFileInfo(), //Output = @"C:\SensorNetworks\Output\Sonograms\BAC1_20071008-081607.png".ToFileInfo(), Source = @"C:\SensorNetworks\WavFiles\TestRecordings\BAC\BAC2_20071008-085040.wav".ToFileInfo(), - Output = @"C:\SensorNetworks\Output\Sonograms\BAC2Sonograms\BAC2_20071008-085040.png".ToFileInfo(), - Config = @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\Towsey.Sonogram.yml".ToFileInfo(), + Output = @"C:\SensorNetworks\Output\Sonograms\BAC2Sonograms\BAC2_20071008-085040.png", + Config = @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\Towsey.Sonogram.yml", }; //throw new NoDeveloperMethodException(); @@ -71,20 +72,22 @@ public static void Main(Arguments arguments) arguments = Dev(); } - arguments.Output.CreateParentDirectories(); + //1. set up the necessary files + //DirectoryInfo diSource = arguments.Source.Directory; + FileInfo fiSourceRecording = arguments.Source; + FileInfo fiConfig = arguments.Config.ToFileInfo(); + FileInfo fiImage = arguments.Output.ToFileInfo(); + + fiImage.CreateParentDirectories(); string title = "# CREATE FOUR (4) SONOGRAMS FROM AUDIO RECORDING"; string date = "# DATE AND TIME: " + DateTime.Now; LoggedConsole.WriteLine(title); LoggedConsole.WriteLine(date); - LoggedConsole.WriteLine("# Input audio file: " + arguments.Source.Name); - LoggedConsole.WriteLine("# Output image file: " + arguments.Output); + LoggedConsole.WriteLine("# Input audio file: " + fiSourceRecording.Name); + LoggedConsole.WriteLine("# Output image file: " + fiImage); + - //1. set up the necessary files - //DirectoryInfo diSource = arguments.Source.Directory; - FileInfo fiSourceRecording = arguments.Source; - FileInfo fiConfig = arguments.Config; - FileInfo fiImage = arguments.Output; //2. get the config dictionary Config configuration = ConfigFile.Deserialize(fiConfig); @@ -123,7 +126,7 @@ public static void Main(Arguments arguments) if (!((startOffsetMins == TimeSpan.Zero) && (endOffsetMins == TimeSpan.Zero))) { var buffer = new TimeSpan(0, 0, 0); - fiOutputSegment = new FileInfo(Path.Combine(arguments.Output.DirectoryName, "tempWavFile.wav")); + fiOutputSegment = new FileInfo(Path.Combine(fiImage.DirectoryName, "tempWavFile.wav")); //This method extracts segment and saves to disk at the location fiOutputSegment. var resampleRate = configuration.GetIntOrNull(AnalysisKeys.ResampleRate) ?? AppConfigHelper.DefaultTargetSampleRate; diff --git a/src/AnalysisPrograms/DifferenceSpectrogram.cs b/src/AnalysisPrograms/DifferenceSpectrogram.cs index 8f3bd8778..c6959639d 100644 --- a/src/AnalysisPrograms/DifferenceSpectrogram.cs +++ b/src/AnalysisPrograms/DifferenceSpectrogram.cs @@ -27,45 +27,46 @@ public static class DifferenceSpectrogram Description = "[INOPERABLE] Produces a false-colour spectrogram that show only the differences between two spectrograms.")] public class Arguments : SubCommandBase { - [Option("Path to configuration file in YAML format")] + [Option(Description = "Path to configuration file in YAML format")] [ExistingFile] [Required] - public FileInfo Config { get; set; } + [LegalFilePath] + public string Config { get; set; } //[ArgDescription("The directory containing the input files.")] //[Production.ArgExistingDirectory] //[ArgPosition(1)] //[ArgRequired] - //public DirectoryInfo InputDirectory { get; set; } + //public string InputDirectory { get; set; } //[ArgDescription("The directory to place output files.")] //[ArgPosition(2)] //[ArgRequired] - //public DirectoryInfo OutputDirectory { get; set; } + //public string OutputDirectory { get; set; } //[ArgDescription("The first input csv file containing acosutic index data 1.")] //[Production.ArgExistingFile] //[ArgPosition(3)] //[ArgRequired] - //public FileInfo IndexFile1 { get; set; } + //public string IndexFile1 { get; set; } //[ArgDescription("The third input csv file containing standard deviations of index data 1.")] //[Production.ArgExistingFile] //[ArgPosition(4)] //[ArgRequired] - //public FileInfo StdDevFile1 { get; set; } + //public string StdDevFile1 { get; set; } //[ArgDescription("The fourth input csv file containing acosutic index data 2.")] //[Production.ArgExistingFile] //[ArgPosition(5)] //[ArgRequired] - //public FileInfo IndexFile2 { get; set; } + //public string IndexFile2 { get; set; } //[ArgDescription("The fifth input csv file containing standard deviations of index data 2.")] //[Production.ArgExistingFile] //[ArgPosition(6)] //[ArgRequired] - //public FileInfo StdDevFile2 { get; set; } + //public string StdDevFile2 { get; set; } public override Task Execute(CommandLineApplication app) { @@ -123,7 +124,7 @@ public static Arguments Dev() //SET UP THE ARGUMENTS CLASS containing path to the YAML config file var arguments = new Arguments { - Config = configPath.ToFileInfo(), + Config = configPath, }; return arguments; } diff --git a/src/AnalysisPrograms/Draw/Zooming/DrawZoomingSpectrograms.Arguments.cs b/src/AnalysisPrograms/Draw/Zooming/DrawZoomingSpectrograms.Arguments.cs index 7ee555831..dc744a137 100644 --- a/src/AnalysisPrograms/Draw/Zooming/DrawZoomingSpectrograms.Arguments.cs +++ b/src/AnalysisPrograms/Draw/Zooming/DrawZoomingSpectrograms.Arguments.cs @@ -29,30 +29,36 @@ public enum ZoomActionType Tile, } - [Option("When doing a `Focused` stack, which minute to center on. Accepts partial minutes.")] - [InRange(min: 0)] - public double? FocusMinute { get; set; } + [Argument( + 0, + Description = "The source directory of files output from Towsey.Acoustic (the Index analysis) to operate on")] + [Required] + [DirectoryExists] + public string SourceDirectory { get; set; } - [Option(FileSystemProvider.DestinationPath)] + [Argument( + 1, + Description = "User specified file defining valid spectrogram scales. Also should contain a reference to IndexProperties.yml and optionally a LDSpectrogramConfig object")] + [ExistingFile(Extension = ".yml")] + [Required] + public string SpectrogramZoomingConfig { get; set; } + + [Argument( + 2, + Description = FileSystemProvider.DestinationPath)] [Required] [LegalFilePath] public string Output { get; set; } - [Option(FileSystemProvider.DestinationFormat)] + [Option(Description = "When doing a `Focused` stack, which minute to center on. Accepts partial minutes.")] + [InRange(min: 0)] + public double? FocusMinute { get; set; } + + [Option(Description = FileSystemProvider.DestinationFormat)] [OneOfThese("", FileSystemProvider.SqlitePattern)] public string OutputFormat { get; set; } = string.Empty; - [Option("The source directory of files output from Towsey.Acoustic (the Index analysis) to operate on")] - [Required] - [DirectoryExists] - public string SourceDirectory { get; set; } - - [Option("User specified file defining valid spectrogram scales. Also should contain a reference to IndexProperties.yml and optionally a LDSpectrogramConfig object")] - [ExistingFile(Extension = ".yml")] - [Required] - public FileInfo SpectrogramZoomingConfig { get; set; } - - [Option("Choose which action to execute (Focused, or Tile)")] + [Option(Description = "Choose which action to execute (Focused, or Tile)")] [Required] public ZoomActionType ZoomAction { get; set; } diff --git a/src/AnalysisPrograms/DrawEasyImage.cs b/src/AnalysisPrograms/DrawEasyImage.cs index 445b1a0a2..c719160ea 100644 --- a/src/AnalysisPrograms/DrawEasyImage.cs +++ b/src/AnalysisPrograms/DrawEasyImage.cs @@ -13,6 +13,7 @@ namespace AnalysisPrograms using System.Collections.Generic; using System.Drawing; using System.IO; + using System.Linq; using System.Threading.Tasks; using AudioAnalysisTools; using AudioAnalysisTools.Indices; @@ -37,32 +38,45 @@ public class Arguments : SubCommandBase [Option( CommandOptionType.MultipleValue, Description = "One or more directories where the original csv files are located.")] - public DirectoryInfo[] InputDataDirectories { get; set; } + public string[] InputDataDirectories { get; set; } - [Option("Directory where the output is to go.")] - public DirectoryInfo OutputDirectory { get; set; } + [Option(Description = "Directory where the output is to go.")] + [LegalFilePath] + public string OutputDirectory { get; set; } - [Option("Filter string used to search for the required csv files - assumed to be in directory path.")] + [Option( + Description = "Filter string used to search for the required csv files - assumed to be in directory path.", + ShortName = "")] public string FileFilter { get; set; } - [Option("File stem name for output files.")] + [Option( + Description = "File stem name for output files.", + ShortName = "")] public string FileStemName { get; set; } - [Option("The start DateTime.")] + [Option( + CommandOptionType.SingleValue, + Description = "The start DateTime.")] public DateTimeOffset? StartDate { get; set; } - [Option("The end DateTime at which concatenation ends. If missing|null, then will be set = today's date or last available file.")] + [Option( + CommandOptionType.SingleValue, + Description = "The end DateTime at which concatenation ends. If missing|null, then will be set = today's date or last available file.")] public DateTimeOffset? EndDate { get; set; } - [Option("TimeSpan offset hint required if file names do not contain time zone info. Set default to east coast Australia")] + [Option( + CommandOptionType.SingleValue, + Description = "TimeSpan offset hint required if file names do not contain time zone info. Set default to east coast Australia", + ShortName = "z")] public TimeSpan? TimeSpanOffsetHint { get; set; } = new TimeSpan(10, 0, 0); //[ArgDescription("User specified file containing a list of indices and their properties.")] //[Production.ArgExistingFile(Extension = ".yml")] //[ArgPosition(1)] - internal FileInfo IndexPropertiesConfig { get; set; } + internal string IndexPropertiesConfig { get; set; } + + public string BrisbaneSunriseDatafile { get; set; } - public FileInfo BrisbaneSunriseDatafile { get; set; } public override Task Execute(CommandLineApplication app) { DrawEasyImage.Execute(this); @@ -104,14 +118,14 @@ public static Arguments Dev() return new Arguments { - InputDataDirectories = dataDirs, - OutputDirectory = new DirectoryInfo(opPath), - FileFilter = fileFilter, - FileStemName = opFileStem, - StartDate = dtoStart, - EndDate = dtoEnd, - IndexPropertiesConfig = indexPropertiesConfig, - BrisbaneSunriseDatafile = sunrisesetData, +// InputDataDirectories = dataDirs, +// OutputDirectory = new DirectoryInfo(opPath), +// FileFilter = fileFilter, +// FileStemName = opFileStem, +// StartDate = dtoStart, +// EndDate = dtoEnd, +// IndexPropertiesConfig = indexPropertiesConfig, +// BrisbaneSunriseDatafile = sunrisesetData, }; throw new NoDeveloperMethodException(); } @@ -126,6 +140,8 @@ public static void Execute(Arguments arguments) verbose = true; // assume verbose if in dev mode } + var inputDirs = arguments.InputDataDirectories.Select(FileInfoExtensions.ToDirectoryInfo); + var output = arguments.OutputDirectory.ToDirectoryInfo(); if (verbose) { string date = "# DATE AND TIME: " + DateTime.Now; @@ -133,11 +149,11 @@ public static void Execute(Arguments arguments) LoggedConsole.WriteLine("# IT IS ASSUMED THAT THE CSV files are already concatenated into 24 hour files."); LoggedConsole.WriteLine(date); LoggedConsole.WriteLine("# Summary Index.csv files are in directories:"); - foreach (DirectoryInfo dir in arguments.InputDataDirectories) + foreach (DirectoryInfo dir in inputDirs) { LoggedConsole.WriteLine(" {0}", dir.FullName); } - LoggedConsole.WriteLine("# Output directory: " + arguments.OutputDirectory.FullName); + LoggedConsole.WriteLine("# Output directory: " + output); if (arguments.StartDate == null) { LoggedConsole.WriteLine("# Start date = NULL (No argument provided). Will revise start date ...."); @@ -162,7 +178,7 @@ public static void Execute(Arguments arguments) // PATTERN SEARCH FOR SUMMARY INDEX FILES. //string pattern = "*__Towsey.Acoustic.Indices.csv"; - FileInfo[] csvFiles = IndexMatrices.GetFilesInDirectories(arguments.InputDataDirectories, arguments.FileFilter); + FileInfo[] csvFiles = IndexMatrices.GetFilesInDirectories(inputDirs.ToArray(), arguments.FileFilter); if (verbose) { //LoggedConsole.WriteLine("# Subdirectories Count = " + subDirectories.Length); @@ -197,10 +213,10 @@ public static void Execute(Arguments arguments) } // create top level output directory if it does not exist. - DirectoryInfo opDir = arguments.OutputDirectory; + DirectoryInfo opDir = output; if (!opDir.Exists) { - arguments.OutputDirectory.Create(); + opDir.Create(); } // SET UP DEFAULT SITE LOCATION INFO -- DISCUSS IWTH ANTHONY @@ -217,7 +233,7 @@ public static void Execute(Arguments arguments) // require IndexGenerationData and indexPropertiesConfig for drawing //indexGenerationData = IndexGenerationData.GetIndexGenerationData(csvFiles[0].Directory); - indexPropertiesConfig = arguments.IndexPropertiesConfig; + indexPropertiesConfig = arguments.IndexPropertiesConfig.ToFileInfo(); Dictionary listOfIndexProperties = IndexProperties.GetIndexProperties(indexPropertiesConfig); Tuple, List> tuple = CsvTools.ReadCSVFile(csvFiles[0].FullName); var names = tuple.Item1; @@ -387,7 +403,7 @@ public static void Execute(Arguments arguments) // draw on civil dawn and dusk lines int startdayOfYear = ((DateTimeOffset)startDate).DayOfYear; int endDayOfYear = ((DateTimeOffset)endDate).DayOfYear; - SunAndMoon.AddSunRiseSetLinesToImage(bitmap, arguments.BrisbaneSunriseDatafile, startdayOfYear, endDayOfYear, dayPixelHeight); + SunAndMoon.AddSunRiseSetLinesToImage(bitmap, arguments.BrisbaneSunriseDatafile.ToFileInfo(), startdayOfYear, endDayOfYear, dayPixelHeight); // add the time scales Bitmap timeBmp1 = ImageTrack.DrawTimeRelativeTrack(oneDay, graphWidth, trackHeight); diff --git a/src/AnalysisPrograms/DrawLongDurationSpectrograms.cs b/src/AnalysisPrograms/DrawLongDurationSpectrograms.cs index f2ab2f971..f4cc832f0 100644 --- a/src/AnalysisPrograms/DrawLongDurationSpectrograms.cs +++ b/src/AnalysisPrograms/DrawLongDurationSpectrograms.cs @@ -71,21 +71,27 @@ public static class DrawLongDurationSpectrograms Description = "Produces long - duration false - colour spectrograms from matrices of spectral indices.")] public class Arguments : SubCommandBase { - [Option("Directory where the input data is located.")] + [Option(Description = "Directory where the input data is located.")] [DirectoryExists] - public DirectoryInfo InputDataDirectory { get; set; } + [LegalFilePath] + public string InputDataDirectory { get; set; } - [Option("Directory where the output is to go.")] + [Option(Description = "Directory where the output is to go.")] [DirectoryExistsOrCreate(createIfNotExists: true)] - public DirectoryInfo OutputDirectory { get; set; } + [LegalFilePath] + public string OutputDirectory { get; set; } - [Option("User specified file containing a list of indices and their properties.")] + [Option( + Description = "User specified file containing a list of indices and their properties.", + ShortName = "ip")] [ExistingFile(Extension = ".yml")] - public FileInfo IndexPropertiesConfig { get; set; } + [LegalFilePath] + public string IndexPropertiesConfig { get; set; } - [Option("Config file specifying directory containing indices.csv files and other parameters.")] + [Option(Description = "Config file specifying directory containing indices.csv files and other parameters.")] [ExistingFile(Extension = ".yml")] - public FileInfo SpectrogramConfigPath { get; set; } + [LegalFilePath] + public string SpectrogramConfigPath { get; set; } public string ColourMap1 { get; set; } @@ -135,7 +141,7 @@ public static void Execute(Arguments arguments) else { //config = Yaml.Deserialise(arguments.SpectrogramConfigPath).LdSpectrogramConfig; - config = LdSpectrogramConfig.ReadYamlToConfig(arguments.SpectrogramConfigPath); + config = LdSpectrogramConfig.ReadYamlToConfig(arguments.SpectrogramConfigPath.ToFileInfo()); } string originalBaseName; @@ -144,7 +150,8 @@ public static void Execute(Arguments arguments) FilenameHelpers.ParseAnalysisFileName(indexGenerationDataFile, out originalBaseName, out analysisTag, out otherSegments); // CHECK FOR ERROR SEGMENTS - get zero signal array - var csvFile = new FileInfo(Path.Combine(arguments.InputDataDirectory.FullName, originalBaseName + "__Towsey.Acoustic.Indices.csv")); + var input = arguments.InputDataDirectory.ToDirectoryInfo(); + var csvFile = new FileInfo(Path.Combine(input.FullName, originalBaseName + "__Towsey.Acoustic.Indices.csv")); //Dictionary summaryIndices = CsvTools.ReadCSVFile2Dictionary(csvFile.FullName); //var summaryIndices = Csv.ReadFromCsv>(csvFile); var summaryIndices = Csv.ReadFromCsv(csvFile); @@ -156,10 +163,10 @@ public static void Execute(Arguments arguments) //config.IndexCalculationDuration = TimeSpan.FromSeconds(60.0); //config.XAxisTicInterval = TimeSpan.FromSeconds(3600.0); LDSpectrogramRGB.DrawSpectrogramsFromSpectralIndices( - inputDirectory: arguments.InputDataDirectory, - outputDirectory: arguments.OutputDirectory, + inputDirectory: input, + outputDirectory: arguments.OutputDirectory.ToDirectoryInfo(), ldSpectrogramConfig: config, - indexPropertiesConfigPath: arguments.IndexPropertiesConfig, + indexPropertiesConfigPath: arguments.IndexPropertiesConfig.ToFileInfo(), indexGenerationData: indexGenerationData, basename: originalBaseName, analysisType: Acoustic.TowseyAcoustic, @@ -179,14 +186,14 @@ public static int DrawAggregatedSpectrograms(Arguments arguments, string fileSte int frameCount = spectra[keys[0]].GetLength(1); double spectrogramScale = 0.1; TimeSpan timeScale = TimeSpan.FromSeconds(spectrogramScale); - DirectoryInfo outputDirectory = arguments.OutputDirectory; + DirectoryInfo outputDirectory = arguments.OutputDirectory.ToDirectoryInfo(); Image combinedImage = DrawGrayScaleSpectrograms(arguments, fileStem, timeScale, spectra); string fileName = Path.Combine(outputDirectory.FullName, fileStem + ".CombinedGreyScale.png"); combinedImage.Save(fileName); // Draw False-colour Spectrograms - combinedImage = DrawFalseColourSpectrograms(fileStem, timeScale, arguments.IndexPropertiesConfig, spectra); + combinedImage = DrawFalseColourSpectrograms(fileStem, timeScale, arguments.IndexPropertiesConfig.ToFileInfo(), spectra); fileName = Path.Combine(outputDirectory.FullName, fileStem + ".TwoMaps.png"); combinedImage.Save(fileName); return frameCount; @@ -205,12 +212,12 @@ public static Image DrawGrayScaleSpectrograms(Arguments arguments, string fileSt //LoggedConsole.WriteLine("# Spectrogram Config file: " + arguments.SpectrogramConfigPath); //LoggedConsole.WriteLine("# Index Properties Config file: " + arguments.IndexPropertiesConfig); var inputDirectory = arguments.InputDataDirectory; - Dictionary indexProperties = IndexProperties.GetIndexProperties(arguments.IndexPropertiesConfig); + Dictionary indexProperties = IndexProperties.GetIndexProperties(arguments.IndexPropertiesConfig.ToFileInfo()); if (spectra == null) { //C:\SensorNetworks\Output\BIRD50\Training\ID0001\Towsey.Acoustic\ID0001__Towsey.Acoustic.ACI - spectra = IndexMatrices.ReadSpectralIndices(inputDirectory, fileStem, analysisType, keys); + spectra = IndexMatrices.ReadSpectralIndices(inputDirectory.ToDirectoryInfo(), fileStem, analysisType, keys); } // note: the spectra are oriented as per visual orientation, i.e. xAxis = time frames @@ -258,7 +265,7 @@ public static Image DrawGrayScaleSpectrograms(Arguments arguments, string fileSt public static Image DrawFalseColourSpectrograms(Arguments args, string fileStem, Dictionary spectra = null) { //DirectoryInfo inputDirectory = args.InputDataDirectory; - FileInfo indexPropertiesConfig = args.IndexPropertiesConfig; + FileInfo indexPropertiesConfig = args.IndexPropertiesConfig.ToFileInfo(); Dictionary indexProperties = IndexProperties.GetIndexProperties(indexPropertiesConfig); return DrawFalseColourSpectrograms(args, fileStem, indexProperties, spectra); } @@ -276,7 +283,7 @@ public static Image DrawFalseColourSpectrograms(string fileStem, TimeSpan dataSc // args.InputDataDirectory = new DirectoryInfo(Path.Combine(outputDirectory.FullName, recording.BaseName + ".csv")), // args.OutputDirectory = new DirectoryInfo(outputDirectory.FullName + @"/SpectrogramImages"); args.SpectrogramConfigPath = null; - args.IndexPropertiesConfig = indexPropertiesConfig; + args.IndexPropertiesConfig = indexPropertiesConfig.FullName; args.ColourMap1 = LDSpectrogramRGB.DefaultColorMap1; args.ColourMap2 = LDSpectrogramRGB.DefaultColorMap2; args.TemporalScale = dataScale; @@ -336,10 +343,10 @@ public static int DrawRidgeSpectrograms(Arguments arguments, string fileStem, Di // var dataScale = TimeSpan.FromSeconds(spectrogramScale); // draw the spectrogram images - var labelledImage = DrawRidgeSpectrograms(inputDirectory, indexPropertiesConfig, fileStem, spectrogramScale, spectra = null); + var labelledImage = DrawRidgeSpectrograms(inputDirectory.ToDirectoryInfo(), indexPropertiesConfig.ToFileInfo(), fileStem, spectrogramScale, spectra = null); // combine and save - string fileName = Path.Combine(outputDirectory.FullName, fileStem + ".Ridges.png"); + string fileName = Path.Combine(outputDirectory.ToDirectoryInfo().FullName, fileStem + ".Ridges.png"); labelledImage.Save(fileName); return (int)Math.Round(labelledImage.Width * spectrogramScale); } // method DrawRidgeSpectrograms() diff --git a/src/AnalysisPrograms/DrawSummaryIndexTracks.cs b/src/AnalysisPrograms/DrawSummaryIndexTracks.cs index 2252e90d3..1bcf05c19 100644 --- a/src/AnalysisPrograms/DrawSummaryIndexTracks.cs +++ b/src/AnalysisPrograms/DrawSummaryIndexTracks.cs @@ -41,18 +41,23 @@ public class DrawSummaryIndexTracks public class Arguments : SubCommandBase { - [Option("The csv file containing rows of summary indices, one row per time segment - typical one minute segments.")] + [Option(Description = "The csv file containing rows of summary indices, one row per time segment - typical one minute segments.")] [ExistingFile(Extension = ".csv")] - public FileInfo InputCsv { get; set; } + [LegalFilePath] + public string InputCsv { get; set; } - [Option("Config file containing properties of summary indices.")] + [Option( + Description = "Config file containing properties of summary indices.", + ShortName = "ip")] [ExistingFile] - public FileInfo IndexPropertiesConfig { get; set; } + [LegalFilePath] + public string IndexPropertiesConfig { get; set; } - [Option("A file path to write output image")] + [Option(Description = "A file path to write output image")] [NotExistingFile(Extension = ".png")] [Required] - public FileInfo Output { get; set; } + [LegalFilePath] + public string Output { get; set; } public override Task Execute(CommandLineApplication app) { @@ -82,7 +87,7 @@ private static Arguments Dev() return new Arguments { - IndexPropertiesConfig = @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\IndexPropertiesConfig.yml".ToFileInfo(), + //IndexPropertiesConfig = @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\IndexPropertiesConfig.yml".ToFileInfo(), //ImageConfig = @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\Towsey.Acoustic.cfg".ToFileInfo(), //2010 Oct 13th @@ -102,8 +107,8 @@ private static Arguments Dev() //Output = @"C:\SensorNetworks\Output\Test\Test_30April2014\SERF_SE_2010Oct16_SummaryIndices.png".ToFileInfo() //2010 Oct 17th - InputCsv = @"C:\SensorNetworks\Output\SERF\2014Apr24-020709 - Indices, OCT 2010, SERF\SERF\TaggedRecordings\SE\0f2720f2-0caa-460a-8410-df24b9318814_101017-0000.mp3\Towsey.Acoustic\0f2720f2-0caa-460a-8410-df24b9318814_101017-0000_Towsey.Acoustic.Indices.csv".ToFileInfo(), - Output = @"C:\SensorNetworks\Output\Test\Test_30April2014\SERF_SE_2010Oct17_SummaryIndices.png".ToFileInfo(), + //InputCsv = @"C:\SensorNetworks\Output\SERF\2014Apr24-020709 - Indices, OCT 2010, SERF\SERF\TaggedRecordings\SE\0f2720f2-0caa-460a-8410-df24b9318814_101017-0000.mp3\Towsey.Acoustic\0f2720f2-0caa-460a-8410-df24b9318814_101017-0000_Towsey.Acoustic.Indices.csv".ToFileInfo(), + //Output = @"C:\SensorNetworks\Output\Test\Test_30April2014\SERF_SE_2010Oct17_SummaryIndices.png".ToFileInfo(), }; throw new NoDeveloperMethodException(); @@ -133,21 +138,23 @@ public static void Main(Arguments arguments) } - arguments.Output.CreateParentDirectories(); + var input = arguments.InputCsv.ToFileInfo(); + var output = arguments.Output.ToFileInfo(); + output.CreateParentDirectories(); // Find required index generation data - var igd = IndexGenerationData.GetIndexGenerationData(arguments.InputCsv.Directory.ToDirectoryEntry()); + var igd = IndexGenerationData.GetIndexGenerationData(input.Directory.ToDirectoryEntry()); // Convert summary indices to image - string fileName = Path.GetFileNameWithoutExtension(arguments.InputCsv.Name); + string fileName = input.BaseName(); string title = $"SOURCE:{fileName}, {Meta.OrganizationTag}; "; Bitmap tracksImage = IndexDisplay.DrawImageOfSummaryIndexTracks( - arguments.InputCsv, - arguments.IndexPropertiesConfig, + input, + arguments.IndexPropertiesConfig.ToFileInfo(), title, igd.IndexCalculationDuration, igd.RecordingStartDate); - tracksImage.Save(arguments.Output.FullName); + tracksImage.Save(output.FullName); } } diff --git a/src/AnalysisPrograms/DummyAnalysis.cs b/src/AnalysisPrograms/DummyAnalysis.cs index e4181c7ba..83d6ecc37 100644 --- a/src/AnalysisPrograms/DummyAnalysis.cs +++ b/src/AnalysisPrograms/DummyAnalysis.cs @@ -37,18 +37,18 @@ public class DummyAnalysis Description = "An program desinged to simulate load - does nothing other than burn CPU;")] public class Arguments : SubCommandBase { - [Option("Burn load on multiple CPU threads?")] + [Option(Description = "Burn load on multiple CPU threads?")] public bool Parallel { get; set; } - [Option("How many seconds to run for (roughly)")] + [Option(Description = "How many seconds to run for (roughly)")] [InRange(0, 3600)] public double DurationSeconds { get; set; } = 30; - [Option("How much jitter should be applied to execution time of each thread. A random amount is chosen where 0 <= `Jitter` <= 1.")] + [Option(Description = "How much jitter should be applied to execution time of each thread. A random amount is chosen where 0 <= `Jitter` <= 1.")] [InRange(0.0, 1.0)] public double Jitter { get; set; } = 0.1; - [Option("Supply a seed to repeat the same randomnesss as a previous run")] + [Option(Description = "Supply a seed to repeat the same randomnesss as a previous run")] public int? Seed { get; set; } public override Task Execute(CommandLineApplication app) diff --git a/src/AnalysisPrograms/EPR.cs b/src/AnalysisPrograms/EPR.cs index ed139bec9..daa52274d 100644 --- a/src/AnalysisPrograms/EPR.cs +++ b/src/AnalysisPrograms/EPR.cs @@ -166,7 +166,7 @@ public class EPR Description = "[UNMAINTAINED] Event Pattern Recognition - used for ground-parrots (TOWSEY version). Revise code if intend to use.")] public class Arguments : SourceAndConfigArguments { - [Option("prefix of name of the created output files")] + [Option(Description = "prefix of name of the created output files")] [LegalFilePath] [Required] public string Target { get; set; } @@ -189,9 +189,11 @@ public static void Execute(Arguments arguments) string targetName = arguments.Target; // prefix of name of created files - string recordingFileName = arguments.Source.Name; - string recordingDirectory = arguments.Source.DirectoryName; - DirectoryInfo outputDir = arguments.Config.Directory; + var input = arguments.Source; + + string recordingFileName = input.Name; + string recordingDirectory = input.DirectoryName; + DirectoryInfo outputDir = arguments.Config.ToFileInfo().Directory; FileInfo targetPath = outputDir.CombineFile(targetName + "_target.txt"); FileInfo targetNoNoisePath = outputDir.CombineFile(targetName + "_targetNoNoise.txt"); FileInfo noisePath = outputDir.CombineFile(targetName + "_noise.txt"); @@ -201,7 +203,7 @@ public static void Execute(Arguments arguments) Log.WriteIfVerbose("# Output folder =" + outputDir); //i: GET RECORDING - AudioRecording recording = new AudioRecording(arguments.Source.FullName); + AudioRecording recording = new AudioRecording(input.FullName); //if (recording.SampleRate != 22050) recording.ConvertSampleRate22kHz(); THIS METHOD CALL IS OBSOLETE int sr = recording.SampleRate; diff --git a/src/AnalysisPrograms/EventStatistics/EventStatisticsEntry.Arguments.cs b/src/AnalysisPrograms/EventStatistics/EventStatisticsEntry.Arguments.cs index a537c9c00..956d34136 100644 --- a/src/AnalysisPrograms/EventStatistics/EventStatisticsEntry.Arguments.cs +++ b/src/AnalysisPrograms/EventStatistics/EventStatisticsEntry.Arguments.cs @@ -39,16 +39,20 @@ At this stage only remote analysis is supported - that means querying an Acousti public class Arguments : SourceConfigOutputDirArguments { - [Option("The source event (annotations) file to operate on")] + [Argument( + 0, + Description = "The source event (annotations) file to operate on")] [ExistingFile] [Required] - public new FileInfo Source { get; set; } + [LegalFilePath] + public override FileInfo Source { get; set; } - [Option("A TEMP directory where cut files will be stored. Use this option for efficiency (e.g. write to a RAM Disk).")] + [Option(Description = "A TEMP directory where cut files will be stored. Use this option for efficiency (e.g. write to a RAM Disk).")] [DirectoryExistsOrCreate(createIfNotExists: true)] + [LegalFilePath] public DirectoryInfo TempDir { get; set; } - [Option("An array of channels to select. Default is all channels.")] + [Option(Description = "An array of channels to select. Default is all channels.")] public int[] Channels { get; set; } = null; [Option( @@ -56,18 +60,20 @@ public class Arguments Description = "Mix all selected input channels down into one mono channel. Default is to mixdown.")] public bool MixDownToMono { get; set; } = true; - [Option("The Acoustic Workbench website to download data from. Defaults to https://www.ecosounds.org")] + [Option(Description = "The Acoustic Workbench website to download data from. Defaults to https://www.ecosounds.org")] public string WorkbenchApi { get; set; } - [Option("The authentication token to use for the Acoustic Workbench website. If not specified you will prompted for log in credentials.")] + [Option(Description = "The authentication token to use for the Acoustic Workbench website. If not specified you will prompted for log in credentials.")] public string AuthenticationToken { get; set; } - [Option("Whether or not run this analysis in parallel - multiple segments can be analyzed at the same time")] + [Option(Description = "Whether or not run this analysis in parallel - multiple segments can be analyzed at the same time")] public bool Parallel { get; set; } /* Arguments for local event analysis: - [Option("TimeSpan offset hint required if file names do not contain time zone info. NO DEFAULT IS SET")] + [Option( + Description = "TimeSpan offset hint required if file names do not contain time zone info. NO DEFAULT IS SET", + ShortName = "z")] public TimeSpan? TimeSpanOffsetHint { get; set; } */ diff --git a/src/AnalysisPrograms/EventStatistics/EventStatisticsEntry.cs b/src/AnalysisPrograms/EventStatistics/EventStatisticsEntry.cs index ee3d3a897..7e17f2453 100644 --- a/src/AnalysisPrograms/EventStatistics/EventStatisticsEntry.cs +++ b/src/AnalysisPrograms/EventStatistics/EventStatisticsEntry.cs @@ -38,28 +38,29 @@ public static async Task ExecuteAsync(Arguments arguments) // validate arguments - if (!arguments.Source.Exists) + var input = arguments.Source; + var config = arguments.Config.ToFileInfo(); + + if (!input.Exists) { - throw new FileNotFoundException("Cannot find source file", arguments.Source.FullName); + throw new FileNotFoundException("Cannot find source file", input.FullName); } // try an automatically find the config file - if (arguments.Config == null) + if (config == null) { throw new FileNotFoundException("No config file argument provided"); } - else if (!arguments.Config.Exists) + else if (!config.Exists) { - Log.Warn($"Config file {arguments.Config.FullName} not found... attempting to resolve config file"); + Log.Warn($"Config file {config.FullName} not found... attempting to resolve config file"); - // we use .ToString() here to get the original input string - Using fullname always produces an + // we use the original input string - Using FileInfo fullname always produces an // absolute path wrt to pwd... we don't want to prematurely make assumptions: // 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 - arguments.Config = ConfigFile.Resolve( - arguments.Config.ToString(), - Directory.GetCurrentDirectory().ToDirectoryInfo()); + config = ConfigFile.Resolve(arguments.Config, Directory.GetCurrentDirectory().ToDirectoryInfo()); } // if a temp dir is not given, use output dir as temp dir @@ -72,8 +73,8 @@ public static async Task ExecuteAsync(Arguments arguments) IApi api = arguments.WorkbenchApi.IsNullOrEmpty() ? Api.Default : Api.Parse(arguments.WorkbenchApi); // log some helpful messages - Log.Info("Events file: " + arguments.Source); - Log.Info("Configuration file: " + arguments.Config); + Log.Info("Events file: " + input); + Log.Info("Configuration file: " + config); Log.Info("Output folder: " + arguments.Output); Log.Info("Temp File Directory: " + arguments.TempDir); Log.Info("Api: " + api); @@ -107,7 +108,7 @@ public static async Task ExecuteAsync(Arguments arguments) // Read events from provided CSV file. // Also tag them with an order index to allow sorting in the same order as they were provided to us. var events = Csv - .ReadFromCsv(arguments.Source, throwOnMissingField: false) + .ReadFromCsv(input, throwOnMissingField: false) .Select( (x, i) => { @@ -119,7 +120,7 @@ public static async Task ExecuteAsync(Arguments arguments) if (events.Length == 0) { Log.Warn("No events imported - source file empty. Exiting"); - return ExceptionLookup.Fail; + return ExceptionLookup.NoData; } Log.Info($"Events read, {events.Length} read."); @@ -162,7 +163,7 @@ public static async Task ExecuteAsync(Arguments arguments) EventStatisticsAnalysis analysis = new EventStatisticsAnalysis(); // derserialize the config file - var configuration = analysis.ParseConfig(arguments.Config); + var configuration = analysis.ParseConfig(config); AnalysisSettings settings = analysis.DefaultSettings; settings.AnalysisOutputDirectory = arguments.Output; @@ -194,7 +195,7 @@ public static async Task ExecuteAsync(Arguments arguments) var resultName = FilenameHelpers.AnalysisResultPath( instanceOutputDirectory, - arguments.Source, + input, analysis.Identifier, "csv"); diff --git a/src/AnalysisPrograms/FileRenamer.cs b/src/AnalysisPrograms/FileRenamer.cs index 8ab0ea63a..8e12cde35 100644 --- a/src/AnalysisPrograms/FileRenamer.cs +++ b/src/AnalysisPrograms/FileRenamer.cs @@ -28,42 +28,30 @@ public class FileRenamer Description = "[UNMAINTAINED] Renames files based on modified and created date.")] public class Arguments : SubCommandBase { - [Option("The directory containing audio files.")] + [Option( + CommandOptionType.SingleValue, + Description = "The directory containing audio files.")] [DirectoryExists] [Required] public virtual DirectoryInfo InputDir { get; set; } - [Option("Specify the timezone (e.g. '+1000', '-0700').")] + [Option( + CommandOptionType.SingleValue, + Description = "Specify the timezone (e.g. '+1000', '-0700').", + ShortName = "z")] [Required] - public string Timezone { get; set; } + public TimeSpan Timezone { get; set; } - [Option("Whether to recurse into subfolders.")] + [Option(Description = "Whether to recurse into subfolders.")] public bool Recursive { get; set; } - [Option("Only print rename actions, don't actually rename files.")] + [Option( + Description = "Only print rename actions, don't actually rename files.", + ShortName = "n")] public bool DryRun { get; set; } public void Validate() { - if (this.Timezone.Length != 5) - { - throw new ArgumentException("Timezone must be exactly 5 characters long."); - } - - var validPlusMinus = new[] { '+', '-' }; - if (!validPlusMinus.Contains(this.Timezone[0])) - { - throw new ArgumentException("Timezone must start with '+' or '-'."); - } - - var validChars = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; - foreach (var item in this.Timezone.Substring(1)) - { - if (!validChars.Contains(item)) - { - throw new ArgumentException("Timezone must contain only digits after initial '+' or '-'."); - } - } } public override Task Execute(CommandLineApplication app) diff --git a/src/AnalysisPrograms/GroundParrotRecogniser.cs b/src/AnalysisPrograms/GroundParrotRecogniser.cs index a8349d893..ab9f8c5e6 100644 --- a/src/AnalysisPrograms/GroundParrotRecogniser.cs +++ b/src/AnalysisPrograms/GroundParrotRecogniser.cs @@ -181,7 +181,8 @@ public static void Dev(Arguments arguments) var config = ConfigFile.Deserialize(arguments.Config); var aedConfig = Aed.GetAedParametersFromConfigFileOrDefaults(config); - Tuple> result = Detect(arguments.Source, aedConfig, Default.eprNormalisedMinScore, TimeSpan.Zero); + var input = arguments.Source; + Tuple> result = Detect(input, aedConfig, Default.eprNormalisedMinScore, TimeSpan.Zero); List eprEvents = result.Item2; eprEvents.Sort((ae1, ae2) => ae1.TimeStart.CompareTo(ae2.TimeStart)); @@ -194,8 +195,8 @@ public static void Dev(Arguments arguments) LoggedConsole.WriteLine(); - string outputFolder = arguments.Config.DirectoryName; - string wavFilePath = arguments.Source.FullName; + string outputFolder = arguments.Config.ToFileInfo().DirectoryName; + string wavFilePath = input.FullName; BaseSonogram sonogram = result.Item1; string imagePath = Path.Combine(outputFolder, Path.GetFileNameWithoutExtension(wavFilePath) + ".png"); var image = Aed.DrawSonogram(sonogram, eprEvents); diff --git a/src/AnalysisPrograms/HerveGlotinCollaboration.cs b/src/AnalysisPrograms/HerveGlotinCollaboration.cs index 93c0c188d..c95ecd7a3 100644 --- a/src/AnalysisPrograms/HerveGlotinCollaboration.cs +++ b/src/AnalysisPrograms/HerveGlotinCollaboration.cs @@ -123,7 +123,7 @@ public static void HiRes1() var audio2csvArguments = new AnalyseLongRecordings.AnalyseLongRecording.Arguments { Source = recordingPath.ToFileInfo(), - Config = audio2csvConfigPath.ToFileInfo(), + Config = audio2csvConfigPath, Output = outputDir.ToDirectoryInfo(), }; @@ -132,7 +132,7 @@ public static void HiRes1() LoggedConsole.WriteWarnLine(" >>>>>>>>>>>> WARNING! The Source Recording file cannot be found! This will cause an exception."); } - if (!audio2csvArguments.Config.Exists) + if (!File.Exists(audio2csvArguments.Config)) { LoggedConsole.WriteWarnLine(" >>>>>>>>>>>> WARNING! The Configuration file cannot be found! This will cause an exception."); } @@ -143,7 +143,7 @@ public static void HiRes1() // Use the Zoomingspectrograms action. // need to find out how long the recording is. - string fileName = Path.GetFileNameWithoutExtension(audio2csvArguments.Source.FullName); + string fileName = audio2csvArguments.Source.BaseName(); string testFileName = fileName + @"__Towsey.Acoustic.ACI.csv"; List data = FileTools.ReadTextFile(Path.Combine(csvDir, testFileName)); int lineCount = data.Count - 1; // -1 for header. @@ -162,7 +162,7 @@ public static void HiRes1() // use the default set of index properties in the AnalysisConfig directory. SourceDirectory = csvDir, Output = zoomOutputDir, - SpectrogramZoomingConfig = hiResZoomConfigPath.ToFileInfo(), + SpectrogramZoomingConfig = hiResZoomConfigPath, // draw a focused multi-resolution pyramid of images ZoomAction = DrawZoomingSpectrograms.Arguments.ZoomActionType.Focused, @@ -175,7 +175,7 @@ public static void HiRes1() var common = new ZoomParameters(zoomingArguments.SourceDirectory.ToDirectoryEntry(), zoomingArguments.SpectrogramZoomingConfig.ToFileEntry(), false); // Create directory if not exists - if (!zoomingArguments.Output.ToDirectoryInfo().Exists) + if (!Directory.Exists(zoomingArguments.Output)) { Directory.CreateDirectory(zoomingArguments.Output); } @@ -198,17 +198,11 @@ public static void HiRes1() var ldfcSpectrogramArguments = new DrawLongDurationSpectrograms.Arguments { // use the default set of index properties in the AnalysisConfig directory. - InputDataDirectory = csvDir.ToDirectoryInfo(), - OutputDirectory = imageOutputDir.ToDirectoryInfo(), - IndexPropertiesConfig = indexPropertiesConfig.ToFileInfo(), + InputDataDirectory = csvDir, + OutputDirectory = imageOutputDir, + IndexPropertiesConfig = indexPropertiesConfig, }; - // Create directory if not exists - if (!ldfcSpectrogramArguments.OutputDirectory.Exists) - { - ldfcSpectrogramArguments.OutputDirectory.Create(); - } - // there are two possible tasks // 1: draw the aggregated grey scale spectrograms int secDuration = DrawLongDurationSpectrograms.DrawAggregatedSpectrograms(ldfcSpectrogramArguments, fileStem); @@ -287,9 +281,9 @@ public static void HiRes2() var ldfcSpectrogramArguments = new DrawLongDurationSpectrograms.Arguments { // use the default set of index properties in the AnalysisConfig directory. - InputDataDirectory = dataDir.ToDirectoryInfo(), - OutputDirectory = imageOutputDir.ToDirectoryInfo(), - IndexPropertiesConfig = indexPropertiesConfig.ToFileInfo(), + InputDataDirectory = dataDir, + OutputDirectory = imageOutputDir, + IndexPropertiesConfig = indexPropertiesConfig, }; // there are two possible tasks @@ -362,9 +356,9 @@ public static void HiRes3() var ldfcSpectrogramArguments = new DrawLongDurationSpectrograms.Arguments { // use the default set of index properties in the AnalysisConfig directory. - InputDataDirectory = dataDir.ToDirectoryInfo(), - OutputDirectory = imageOutputDir.ToDirectoryInfo(), - IndexPropertiesConfig = indexPropertiesConfig.ToFileInfo(), + InputDataDirectory = dataDir, + OutputDirectory = imageOutputDir, + IndexPropertiesConfig = indexPropertiesConfig, }; // there are two possible tasks diff --git a/src/AnalysisPrograms/LSKiwi3.cs b/src/AnalysisPrograms/LSKiwi3.cs index 6f1ae3131..3d8920983 100644 --- a/src/AnalysisPrograms/LSKiwi3.cs +++ b/src/AnalysisPrograms/LSKiwi3.cs @@ -118,11 +118,11 @@ public static void Dev(Arguments arguments) arguments = new Arguments { - Source = recordingPath.ToFileInfo(), - Config = configPath.ToFileInfo(), - Output = outputDir.ToDirectoryInfo(), - Start = tsStart.TotalSeconds, - Duration = tsDuration.TotalSeconds, +// Source = recordingPath.ToFileInfo(), +// Config = configPath.ToFileInfo(), +// Output = outputDir.ToDirectoryInfo(), +// Start = tsStart.TotalSeconds, +// Duration = tsDuration.TotalSeconds, }; } diff --git a/src/AnalysisPrograms/LSKiwiROC.cs b/src/AnalysisPrograms/LSKiwiROC.cs index fd2e43111..e3fade1b8 100644 --- a/src/AnalysisPrograms/LSKiwiROC.cs +++ b/src/AnalysisPrograms/LSKiwiROC.cs @@ -39,14 +39,16 @@ public class LSKiwiROC Description = "[DEPRACATED]. Only used in 2012 to analyse output from LSKiwi3.Dev().")] public class Arguments : SubCommandBase { - [Option("Full path of the csv file containing description of potential kiwi calls. File must be in correct csv format.")] + [Option(Description = "Full path of the csv file containing description of potential kiwi calls. File must be in correct csv format.")] [ExistingFile(Extension = ".csv")] [Required] - public FileInfo Events { get; set; } + [LegalFilePath] + public string Events { get; set; } - [Option("Full path of the csv file containing description of true kiwi calls. File must be in the correct format.")] + [Option(Description = "Full path of the csv file containing description of true kiwi calls. File must be in the correct format.")] [ExistingFile(Extension = ".csv")] - public FileInfo Selections { get; set; } + [LegalFilePath] + public string Selections { get; set; } public override Task Execute(CommandLineApplication app) { @@ -83,17 +85,17 @@ public static void Main(Arguments arguments) LoggedConsole.WriteLine(date); } - string outputDir = arguments.Events.DirectoryName; + var fiKiwiCallPredictions = arguments.Events.ToFileInfo(); + string outputDir = fiKiwiCallPredictions.DirectoryName; - var fiKiwiCallPredictions = arguments.Events; - var fiGroundTruth = arguments.Selections; + var fiGroundTruth = arguments.Selections.ToFileInfo(); //InitOutputTableColumns(); //############################################################################ DataTable dt = CalculateRecallPrecision(fiKiwiCallPredictions, fiGroundTruth); //############################################################################ - string opFileStem = Path.GetFileNameWithoutExtension(arguments.Events.Name); + string opFileStem = fiKiwiCallPredictions.BaseName(); string fName = "LSKRoc_Report_" + opFileStem + ".csv"; string reportROCPath = Path.Combine(outputDir, fName); CsvTools.DataTable2CSV(dt, reportROCPath); diff --git a/src/AnalysisPrograms/MainEntry.cs b/src/AnalysisPrograms/MainEntry.cs index 851864c54..d08fc38e4 100644 --- a/src/AnalysisPrograms/MainEntry.cs +++ b/src/AnalysisPrograms/MainEntry.cs @@ -7,14 +7,16 @@ // // -------------------------------------------------------------------------------------------------------------------- -using System.Threading.Tasks; - namespace AnalysisPrograms { using System; + using System.IO; using System.Reflection; + using System.Threading.Tasks; + using AnalysisPrograms.Production.Parsers; using log4net; using McMaster.Extensions.CommandLineUtils; + using McMaster.Extensions.CommandLineUtils.Abstractions; using Production; using Production.Arguments; @@ -39,11 +41,18 @@ public static async Task Main(string[] args) NoConsole.Log.Info("Executable called with these arguments: {1}{0}{1}".Format2(Environment.CommandLine, Environment.NewLine)); - IConsole loggingConsole = new PhysicalConsoleLogger(); + var console = PhysicalConsoleLogger.Default; + var helpText = CustomHelpTextGenerator.Singleton; + helpText.EnvironmentOptions = EnvironmentOptions; + + ValueParserProvider.Default.AddParser(typeof(DateTimeOffset), new DateTimeOffsetParser()); + ValueParserProvider.Default.AddParser(typeof(TimeSpan), new TimeSpanParser()); + ValueParserProvider.Default.AddParser(typeof(DirectoryInfo), new DirectoryInfoParser()); + ValueParserProvider.Default.AddParser(typeof(FileInfo), new FileInfoParser()); // Note: See MainEntry.BeforeExecute for commands run before invocation. // note: Exception handling can be found in CurrentDomainOnUnhandledException - var result = await CommandLineApplication.ExecuteAsync(loggingConsole, args); + var result = await CommandLineApplication.ExecuteAsync(console, args); LogProgramStats(); diff --git a/src/AnalysisPrograms/OscillationRecogniser.cs b/src/AnalysisPrograms/OscillationRecogniser.cs index 8792593bf..913387d5a 100644 --- a/src/AnalysisPrograms/OscillationRecogniser.cs +++ b/src/AnalysisPrograms/OscillationRecogniser.cs @@ -93,8 +93,8 @@ public static void Execute(Arguments arguments) Log.Verbosity = 1; - FileInfo recordingPath = arguments.Source; - FileInfo iniPath = arguments.Config; + FileInfo recordingFile = arguments.Source; + FileInfo iniPath = arguments.Config.ToFileInfo(); DirectoryInfo outputDir = arguments.Output; string opFName = "OcillationReconiserResults.cav"; string opPath = outputDir + opFName; @@ -124,7 +124,7 @@ public static void Execute(Arguments arguments) Log.WriteIfVerbose("Duration bounds: " + minDuration + " - " + maxDuration + " seconds"); //############################################################################################################################################# - var results = Execute_ODDetect(recordingPath, doSegmentation, minHz, maxHz, frameOverlap, dctDuration, dctThreshold, + var results = Execute_ODDetect(recordingFile, doSegmentation, minHz, maxHz, frameOverlap, dctDuration, dctThreshold, minOscilFreq, maxOscilFreq, eventThreshold, minDuration, maxDuration); Log.WriteLine("# Finished detecting oscillation events."); //############################################################################################################################################# @@ -145,7 +145,7 @@ public static void Execute(Arguments arguments) //write event count to results file. double sigDuration = sonogram.Duration.TotalSeconds; - string fname = arguments.Source.Name; + string fname = recordingFile.BaseName(); int count = predictedEvents.Count; //string str = String.Format("#RecordingName\tDuration(sec)\t#Ev\tCompT(ms)\t%hiFrames\n{0}\t{1}\t{2}\t{3}\t{4}\n", fname, sigDuration, count, analysisDuration.TotalMilliseconds, pcHIF); string str = string.Format("{0}\t{1}\t{2}\t{3}\t{4}", fname, sigDuration, count, analysisDuration.TotalMilliseconds, pcHIF); @@ -153,7 +153,7 @@ public static void Execute(Arguments arguments) FileTools.WriteTextFile(opPath, sb.ToString()); //draw images of sonograms - string imagePath = outputDir + Path.GetFileNameWithoutExtension(arguments.Source.Name) + ".png"; + string imagePath = outputDir + fname + ".png"; if (DRAW_SONOGRAMS == 2) { DrawSonogram(sonogram, imagePath, hits, scores, predictedEvents, eventThreshold, intensity); @@ -164,7 +164,7 @@ public static void Execute(Arguments arguments) DrawSonogram(sonogram, imagePath, hits, scores, predictedEvents, eventThreshold, intensity); } - Log.WriteLine("# Finished recording:- " + arguments.Source.Name); + Log.WriteLine("# Finished recording:- " + recordingFile.Name); } public static Tuple, double[], TimeSpan> Execute_ODDetect(FileInfo wavPath, diff --git a/src/AnalysisPrograms/OscillationsGeneric.cs b/src/AnalysisPrograms/OscillationsGeneric.cs index 249fa395e..6322d4712 100644 --- a/src/AnalysisPrograms/OscillationsGeneric.cs +++ b/src/AnalysisPrograms/OscillationsGeneric.cs @@ -79,11 +79,11 @@ public static class OscillationsGeneric Description = "Does a generic search for oscillations in the passed audio file. Short recordings only.")] public class Arguments : SourceConfigOutputDirArguments { - [Option("The start offset (in seconds) of the source audio file to operate on")] + [Option(Description = "The start offset (in seconds) of the source audio file to operate on")] [InRange(0, double.MaxValue)] public double? StartOffset { get; set; } - [Option("The end offset (in minutes) of the source audio file to operate on")] + [Option(Description = "The end offset (in minutes) of the source audio file to operate on")] [InRange(0, double.MaxValue)] public double? EndOffset { get; set; } @@ -102,7 +102,7 @@ private static Arguments Dev() { //Source = @"C:\SensorNetworks\WavFiles\LewinsRail\BAC2_20071008-062040.wav".ToFileInfo(), //Source = @"C:\SensorNetworks\WavFiles\LewinsRail\BAC1_20071008-081607.wav".ToFileInfo(), - Source = @"C:\SensorNetworks\WavFiles\LewinsRail\BAC2_20071008-085040.wav".ToFileInfo(), + //Source = @"C:\SensorNetworks\WavFiles\LewinsRail\BAC2_20071008-085040.wav".ToFileInfo(), //Source = @"C:\SensorNetworks\WavFiles\Canetoad\FromPaulRoe\canetoad_CubberlaCreek_100529_16bitPCM.wav".ToFileInfo(), //Source = @"Y:\Jie Frogs\Recording_1.wav".ToFileInfo(), @@ -110,8 +110,8 @@ private static Arguments Dev() //Source = @"C:\SensorNetworks\WavFiles\Canetoad\FromPaulRoe\canetoad_CubberlaCreek_100530_1.wav".ToFileInfo(), //Source = @"C:\SensorNetworks\WavFiles\Frogs\MiscillaneousDataSet\CaneToads_rural1_20_MONO.wav".ToFileInfo(), - Config = @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\Towsey.OscillationsGeneric.yml".ToFileInfo(), - Output = @"C:\SensorNetworks\Output\Sonograms".ToDirectoryInfo(), + //Config = @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\Towsey.OscillationsGeneric.yml".ToFileInfo(), + //Output = @"C:\SensorNetworks\Output\Sonograms".ToDirectoryInfo(), }; throw new NoDeveloperMethodException(); @@ -125,7 +125,12 @@ public static void Main(Arguments arguments) arguments = Dev(); } - arguments.Output.Create(); + // 1. set up the necessary files + FileInfo sourceRecording = arguments.Source; + FileInfo configFile = arguments.Config.ToFileInfo(); + DirectoryInfo opDir = arguments.Output; + + opDir.Create(); if (arguments.StartOffset.HasValue ^ arguments.EndOffset.HasValue) { @@ -147,12 +152,9 @@ public static void Main(Arguments arguments) string date = "# DATE AND TIME: " + DateTime.Now; LoggedConsole.WriteLine(Title); LoggedConsole.WriteLine(date); - LoggedConsole.WriteLine("# Input audio file: " + arguments.Source.Name); + LoggedConsole.WriteLine("# Input audio file: " + sourceRecording.Name); + - // 1. set up the necessary files - FileInfo sourceRecording = arguments.Source; - FileInfo configFile = arguments.Config; - DirectoryInfo opDir = arguments.Output; string sourceName = Path.GetFileNameWithoutExtension(sourceRecording.FullName); @@ -176,8 +178,8 @@ public static void Main(Arguments arguments) configDict[AnalysisKeys.AddAxes] = configuration[AnalysisKeys.AddAxes] ?? "true"; configDict[AnalysisKeys.AddSegmentationTrack] = configuration[AnalysisKeys.AddSegmentationTrack] ?? "true"; - configDict[ConfigKeys.Recording.Key_RecordingCallName] = arguments.Source.FullName; - configDict[ConfigKeys.Recording.Key_RecordingFileName] = arguments.Source.Name; + configDict[ConfigKeys.Recording.Key_RecordingCallName] = sourceRecording.FullName; + configDict[ConfigKeys.Recording.Key_RecordingFileName] = sourceRecording.Name; configDict[AnalysisKeys.AddTimeScale] = (string)configuration[AnalysisKeys.AddTimeScale] ?? "true"; configDict[AnalysisKeys.AddAxes] = (string)configuration[AnalysisKeys.AddAxes] ?? "true"; diff --git a/src/AnalysisPrograms/Production/Arguments/AnalyserArguments.cs b/src/AnalysisPrograms/Production/Arguments/AnalyserArguments.cs index 8299ca354..a91e17fde 100644 --- a/src/AnalysisPrograms/Production/Arguments/AnalyserArguments.cs +++ b/src/AnalysisPrograms/Production/Arguments/AnalyserArguments.cs @@ -4,6 +4,7 @@ namespace AnalysisPrograms.Production.Arguments { + using System; using System.IO; using AnalysisBase; using McMaster.Extensions.CommandLineUtils; @@ -12,11 +13,11 @@ namespace AnalysisPrograms.Production.Arguments public abstract class AnalyserArguments : SourceConfigOutputDirArguments { - [Option("The start offset to start analysing from (in seconds)")] + [Option(Description = "The start offset to start analysing from (in seconds)")] [InRange(min: 0)] public double? Start { get; set; } - [Option("The duration of each segment to analyse (seconds) - a maximum of 10 minutes")] + [Option(Description = "The duration of each segment to analyse (seconds) - a maximum of 10 minutes")] [InRange(0, 10 * 60)] public double? Duration { get; set; } diff --git a/src/AnalysisPrograms/Production/Arguments/HelpArgs.cs b/src/AnalysisPrograms/Production/Arguments/HelpArgs.cs index 988c084c6..382adbee8 100644 --- a/src/AnalysisPrograms/Production/Arguments/HelpArgs.cs +++ b/src/AnalysisPrograms/Production/Arguments/HelpArgs.cs @@ -7,34 +7,35 @@ namespace AnalysisPrograms.Production.Arguments using System; using System.Linq; using System.Threading.Tasks; + + using Acoustics.Shared; + using McMaster.Extensions.CommandLineUtils; - [Command("help", Description = "Prints the full help for the program and all actions")] + [Command(HelpCommandName, Description = "Prints the full help for the program and all actions")] public class HelpArgs : SubCommandBase { + public const string HelpCommandName = "help"; + [Argument(0, Description = "The command to get help on")] public string CommandName { get; set; } public override Task Execute(CommandLineApplication app) { if (this.CommandName.IsNullOrEmpty()) + { + MainEntry.PrintUsage(null, MainEntry.Usages.Single, HelpCommandName); + } + else if (this.CommandName == Meta.Name) { MainEntry.PrintUsage(null, MainEntry.Usages.All); } - - var command = app.Commands.FirstOrDefault(x => - x.Name.Equals(this.CommandName, StringComparison.InvariantCultureIgnoreCase)); - - if (command == null) + else { - throw new CommandParsingException( - app, - $"Could not find a command with name that matches `{this.CommandName}`."); + MainEntry.PrintUsage(null, MainEntry.Usages.Single, this.CommandName); } - MainEntry.PrintUsage(null, MainEntry.Usages.Single, command.Name); - return this.Ok(); } } diff --git a/src/AnalysisPrograms/Production/Arguments/ListArgs.cs b/src/AnalysisPrograms/Production/Arguments/ListArgs.cs index 807d37fab..a5861444e 100644 --- a/src/AnalysisPrograms/Production/Arguments/ListArgs.cs +++ b/src/AnalysisPrograms/Production/Arguments/ListArgs.cs @@ -3,7 +3,9 @@ using System.Threading.Tasks; using McMaster.Extensions.CommandLineUtils; - [Command] + [Command( + "list", + Description = "Lists known commands")] public class ListArgs : SubCommandBase { diff --git a/src/AnalysisPrograms/Production/Arguments/MainArgs.cs b/src/AnalysisPrograms/Production/Arguments/MainArgs.cs index ae974b8e2..12b4f668c 100644 --- a/src/AnalysisPrograms/Production/Arguments/MainArgs.cs +++ b/src/AnalysisPrograms/Production/Arguments/MainArgs.cs @@ -1,9 +1,10 @@ -// +// // All code in this file and all associated files are the copyright and property of the QUT Ecoacoustics Research Group (formerly MQUTeR, and formerly QUT Bioacoustics Research Group). // namespace AnalysisPrograms.Production.Arguments { + using System; using System.Threading.Tasks; using Acoustics.Shared; using AnalyseLongRecordings; @@ -17,7 +18,7 @@ namespace AnalysisPrograms.Production.Arguments AllowArgumentSeparator = true, Description = Meta.Description, ThrowOnUnexpectedArgument = true)] - [HelpOption] + [HelpOption(Inherited = true, ShowInHelpText = true)] [Subcommand("help", typeof(HelpArgs))] [Subcommand("list", typeof(ListArgs))] [Subcommand("AnalysesAvailable", typeof(AnalysesAvailable))] @@ -53,11 +54,13 @@ public class MainArgs { private async Task OnExecuteAsync(CommandLineApplication app) { + MainEntry.BeforeExecute(this, app); - return ExceptionLookup.Ok; + MainEntry.PrintUsage("A command must be provided, here are some suggestions:", MainEntry.Usages.All); + return ExceptionLookup.ActionRequired; } - private LogVerbosity logLevel; + private LogVerbosity logLevel = LogVerbosity.Info; public MainArgs() { @@ -115,12 +118,10 @@ public DebugOptions DebugOption } } - [Option( Description = "Set the log vebosity level. Valid values: None = 0, Error = 1, Warn = 2, Info = 3, Debug = 4, Trace = 5, Verbose = 6, All = 7", Inherited = true, - ShortName = null - )] + ShortName = null)] public LogVerbosity LogLevel { get @@ -153,16 +154,28 @@ public LogVerbosity LogLevel } } - [Option("-v", Description = "Set the logging to be verbose. Equivalent to LogLevel = Debug = 4")] + [Option( + "-v", + Inherited = true, + Description = "Set the logging to be verbose. Equivalent to LogLevel = Debug = 4")] public bool Verbose { get; set; } - [Option("-vv", Description = "Set the logging to very verbose. Equivalent to LogLevel = Trace = 4")] + [Option( + "-vv", + Inherited = true, + Description = "Set the logging to very verbose. Equivalent to LogLevel = Trace = 4")] public bool VVerbose { get; set; } - [Option("-vvv", Description = "Set the logging to very very verbose. Equivalent to LogLevel = ALL = 7")] + [Option( + "-vvv", + Inherited = true, + Description = "Set the logging to very very verbose. Equivalent to LogLevel = ALL = 7")] public bool VVVerbose { get; set; } - [Option("--quiet", Description = "Reduce console logging to WARN and ERROR. Full logs still logged in file.")] + [Option( + "--quiet", + Inherited = true, + Description = "Reduce console logging to WARN and ERROR. Full logs still logged in file.")] public bool QuietConsole { get; set; } } } diff --git a/src/AnalysisPrograms/Production/Arguments/SourceAndConfigArguments.cs b/src/AnalysisPrograms/Production/Arguments/SourceAndConfigArguments.cs index ecdd53058..5ae481969 100644 --- a/src/AnalysisPrograms/Production/Arguments/SourceAndConfigArguments.cs +++ b/src/AnalysisPrograms/Production/Arguments/SourceAndConfigArguments.cs @@ -11,12 +11,11 @@ namespace AnalysisPrograms.Production.Arguments public abstract class SourceAndConfigArguments : SourceArguments { - [Option( - "The path to the config file.If not found it will attempt to use the default config file of the same name.", - ShortName = "c", - ValueName = "FILE")] + [Argument( + 1, + Description = "The path to the config file. If not found it will attempt to use the default config file of the same name.")] [Required] [LegalFilePath] - public FileInfo Config { get; set; } + public string Config { get; set; } } } \ No newline at end of file diff --git a/src/AnalysisPrograms/Production/Arguments/SourceArguments.cs b/src/AnalysisPrograms/Production/Arguments/SourceArguments.cs index ac19a6e2f..f63d3ed22 100644 --- a/src/AnalysisPrograms/Production/Arguments/SourceArguments.cs +++ b/src/AnalysisPrograms/Production/Arguments/SourceArguments.cs @@ -11,12 +11,11 @@ namespace AnalysisPrograms.Production.Arguments public abstract class SourceArguments : SubCommandBase { - [Option( - "The source audio file to operate on", - ShortName = "s", - ValueName = "FILE")] + [Argument( + 0, + Description = "The source audio file to operate on")] [Required] [FileExists] - public FileInfo Source { get; set; } + public virtual FileInfo Source { get; set; } } } \ No newline at end of file diff --git a/src/AnalysisPrograms/Production/Arguments/SourceConfigOutputDirArguments.cs b/src/AnalysisPrograms/Production/Arguments/SourceConfigOutputDirArguments.cs index 5cbf3c365..cdf102fde 100644 --- a/src/AnalysisPrograms/Production/Arguments/SourceConfigOutputDirArguments.cs +++ b/src/AnalysisPrograms/Production/Arguments/SourceConfigOutputDirArguments.cs @@ -17,12 +17,12 @@ namespace AnalysisPrograms.Production.Arguments public abstract class SourceConfigOutputDirArguments : SourceAndConfigArguments { - [Option( - "A directory to write output to", - ShortName = "o", - ValueName = "FILE")] + [Argument( + 2, + Description = "A directory to write output to")] [Required] [DirectoryExistsOrCreate(createIfNotExists: true)] + [LegalFilePath] public virtual DirectoryInfo Output { get; set; } /// @@ -47,13 +47,14 @@ public virtual AnalysisSettings ToAnalysisSettings( { var analysisSettings = defaults ?? new AnalysisSettings(); - analysisSettings.ConfigFile = this.Config; + analysisSettings.ConfigFile = this.Config.ToFileInfo(); - var resultDirectory = resultSubDirectory.IsNullOrEmpty() ? this.Output : this.Output.Combine(resultSubDirectory); + var output = this.Output; + var resultDirectory = resultSubDirectory.IsNullOrEmpty() ? output : output.Combine(resultSubDirectory); resultDirectory.Create(); - analysisSettings.AnalysisOutputDirectory = this.Output; - analysisSettings.AnalysisTempDirectory = this.Output; + analysisSettings.AnalysisOutputDirectory = output; + analysisSettings.AnalysisTempDirectory = output; if (outputIntermediate) { diff --git a/src/AnalysisPrograms/Production/CommandLineApplicationExtensions.cs b/src/AnalysisPrograms/Production/CommandLineApplicationExtensions.cs new file mode 100644 index 000000000..284692597 --- /dev/null +++ b/src/AnalysisPrograms/Production/CommandLineApplicationExtensions.cs @@ -0,0 +1,33 @@ +// +// All code in this file and all associated files are the copyright and property of the QUT Ecoacoustics Research Group (formerly MQUTeR, and formerly QUT Bioacoustics Research Group). +// + +namespace AnalysisPrograms.Production +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Reflection; + using System.Text; + using System.Threading.Tasks; + + using AnalysisPrograms.Production.Parsers; + + using McMaster.Extensions.CommandLineUtils; + using McMaster.Extensions.CommandLineUtils.Abstractions; + + public static class CommandLineApplicationExtensions + { + public static CommandLineApplication Root(this CommandLineApplication app) + { + var root = app; + while (root.Parent != null) + { + root = root.Parent; + } + + return root; + } + } +} diff --git a/src/AnalysisPrograms/Production/CustomHelpTextGenerator.cs b/src/AnalysisPrograms/Production/CustomHelpTextGenerator.cs new file mode 100644 index 000000000..ad56b7b4d --- /dev/null +++ b/src/AnalysisPrograms/Production/CustomHelpTextGenerator.cs @@ -0,0 +1,156 @@ +// +// All code in this file and all associated files are the copyright and property of the QUT Ecoacoustics Research Group (formerly MQUTeR, and formerly QUT Bioacoustics Research Group). +// +// Copyright (c) Nate McMaster. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//https://raw.githubusercontent.com/natemcmaster/CommandLineUtils/d1b6b5b91bcffd41d8e848b270f288baf72ae955/src/CommandLineUtils/HelpText/DefaultHelpTextGenerator.cs + +namespace AnalysisPrograms.Production +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + + using McMaster.Extensions.CommandLineUtils; + using McMaster.Extensions.CommandLineUtils.HelpText; + + /// + /// A default implementation of help text generation. + /// + public class CustomHelpTextGenerator : IHelpTextGenerator + { + /// + /// A singleton instance of . + /// + public static CustomHelpTextGenerator Singleton { get; } = new CustomHelpTextGenerator(); + + private CustomHelpTextGenerator() + { + } + + public Dictionary EnvironmentOptions { get; set; } = null; + + /// + public void Generate(CommandLineApplication application, TextWriter output) + { + var nameAndVersion = application.GetFullNameAndVersion(); + if (!string.IsNullOrEmpty(nameAndVersion)) + { + output.WriteLine(nameAndVersion); + output.WriteLine(); + } + + output.Write("Usage:"); + var stack = new Stack(); + for (var cmd = application; cmd != null; cmd = cmd.Parent) + { + stack.Push(cmd.Name); + } + + while (stack.Count > 0) + { + output.Write(' '); + output.Write(stack.Pop()); + } + + var arguments = application.Arguments.Where(a => a.ShowInHelpText).ToList(); + var options = application.GetOptions().Where(o => o.ShowInHelpText).ToList(); + var commands = application.Commands.Where(c => c.ShowInHelpText).ToList(); + + if (arguments.Any()) + { + output.Write(" [arguments]"); + } + + if (options.Any()) + { + output.Write(" [options]"); + } + + if (commands.Any()) + { + output.Write(" [command]"); + } + + if (application.AllowArgumentSeparator) + { + output.Write(" [[--] ...]"); + } + + output.WriteLine(); + + if (this.EnvironmentOptions.Any()) + { + output.WriteLine(); + output.WriteLine("Environment variables: "); + output.Write(this.FormatEnvironmentVariables()); + } + + if (arguments.Any()) + { + output.WriteLine(); + output.WriteLine("Arguments:"); + var maxArgLen = arguments.Max(a => a.Name.Length); + var outputFormat = string.Format(" {{0, -{0}}}{{1}}", maxArgLen + 2); + foreach (var arg in arguments) + { + output.Write(outputFormat, arg.Name, arg.Description); + output.WriteLine(); + } + } + + if (options.Any()) + { + output.WriteLine(); + output.WriteLine("Options:"); + var maxOptLen = options.Max(o => o.Template?.Length ?? 0); + var outputFormat = string.Format(" {{0, -{0}}}{{1}}", maxOptLen + 2); + foreach (var opt in options) + { + output.Write(outputFormat, opt.Template, opt.Description); + output.WriteLine(); + } + } + + if (commands.Any()) + { + this.FormatCommands(output, commands); + + if (application.OptionHelp != null) + { + output.WriteLine(); + output.WriteLine($"Use \"{application.Name} [command] --{application.OptionHelp.LongName}\" for more information about a command."); + } + } + + output.Write(application.ExtendedHelpText); + } + + public void FormatCommands(TextWriter output, List commands) + { + output.WriteLine(); + output.WriteLine("Commands:"); + var maxCmdLen = commands.Max(c => c.Name?.Length ?? 0); + var outputFormat = $" {{0, -{maxCmdLen + 2}}}{{1}}"; + foreach (var cmd in commands.OrderBy(c => c.Name)) + { + output.Write(outputFormat, cmd.Name, cmd.Description); + output.WriteLine(); + } + } + + public string FormatEnvironmentVariables() + { + var result = string.Empty; + int varLength = this.EnvironmentOptions.Keys.Max(s => s.Length) + 2; + var format = $" {{0, -{varLength}}}{{1}}\n"; + foreach (var envOption in this.EnvironmentOptions) + { + result += string.Format(format, envOption.Key, envOption.Value); + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/AnalysisPrograms/Production/Exceptions.cs b/src/AnalysisPrograms/Production/Exceptions.cs index 0e23d4375..41cc9d777 100644 --- a/src/AnalysisPrograms/Production/Exceptions.cs +++ b/src/AnalysisPrograms/Production/Exceptions.cs @@ -41,11 +41,11 @@ static ExceptionLookup() { { typeof(ValidationException), - new ExceptionStyle { ErrorCode = 2 } + new ExceptionStyle { ErrorCode = ValiationError, PrintUsage = false } }, { typeof(CommandLineArgumentException), - new ExceptionStyle() { ErrorCode = 3 } + new ExceptionStyle() { ErrorCode = 3} }, { typeof(DirectoryNotFoundException), @@ -65,19 +65,19 @@ static ExceptionLookup() }, { typeof(InvalidFileDateException), - new ExceptionStyle() { ErrorCode = 102, PrintUsage = false} + new ExceptionStyle() { ErrorCode = 102, PrintUsage = false } }, { typeof(ConfigFileException), - new ExceptionStyle() {ErrorCode = 103, PrintUsage = false} + new ExceptionStyle() { ErrorCode = 103, PrintUsage = false } }, { typeof(AudioRecordingTooShortException), - new ExceptionStyle() {ErrorCode = 104, PrintUsage = false } + new ExceptionStyle() { ErrorCode = 104, PrintUsage = false } }, { typeof(InvalidAudioChannelException), - new ExceptionStyle() {ErrorCode = 105, PrintUsage = false } + new ExceptionStyle() { ErrorCode = 105, PrintUsage = false } }, { typeof(AnalysisOptionDevilException), @@ -106,21 +106,13 @@ static ExceptionLookup() #region Public Properties - public static int Ok - { - get - { - return 0; - } - } + public static int Ok => 0; - public static int Fail - { - get - { - return 1; - } - } + public static int ValiationError => 2; + + public static int ActionRequired => 2; + + public static int NoData => 10; public static int SpecialExceptionErrorLevel { diff --git a/src/AnalysisPrograms/Production/MainEntryUtilities.cs b/src/AnalysisPrograms/Production/MainEntryUtilities.cs index 8919f9a93..a0da2c9f8 100644 --- a/src/AnalysisPrograms/Production/MainEntryUtilities.cs +++ b/src/AnalysisPrograms/Production/MainEntryUtilities.cs @@ -18,6 +18,7 @@ namespace AnalysisPrograms using System.ComponentModel; using System.Diagnostics; using System.Globalization; + using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Serialization; @@ -72,7 +73,7 @@ private static void Copyright() Assembly asm = Assembly.GetExecutingAssembly(); FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(asm.Location); - LoggedConsole.WriteLine(Meta.Description + fvi.FileVersion + " (" + (InDEBUG ? "DEBUG" : "RELEASE") + LoggedConsole.WriteLine(Meta.Description + " - version " + fvi.FileVersion + " (" + (InDEBUG ? "DEBUG" : "RELEASE") + " build, " + RetrieveLinkerTimestamp().ToString("g") + ") \n" + "Git branch-version: " + fvi.ProductVersion + "\n" + "Copyright QUT " + DateTime.Now.Year.ToString("0000")); @@ -112,19 +113,18 @@ internal static void AttachDebugger(ref DebugOptions options) { if (options == DebugOptions.Prompt) { - // then prompt manually - LoggedConsole.WriteLine( - "Do you wish to debug? Attach now or press [Y] to attach. Press any key other key to continue."); - options = Console.ReadKey(true).KeyChar.ToString(CultureInfo.InvariantCulture).ToLower() == "y" - ? DebugOptions.Yes - : DebugOptions.No; + var response = Prompt.GetYesNo( + "Do you wish to debug? Attach now or press [Y] to attach. Press [N] or [ENTER] to continue.", + defaultAnswer: false, + promptColor: ConsoleColor.Cyan); + options = response ? DebugOptions.Yes : DebugOptions.No; } if (options == DebugOptions.Yes) { var vsProcess = VisualStudioAttacher.GetVisualStudioForSolutions( - new List { "AudioAnalysis2012.sln", "AudioAnalysis.sln" }); + new List { "AudioAnalysis.sln" }); if (vsProcess != null) { @@ -141,9 +141,9 @@ internal static void AttachDebugger(ref DebugOptions options) { LoggedConsole.WriteLine("\t>>> Attach sucessful"); } - - LoggedConsole.WriteLine(); } + + LoggedConsole.WriteLine(); } #endif } @@ -170,10 +170,16 @@ internal enum Usages } private const string ApPlainLogging = "AP_PLAIN_LOGGING"; + private const string ApMetrics = "AP_METRICS"; internal static void PrintUsage(string message, Usages usageStyle, string commandName = null) { - //Contract.Requires(usageStyle != Usages.Single || commandName != null); + //Contract.Requires(usageStyle != Usages.Single || commandName != null);q + // HACK: override help text generator here because we can't change it in MainEntry -- yet + // https://github.com/natemcmaster/CommandLineUtils/issues/52 + CommandLineApplication.HelpTextGenerator = CustomHelpTextGenerator.Singleton; + + var root = CommandLineApplication.Root(); if (!string.IsNullOrWhiteSpace(message)) { @@ -183,41 +189,36 @@ internal static void PrintUsage(string message, Usages usageStyle, string comman if (usageStyle == Usages.All) { // print entire usage - CommandLineApplication.GetHelpText(); + root.ShowHelp(); } else if (usageStyle == Usages.Single) { - if (string.IsNullOrWhiteSpace(commandName)) - { - Log.Error("************* Can't print usage due to empty action name **************"); - } - else + var command = root.Commands.FirstOrDefault(x => + x.Name.Equals(commandName, StringComparison.InvariantCultureIgnoreCase)); + + if (command == null) { - CommandLineApplication.ShowHelp(commandName); + throw new CommandParsingException( + CommandLineApplication, + $"Could not find a command with name that matches `{commandName}`."); } + + command.ShowHelp(); } else if (usageStyle == Usages.ListAvailable) { - var commands = CommandLineApplication.Commands; + var commands = root.Commands; - var sb = new StringBuilder(); - - sb.AppendLine("Available actions - "); - - foreach (var command in commands) + using (var sb = new StringWriter()) { - sb.AppendLine("\t" + command.Name + " - " + command.Description); - } + CustomHelpTextGenerator.Singleton.FormatCommands(sb, commands); - LoggedConsole.WriteLine(sb.ToString()); + LoggedConsole.WriteLine(sb.ToString()); + } } else if (usageStyle == Usages.NoAction) { - // this branch should no longer be needed because new command line utils handles this natively - throw new InvalidOperationException(); - - //var usage = ArgUsage.GetStyledUsage(options: UsagePrintOptions, includedActions: new[] { "list", "help" }); - //LoggedConsole.WriteLine(InsertEnvironmentVariablesIntoUsage(usage.ToString())); + CommandLineApplication.ShowHint(); } else { @@ -226,34 +227,43 @@ internal static void PrintUsage(string message, Usages usageStyle, string comman } - internal static string InsertEnvironmentVariablesIntoUsage(string usage) - { - return usage.Insert( - usage.IndexOf("Global options:", StringComparison.Ordinal), - "Environment variables:\n" + - " " + - ApPlainLogging + - " [true|false]\t Enable simpler logging - the default value is `false`\n"); - } + public static readonly Dictionary EnvironmentOptions = + new Dictionary + { + { + ApPlainLogging, + "[true|false]\t Enable simpler logging - the default value is `false`" + }, + { + ApMetrics, + "[true|false]\t (Not implemented) Enable or disable metrics - default value is `true`" + }, + }; private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs) { Contract.Requires(unhandledExceptionEventArgs != null); Contract.Requires(unhandledExceptionEventArgs.ExceptionObject != null); - const string fatalMessage = "FATAL ERROR:\n\t"; + const string FatalMessage = "Fatal error:\n "; var ex = (Exception)unhandledExceptionEventArgs.ExceptionObject; ExceptionLookup.ExceptionStyle style; bool found = false; - if (ex is AggregateException original) - { - found = ExceptionLookup.ErrorLevels.TryGetValue(original.InnerException.GetType(), out style); - } - else + Exception inner = ex; + switch (ex) { - found = ExceptionLookup.ErrorLevels.TryGetValue(ex.GetType(), out style); + case TargetInvocationException _: + case AggregateException _: + // unwrap + inner = ex.InnerException; + Log.Debug($"Unwrapped {ex.GetType().Name} exception to show a {inner.GetType().Name}"); + found = ExceptionLookup.ErrorLevels.TryGetValue(inner.GetType(), out style); + break; + default: + found = ExceptionLookup.ErrorLevels.TryGetValue(ex.GetType(), out style); + break; } found = found && style.Handle; @@ -261,57 +271,18 @@ private static void CurrentDomainOnUnhandledException(object sender, UnhandledEx // if found, print message only if usage printing disabled if (found && !style.PrintUsage) { - // this branch prints the message but suppresses the stack trace - LoggedConsole.WriteFatalLine(fatalMessage, ex); + // this branch prints the message but suppresses the stack trace in the console + NoConsole.Log.Fatal(FatalMessage, ex); + LoggedConsole.WriteFatalLine(FatalMessage + inner.Message); } else if (found && ex.GetType() != typeof(Exception)) { - NoConsole.Log.Fatal("Fatal exception:", ex); - - var action = CommandLineApplication.Name; - // print usage, if exception is recognized - // -- - // attempt to retrieve action -// string action = null; -// if (Arguments != null) -// { -// action = ((MainEntryArguments)Arguments.Value).Action; -// } -// else if (ex is ArgException) -// { -// action = (ex as ArgException).Action; -// } -// else if (ArgException.LastAction.IsNotWhitespace()) -// { -// action = ArgException.LastAction; -// } -// -// if (ex is MissingArgException && ex.Message.Contains("action")) -// { -// PrintUsage("An action is required to run the program, here are some suggestions:", Usages.NoAction); -// } -// else if (ex is UnknownActionArgException) -// { -// PrintUsage(ex.Message, Usages.NoAction); -// } -// else if (ex is ValidationArgException) -// { -// // for validation exceptions, use the inner exception -// ExceptionLookup.ErrorLevels.TryGetValue(ex.InnerException.GetType(), out style); -// PrintUsage(ex.Message, Usages.Single, action); -// } - - if (ex.InnerException is TargetInvocationException) - { - var message = fatalMessage; - message += FormatTargetInvocationException(ex.InnerException); - PrintUsage(message, Usages.Single, action ?? string.Empty); - } - else - { - var message = fatalMessage + ex.Message; - PrintUsage(message, Usages.Single, action ?? string.Empty); - } + // this branch prints the message, and command usage, but suppressed the stack trace in the console + NoConsole.Log.Fatal(FatalMessage, ex); + + var command = CommandLineApplication.Name; + var message = FatalMessage + inner.Message; + PrintUsage(message, Usages.Single, command ?? string.Empty); } else { @@ -366,32 +337,6 @@ private static void PrintAggregateException(Exception ex, ref StringBuilder inne } } } - - } - - /// - /// Return the message of the first inner exception that is not an Invocation exception. - /// - /// - /// - private static string FormatTargetInvocationException(Exception ex, int depth = 0) - { - var depthString = "==".PadLeft(depth * 2, '='); - - var message = string.Empty; - - if (ex is TargetInvocationException) - { - var tiex = (TargetInvocationException)ex; - - message += FormatTargetInvocationException(tiex.InnerException, depth + 1); - } - else - { - message = depthString + "> Inner exception: " + ex.Message + Environment.NewLine; - } - - return message; } /// @@ -433,6 +378,15 @@ private static void ParseEnvirionemnt() { root.RemoveAppender("SimpleConsoleAppender"); } + + if (bool.TryParse(Environment.GetEnvironmentVariable(ApMetrics), out var parseMetrics)) + { + Log.Trace("Metric reporting enabled but not implemented"); + } + else + { + Log.Trace("Metric reporting disabled"); + } } private static void ModifyVerbosity(MainArgs arguments) diff --git a/src/AnalysisPrograms/Production/Parsers/DateTimeOffsetParser.cs b/src/AnalysisPrograms/Production/Parsers/DateTimeOffsetParser.cs new file mode 100644 index 000000000..bb05bc979 --- /dev/null +++ b/src/AnalysisPrograms/Production/Parsers/DateTimeOffsetParser.cs @@ -0,0 +1,27 @@ +// +// All code in this file and all associated files are the copyright and property of the QUT Ecoacoustics Research Group (formerly MQUTeR, and formerly QUT Bioacoustics Research Group). +// + +namespace AnalysisPrograms.Production.Parsers +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + using McMaster.Extensions.CommandLineUtils; + + public class DateTimeOffsetParser : IValueParser + { + public object Parse(string argName, string value) + { + if (!DateTimeOffset.TryParse(value, out var result)) + { + throw new CommandParsingException(null, $"Invalid value specified for {argName}. '{value} is not a valid date time (with offset)"); + } + + return result; + } + } +} diff --git a/src/AnalysisPrograms/Production/Parsers/DirectoryInfoParser.cs b/src/AnalysisPrograms/Production/Parsers/DirectoryInfoParser.cs new file mode 100644 index 000000000..19ceabbb5 --- /dev/null +++ b/src/AnalysisPrograms/Production/Parsers/DirectoryInfoParser.cs @@ -0,0 +1,23 @@ +// +// All code in this file and all associated files are the copyright and property of the QUT Ecoacoustics Research Group (formerly MQUTeR, and formerly QUT Bioacoustics Research Group). +// + +namespace AnalysisPrograms.Production.Parsers +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + using McMaster.Extensions.CommandLineUtils; + + public class DirectoryInfoParser : IValueParser + { + public object Parse(string argName, string value) + { + return value.IsNotWhitespace() ? new DirectoryInfo(value) : null; + } + } +} diff --git a/src/AnalysisPrograms/Production/Parsers/FileInfoParser.cs b/src/AnalysisPrograms/Production/Parsers/FileInfoParser.cs new file mode 100644 index 000000000..50fc91c80 --- /dev/null +++ b/src/AnalysisPrograms/Production/Parsers/FileInfoParser.cs @@ -0,0 +1,23 @@ +// +// All code in this file and all associated files are the copyright and property of the QUT Ecoacoustics Research Group (formerly MQUTeR, and formerly QUT Bioacoustics Research Group). +// + +namespace AnalysisPrograms.Production.Parsers +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + using McMaster.Extensions.CommandLineUtils; + + public class FileInfoParser : IValueParser + { + public object Parse(string argName, string value) + { + return value.IsNotWhitespace() ? new FileInfo(value) : null; + } + } +} diff --git a/src/AnalysisPrograms/Production/Parsers/TimeSpanParser.cs b/src/AnalysisPrograms/Production/Parsers/TimeSpanParser.cs new file mode 100644 index 000000000..bc47a98fc --- /dev/null +++ b/src/AnalysisPrograms/Production/Parsers/TimeSpanParser.cs @@ -0,0 +1,27 @@ +// +// All code in this file and all associated files are the copyright and property of the QUT Ecoacoustics Research Group (formerly MQUTeR, and formerly QUT Bioacoustics Research Group). +// + +namespace AnalysisPrograms.Production.Parsers +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + using McMaster.Extensions.CommandLineUtils; + + public class TimeSpanParser : IValueParser + { + public object Parse(string argName, string value) + { + if (!TimeSpan.TryParse(value, out var result)) + { + throw new CommandParsingException(null, $"Invalid value specified for {argName}. '{value} is not a valid date time (with offset)"); + } + + return result; + } + } +} diff --git a/src/AnalysisPrograms/Recognizers/Base/RecognizerBase.cs b/src/AnalysisPrograms/Recognizers/Base/RecognizerBase.cs index 469dfc30b..6fe6f342a 100644 --- a/src/AnalysisPrograms/Recognizers/Base/RecognizerBase.cs +++ b/src/AnalysisPrograms/Recognizers/Base/RecognizerBase.cs @@ -190,24 +190,19 @@ private void DrawLongDurationSpectrogram( FileInfo spectrogramConfig = ConfigFile.Resolve(acousticIndicesConfig["SpectrogramConfig"]); // Assemble arguments for drawing the GRAY-SCALE and RIDGE SPECTROGRAMS + var output = outputDirectory.Combine("SpectrogramImages"); var ldfcSpectrogramArguments = new DrawLongDurationSpectrograms.Arguments { // passed null for first InputDataDirectory on purpose: we don't want to read files off disk InputDataDirectory = null, - OutputDirectory = outputDirectory.Combine("SpectrogramImages"), - SpectrogramConfigPath = spectrogramConfig, - IndexPropertiesConfig = ipConfig, + OutputDirectory = output.FullName, + SpectrogramConfigPath = spectrogramConfig.FullName, + IndexPropertiesConfig = ipConfig.FullName, ColourMap1 = "BGN-DMN-EVN", ColourMap2 = "R3D-RVT-SPT", //R3D replaces PHN as new derived index TemporalScale = hiResTimeScale, }; - // Create output directory if it does not exist - if (!ldfcSpectrogramArguments.OutputDirectory.Exists) - { - ldfcSpectrogramArguments.OutputDirectory.Create(); - } - bool saveRidgeSpectrograms = acousticIndicesConfig.GetBoolOrNull("SaveRidgeSpectrograms") ?? false; if (saveRidgeSpectrograms) { @@ -221,7 +216,7 @@ private void DrawLongDurationSpectrogram( // combine and save //Image opImage = ImageTools.CombineImagesVertically(opImages); - var fileName = FilenameHelpers.AnalysisResultPath(ldfcSpectrogramArguments.OutputDirectory, fileStem, "Ridges", ".png"); + var fileName = FilenameHelpers.AnalysisResultPath(output, fileStem, "Ridges", ".png"); //opImage.Save(fileName); ridgeSpectrogram.Save(fileName); } // if (saveRidgeSpectrograms) @@ -232,7 +227,7 @@ private void DrawLongDurationSpectrogram( if (saveGrayScaleSpectrograms) { opImage = DrawLongDurationSpectrograms.DrawGrayScaleSpectrograms(ldfcSpectrogramArguments, fileStem, hiResTimeScale, dictionaryOfSpectra); - var fileName = FilenameHelpers.AnalysisResultPath(ldfcSpectrogramArguments.OutputDirectory, fileStem, "CombinedGreyScale", ".png"); + var fileName = FilenameHelpers.AnalysisResultPath(output, fileStem, "CombinedGreyScale", ".png"); opImage.Save(fileName); } @@ -243,7 +238,7 @@ private void DrawLongDurationSpectrogram( opImage = DrawLongDurationSpectrograms.DrawFalseColourSpectrograms(ldfcSpectrogramArguments, fileStem, dictionaryOfSpectra); var opImages = new List {opImage, scoreTrack}; opImage = ImageTools.CombineImagesVertically(opImages); - var fileName = FilenameHelpers.AnalysisResultPath(ldfcSpectrogramArguments.OutputDirectory, fileStem, "TwoMaps", ".png"); + var fileName = FilenameHelpers.AnalysisResultPath(output, fileStem, "TwoMaps", ".png"); opImage.Save(fileName); } } diff --git a/src/AnalysisPrograms/Recognizers/Base/RecognizerEntry.cs b/src/AnalysisPrograms/Recognizers/Base/RecognizerEntry.cs index 8e1ce1ffb..58c63aa70 100644 --- a/src/AnalysisPrograms/Recognizers/Base/RecognizerEntry.cs +++ b/src/AnalysisPrograms/Recognizers/Base/RecognizerEntry.cs @@ -43,7 +43,7 @@ public class RecognizerEntry Description = Description)] public class Arguments : SourceConfigOutputDirArguments { - [Option("Sets the name of the analysis to run. If not set, analysis identifer is parsed from the config file name.")] + [Option(Description = "Sets the name of the analysis to run. If not set, analysis identifer is parsed from the config file name.")] public string AnalysisIdentifier { get; set; } public override Task Execute(CommandLineApplication app) @@ -236,9 +236,9 @@ public static Arguments Dev() var arguments = new Arguments { - Source = recordingPath.ToFileInfo(), - Config = configPath.ToFileInfo(), - Output = outputPath.ToDirectoryInfo(), +// Source = recordingPath.ToFileInfo(), +// Config = configPath.ToFileInfo(), +// Output = outputPath.ToDirectoryInfo(), }; // ######### NOTE: All other parameters are set in the .yml file assigned to configPath variable above. @@ -258,7 +258,7 @@ public static void Execute(Arguments arguments) Log.Info("Running event recognizer"); var sourceAudio = arguments.Source; - var configFile = arguments.Config; + var configFile = arguments.Config.ToFileInfo(); var outputDirectory = arguments.Output; if (configFile == null) @@ -268,15 +268,13 @@ public static void Execute(Arguments arguments) else if (!configFile.Exists) { Log.Warn($"Config file {configFile.FullName} not found... attempting to resolve config file"); - arguments.Config = configFile = ConfigFile.Resolve(configFile.Name, Directory.GetCurrentDirectory().ToDirectoryInfo()); + 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( arguments.AnalysisIdentifier, @@ -308,11 +306,11 @@ public static void Execute(Arguments arguments) TargetSampleRate = analysisSettings.AnalysisTargetSampleRate, }; var preparedFile = AudioFilePreparer.PrepareFile( - arguments.Output, - arguments.Source, + outputDirectory, + sourceAudio, MediaTypes.MediaTypeWav, audioUtilityRequest, - arguments.Output); + outputDirectory); var source = preparedFile.SourceInfo.ToSegment(); var prepared = preparedFile.TargetInfo.ToSegment(FileSegment.FileDateBehavior.None); diff --git a/src/AnalysisPrograms/SURFAnalysis.cs b/src/AnalysisPrograms/SURFAnalysis.cs index 5e1df093f..da1fa2863 100644 --- a/src/AnalysisPrograms/SURFAnalysis.cs +++ b/src/AnalysisPrograms/SURFAnalysis.cs @@ -50,17 +50,17 @@ public class SurfAnalysis Description = "[UNMAINTAINED] Uses SURF points of interest to classify recording segments of bird calls.")] public class Arguments : SourceConfigOutputDirArguments { - [Option] - public FileInfo QueryWavFile { get; set; } + [Option(ShortName = "")] + public string QueryWavFile { get; set; } - [Option] - public FileInfo QueryCsvFile { get; set; } + [Option(ShortName = "")] + public string QueryCsvFile { get; set; } - [Option] - public FileInfo TargtWavFile { get; set; } + [Option(ShortName = "")] + public string TargtWavFile { get; set; } - [Option] - public FileInfo TargtCsvFile { get; set; } + [Option(ShortName = "")] + public string TargtCsvFile { get; set; } public override Task Execute(CommandLineApplication app) { @@ -71,27 +71,28 @@ public override Task Execute(CommandLineApplication app) public static void Main(Arguments arguments) { - if (!arguments.Output.Exists) + var output = arguments.Output; + if (!output.Exists) { - arguments.Output.Create(); + output.Create(); } const string title = "# PRE-PROCESS SHORT AUDIO RECORDINGS FOR Convolutional DNN"; string date = "# DATE AND TIME: " + DateTime.Now; LoggedConsole.WriteLine(title); LoggedConsole.WriteLine(date); - LoggedConsole.WriteLine("# Input Query file: " + arguments.QueryWavFile.Name); - LoggedConsole.WriteLine("# Input target file: " + arguments.TargtWavFile.Name); - LoggedConsole.WriteLine("# Configure file: " + arguments.Config.Name); - LoggedConsole.WriteLine("# Output directory: " + arguments.Output.Name); + LoggedConsole.WriteLine("# Input Query file: " + arguments.QueryWavFile); + LoggedConsole.WriteLine("# Input target file: " + arguments.TargtWavFile); + LoggedConsole.WriteLine("# Configure file: " + arguments.Config); + LoggedConsole.WriteLine("# Output directory: " + output.Name); // 1. set up the necessary files - FileInfo queryWavfile = arguments.QueryWavFile; - FileInfo queryCsvfile = arguments.QueryCsvFile; - FileInfo targtWavfile = arguments.TargtWavFile; - FileInfo targtCsvfile = arguments.TargtCsvFile; - FileInfo configFile = arguments.Config; - DirectoryInfo opDir = arguments.Output; + FileInfo queryWavfile = arguments.QueryWavFile.ToFileInfo(); + FileInfo queryCsvfile = arguments.QueryCsvFile.ToFileInfo(); + FileInfo targtWavfile = arguments.TargtWavFile.ToFileInfo(); + FileInfo targtCsvfile = arguments.TargtCsvFile.ToFileInfo(); + FileInfo configFile = arguments.Config.ToFileInfo(); + DirectoryInfo opDir = output; // 2. get the config dictionary Config configuration = ConfigFile.Deserialize(configFile); @@ -135,7 +136,7 @@ public static void Main(Arguments arguments) if (!queryWavfile.Exists) { - string warning = string.Format("FILE DOES NOT EXIST >>>," + arguments.QueryWavFile.Name); + string warning = string.Format("FILE DOES NOT EXIST >>>," + arguments.QueryWavFile); LoggedConsole.WriteWarnLine(warning); return; } diff --git a/src/AnalysisPrograms/Sandpit.cs b/src/AnalysisPrograms/Sandpit.cs index 50e7ef124..bc800c8e7 100644 --- a/src/AnalysisPrograms/Sandpit.cs +++ b/src/AnalysisPrograms/Sandpit.cs @@ -130,7 +130,7 @@ public static void Audio2CsvOverMultipleFiles() var devArguments = new AnalyseLongRecording.Arguments { Source = files[i].ToFileInfo(), - Config = configPath.ToFileInfo(), + Config = configPath, Output = outputDirectory.ToDirectoryInfo(), MixDownToMono = true, }; @@ -139,15 +139,11 @@ public static void Audio2CsvOverMultipleFiles() } // (2) now do the CONCATENATION - DirectoryInfo[] dataDirs = - { - new DirectoryInfo(outputDir), - }; string directoryFilter = "Towsey.Acoustic"; // this is a directory filter to locate only the required files string opFileStem = "IvanCampos_INCIPO01_20161031"; string opPath = $"{drive}:\\SensorNetworks\\Output\\IvanCampos"; - var falseColourSpgConfig = new FileInfo( - $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatSpectrogramFalseColourConfig.yml"); + var falseColourSpgConfig = + $"{drive}:\\SensorNetworks\\SoftwareTests\\TestConcatenation\\Data\\ConcatSpectrogramFalseColourConfig.yml"; // start and end dates INCLUSIVE var dtoStart = new DateTimeOffset(2016, 10, 31, 0, 0, 0, TimeSpan.Zero); @@ -155,13 +151,13 @@ public static void Audio2CsvOverMultipleFiles() // there are three options for rendering of gaps/missing data: NoGaps, TimedGaps and EchoGaps. string gapRendering = "NoGaps"; - var indexPropertiesConfig = new FileInfo( - @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\IndexPropertiesConfig.yml"); + var indexPropertiesConfig = + @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\IndexPropertiesConfig.yml"; var concatArgs = new ConcatenateIndexFiles.Arguments { - InputDataDirectories = dataDirs, - OutputDirectory = new DirectoryInfo(opPath), + InputDataDirectories = outputDir.ToDirectoryInfo().AsArray(), + OutputDirectory = opPath.ToDirectoryInfo(), DirectoryFilter = directoryFilter, FileStemName = opFileStem, StartDate = dtoStart, @@ -443,24 +439,11 @@ public static void Audio2CsvOverOneFile() var arguments = new AnalyseLongRecording.Arguments { Source = recordingPath.ToFileInfo(), - Config = configPath.ToFileInfo(), + Config = configPath, Output = outputPath.ToDirectoryInfo(), MixDownToMono = true, }; - // ######### NOTE: All other parameters are set in the file - if (!arguments.Source.Exists) - { - LoggedConsole.WriteWarnLine( - " >>>>>>>>>>>> WARNING! The Source Recording file cannot be found! This will cause an exception."); - } - - if (!arguments.Config.Exists) - { - LoggedConsole.WriteWarnLine( - " >>>>>>>>>>>> WARNING! The Configuration file cannot be found! This will cause an exception."); - } - AnalyseLongRecording.Execute(arguments); } @@ -585,10 +568,10 @@ public static void DrawLongDurationSpectrogram() var args = new DrawLongDurationSpectrograms.Arguments { - InputDataDirectory = new DirectoryInfo(ipdir), - OutputDirectory = new DirectoryInfo(opdir), - IndexPropertiesConfig = new FileInfo(indexPropertiesFile), - SpectrogramConfigPath = new FileInfo(spectrogramConfigFile), + InputDataDirectory = (ipdir), + OutputDirectory = (opdir), + IndexPropertiesConfig = (indexPropertiesFile), + SpectrogramConfigPath = (spectrogramConfigFile), }; DrawLongDurationSpectrograms.Execute(args); } @@ -599,8 +582,8 @@ public static void DrawLongDurationSpectrogram() public static void ConcatenateIndexFilesAndSpectrograms() { // set the default values here - var indexPropertiesConfig = new FileInfo( - @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\IndexPropertiesConfig.yml"); + var indexPropertiesConfig = + @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\IndexPropertiesConfig.yml"; var timeSpanOffsetHint = TimeSpan.FromHours(10); // default = Brisbane time var drawImages = true; @@ -610,7 +593,7 @@ public static void ConcatenateIndexFilesAndSpectrograms() // files containing output from event recognizers. // Used only to get Event Recognizer files - set eventDirs=null if not used - DirectoryInfo[] eventDirs = null; + string[] eventDirs = null; string eventFilePattern = string.Empty; // The drive: local = C; work = G; home = E @@ -673,15 +656,15 @@ public static void ConcatenateIndexFilesAndSpectrograms() drive = "G"; // top level directory AVAILAE JOB #181 - DirectoryInfo[] dataDirs = + string[] dataDirs = { - new DirectoryInfo($"{drive}:\\SensorNetworks\\Output\\BradLaw\\PillagaData"), + ($"{drive}:\\SensorNetworks\\Output\\BradLaw\\PillagaData"), }; string directoryFilter = "Pillaga*"; // this is a directory filter to locate only the required files string opFileStem = "PillagaForest20121125"; string opPath = $"{drive}:\\SensorNetworks\\Output\\BradLaw"; - var falseColourSpgConfig = new FileInfo( - $"{drive}:\\SensorNetworks\\Output\\Bats\\config\\SpectrogramFalseColourConfig.yml"); + var falseColourSpgConfig = + $"{drive}:\\SensorNetworks\\Output\\Bats\\config\\SpectrogramFalseColourConfig.yml"; FileInfo sunriseDatafile = null; concatenateEverythingYouCanLayYourHandsOn = true; @@ -949,10 +932,6 @@ public static void ConcatenateIndexFilesAndSpectrograms() //string opFileStem = "Site1_20150709"; // ########################## END of GRIFFITH - SIMON/TOBY FRESH-WATER RECORDINGS - if (!indexPropertiesConfig.Exists) - { - LoggedConsole.WriteErrorLine("# indexPropertiesConfig FILE DOES NOT EXIST."); - } // DISCUSS THE FOLLOWING WITH ANTHONY // Anthony says we would need to serialise the class. Skip this for the moment. @@ -966,8 +945,8 @@ public static void ConcatenateIndexFilesAndSpectrograms() var args = new ConcatenateIndexFiles.Arguments { - InputDataDirectories = dataDirs, - OutputDirectory = new DirectoryInfo(opPath), + InputDataDirectories = dataDirs.Select(FileInfoExtensions.ToDirectoryInfo).ToArray(), + OutputDirectory = opPath.ToDirectoryInfo(), DirectoryFilter = directoryFilter, FileStemName = opFileStem, StartDate = dtoStart, @@ -1208,10 +1187,10 @@ public static void TestAnalyseLongRecordingUsingArtificialSignal() double duration = 420; // signal duration in seconds = 7 minutes int[] harmonics = { 500, 1000, 2000, 4000, 8000 }; var recording = DspFilters.GenerateTestRecording(sampleRate, duration, harmonics, WaveType.Consine); - var outputDirectory = new DirectoryInfo(@"C:\SensorNetworks\SoftwareTests\TestLongDurationRecordings"); - var recordingPath = outputDirectory.CombineFile("TemporaryRecording.wav"); - WavWriter.WriteWavFileViaFfmpeg(recordingPath, recording.WavReader); - var configPath = new FileInfo( + var outputDirectory = (@"C:\SensorNetworks\SoftwareTests\TestLongDurationRecordings"); + var recordingPath = Path.Combine(outputDirectory, "TemporaryRecording.wav"); + WavWriter.WriteWavFileViaFfmpeg(recordingPath.ToFileInfo(), recording.WavReader); + var configPath = ( @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\Towsey.Acoustic.yml"); // draw the signal as spectrogram just for debugging purposes @@ -1234,14 +1213,15 @@ public static void TestAnalyseLongRecordingUsingArtificialSignal() var argumentsForAlr = new AnalyseLongRecording.Arguments { - Source = recordingPath, + Source = recordingPath.ToFileInfo(), Config = configPath, - Output = outputDirectory, + Output = outputDirectory.ToDirectoryInfo(), MixDownToMono = true, }; AnalyseLongRecording.Execute(argumentsForAlr); - var resultsDirectory = outputDirectory.Combine("Towsey.Acoustic"); + var outputDir = outputDirectory.ToDirectoryInfo(); + var resultsDirectory = outputDir.Combine("Towsey.Acoustic"); var listOfFiles = resultsDirectory.EnumerateFiles(); int count = listOfFiles.Count(); var csvCount = listOfFiles.Count(f => f.Name.EndsWith(".csv")); @@ -1271,7 +1251,7 @@ public static void TestAnalyseLongRecordingUsingArtificialSignal() // draw array just to check peaks are in correct places. var normalisedIndex = DataTools.normalise(array); var image2 = GraphsAndCharts.DrawGraph("LD BGN SPECTRUM", normalisedIndex, 100); - var ldsBgnSpectrumFile = outputDirectory.CombineFile("Spectrum2.png"); + var ldsBgnSpectrumFile = outputDir.CombineFile("Spectrum2.png"); image2.Save(ldsBgnSpectrumFile.FullName); } diff --git a/src/AnalysisPrograms/Segment.cs b/src/AnalysisPrograms/Segment.cs index 61403e36c..68cc93e01 100644 --- a/src/AnalysisPrograms/Segment.cs +++ b/src/AnalysisPrograms/Segment.cs @@ -75,7 +75,7 @@ public static void Execute(Arguments arguments) Log.Verbosity = 1; FileInfo recordingPath = arguments.Source; - FileInfo iniPath = arguments.Config; + FileInfo iniPath = arguments.Config.ToFileInfo(); DirectoryInfo outputDir = arguments.Output; string opFName = "segment-output.txt"; FileInfo opPath = outputDir.CombineFile(opFName); diff --git a/src/AnalysisPrograms/SnrAnalysis.cs b/src/AnalysisPrograms/SnrAnalysis.cs index ff0b402fb..32c63b7e2 100644 --- a/src/AnalysisPrograms/SnrAnalysis.cs +++ b/src/AnalysisPrograms/SnrAnalysis.cs @@ -47,16 +47,16 @@ private static Arguments Dev() //Source = @"C:\SensorNetworks\WavFiles\TestRecordings\BAC1_20071008-081607.wav".ToFileInfo(), //Source = @"C:\SensorNetworks\WavFiles\TestRecordings\BAC2_20071008-045040_birds.wav".ToFileInfo(), //Source = @"C:\SensorNetworks\WavFiles\TestRecordings\CaneToads_rural1_20.mp3".ToFileInfo(), - Source = - @"C:\SensorNetworks\WavFiles\TestRecordings\AdelotusBrevis_extract.mp3" - .ToFileInfo(), +// Source = +// @"C:\SensorNetworks\WavFiles\TestRecordings\AdelotusBrevis_extract.mp3" +// .ToFileInfo(), //Source = @"C:\SensorNetworks\WavFiles\TestRecordings\BAC2_20071008-143516_speech.wav".ToFileInfo(), //Source = @"C:\SensorNetworks\WavFiles\TestRecordings\groundParrot_Perigian_TEST_1min.wav".ToFileInfo(), //Source = @"C:\SensorNetworks\WavFiles\TestRecordings\TOWERB_20110302_202900_22.LSK.F.wav".ToFileInfo(), - Config = - @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\SNRConfig.yml".ToFileInfo(), - Output = @"C:\SensorNetworks\Output\SNR".ToDirectoryInfo(), +// Config = +// @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\SNRConfig.yml".ToFileInfo(), +// Output = @"C:\SensorNetworks\Output\SNR".ToDirectoryInfo(), }; throw new NotImplementedException(); } @@ -74,15 +74,16 @@ public static void Execute(Arguments arguments) Log.WriteLine(date); Log.Verbosity = 1; - var sourceFileName = arguments.Source.Name; + var input = arguments.Source; + var sourceFileName = input.Name; var outputDir = arguments.Output; - var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(arguments.Source.FullName); + var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(input.FullName); var outputTxtPath = Path.Combine(outputDir.FullName, fileNameWithoutExtension + ".txt").ToFileInfo(); - Log.WriteIfVerbose("# Recording file: " + arguments.Source.FullName); - Log.WriteIfVerbose("# Config file: " + arguments.Config.FullName); + Log.WriteIfVerbose("# Recording file: " + input.FullName); + Log.WriteIfVerbose("# Config file: " + arguments.Config); Log.WriteIfVerbose("# Output folder =" + outputDir.FullName); - FileTools.WriteTextFile(outputTxtPath.FullName, date + "\n# Recording file: " + arguments.Source.FullName); + FileTools.WriteTextFile(outputTxtPath.FullName, date + "\n# Recording file: " + input.FullName); //READ PARAMETER VALUES FROM INI FILE // load YAML configuration @@ -90,7 +91,7 @@ public static void Execute(Arguments arguments) //ii: SET SONOGRAM CONFIGURATION SonogramConfig sonoConfig = new SonogramConfig(); //default values config - sonoConfig.SourceFName = arguments.Source.FullName; + sonoConfig.SourceFName = input.FullName; sonoConfig.WindowSize = configuration.GetIntOrNull(AnalysisKeys.KeyFrameSize) ?? 512; // sonoConfig.WindowOverlap = configuration.GetDoubleOrNull(AnalysisKeys.FrameOverlap) ?? 0.5; sonoConfig.WindowFunction = configuration[AnalysisKeys.KeyWindowFunction]; @@ -121,7 +122,7 @@ public static void Execute(Arguments arguments) } var convertedFileInfo = AudioFilePreparer.PrepareFile( - arguments.Source, + input, fileToAnalyse, convertParameters, outputDir); @@ -271,7 +272,7 @@ public static void Execute(Arguments arguments) // (G) ################################## Calculate modal background noise spectrum in decibels - Log.WriteLine("# Finished recording:- " + arguments.Source.Name); + Log.WriteLine("# Finished recording:- " + input.Name); } private static Image DrawSonogram( diff --git a/src/AnalysisPrograms/SpeciesAccumulationCurve.cs b/src/AnalysisPrograms/SpeciesAccumulationCurve.cs index e1e941905..3b0b9163e 100644 --- a/src/AnalysisPrograms/SpeciesAccumulationCurve.cs +++ b/src/AnalysisPrograms/SpeciesAccumulationCurve.cs @@ -148,14 +148,16 @@ public class SpeciesAccumulationCurve Description = "[INOPERABLE]")] public class Arguments { - [Option("Path of the input file to be processed.")] - [ExistingFile()] + [Option(Description = "Path of the input file to be processed.")] + [ExistingFile] [Required] - public FileInfo Source { get; set; } + [LegalFilePath] + public string Source { get; set; } - [Option("Path of the output file to store results.")] + [Option(Description = "Path of the output file to store results.")] [Required] - public FileInfo Output { get; set; } + [LegalFilePath] + public string Output { get; set; } } /// @@ -480,8 +482,8 @@ public static void Execute(Arguments arguments) Log.Verbosity = 0; bool doStoreImages = false; - FileInfo recordingPath = arguments.Source; - FileInfo opPath = arguments.Output; + FileInfo recordingPath = arguments.Source.ToFileInfo(); + FileInfo opPath = arguments.Output.ToFileInfo(); opPath.CreateParentDirectories(); diff --git a/src/AnalysisPrograms/Spt.cs b/src/AnalysisPrograms/Spt.cs index bc488cb48..bf8ee4326 100644 --- a/src/AnalysisPrograms/Spt.cs +++ b/src/AnalysisPrograms/Spt.cs @@ -37,16 +37,18 @@ public class SPT public class Arguments : SubCommandBase { - [Option("The source audio file to operate on")] - [ExistingFile()] + [Option(Description = "The source audio file to operate on")] + [ExistingFile] [Required] - public FileInfo Source{get;set;} + [LegalFilePath] + public string Source { get; set; } - [Option("A directory to write output to")] + [Option(Description = "A directory to write output to")] [DirectoryExistsOrCreate(createIfNotExists: true)] - public DirectoryInfo Output{get;set;} + [LegalFilePath] + public string Output{get;set;} - [Option("Intensity Threshold")] + [Option(Description = "Intensity Threshold")] [Required] public double IntensityThreshold { get; set; } @@ -71,8 +73,8 @@ public static void Execute(Arguments arguments) Log.Verbosity = 1; - FileInfo wavFilePath = arguments.Source; - DirectoryInfo opDir = arguments.Output; + FileInfo wavFilePath = arguments.Source.ToFileInfo(); + DirectoryInfo opDir = arguments.Output.ToDirectoryInfo(); double intensityThreshold = arguments.IntensityThreshold; Log.WriteLine("intensityThreshold = " + intensityThreshold); int smallLengthThreshold = 50; diff --git a/src/AnalysisPrograms/packages.config b/src/AnalysisPrograms/packages.config index 31dc02241..bebe8c1fd 100644 --- a/src/AnalysisPrograms/packages.config +++ b/src/AnalysisPrograms/packages.config @@ -11,7 +11,7 @@ - + diff --git a/tests/Acoustics.Test/AnalysisPrograms/AnalyzeLongRecordings/TestAnalyzeLongRecording.cs b/tests/Acoustics.Test/AnalysisPrograms/AnalyzeLongRecordings/TestAnalyzeLongRecording.cs index eed61a5e5..7b06d00c5 100644 --- a/tests/Acoustics.Test/AnalysisPrograms/AnalyzeLongRecordings/TestAnalyzeLongRecording.cs +++ b/tests/Acoustics.Test/AnalysisPrograms/AnalyzeLongRecordings/TestAnalyzeLongRecording.cs @@ -86,7 +86,7 @@ public void TestAnalyzeSr22050Recording() var arguments = new AnalyseLongRecording.Arguments { Source = recordingPath, - Config = configPath, + Config = configPath.FullName, Output = this.outputDirectory, MixDownToMono = true, }; @@ -194,7 +194,7 @@ public void TestAnalyzeSr64000Recording() var arguments = new AnalyseLongRecording.Arguments { Source = recordingPath, - Config = newConfigPath, + Config = newConfigPath.FullName, Output = this.outputDirectory, MixDownToMono = true, }; @@ -297,7 +297,7 @@ public void TestEnsuresFailureForNoDate() var arguments = new AnalyseLongRecording.Arguments { Source = recordingPath, - Config = newConfigPath, + Config = newConfigPath.FullName, Output = this.outputDirectory, MixDownToMono = true, }; @@ -326,7 +326,7 @@ public void TestEnsuresFailureWithAmbiguousDate() var arguments = new AnalyseLongRecording.Arguments { Source = recordingPath, - Config = newConfigPath, + Config = newConfigPath.FullName, Output = this.outputDirectory, MixDownToMono = true, }; diff --git a/tests/Acoustics.Test/AnalysisPrograms/Concatenation/ConcatenationTests.cs b/tests/Acoustics.Test/AnalysisPrograms/Concatenation/ConcatenationTests.cs index 190fbff4a..0b6c73047 100644 --- a/tests/Acoustics.Test/AnalysisPrograms/Concatenation/ConcatenationTests.cs +++ b/tests/Acoustics.Test/AnalysisPrograms/Concatenation/ConcatenationTests.cs @@ -76,8 +76,8 @@ public void ConcatenateEverythingYouCanLayYourHandsOn() FileStemName = "Test1_Indonesia", StartDate = new DateTimeOffset(2016, 07, 25, 0, 0, 0, TimeSpan.Zero), EndDate = new DateTimeOffset(2016, 07, 25, 0, 0, 0, TimeSpan.Zero), - IndexPropertiesConfig = indexPropertiesConfig, - FalseColourSpectrogramConfig = testConfig, + IndexPropertiesConfig = indexPropertiesConfig.FullName, + FalseColourSpectrogramConfig = testConfig.FullName, ColorMap1 = LDSpectrogramRGB.DefaultColorMap1, ColorMap2 = "BGN-POW-EVN", // POW was depracated post May 2017 ConcatenateEverythingYouCanLayYourHandsOn = true, // join everything found @@ -131,8 +131,8 @@ public void ConcatenateIndexFilesTest24Hour() FileStemName = "Test2_Indonesia", StartDate = new DateTimeOffset(2016, 07, 26, 0, 0, 0, TimeSpan.Zero), EndDate = new DateTimeOffset(2016, 07, 27, 0, 0, 0, TimeSpan.Zero), - IndexPropertiesConfig = indexPropertiesConfig, - FalseColourSpectrogramConfig = testConfig, + IndexPropertiesConfig = indexPropertiesConfig.FullName, + FalseColourSpectrogramConfig = testConfig.FullName, ColorMap1 = LDSpectrogramRGB.DefaultColorMap1, ColorMap2 = "BGN-POW-EVN", // POW was depracated post May 2017 ConcatenateEverythingYouCanLayYourHandsOn = false, // 24 hour blocks only @@ -188,8 +188,8 @@ public void ConcatenateIndexFilesTest24HourWithoutDateRange() FileStemName = "Test3_Indonesia", StartDate = null, EndDate = null, - IndexPropertiesConfig = indexPropertiesConfig, - FalseColourSpectrogramConfig = testConfig, + IndexPropertiesConfig = indexPropertiesConfig.FullName, + FalseColourSpectrogramConfig = testConfig.FullName, ColorMap1 = LDSpectrogramRGB.DefaultColorMap1, ColorMap2 = "BGN-POW-EVN", // POW was depracated post May 2017 ConcatenateEverythingYouCanLayYourHandsOn = false, // 24 hour blocks only @@ -266,8 +266,8 @@ public void ConcatenateIndexFilesTestConfigFileChanges() FileStemName = "Test2_Indonesia", StartDate = new DateTimeOffset(2016, 07, 26, 0, 0, 0, TimeSpan.Zero), EndDate = new DateTimeOffset(2016, 07, 27, 0, 0, 0, TimeSpan.Zero), - IndexPropertiesConfig = indexPropertiesConfig, - FalseColourSpectrogramConfig = testConfig, + IndexPropertiesConfig = indexPropertiesConfig.FullName, + FalseColourSpectrogramConfig = testConfig.FullName, ConcatenateEverythingYouCanLayYourHandsOn = false, // 24 hour blocks only TimeSpanOffsetHint = TimeSpan.FromHours(8), DrawImages = true, @@ -313,8 +313,8 @@ public void SampledDataConcatModeTests(ConcatMode gapRendering, int[] expectedWi FileStemName = Ark01, StartDate = null, EndDate = null, - IndexPropertiesConfig = PathHelper.ResolveConfigFile("IndexPropertiesConfig.yml"), - FalseColourSpectrogramConfig = PathHelper.ResolveConfigFile("SpectrogramFalseColourConfig.yml"), + IndexPropertiesConfig = PathHelper.ResolveConfigFile("IndexPropertiesConfig.yml").FullName, + FalseColourSpectrogramConfig = PathHelper.ResolveConfigFile("SpectrogramFalseColourConfig.yml").FullName, ColorMap1 = null, ColorMap2 = null, ConcatenateEverythingYouCanLayYourHandsOn = false, diff --git a/tests/Acoustics.Test/AnalysisPrograms/Draw/Zooming/DrawZoomingTests.cs b/tests/Acoustics.Test/AnalysisPrograms/Draw/Zooming/DrawZoomingTests.cs index 93d0f01c8..9ed0b6a81 100644 --- a/tests/Acoustics.Test/AnalysisPrograms/Draw/Zooming/DrawZoomingTests.cs +++ b/tests/Acoustics.Test/AnalysisPrograms/Draw/Zooming/DrawZoomingTests.cs @@ -40,7 +40,7 @@ public static void ClassInitialize(TestContext context) var arguments = new AnalyseLongRecording.Arguments { Source = recordingPath, - Config = configPath, + Config = configPath.FullName, Output = SharedDirectory, TempDir = SharedDirectory.Combine("Temp"), }; @@ -87,7 +87,7 @@ void SetupAndRun(params double[] scales) { Output = this.outputDirectory.FullName, SourceDirectory = ResultsDirectory.FullName, - SpectrogramZoomingConfig = newConfigFile, + SpectrogramZoomingConfig = newConfigFile.FullName, ZoomAction = DrawZoomingSpectrograms.Arguments.ZoomActionType.Tile, }); } @@ -112,7 +112,7 @@ public void TestGenerateTiles() { Output = zoomOutput.FullName, SourceDirectory = ResultsDirectory.FullName, - SpectrogramZoomingConfig = PathHelper.ResolveConfigFile("SpectrogramZoomingConfig.yml"), + SpectrogramZoomingConfig = PathHelper.ResolveConfigFile("SpectrogramZoomingConfig.yml").FullName, ZoomAction = DrawZoomingSpectrograms.Arguments.ZoomActionType.Tile, }); @@ -149,7 +149,7 @@ public void TestGenerateTilesSqlite() Output = zoomOutput.FullName, OutputFormat = "sqlite3", SourceDirectory = ResultsDirectory.FullName, - SpectrogramZoomingConfig = PathHelper.ResolveConfigFile("SpectrogramZoomingConfig.yml"), + SpectrogramZoomingConfig = PathHelper.ResolveConfigFile("SpectrogramZoomingConfig.yml").FullName, ZoomAction = DrawZoomingSpectrograms.Arguments.ZoomActionType.Tile, });