diff --git a/Cavern.QuickEQ.Format/ConfigurationFile/ConfigurationFile.cs b/Cavern.QuickEQ.Format/ConfigurationFile/ConfigurationFile.cs
index 4c4cf331..5935709b 100644
--- a/Cavern.QuickEQ.Format/ConfigurationFile/ConfigurationFile.cs
+++ b/Cavern.QuickEQ.Format/ConfigurationFile/ConfigurationFile.cs
@@ -70,7 +70,6 @@ public void AddSplitPoint(int index, string name) {
ReferenceChannel channel = ((InputChannel)end[i].Filter).Channel;
end[i].AddChild(new OutputChannel(channel));
}
- return;
}
}
diff --git a/CavernSamples/Cavern.WPF/BaseClasses/OkCancelDialog.cs b/CavernSamples/Cavern.WPF/BaseClasses/OkCancelDialog.cs
new file mode 100644
index 00000000..08e3dcf3
--- /dev/null
+++ b/CavernSamples/Cavern.WPF/BaseClasses/OkCancelDialog.cs
@@ -0,0 +1,21 @@
+using System.Windows;
+
+namespace Cavern.WPF.BaseClasses {
+ ///
+ /// A dialog with OK and Cancel buttons.
+ ///
+ public class OkCancelDialog : Window {
+ ///
+ /// Closes the dialog with the settings applied.
+ ///
+ protected virtual void OK(object _, RoutedEventArgs e) {
+ DialogResult = true;
+ Close();
+ }
+
+ ///
+ /// Closes the dialog with no change applied.
+ ///
+ protected void Cancel(object _, RoutedEventArgs e) => Close();
+ }
+}
\ No newline at end of file
diff --git a/CavernSamples/Cavern.WPF/BiquadEditor.xaml b/CavernSamples/Cavern.WPF/BiquadEditor.xaml
index 86d3dd17..6926fe8e 100644
--- a/CavernSamples/Cavern.WPF/BiquadEditor.xaml
+++ b/CavernSamples/Cavern.WPF/BiquadEditor.xaml
@@ -1,8 +1,9 @@
-
@@ -34,4 +35,4 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/CavernSamples/Cavern.WPF/BiquadEditor.xaml.cs b/CavernSamples/Cavern.WPF/BiquadEditor.xaml.cs
index ce88458a..2660760e 100644
--- a/CavernSamples/Cavern.WPF/BiquadEditor.xaml.cs
+++ b/CavernSamples/Cavern.WPF/BiquadEditor.xaml.cs
@@ -10,6 +10,7 @@
using Cavern.QuickEQ.Graphing.Overlays;
using Cavern.QuickEQ.Utilities;
using Cavern.Utilities;
+using Cavern.WPF.BaseClasses;
using Color = System.Windows.Media.Color;
@@ -17,7 +18,7 @@ namespace Cavern.WPF {
///
/// Biquad filter customization/editor window.
///
- public partial class BiquadEditor : Window {
+ public partial class BiquadEditor : OkCancelDialog {
///
/// The filter created by the user's inputs.
///
@@ -131,18 +132,5 @@ void Draw() {
/// use this check handler to never let it be checked.
///
void DisableCheck(object sender, RoutedEventArgs _) => ((CheckBox)sender).IsChecked = false;
-
- ///
- /// Closes the dialog with the filter selected.
- ///
- void OK(object _, RoutedEventArgs e) {
- DialogResult = true;
- Close();
- }
-
- ///
- /// Closes the dialog with no filter selected.
- ///
- void Cancel(object _, RoutedEventArgs e) => Close();
}
}
\ No newline at end of file
diff --git a/CavernSamples/Cavern.WPF/ChannelSelector.xaml b/CavernSamples/Cavern.WPF/ChannelSelector.xaml
index 6664cf63..e40ca08d 100644
--- a/CavernSamples/Cavern.WPF/ChannelSelector.xaml
+++ b/CavernSamples/Cavern.WPF/ChannelSelector.xaml
@@ -1,8 +1,9 @@
-
@@ -70,4 +71,4 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/CavernSamples/Cavern.WPF/ChannelSelector.xaml.cs b/CavernSamples/Cavern.WPF/ChannelSelector.xaml.cs
index 4bb73497..d732c852 100644
--- a/CavernSamples/Cavern.WPF/ChannelSelector.xaml.cs
+++ b/CavernSamples/Cavern.WPF/ChannelSelector.xaml.cs
@@ -2,12 +2,13 @@
using System.Windows.Controls;
using Cavern.Channels;
+using Cavern.WPF.BaseClasses;
namespace Cavern.WPF {
///
/// Channel layout selector dialog.
///
- public partial class ChannelSelector : Window {
+ public partial class ChannelSelector : OkCancelDialog {
///
/// Set when OK is clicked, contains the channels selected by the user.
///
@@ -63,18 +64,5 @@ public ChannelSelector() {
[ReferenceChannel.TopRearRight] = topRearRight,
};
}
-
- ///
- /// Closes the dialog with the filter selected.
- ///
- void OK(object _, RoutedEventArgs e) {
- DialogResult = true;
- Close();
- }
-
- ///
- /// Closes the dialog with no filter selected.
- ///
- void Cancel(object _, RoutedEventArgs e) => Close();
}
}
\ No newline at end of file
diff --git a/CavernSamples/Cavern.WPF/Consts/Language.cs b/CavernSamples/Cavern.WPF/Consts/Language.cs
index 16731e94..3921d396 100644
--- a/CavernSamples/Cavern.WPF/Consts/Language.cs
+++ b/CavernSamples/Cavern.WPF/Consts/Language.cs
@@ -1,11 +1,13 @@
using System.Globalization;
using System.Windows;
+using Cavern.Channels;
+
namespace Cavern.WPF.Consts {
///
/// Handle fetching of language strings and translations.
///
- static class Language {
+ public static class Language {
///
/// Get the shared translation between windows.
///
@@ -21,6 +23,38 @@ static class Language {
///
public static ResourceDictionary GetChannelSelectorStrings() => channelSelectorCache ??= GetFor("ChannelSelectorStrings");
+ ///
+ /// Return a channel's name in the user's language or fall back to its short name.
+ ///
+ public static string Translate(this ReferenceChannel channel) {
+ ResourceDictionary dictionary = GetChannelSelectorStrings();
+ return channel switch {
+ ReferenceChannel.FrontLeft => (string)dictionary["SpGFL"],
+ ReferenceChannel.FrontLeftCenter => (string)dictionary["SpFLC"],
+ ReferenceChannel.FrontCenter => (string)dictionary["SpGFC"],
+ ReferenceChannel.FrontRightCenter => (string)dictionary["SpFRC"],
+ ReferenceChannel.FrontRight => (string)dictionary["SpGFR"],
+ ReferenceChannel.WideLeft => (string)dictionary["SpGWL"],
+ ReferenceChannel.WideRight => (string)dictionary["SpGWR"],
+ ReferenceChannel.SideLeft => (string)dictionary["SpGSL"],
+ ReferenceChannel.ScreenLFE => (string)dictionary["SpLFE"],
+ ReferenceChannel.SideRight => (string)dictionary["SpGSR"],
+ ReferenceChannel.RearLeft => (string)dictionary["SpGRL"],
+ ReferenceChannel.RearCenter => (string)dictionary["SpGRC"],
+ ReferenceChannel.RearRight => (string)dictionary["SpGRR"],
+ ReferenceChannel.TopFrontLeft => (string)dictionary["SpTFL"],
+ ReferenceChannel.TopFrontCenter => (string)dictionary["SpTFC"],
+ ReferenceChannel.TopFrontRight => (string)dictionary["SpTFR"],
+ ReferenceChannel.TopSideLeft => (string)dictionary["SpTSL"],
+ ReferenceChannel.GodsVoice => (string)dictionary["SpTGV"],
+ ReferenceChannel.TopSideRight => (string)dictionary["SpTSR"],
+ ReferenceChannel.TopRearLeft => (string)dictionary["SpTRL"],
+ ReferenceChannel.TopRearCenter => (string)dictionary["SpTRC"],
+ ReferenceChannel.TopRearRight => (string)dictionary["SpTRR"],
+ _ => channel.GetShortName()
+ };
+ }
+
///
/// Get the translation of a resource file in the user's language, or in English if a translation couldn't be found.
///
diff --git a/CavernSamples/Cavern.WPF/Utils/ChannelOnUI.cs b/CavernSamples/Cavern.WPF/Utils/ChannelOnUI.cs
new file mode 100644
index 00000000..fdb03c97
--- /dev/null
+++ b/CavernSamples/Cavern.WPF/Utils/ChannelOnUI.cs
@@ -0,0 +1,17 @@
+using Cavern.Channels;
+using Cavern.WPF.Consts;
+
+namespace Cavern.WPF.Utils {
+ ///
+ /// Used to display a channel's name on a UI in the user's language and contain which it is.
+ ///
+ public class ChannelOnUI(ReferenceChannel channel) {
+ ///
+ /// The displayed channel.
+ ///
+ public ReferenceChannel Channel = channel;
+
+ ///
+ public override string ToString() => Channel.Translate();
+ }
+}
\ No newline at end of file
diff --git a/CavernSamples/FilterStudio/Consts/Language.cs b/CavernSamples/FilterStudio/Consts/Language.cs
index b409ca23..2b220acd 100644
--- a/CavernSamples/FilterStudio/Consts/Language.cs
+++ b/CavernSamples/FilterStudio/Consts/Language.cs
@@ -3,6 +3,7 @@
using System.Windows;
using FilterStudio.Windows;
+using FilterStudio.Windows.PipelineSteps;
namespace FilterStudio.Consts {
///
@@ -20,6 +21,12 @@ static class Language {
public static ResourceDictionary GetConvolutionLengthDialogStrings() =>
convolutionLengthDialogCache ??= GetFor("ConvolutionLengthDialogStrings");
+ ///
+ /// Get the 's translation.
+ ///
+ public static ResourceDictionary GetCrossoverDialogStrings() =>
+ crossoverDialogCache ??= GetFor("CrossoverDialogStrings");
+
///
/// Get the translation of a resource file in the user's language, or in English if a translation couldn't be found.
///
@@ -46,5 +53,10 @@ static ResourceDictionary GetFor(string resource) {
/// The loaded translation of the for reuse.
///
static ResourceDictionary convolutionLengthDialogCache;
+
+ ///
+ /// The loaded translation of the for reuse.
+ ///
+ static ResourceDictionary crossoverDialogCache;
}
}
\ No newline at end of file
diff --git a/CavernSamples/FilterStudio/FilterStudio.csproj.user b/CavernSamples/FilterStudio/FilterStudio.csproj.user
index 42699006..5322d117 100644
--- a/CavernSamples/FilterStudio/FilterStudio.csproj.user
+++ b/CavernSamples/FilterStudio/FilterStudio.csproj.user
@@ -10,6 +10,12 @@
Code
+
+ Code
+
+
+ Code
+
@@ -27,11 +33,23 @@
Designer
+
+ Designer
+
+
+ Designer
+ DesignerDesigner
+
+ Designer
+
+
+ Designer
+
\ No newline at end of file
diff --git a/CavernSamples/FilterStudio/MainWindow.Pipeline.cs b/CavernSamples/FilterStudio/MainWindow.Pipeline.cs
index cccdb9c3..fdd409ad 100644
--- a/CavernSamples/FilterStudio/MainWindow.Pipeline.cs
+++ b/CavernSamples/FilterStudio/MainWindow.Pipeline.cs
@@ -6,6 +6,7 @@
using VoidX.WPF;
using FilterStudio.Graphs;
+using FilterStudio.Windows.PipelineSteps;
namespace FilterStudio {
// Handlers of the pipeline graph control
@@ -13,26 +14,23 @@ partial class MainWindow {
///
/// Add a new empty pipeline step after the selected node.
///
- void AddStep(object sender, RoutedEventArgs e) {
- StyledNode node = pipeline.GetSelectedNode(sender);
- if (node == null) {
- Error((string)language["NPNod"]);
- return;
- }
+ void AddStep(object sender, RoutedEventArgs e) => PipelineAddition(sender, (uid) => {
+ pipeline.Source.AddSplitPoint(uid, (string)language["NSNew"]);
+ return true;
+ });
- if (!int.TryParse(node.Id, out int uid)) {
- if (node.Id == PipelineEditor.inNodeUid) {
- Error((string)language["NPNod"]);
- return;
- } else {
- uid = pipeline.Source.SplitPoints.Count;
- }
+ ///
+ /// Add a new crossover step after the selected node.
+ ///
+ void AddCrossover(object sender, RoutedEventArgs e) => PipelineAddition(sender, (uid) => {
+ CrossoverDialog dialog = new(GetChannels());
+ if (!dialog.ShowDialog().Value) {
+ return false;
}
- pipeline.Source.AddSplitPoint(uid, (string)language["NSNew"]);
- pipeline.Source = pipeline.Source; // Force a reload of the pipeline graph
- pipeline.SelectNode(uid.ToString()); // Force a reload of the filter graph on the new step
- }
+ // TODO: create the crossover graph, using grouping by frequencies -> make it in Cavern.QuickEQ.Format to be reused
+ return true;
+ });
///
/// Clear the currently selected pipeline step (remove all its filters).
@@ -77,6 +75,33 @@ void DeleteStep(object sender, RoutedEventArgs e) {
ReloadGraph(); // Force a reload of the filter graph
}
+ ///
+ /// Add a new step in the pipeline before the currently selected node.
+ ///
+ /// Either the selected node or a control if not called from a right-click menu
+ /// Tries to add the step to the pipeline and returns if the addition was successful
+ void PipelineAddition(object sender, Func addBeforeUid) {
+ StyledNode node = pipeline.GetSelectedNode(sender);
+ if (node == null) {
+ Error((string)language["NPNod"]);
+ return;
+ }
+
+ if (!int.TryParse(node.Id, out int uid)) {
+ if (node.Id == PipelineEditor.inNodeUid) {
+ Error((string)language["NPNod"]);
+ return;
+ } else {
+ uid = pipeline.Source.SplitPoints.Count;
+ }
+ }
+
+ if (addBeforeUid(uid)) {
+ pipeline.Source = pipeline.Source; // Force a reload of the pipeline graph
+ pipeline.SelectNode(uid.ToString()); // Force a reload of the filter graph on the new step
+ }
+ }
+
///
/// Handle right-clicking on a pipeline .
///
@@ -87,6 +112,7 @@ void PipelineRightClick(object element) {
List<(string, Action