diff --git a/docs/guides/generic_recognizers.md b/docs/guides/generic_recognizers.md index 78bf8206e..7dc35b395 100644 --- a/docs/guides/generic_recognizers.md +++ b/docs/guides/generic_recognizers.md @@ -248,10 +248,10 @@ the names of the algorithms (used to find those events) describe how the algorit | Acoustic Event | Algorithm name | Parameters name | |:--------------:|:-----------------:|:----------------------------:| -| Shriek | `Blob` | `!BlobParameters` | -| Whistle | `HorizontalTrack` | `!HorizontalTrackParameters` | +| Shriek | `Blob` | `!BlobParameters` | +| Whistle | `OnebinTrack` | `!OnebinTrackParameters` | | Chirp | `ForwardTrack` | `!ForwardTrackParameters` | -| Whip | `UpwardsTrack` | `!UpwardsTrackParameters` | +| Whip | `UpwardTrack` | `!UpwardTrackParameters` | | Click | `VerticalTrack` | `!VerticalTrackParameters` | | Oscillation | `Oscillation` | `!OscillationParameters` | | Harmonic | `Harmonic` | `!HarmonicParameters` | @@ -374,10 +374,10 @@ Some of these algorithms have extra parameters, some do not, but all do have the | I want to find a | I'll use this algorithm | |------------------|------------------------------------------------------------------------------------------| | Shriek | [!BlobParameters](xref:AnalysisPrograms.Recognizers.Base.BlobParameters) | -| Whistle | 🚧 !HorizontalTrackParameters 🚧 | +| Whistle | 🚧 !OnebinTrackAlgorithm 🚧 | | Chirp | [!ForwardTrackParameters](xref:AnalysisPrograms.Recognizers.Base.ForwardTrackParameters) | -| Whip | 🚧!UpwardsTrackParameters 🚧 | -| Click | 🚧 !VerticalTrackParameters 🚧 | +| Whip | 🚧!UpwardTrackParameters 🚧 | +| Click | 🚧 !OneframeTrackParameters 🚧 | | Oscillation | [!OscillationParameters](xref:AnalysisPrograms.Recognizers.Base.OscillationParameters) | | Harmonic | [!HarmonicParameters](xref:AnalysisPrograms.Recognizers.Base.HarmonicParameters) | diff --git a/docs/technical/apidoc/GenericRecognizerConfig.md b/docs/technical/apidoc/GenericRecognizerConfig.md index 85186028a..9b3ea092d 100644 --- a/docs/technical/apidoc/GenericRecognizerConfig.md +++ b/docs/technical/apidoc/GenericRecognizerConfig.md @@ -10,4 +10,4 @@ with two sections: `Profiles` and `PostProcessing`: ```yaml Profiles: PostProcessing: -``` \ No newline at end of file +``` diff --git a/docs/technical/apidoc/OscillationParameters.md b/docs/technical/apidoc/OscillationParameters.md index f95b2e545..3e0a7f85b 100644 --- a/docs/technical/apidoc/OscillationParameters.md +++ b/docs/technical/apidoc/OscillationParameters.md @@ -4,7 +4,10 @@ uid: AnalysisPrograms.Recognizers.Base.OscillationParameters ## Oscillation Event detection -The algorithm to find oscillation events uses a `discrete cosine transform` or *DCT*. Setting the correct DCT for the target syllable requires additional parameters. Here is the `Profiles` declaration in the config file for the _flying fox_. It contains two profiles, the first for a vocalization and the second to detect the rhythmic sound of wing beats as a flying fox takes off or comes in to land. +The algorithm to find oscillation events uses a `discrete cosine transform` or *DCT*. Setting the correct DCT for the +target syllable requires additional parameters. Here is the `Profiles` declaration in the config file for the +_flying fox_. It contains two profiles, the first for a vocalization and the second to detect the rhythmic sound of wing +beats as a flying fox takes off or comes in to land. ```yml Profiles: diff --git a/src/Acoustics.Shared/ConfigFile/ConfigFileException.cs b/src/Acoustics.Shared/ConfigFile/ConfigFileException.cs index 4127dde17..d6f51180b 100644 --- a/src/Acoustics.Shared/ConfigFile/ConfigFileException.cs +++ b/src/Acoustics.Shared/ConfigFile/ConfigFileException.cs @@ -10,17 +10,33 @@ namespace Acoustics.Shared.ConfigFile { using System; + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; using System.IO; + using System.Linq; + using System.Runtime.Serialization; public class ConfigFileException : Exception { - public const string Prelude = "Configuration exception: "; + public const string Prelude = "Configuration exception"; public ConfigFileException(string message) : base(message) { } + public ConfigFileException(IEnumerable validations, string file) + : base(FormatValidations(validations)) + { + this.File = file; + } + + public ConfigFileException(IEnumerable validations, FileInfo file) + : base(FormatValidations(validations)) + { + this.File = file.FullName; + } + public ConfigFileException(string message, string file) : base(message) { @@ -45,6 +61,21 @@ public ConfigFileException() public string File { get; set; } - public override string Message => Prelude + base.Message; + public string ProfileName { get; init; } + + public override string Message => Prelude + this.ProfileName?.Prepend(" in profile ") + ":" + base.Message + "\nin config file: " + (this.File ?? ""); + + private static string FormatValidations(IEnumerable validations) + { + var filtered = validations?.Where(v => v is not null); + if (filtered.IsNullOrEmpty()) + { + throw new ArgumentException("There are no errors in the validation list"); + } + + return filtered + .Select(x => x.MemberNames.Join(",") + ": " + x.ErrorMessage) + .FormatList(); + } } } \ No newline at end of file diff --git a/src/Acoustics.Shared/Extensions/ConfigFileExtensions.cs b/src/Acoustics.Shared/Extensions/ConfigFileExtensions.cs index 6d0d716a7..e0ecbda60 100644 --- a/src/Acoustics.Shared/Extensions/ConfigFileExtensions.cs +++ b/src/Acoustics.Shared/Extensions/ConfigFileExtensions.cs @@ -4,6 +4,8 @@ namespace Acoustics.Shared.ConfigFile { + using System; + using System.ComponentModel.DataAnnotations; using System.IO; using JetBrains.Annotations; @@ -17,5 +19,15 @@ public static void NotNull(this object value, FileInfo file, [System.Runtime.Com throw new ConfigFileException(name + " " + message, file); } } + + public static ValidationResult ValidateNotNull(this object value, string name, string message = "must be set and be not null in the config file") + { + if (value == null) + { + return new ValidationResult(message, name.Wrap()); + } + + return ValidationResult.Success; + } } } \ No newline at end of file diff --git a/src/AnalysisPrograms/Recognizers/GenericRecognizer.cs b/src/AnalysisPrograms/Recognizers/GenericRecognizer.cs index 39f7872e9..7cbe7876e 100644 --- a/src/AnalysisPrograms/Recognizers/GenericRecognizer.cs +++ b/src/AnalysisPrograms/Recognizers/GenericRecognizer.cs @@ -72,8 +72,11 @@ public static void ValidateProfileTagsMatchAlgorithms(Dictionary { if (profile is CommonParameters c) { - c.MinHertz.NotNull(file); - c.MaxHertz.NotNull(file); + var checks = c.Validate(null).Where(v => v is not null); + if (checks.Any()) + { + throw new ConfigFileException(checks, file) { ProfileName = profileName }; + } } string algorithmName; diff --git a/src/AudioAnalysisTools/CommonParameters.cs b/src/AudioAnalysisTools/CommonParameters.cs index 3c238f5e5..b44cbc45e 100644 --- a/src/AudioAnalysisTools/CommonParameters.cs +++ b/src/AudioAnalysisTools/CommonParameters.cs @@ -4,13 +4,16 @@ namespace AnalysisPrograms.Recognizers.Base { + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; + using Acoustics.Shared.ConfigFile; using AudioAnalysisTools.DSP; using TowseyLibrary; /// /// Common parameters needed from a config file to detect components. /// - public abstract class CommonParameters + public abstract class CommonParameters : IValidatableObject { /// /// Gets or sets the name species name to give to a component. @@ -44,7 +47,7 @@ public abstract class CommonParameters /// public double? BgNoiseThreshold { get; set; } - /// + /// snr /// Gets or sets the bottom bound of the rectangle. Units are Hertz. /// public int? MinHertz { get; set; } @@ -89,5 +92,11 @@ public abstract class CommonParameters /// /// One of the values. public NoiseReductionType? NoiseReductionType { get; set; } + + public virtual IEnumerable Validate(ValidationContext validationContext) + { + yield return this.MinHertz.ValidateNotNull(nameof(this.MinHertz)); + yield return this.MaxHertz.ValidateNotNull(nameof(this.MaxHertz)); + } } } \ No newline at end of file diff --git a/src/AudioAnalysisTools/Tracks/ForwardTrackParameters.cs b/src/AudioAnalysisTools/Tracks/ForwardTrackParameters.cs index 7946c819a..58903eca8 100644 --- a/src/AudioAnalysisTools/Tracks/ForwardTrackParameters.cs +++ b/src/AudioAnalysisTools/Tracks/ForwardTrackParameters.cs @@ -9,8 +9,8 @@ namespace AnalysisPrograms.Recognizers.Base using Acoustics.Shared; /// - /// Parameters needed from a config file to detect fowards spectral peak tracks. - /// A FowardTrack sounds like a fluctuating tone or technically, a chirp. Each track point advances one time step. Points may move up or down by at most two frequency bins. + /// Parameters needed from a config file to detect forwards spectral peak tracks. + /// A ForwardTrack sounds like a fluctuating tone or technically, a chirp. Each track point advances one time step. Points may move up or down by at most two frequency bins. /// [YamlTypeTag(typeof(ForwardTrackParameters))] public class ForwardTrackParameters : CommonParameters diff --git a/src/AudioAnalysisTools/Tracks/MinAndMaxBandwidthParameters.cs b/src/AudioAnalysisTools/Tracks/MinAndMaxBandwidthParameters.cs new file mode 100644 index 000000000..9b0f920ca --- /dev/null +++ b/src/AudioAnalysisTools/Tracks/MinAndMaxBandwidthParameters.cs @@ -0,0 +1,34 @@ +// +// 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.Recognizers.Base +{ + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; + using Acoustics.Shared.ConfigFile; + + public class MinAndMaxBandwidthParameters : CommonParameters + { + /// + /// Gets or sets the minimum bandwidth, units = Hertz. + /// + public int? MinBandwidthHertz { get; set; } + + /// + /// Gets or sets maximum bandwidth, units = Hertz. + /// + public int? MaxBandwidthHertz { get; set; } + + public override IEnumerable Validate(ValidationContext validationContext) + { + yield return this.MinBandwidthHertz.ValidateNotNull(nameof(this.MinBandwidthHertz)); + yield return this.MaxBandwidthHertz.ValidateNotNull(nameof(this.MaxBandwidthHertz)); + + foreach (var validation in base.Validate(validationContext)) + { + yield return validation; + } + } + } +} diff --git a/src/AudioAnalysisTools/Tracks/OneframeTrackParameters.cs b/src/AudioAnalysisTools/Tracks/OneframeTrackParameters.cs index 23bc73290..25c618b36 100644 --- a/src/AudioAnalysisTools/Tracks/OneframeTrackParameters.cs +++ b/src/AudioAnalysisTools/Tracks/OneframeTrackParameters.cs @@ -6,6 +6,7 @@ namespace AnalysisPrograms.Recognizers.Base { using System; using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; using System.Linq; using Acoustics.Shared; using AudioAnalysisTools; @@ -17,18 +18,8 @@ namespace AnalysisPrograms.Recognizers.Base /// Parameters needed from a config file to detect click components. /// [YamlTypeTag(typeof(OneframeTrackParameters))] - public class OneframeTrackParameters : CommonParameters + public class OneframeTrackParameters : MinAndMaxBandwidthParameters { - /// - /// Gets or sets the minimum bandwidth, units = Hertz. - /// - public int? MinBandwidthHertz { get; set; } - - /// - /// Gets or sets maximum bandwidth, units = Hertz. - /// - public int? MaxBandwidthHertz { get; set; } - /// /// MAY NOT WANT TO COMBINE CLICK EVENTS. /// Gets or sets a value indicating whether proximal similar clicks are to be combined. diff --git a/src/AudioAnalysisTools/Tracks/UpwardTrackAlgorithm.cs b/src/AudioAnalysisTools/Tracks/UpwardTrackAlgorithm.cs index 78d76d8c4..d58895bb1 100644 --- a/src/AudioAnalysisTools/Tracks/UpwardTrackAlgorithm.cs +++ b/src/AudioAnalysisTools/Tracks/UpwardTrackAlgorithm.cs @@ -9,6 +9,7 @@ namespace AudioAnalysisTools.Tracks using System.Linq; using System.Text; using Acoustics.Shared; + using Acoustics.Shared.ConfigFile; using AnalysisPrograms.Recognizers.Base; using AudioAnalysisTools.Events; using AudioAnalysisTools.Events.Tracks; @@ -71,8 +72,8 @@ public static (List Events, double[] CombinedIntensity) GetUpwardTr double binWidth = nyquist / (double)binCount; int minBin = (int)Math.Round(parameters.MinHertz.Value / binWidth); int maxBin = (int)Math.Round(parameters.MaxHertz.Value / binWidth); - var minBandwidthHertz = parameters.MinBandwidthHertz.Value; - var maxBandwidthHertz = parameters.MaxBandwidthHertz.Value; + var minBandwidthHertz = parameters.MinBandwidthHertz ?? throw new ArgumentNullException($"{nameof(UpwardTrackParameters.MinBandwidthHertz)} must be set. Check your config file?"); + var maxBandwidthHertz = parameters.MaxBandwidthHertz ?? throw new ArgumentNullException($"{nameof(UpwardTrackParameters.MinBandwidthHertz)} must be set. Check your config file?"); // Calculate the max score for normalisation purposes var maxScore = decibelThreshold * 5; diff --git a/src/AudioAnalysisTools/Tracks/UpwardTrackParameters.cs b/src/AudioAnalysisTools/Tracks/UpwardTrackParameters.cs index d565f330f..03c3d0fb3 100644 --- a/src/AudioAnalysisTools/Tracks/UpwardTrackParameters.cs +++ b/src/AudioAnalysisTools/Tracks/UpwardTrackParameters.cs @@ -6,7 +6,9 @@ namespace AnalysisPrograms.Recognizers.Base { using System; using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; using Acoustics.Shared; + using Acoustics.Shared.ConfigFile; using AudioAnalysisTools; using AudioAnalysisTools.Events; using AudioAnalysisTools.Events.Interfaces; @@ -18,18 +20,8 @@ namespace AnalysisPrograms.Recognizers.Base /// An UpwardTrack sounds like a whip. Each track point ascends one frequency bin. Points may move forwards or back one frame step. /// [YamlTypeTag(typeof(UpwardTrackParameters))] - public class UpwardTrackParameters : CommonParameters + public class UpwardTrackParameters : MinAndMaxBandwidthParameters { - /// - /// Gets or sets the minimum bandwidth, units = Hertz. - /// - public int? MinBandwidthHertz { get; set; } - - /// - /// Gets or sets maximum bandwidth, units = Hertz. - /// - public int? MaxBandwidthHertz { get; set; } - /// /// Gets or sets a value indicating whether proximal similar vertical tracks are to be combined. /// Proximal means track time starts are not separated by more than the specified seconds interval. @@ -41,4 +33,5 @@ public class UpwardTrackParameters : CommonParameters public int SyllableHertzDifference { get; set; } } + }