From 9749eab44e439a249f4ac6d16916f2874af6a63d Mon Sep 17 00:00:00 2001 From: VoidX Date: Fri, 24 May 2024 22:43:28 +0200 Subject: [PATCH] Filter graph merging to convolutions --- .../Filters/Utilities/FilterGraphNodeUtils.cs | 49 +++++++++ Cavern/Utilities/ArrayExtensions.cs | 17 +++ .../EQAPOtoFIR/Controls/FFTSize.xaml | 24 ---- .../EQAPOtoFIR/Controls/FFTSize.xaml.cs | 103 ------------------ .../EQAPOtoFIR/Dialogs/SegmentsDialog.xaml | 8 +- CavernSamples/EQAPOtoFIR/EQAPOtoFIR.csproj | 1 + CavernSamples/EQAPOtoFIR/MainWindow.xaml | 3 +- CavernSamples/FilterStudio/App.xaml.cs | 5 +- CavernSamples/FilterStudio/Consts/Language.cs | 10 ++ .../FilterStudio/FilterStudio.csproj | 5 + .../FilterStudio/FilterStudio.csproj.user | 17 +++ .../FilterStudio/Graphs/ManipulatableGraph.cs | 45 +++++++- .../FilterStudio/MainWindow.Graph.cs | 29 ++++- CavernSamples/FilterStudio/MainWindow.xaml | 4 + CavernSamples/FilterStudio/MainWindow.xaml.cs | 9 ++ .../Resources/DialogStrings.hu-HU.xaml | 6 + .../FilterStudio/Resources/DialogStrings.xaml | 6 + .../Resources/MainWindowStrings.hu-HU.xaml | 1 + .../Resources/MainWindowStrings.xaml | 1 + .../Windows/ConvolutionLengthDialog.xaml | 25 +++++ .../Windows/ConvolutionLengthDialog.xaml.cs | 31 ++++++ CavernSamples/VoidX.WPF/FFTSize.cs | 21 ++++ CavernSamples/VoidX.WPF/NumericUpDown.xaml.cs | 48 +++++--- 23 files changed, 311 insertions(+), 157 deletions(-) delete mode 100644 CavernSamples/EQAPOtoFIR/Controls/FFTSize.xaml delete mode 100644 CavernSamples/EQAPOtoFIR/Controls/FFTSize.xaml.cs create mode 100644 CavernSamples/FilterStudio/Resources/DialogStrings.hu-HU.xaml create mode 100644 CavernSamples/FilterStudio/Resources/DialogStrings.xaml create mode 100644 CavernSamples/FilterStudio/Windows/ConvolutionLengthDialog.xaml create mode 100644 CavernSamples/FilterStudio/Windows/ConvolutionLengthDialog.xaml.cs create mode 100644 CavernSamples/VoidX.WPF/FFTSize.cs diff --git a/Cavern/Filters/Utilities/FilterGraphNodeUtils.cs b/Cavern/Filters/Utilities/FilterGraphNodeUtils.cs index 4449a929..cd76af34 100644 --- a/Cavern/Filters/Utilities/FilterGraphNodeUtils.cs +++ b/Cavern/Filters/Utilities/FilterGraphNodeUtils.cs @@ -1,10 +1,33 @@ using System.Collections.Generic; +using System.Linq; namespace Cavern.Filters.Utilities { /// /// Special functions for handling s. /// public static class FilterGraphNodeUtils { + /// + /// Convert the filter graph's filters to convolutions, and merge chains together to a single filter. + /// + public static void ConvertToConvolution(IEnumerable rootNodes, int filterLength) { + HashSet visited = new HashSet(); + Queue queue = new Queue(rootNodes); + while (queue.Count > 0) { + FilterGraphNode currentNode = queue.Dequeue(); + if (visited.Contains(currentNode)) { + continue; + } + + if (!(currentNode.Filter is BypassFilter || currentNode.Filter.GetType() == typeof(FastConvolver))) { + ConvertToConvolution(currentNode, filterLength); + } + visited.Add(currentNode); + foreach (FilterGraphNode child in currentNode.Children) { + queue.Enqueue(child); + } + } + } + /// /// Check if the graph has cycles. /// @@ -44,6 +67,32 @@ public static HashSet MapGraph(IEnumerable roo return visited; } + /// + /// Converts this filter to a convolution and upmerges all children until possible. + /// + static void ConvertToConvolution(FilterGraphNode node, int filterLength) { + float[] impulse = new float[filterLength]; + impulse[0] = 1; + + FilterGraphNode downmergeUntil = node; + while (true) { + downmergeUntil.Filter.Process(impulse); + if (downmergeUntil.Children.Count != 1 || downmergeUntil.Children[0].Parents.Count != 1 || + downmergeUntil.Children[0].Filter is BypassFilter) { + break; + } + downmergeUntil = downmergeUntil.Children[0]; + } + + FilterGraphNode[] newChildren = downmergeUntil.Children.ToArray(); + downmergeUntil.DetachChildren(); + node.Filter = new FastConvolver(impulse); + node.DetachChildren(); + for (int i = 0; i < newChildren.Length; i++) { + node.AddChild(newChildren[i]); + } + } + /// /// Starting from a single node, checks if the graph has cycles. /// diff --git a/Cavern/Utilities/ArrayExtensions.cs b/Cavern/Utilities/ArrayExtensions.cs index 427185d3..9efb17c7 100644 --- a/Cavern/Utilities/ArrayExtensions.cs +++ b/Cavern/Utilities/ArrayExtensions.cs @@ -143,6 +143,23 @@ public static float Nearest(this float[] source, float value) { return closest; } + /// + /// Removes a from an if it contains it. + /// + public static void Remove(ref T[] array, T value) { + for (int i = 0; i < array.Length; i++) { + if (!array[i].Equals(value)) { + continue; + } + + T[] replacement = new T[array.Length - 1]; + Array.Copy(array, 0, replacement, 0, i); + Array.Copy(array, i + 1, replacement, i, array.Length - i - 1); + array = replacement; + return; + } + } + /// /// Remove the 0 or default elements from the end of an array. /// diff --git a/CavernSamples/EQAPOtoFIR/Controls/FFTSize.xaml b/CavernSamples/EQAPOtoFIR/Controls/FFTSize.xaml deleted file mode 100644 index d93b86b5..00000000 --- a/CavernSamples/EQAPOtoFIR/Controls/FFTSize.xaml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/CavernSamples/EQAPOtoFIR/Controls/FFTSize.xaml.cs b/CavernSamples/EQAPOtoFIR/Controls/FFTSize.xaml.cs deleted file mode 100644 index c29d994b..00000000 --- a/CavernSamples/EQAPOtoFIR/Controls/FFTSize.xaml.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using System.Windows; -using System.Windows.Controls; - -namespace EQAPOtoFIR { - /// - /// FFT size (numbers that are a power of 2) selector control with limits. - /// - public partial class FFTSize : UserControl { - /// - /// Lower limit for the (inclusive). - /// - public int Minimum { - get => minimum; - set { - minimum = value; - CheckValue(); - } - } - int minimum = 1; - - /// - /// Upper limit for the (inclusive). - /// - public int Maximum { - get => maximum; - set { - maximum = value; - CheckValue(); - } - } - int maximum = 100; - - /// - /// User entered value. - /// - public int Value { - get => value; - set { - valueField.Text = value.ToString(); - CheckValue(); - } - } - int value = 10; - int oldValue; - - /// - /// Number selector control with limits. - /// - public FFTSize() { - oldValue = Value; - InitializeComponent(); - valueField.Text = value.ToString(); - } - - /// - /// Value changed delegate. - /// - public delegate void OnValueChanged(object sender, RoutedEventArgs e); - - /// - /// Called when the is changed. - /// - public event OnValueChanged ValueChanged; - - void CheckValue() { - value = (int)Math.Pow(2, Math.Round(Math.Log(value, 2))); - if (Minimum <= Maximum) { - if (value < Minimum) { - value = Minimum; - valueField.Text = Minimum.ToString(); - } - if (value > Maximum) { - value = Maximum; - valueField.Text = Maximum.ToString(); - } - } - if (oldValue != value) { - oldValue = value; - valueField.Text = value.ToString(); - ValueChanged?.Invoke(valueField, null); - } - } - - void Increase(object sender, RoutedEventArgs e) => Value = (int)Math.Pow(2, Math.Log(Value, 2) + 1); - - void Decrease(object sender, RoutedEventArgs e) => Value = (int)Math.Pow(2, Math.Log(Value, 2) - 1); - - void ManualUpdate(object sender, TextChangedEventArgs e) { - for (int i = 0; i < valueField.Text.Length; ++i) { - if ((valueField.Text[i] < '0' || valueField.Text[i] > '9') && (i != 0 || valueField.Text[i] != '-')) { - valueField.Text = valueField.Text.Remove(i, 1); - ManualUpdate(sender, e); - return; - } - } - if (valueField.Text.Length != 0 && !valueField.Text.Equals("-")) { - value = int.Parse(valueField.Text); - CheckValue(); - } - } - } -} \ No newline at end of file diff --git a/CavernSamples/EQAPOtoFIR/Dialogs/SegmentsDialog.xaml b/CavernSamples/EQAPOtoFIR/Dialogs/SegmentsDialog.xaml index b949dd2b..493c8855 100644 --- a/CavernSamples/EQAPOtoFIR/Dialogs/SegmentsDialog.xaml +++ b/CavernSamples/EQAPOtoFIR/Dialogs/SegmentsDialog.xaml @@ -3,14 +3,14 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:local="clr-namespace:EQAPOtoFIR.Dialogs" - xmlns:EQAPOtoFIR="clr-namespace:EQAPOtoFIR" x:Class="EQAPOtoFIR.Dialogs.SegmentsDialog" + xmlns:voidx="clr-namespace:VoidX.WPF" + x:Class="EQAPOtoFIR.Dialogs.SegmentsDialog" mc:Ignorable="d" Title="C array splitting" Height="100" Width="300">