From 074528238e5078d4b95673ad1ed7d68c13483c94 Mon Sep 17 00:00:00 2001 From: Wilbert Bongers Date: Tue, 2 Aug 2022 10:12:24 +0200 Subject: [PATCH] added HighPass filter, LowPass filter, Audiogate and Silencedetection --- .../FFMpeg/Arguments/AudioGateArgument.cs | 55 +++++++++++++++++ .../Arguments/HighPassFilterArgument.cs | 60 +++++++++++++++++++ .../FFMpeg/Arguments/LowPassFilterArgument.cs | 60 +++++++++++++++++++ .../FFMpeg/Arguments/SilenceDetectArgument.cs | 38 ++++++++++++ 4 files changed, 213 insertions(+) create mode 100644 FFMpegCore/FFMpeg/Arguments/AudioGateArgument.cs create mode 100644 FFMpegCore/FFMpeg/Arguments/HighPassFilterArgument.cs create mode 100644 FFMpegCore/FFMpeg/Arguments/LowPassFilterArgument.cs create mode 100644 FFMpegCore/FFMpeg/Arguments/SilenceDetectArgument.cs diff --git a/FFMpegCore/FFMpeg/Arguments/AudioGateArgument.cs b/FFMpegCore/FFMpeg/Arguments/AudioGateArgument.cs new file mode 100644 index 0000000..e95a465 --- /dev/null +++ b/FFMpegCore/FFMpeg/Arguments/AudioGateArgument.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace FFMpegCore.Arguments +{ + public class AudioGateArgument : IAudioFilterArgument + { + private readonly Dictionary _arguments = new Dictionary(); + + /// + /// Audio Gate. + /// + /// Set input level before filtering. Default is 1. Allowed range is from 0.015625 to 64. + /// Set the mode of operation. Can be upward or downward. Default is downward. If set to upward mode, higher parts of signal will be amplified, expanding dynamic range in upward direction. Otherwise, in case of downward lower parts of signal will be reduced. + /// Set the level of gain reduction when the signal is below the threshold. Default is 0.06125. Allowed range is from 0 to 1. Setting this to 0 disables reduction and then filter behaves like expander. + /// If a signal rises above this level the gain reduction is released. Default is 0.125. Allowed range is from 0 to 1. + /// Set a ratio by which the signal is reduced. Default is 2. Allowed range is from 1 to 9000. + /// Amount of milliseconds the signal has to rise above the threshold before gain reduction stops. Default is 20 milliseconds. Allowed range is from 0.01 to 9000. + /// Amount of milliseconds the signal has to fall below the threshold before the reduction is increased again. Default is 250 milliseconds. Allowed range is from 0.01 to 9000. + /// Set amount of amplification of signal after processing. Default is 1. Allowed range is from 1 to 64. + /// Curve the sharp knee around the threshold to enter gain reduction more softly. Default is 2.828427125. Allowed range is from 1 to 8. + /// Choose if exact signal should be taken for detection or an RMS like one. Default is rms. Can be peak or rms. + /// Choose if the average level between all channels or the louder channel affects the reduction. Default is average. Can be average or maximum. + public AudioGateArgument(double level_in = 1, string mode = "downward", double range = 0.06125, double threshold = 0.125, int ratio = 2, double attack = 20, double release = 250, int makeup = 1, double knee = 2.828427125, string detection = "rms", string link = "average") + { + if (level_in < 0.015625 || level_in > 64) throw new ArgumentOutOfRangeException(nameof(level_in), "Level in must be between 0.015625 to 64"); + if (!(mode == "upward" || mode == "downward")) throw new ArgumentOutOfRangeException(nameof(mode), "Mode must be either upward or downward"); + if (range <= 0 || range > 1) throw new ArgumentOutOfRangeException(nameof(range)); + if (threshold < 0 || threshold > 1) throw new ArgumentOutOfRangeException(nameof(threshold), "Threshold must be between 0 and 1"); + if (ratio < 1 || ratio > 9000) throw new ArgumentOutOfRangeException(nameof(ratio), "Ratio must be between 1 and 9000"); + if (attack < 0.01 || attack > 9000) throw new ArgumentOutOfRangeException(nameof(attack), "Attack must be between 0.01 and 9000"); + if (release < 0.01 || release > 9000) throw new ArgumentOutOfRangeException(nameof(release), "Release must be between 0.01 and 9000"); + if (makeup < 1 || makeup > 64) throw new ArgumentOutOfRangeException(nameof(makeup), "Makeup Gain must be between 1 and 64"); + if (!(detection == "peak" || detection == "rms")) throw new ArgumentOutOfRangeException(nameof(detection), "Detection must be either peak or rms"); + if (!(link != "average" || link != "maximum")) throw new ArgumentOutOfRangeException(nameof(link), "Link must be either average or maximum"); + + _arguments.Add("level_in", level_in.ToString("0.00", CultureInfo.InvariantCulture)); + _arguments.Add("mode", mode.ToString()); + _arguments.Add("range", range.ToString("0.00", CultureInfo.InvariantCulture)); + _arguments.Add("threshold", threshold.ToString("0.00", CultureInfo.InvariantCulture)); + _arguments.Add("ratio", ratio.ToString()); + _arguments.Add("attack", attack.ToString("0.00", CultureInfo.InvariantCulture)); + _arguments.Add("release", release.ToString("0.00", CultureInfo.InvariantCulture)); + _arguments.Add("makeup", makeup.ToString()); + _arguments.Add("detection", detection.ToString()); + _arguments.Add("link", link.ToString()); + } + + public string Key { get; } = "agate"; + + public string Value => string.Join(":", _arguments.Select(pair => $"{pair.Key}={pair.Value}")); + } +} diff --git a/FFMpegCore/FFMpeg/Arguments/HighPassFilterArgument.cs b/FFMpegCore/FFMpeg/Arguments/HighPassFilterArgument.cs new file mode 100644 index 0000000..bff7c63 --- /dev/null +++ b/FFMpegCore/FFMpeg/Arguments/HighPassFilterArgument.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace FFMpegCore.Arguments +{ + public class HighPassFilterArgument : IAudioFilterArgument + { + private readonly Dictionary _arguments = new Dictionary(); + private readonly List _widthTypes = new List{"h", "q", "o", "s", "k"}; + private readonly List _transformTypes = new List{"di", "dii", "tdi", "tdii", "latt", "svf", "zdf"}; + private readonly List _precision = new List { "auto", "s16", "s32", "f32", "f64" }; + /// + /// HighPass Filter. + /// + /// Set frequency in Hz. Default is 3000. + /// Set number of poles. Default is 2. + /// Set method to specify band-width of filter, possible values are: h, q, o, s, k + /// Specify the band-width of a filter in width_type units. Applies only to double-pole filter. The default is 0.707q and gives a Butterworth response. + /// How much to use filtered signal in output. Default is 1. Range is between 0 and 1. + /// Specify which channels to filter, by default all available are filtered. + /// Normalize biquad coefficients, by default is disabled. Enabling it will normalize magnitude response at DC to 0dB. + /// Set transform type of IIR filter, possible values are: di, dii, tdi, tdii, latt, svf, zdf + /// Set precison of filtering, possible values are: auto, s16, s32, f32, f64. + /// Set block size used for reverse IIR processing. If this value is set to high enough value (higher than impulse response length truncated when reaches near zero values) filtering will become linear phase otherwise if not big enough it will just produce nasty artifacts. + public HighPassFilterArgument(double frequency = 3000, int poles = 2, string width_type = "q", double width = 0.707, double mix = 1, string channels = "", bool normalize = false, string transform = "", string precision = "auto", int? block_size = null) + { + if (frequency < 0) throw new ArgumentOutOfRangeException(nameof(frequency), "Frequency must be a positive number"); + if (poles < 1 || poles > 2) throw new ArgumentOutOfRangeException(nameof(poles), "Poles must be either 1 or 2"); + if (!_widthTypes.Contains(width_type)) throw new ArgumentOutOfRangeException(nameof(width_type), "Width type must be either " + _widthTypes.ToString()); + if (mix < 0 || mix > 1) throw new ArgumentOutOfRangeException(nameof(mix), "Mix must be between 0 and 1"); + if (!_precision.Contains(precision)) throw new ArgumentOutOfRangeException(nameof(precision), "Precision must be either " + _precision.ToString()); + + _arguments.Add("f", frequency.ToString("0.00", CultureInfo.InvariantCulture)); + _arguments.Add("p", poles.ToString()); + _arguments.Add("t", width_type); + _arguments.Add("w", width.ToString("0.00", CultureInfo.InvariantCulture)); + _arguments.Add("m", mix.ToString("0.00", CultureInfo.InvariantCulture)); + if (channels != "") + { + _arguments.Add("c", channels); + } + _arguments.Add("n", (normalize ? 1 : 0).ToString()); + if (transform != "" && _transformTypes.Contains(transform)) + { + _arguments.Add("a", transform); + } + _arguments.Add("r", precision); + if (block_size != null && block_size >= 0) + { + _arguments.Add("b", block_size.ToString()); + } + } + + public string Key { get; } = "highpass"; + + public string Value => string.Join(":", _arguments.Select(pair => $"{pair.Key}={pair.Value}")); + } +} diff --git a/FFMpegCore/FFMpeg/Arguments/LowPassFilterArgument.cs b/FFMpegCore/FFMpeg/Arguments/LowPassFilterArgument.cs new file mode 100644 index 0000000..ea7d5d7 --- /dev/null +++ b/FFMpegCore/FFMpeg/Arguments/LowPassFilterArgument.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace FFMpegCore.Arguments +{ + public class LowPassFilterArgument : IAudioFilterArgument + { + private readonly Dictionary _arguments = new Dictionary(); + private readonly List _widthTypes = new List { "h", "q", "o", "s", "k" }; + private readonly List _transformTypes = new List { "di", "dii", "tdi", "tdii", "latt", "svf", "zdf" }; + private readonly List _precision = new List { "auto", "s16", "s32", "f32", "f64" }; + /// + /// LowPass Filter. + /// + /// Set frequency in Hz. Default is 3000. + /// Set number of poles. Default is 2. + /// Set method to specify band-width of filter, possible values are: h, q, o, s, k + /// Specify the band-width of a filter in width_type units. Applies only to double-pole filter. The default is 0.707q and gives a Butterworth response. + /// How much to use filtered signal in output. Default is 1. Range is between 0 and 1. + /// Specify which channels to filter, by default all available are filtered. + /// Normalize biquad coefficients, by default is disabled. Enabling it will normalize magnitude response at DC to 0dB. + /// Set transform type of IIR filter, possible values are: di, dii, tdi, tdii, latt, svf, zdf + /// Set precison of filtering, possible values are: auto, s16, s32, f32, f64. + /// Set block size used for reverse IIR processing. If this value is set to high enough value (higher than impulse response length truncated when reaches near zero values) filtering will become linear phase otherwise if not big enough it will just produce nasty artifacts. + public LowPassFilterArgument(double frequency = 3000, int poles = 2, string width_type = "q", double width = 0.707, double mix = 1, string channels = "", bool normalize = false, string transform = "", string precision = "auto", int? block_size = null) + { + if (frequency < 0) throw new ArgumentOutOfRangeException(nameof(frequency), "Frequency must be a positive number"); + if (poles < 1 || poles > 2) throw new ArgumentOutOfRangeException(nameof(poles), "Poles must be either 1 or 2"); + if (!_widthTypes.Contains(width_type)) throw new ArgumentOutOfRangeException(nameof(width_type), "Width type must be either " + _widthTypes.ToString()); + if (mix < 0 || mix > 1) throw new ArgumentOutOfRangeException(nameof(mix), "Mix must be between 0 and 1"); + if (!_precision.Contains(precision)) throw new ArgumentOutOfRangeException(nameof(precision), "Precision must be either " + _precision.ToString()); + + _arguments.Add("f", frequency.ToString("0.00", CultureInfo.InvariantCulture)); + _arguments.Add("p", poles.ToString()); + _arguments.Add("t", width_type); + _arguments.Add("w", width.ToString("0.00", CultureInfo.InvariantCulture)); + _arguments.Add("m", mix.ToString("0.00", CultureInfo.InvariantCulture)); + if (channels != "") + { + _arguments.Add("c", channels); + } + _arguments.Add("n", (normalize ? 1 : 0).ToString()); + if (transform != "" && _transformTypes.Contains(transform)) + { + _arguments.Add("a", transform); + } + _arguments.Add("r", precision); + if (block_size != null && block_size >= 0) + { + _arguments.Add("b", block_size.ToString()); + } + } + + public string Key { get; } = "lowpass"; + + public string Value => string.Join(":", _arguments.Select(pair => $"{pair.Key}={pair.Value}")); + } +} diff --git a/FFMpegCore/FFMpeg/Arguments/SilenceDetectArgument.cs b/FFMpegCore/FFMpeg/Arguments/SilenceDetectArgument.cs new file mode 100644 index 0000000..fff5917 --- /dev/null +++ b/FFMpegCore/FFMpeg/Arguments/SilenceDetectArgument.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace FFMpegCore.Arguments +{ + public class SilenceDetectArgument : IAudioFilterArgument + { + private readonly Dictionary _arguments = new Dictionary(); + /// + /// Silence Detection. + /// + /// Set noise type to db (decibel) or ar (amplitude ratio). Default is dB + /// Set noise tolerance. Can be specified in dB (in case "dB" is appended to the specified value) or amplitude ratio. Default is -60dB, or 0.001. + /// Set silence duration until notification (default is 2 seconds). See (ffmpeg-utils)the Time duration section in the ffmpeg-utils(1) manual for the accepted syntax. + /// Process each channel separately, instead of combined. By default is disabled. + + public SilenceDetectArgument(string noise_type = "db", double noise = 60, double duration = 2, bool mono = false) + { + if(noise_type == "db") + { + _arguments.Add("n", $"{noise.ToString("0.0", CultureInfo.InvariantCulture)}dB"); + } + else if (noise_type == "ar") + { + _arguments.Add("n", noise.ToString("0.00", CultureInfo.InvariantCulture)); + } + else throw new ArgumentOutOfRangeException(nameof(noise_type), "Noise type must be either db or ar"); + _arguments.Add("d", duration.ToString("0.00", CultureInfo.InvariantCulture)); + _arguments.Add("m", (mono ? 1 : 0).ToString()); + } + + public string Key { get; } = "silencedetect"; + + public string Value => string.Join(":", _arguments.Select(pair => $"{pair.Key}={pair.Value}")); + } +}