diff --git a/Cavern.QuickEQ.Format/ConfigurationFile/CavernFilterStudioConfigurationFile.cs b/Cavern.QuickEQ.Format/ConfigurationFile/CavernFilterStudioConfigurationFile.cs
index aefd61ad..fdb0f6d8 100644
--- a/Cavern.QuickEQ.Format/ConfigurationFile/CavernFilterStudioConfigurationFile.cs
+++ b/Cavern.QuickEQ.Format/ConfigurationFile/CavernFilterStudioConfigurationFile.cs
@@ -8,9 +8,10 @@ namespace Cavern.Format.ConfigurationFile {
///
public class CavernFilterStudioConfigurationFile : ConfigurationFile {
///
- /// Cavern Filter Studio's own export format for full grouped filter pipelines.
+ /// Create an empty file for a standard layout.
///
- public CavernFilterStudioConfigurationFile(int channelCount) : base(ChannelPrototype.GetStandardMatrix(channelCount)) {
+ public CavernFilterStudioConfigurationFile(string name, int channelCount) :
+ base(name, ChannelPrototype.GetStandardMatrix(channelCount)) {
for (int i = 0; i < channelCount; i++) { // Output markers
InputChannels[i].root.AddChild(new FilterGraphNode(new OutputChannel(InputChannels[i].name)));
}
diff --git a/Cavern.QuickEQ.Format/ConfigurationFile/ConfigurationFile.cs b/Cavern.QuickEQ.Format/ConfigurationFile/ConfigurationFile.cs
index 480f23f4..4f89d515 100644
--- a/Cavern.QuickEQ.Format/ConfigurationFile/ConfigurationFile.cs
+++ b/Cavern.QuickEQ.Format/ConfigurationFile/ConfigurationFile.cs
@@ -1,8 +1,10 @@
using System.Collections.Generic;
+using System.Linq;
using Cavern.Channels;
using Cavern.Filters;
using Cavern.Filters.Utilities;
+using Cavern.Utilities;
namespace Cavern.Format.ConfigurationFile {
///
@@ -12,28 +14,55 @@ public abstract class ConfigurationFile {
///
/// Root nodes of each channel, start attaching their filters as a children chain.
///
- /// The root node has a null filter, it's only used to mark in a single instance if the channel is
- /// processed on two separate pipelines from the root.
public (string name, FilterGraphNode root)[] InputChannels { get; }
+ ///
+ /// Named points where the configuration file can be separated to new sections. Split points only consist of input nodes after the
+ /// previous split point's output nodes.
+ ///
+ public IReadOnlyList<(string name, FilterGraphNode[] roots)> SplitPoints { get; }
+
///
/// Create an empty configuration file with the passed input channels.
///
- protected ConfigurationFile(ReferenceChannel[] inputs) {
+ protected ConfigurationFile(string name, ReferenceChannel[] inputs) {
InputChannels = new (string name, FilterGraphNode root)[inputs.Length];
for (int i = 0; i < inputs.Length; i++) {
InputChannels[i] = (inputs[i].GetShortName(), new FilterGraphNode(new InputChannel(inputs[i])));
}
+
+ SplitPoints = new List<(string, FilterGraphNode[])> {
+ (name, InputChannels.GetItem2s())
+ };
}
///
/// Create an empty configuration file with the passed input channel names/labels.
///
- protected ConfigurationFile(string[] inputs) {
+ protected ConfigurationFile(string name, string[] inputs) {
InputChannels = new (string name, FilterGraphNode root)[inputs.Length];
for (int i = 0; i < inputs.Length; i++) {
InputChannels[i] = (inputs[i], new FilterGraphNode(new InputChannel(inputs[i])));
}
+
+ SplitPoints = new List<(string, FilterGraphNode[])> {
+ (name, InputChannels.GetItem2s())
+ };
+ }
+
+ ///
+ /// Adds an entry to the with the current state of the configuration, creating new
+ /// s after each existing .
+ ///
+ /// If you keep track of your currently handled output nodes, set them to their children,
+ /// because new input nodes are created in this function.
+ protected void CreateNewSplitPoint(string name) {
+ FilterGraphNode[] nodes =
+ FilterGraphNodeUtils.MapGraph(InputChannels.Select(x => x.root)).Where(x => x.Filter is OutputChannel).ToArray();
+ for (int i = 0; i < nodes.Length; i++) {
+ nodes[i] = nodes[i].AddChild(new InputChannel(((OutputChannel)nodes[i].Filter).Channel));
+ }
+ ((List<(string, FilterGraphNode[])>)SplitPoints).Add((name, nodes));
}
///
diff --git a/Cavern.QuickEQ.Format/ConfigurationFile/EqualizerAPOConfigurationFile.cs b/Cavern.QuickEQ.Format/ConfigurationFile/EqualizerAPOConfigurationFile.cs
index e82de23a..04da0053 100644
--- a/Cavern.QuickEQ.Format/ConfigurationFile/EqualizerAPOConfigurationFile.cs
+++ b/Cavern.QuickEQ.Format/ConfigurationFile/EqualizerAPOConfigurationFile.cs
@@ -4,6 +4,7 @@
using System.IO;
using System.Linq;
+using Cavern.Channels;
using Cavern.Filters;
using Cavern.Filters.Utilities;
using Cavern.Format.Common;
@@ -19,7 +20,7 @@ public class EqualizerAPOConfigurationFile : ConfigurationFile {
///
/// Filesystem location of the configuration file
/// The sample rate to use when
- public EqualizerAPOConfigurationFile(string path, int sampleRate) : base(channelLabels) {
+ public EqualizerAPOConfigurationFile(string path, int sampleRate) : base(Path.GetFileName(path), channelLabels) {
Dictionary lastNodes = InputChannels.ToDictionary(x => x.name, x => x.root);
List activeChannels = channelLabels.ToList();
AddConfigFile(path, lastNodes, activeChannels, sampleRate);
@@ -43,6 +44,7 @@ void AddConfigFile(string path, Dictionary lastNodes, L
switch (split[0].ToLower(CultureInfo.InvariantCulture)) {
case "include":
string included = Path.Combine(Path.GetDirectoryName(path), string.Join(' ', split, 1, split.Length - 1));
+ CreateSplit(Path.GetFileName(included), lastNodes);
AddConfigFile(included, lastNodes, activeChannels, sampleRate);
break;
case "channel":
@@ -115,6 +117,21 @@ void AddFilter(Dictionary lastNodes, List chann
}
}
+ ///
+ /// Mark the current point of the configuration as the beginning of the next section of filters or next pipeline step.
+ ///
+ void CreateSplit(string name, Dictionary lastNodes) {
+ KeyValuePair[] outputs =
+ lastNodes.Where(x => ReferenceChannelExtensions.FromStandardName(x.Key) != ReferenceChannel.Unknown).ToArray();
+ for (int i = 0; i < outputs.Length; i++) {
+ lastNodes[outputs[i].Key] = lastNodes[outputs[i].Key].AddChild(new OutputChannel(outputs[i].Key));
+ }
+ CreateNewSplitPoint(name);
+ for (int i = 0; i < outputs.Length; i++) {
+ lastNodes[outputs[i].Key] = lastNodes[outputs[i].Key].Children[0];
+ }
+ }
+
///
/// Default initial channels in Equalizer APO.
///
diff --git a/Cavern/Filters/Utilities/FilterGraphNode.cs b/Cavern/Filters/Utilities/FilterGraphNode.cs
index e5661a39..04a2d5cb 100644
--- a/Cavern/Filters/Utilities/FilterGraphNode.cs
+++ b/Cavern/Filters/Utilities/FilterGraphNode.cs
@@ -36,6 +36,28 @@ public class FilterGraphNode {
/// The wrapped filter
public FilterGraphNode(Filter filter) => Filter = filter;
+ ///
+ /// Place a between this and the .
+ ///
+ public void AddAfterParents(FilterGraphNode newParent) {
+ newParent.parents.AddRange(children);
+ for (int i = 0, c = parents.Count; i < c; i++) {
+ parents[i].children.Clear();
+ parents[i].children.Add(newParent);
+ }
+ parents.Clear();
+ AddParent(newParent);
+ }
+
+ ///
+ /// Place a between this and the , then return the new node containing that filter.
+ ///
+ public FilterGraphNode AddAfterParents(Filter filter) {
+ FilterGraphNode node = new FilterGraphNode(filter);
+ AddAfterParents(node);
+ return node;
+ }
+
///
/// Place a between this and the .
///
@@ -50,7 +72,7 @@ public void AddBeforeChildren(FilterGraphNode newChild) {
}
///
- /// Place a between this and the and return the new node containing that filter.
+ /// Place a between this and the , then return the new node containing that filter.
///
public FilterGraphNode AddBeforeChildren(Filter filter) {
FilterGraphNode node = new FilterGraphNode(filter);
diff --git a/Cavern/Filters/Utilities/FilterGraphNodeUtils.cs b/Cavern/Filters/Utilities/FilterGraphNodeUtils.cs
new file mode 100644
index 00000000..bc75ec4d
--- /dev/null
+++ b/Cavern/Filters/Utilities/FilterGraphNodeUtils.cs
@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+
+namespace Cavern.Filters.Utilities {
+ ///
+ /// Special functions for handling s.
+ ///
+ public static class FilterGraphNodeUtils {
+ ///
+ /// Get all nodes in a filter graph knowing the root nodes.
+ ///
+ public static HashSet MapGraph(IEnumerable rootNodes) {
+ HashSet visited = new HashSet();
+ Queue queue = new Queue(rootNodes);
+ while (queue.Count > 0) {
+ FilterGraphNode currentNode = queue.Dequeue();
+ if (visited.Contains(currentNode)) {
+ continue;
+ }
+
+ visited.Add(currentNode);
+ foreach (FilterGraphNode child in currentNode.Children) {
+ queue.Enqueue(child);
+ }
+ }
+
+ return visited;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Cavern/Utilities/TupleUtils.cs b/Cavern/Utilities/TupleUtils.cs
new file mode 100644
index 00000000..edb98acc
--- /dev/null
+++ b/Cavern/Utilities/TupleUtils.cs
@@ -0,0 +1,17 @@
+namespace Cavern.Utilities {
+ ///
+ /// Advanced functions for handling tuples.
+ ///
+ public static class TupleUtils {
+ ///
+ /// From an array of tuples, get only the second "column".
+ ///
+ public static T2[] GetItem2s(this (T1, T2)[] items) {
+ T2[] result = new T2[items.Length];
+ for (int i = 0; i < items.Length; i++) {
+ result[i] = items[i].Item2;
+ }
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/CavernSamples/CavernSamples.sln b/CavernSamples/CavernSamples.sln
index b98e7a50..325ec046 100644
--- a/CavernSamples/CavernSamples.sln
+++ b/CavernSamples/CavernSamples.sln
@@ -45,7 +45,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cavern.WPF", "Cavern.WPF\Ca
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microprojects", "Microprojects", "{679D71F9-B8C0-4D52-B3C8-8DE338E3888C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FilterStudio", "FilterStudio\FilterStudio.csproj", "{5F0DDE27-A6F0-4A6D-B7D6-BB7E3AC95471}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FilterStudio", "FilterStudio\FilterStudio.csproj", "{5F0DDE27-A6F0-4A6D-B7D6-BB7E3AC95471}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/CavernSamples/FilterStudio/Graphs/ManipulatableGraph.cs b/CavernSamples/FilterStudio/Graphs/ManipulatableGraph.cs
index 40b94b58..1885f2ea 100644
--- a/CavernSamples/FilterStudio/Graphs/ManipulatableGraph.cs
+++ b/CavernSamples/FilterStudio/Graphs/ManipulatableGraph.cs
@@ -63,9 +63,13 @@ public ManipulatableGraph() {
///
public void SelectNode(string uid) {
Node node = viewer.Graph.FindNode(uid);
+ if (node == null) {
+ return;
+ }
+
node.Attr.LineWidth = 2;
Dispatcher.BeginInvoke(() => { // Call after the graph was redrawn
- OnLeftClick(node);
+ OnLeftClick?.Invoke(node);
});
}
diff --git a/CavernSamples/FilterStudio/Graphs/Parsing.cs b/CavernSamples/FilterStudio/Graphs/Parsing.cs
index 440afcae..8bb478da 100644
--- a/CavernSamples/FilterStudio/Graphs/Parsing.cs
+++ b/CavernSamples/FilterStudio/Graphs/Parsing.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Windows.Media;
+using Cavern.Filters;
using Cavern.Filters.Utilities;
using Cavern.Format.ConfigurationFile;
@@ -21,18 +22,17 @@ public static class Parsing {
/// Convert a 's filter graph to an MSAGL .
///
/// Filter graph to convert, from
- /// Graph background color
- public static Graph ParseConfigurationFile((string name, FilterGraphNode root)[] rootNodes, Color background) {
+ public static Graph ParseConfigurationFile(FilterGraphNode[] rootNodes) {
Graph result = new();
- result.Attr.BackgroundColor = background;
-
for (int i = 0; i < rootNodes.Length; i++) {
- result.AddNode(new StyledNode(rootNodes[i].name, rootNodes[i].root.ToString()) {
- Filter = rootNodes[i].root
+ string uid = rootNodes[i].GetHashCode().ToString();
+ result.AddNode(new StyledNode(uid, rootNodes[i].ToString()) {
+ Filter = rootNodes[i]
});
- IReadOnlyList children = rootNodes[i].root.Children;
+
+ IReadOnlyList children = rootNodes[i].Children;
for (int j = 0, c = children.Count; j < c; j++) {
- AddToGraph(rootNodes[i].name, children[j], result);
+ AddToGraph(uid, children[j], result);
}
}
return result;
@@ -60,6 +60,10 @@ static void AddToGraph(string parent, FilterGraphNode source, Graph target) {
}
new StyledEdge(target, parent, uid);
+
+ if (source.Filter is OutputChannel) {
+ return; // Filters after output channels are part of different splits
+ }
for (int i = 0, c = source.Children.Count; i < c; i++) {
AddToGraph(uid, source.Children[i], target);
}
diff --git a/CavernSamples/FilterStudio/Graphs/PipelineEditor.cs b/CavernSamples/FilterStudio/Graphs/PipelineEditor.cs
new file mode 100644
index 00000000..71c21789
--- /dev/null
+++ b/CavernSamples/FilterStudio/Graphs/PipelineEditor.cs
@@ -0,0 +1,92 @@
+using Microsoft.Msagl.Drawing;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+
+using Cavern.Filters.Utilities;
+using Cavern.Format.ConfigurationFile;
+
+using Color = Microsoft.Msagl.Drawing.Color;
+
+namespace FilterStudio.Graphs {
+ ///
+ /// The layout on which the steps of the filter pipeline can be selected. Each step has all input and output channels,
+ /// they're just parts cut off the whole filter pipeline for better presentation. Think of them as groups on the full filter graph.
+ /// The main feature this makes possible is having preset pipeline steps that can be added later with different configurations,
+ /// such as crossovers.
+ ///
+ public class PipelineEditor : ManipulatableGraph {
+ ///
+ /// Pass the root nodes of the user's selected split.
+ ///
+ public event Action OnSplitChanged;
+
+ ///
+ /// Overrides the background color of the graph.
+ ///
+ public Color background;
+
+ ///
+ /// Source of language strings.
+ ///
+ public ResourceDictionary language;
+
+ ///
+ /// The of which its split points will be presented.
+ ///
+ public ConfigurationFile Source {
+ get => source;
+ set {
+ source = value;
+ RecreateGraph();
+ SelectNode(source.SplitPoints[0].roots.GetHashCode().ToString());
+ OnSplitChanged?.Invoke(source.SplitPoints[0].roots);
+ }
+ }
+ ConfigurationFile source;
+
+ ///
+ /// The layout on which the steps of the filter pipeline can be selected.
+ ///
+ public PipelineEditor() {
+ OnLeftClick += LeftClick;
+ }
+
+ ///
+ /// When the has changed, display its split points.
+ ///
+ void RecreateGraph() {
+ IReadOnlyList<(string name, FilterGraphNode[] roots)> splits = source.SplitPoints;
+ Graph graph = new Graph();
+ graph.Attr.BackgroundColor = background;
+ graph.Attr.LayerDirection = LayerDirection.LR;
+
+ string lastUid = "a";
+ graph.AddNode(new StyledNode(lastUid, (string)language["NInpu"]));
+ for (int i = 0, c = splits.Count; i < c; i++) {
+ string newUid = splits[i].roots.GetHashCode().ToString();
+ graph.AddNode(new StyledNode(newUid, splits[i].name));
+ new StyledEdge(graph, lastUid, newUid);
+ lastUid = newUid;
+ }
+ graph.AddNode(new StyledNode("b", (string)language["NOutp"]));
+ new StyledEdge(graph, lastUid, "b");
+ Graph = graph;
+ }
+
+ ///
+ /// Open the split the user selects.
+ ///
+ void LeftClick(object element) {
+ if (element is not StyledNode node) {
+ return;
+ }
+
+ if (int.TryParse(node.Id, out int rootCode)) {
+ (string _, FilterGraphNode[] roots) = source.SplitPoints.FirstOrDefault(x => x.roots.GetHashCode() == rootCode);
+ OnSplitChanged?.Invoke(roots);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CavernSamples/FilterStudio/MainWindow.Graph.cs b/CavernSamples/FilterStudio/MainWindow.Graph.cs
index 21146287..582ff30c 100644
--- a/CavernSamples/FilterStudio/MainWindow.Graph.cs
+++ b/CavernSamples/FilterStudio/MainWindow.Graph.cs
@@ -1,8 +1,7 @@
using Microsoft.Msagl.Drawing;
+using System;
using System.Collections.Generic;
using System.Windows;
-using System.Windows.Media;
-using System;
using VoidX.WPF;
@@ -11,6 +10,44 @@
namespace FilterStudio {
// Handlers of the filter graph control
partial class MainWindow {
+ ///
+ /// The direction where the graph tree is layed out.
+ ///
+ LayerDirection graphDirection;
+
+ ///
+ /// Change the direction where the graph tree is layed out to top to bottom.
+ ///
+ void SetDirectionTB(object _, RoutedEventArgs e) => SetDirection(LayerDirection.TB);
+
+ ///
+ /// Change the direction where the graph tree is layed out to left to right.
+ ///
+ void SetDirectionLR(object _, RoutedEventArgs e) => SetDirection(LayerDirection.LR);
+
+ ///
+ /// Change the direction where the graph tree is layed out to bottom to top.
+ ///
+ void SetDirectionBT(object _, RoutedEventArgs e) => SetDirection(LayerDirection.BT);
+
+ ///
+ /// Change the direction where the graph tree is layed out to right to left.
+ ///
+ void SetDirectionRL(object _, RoutedEventArgs e) => SetDirection(LayerDirection.RL);
+
+ ///
+ /// Change the direction where the graph tree is layed out.
+ ///
+ void SetDirection(LayerDirection direction) {
+ graphDirection = direction;
+ ReloadGraph();
+ }
+
+ ///
+ /// When the user lost the graph because it was moved outside the screen, this function redisplays it in the center of the frame.
+ ///
+ void Recenter(object _, RoutedEventArgs e) => ReloadGraph();
+
///
/// When selecting a node, open it for modification.
///
@@ -48,7 +85,10 @@ void GraphRightClick(object element) {
///
void ReloadGraph() {
if (rootNodes != null) {
- graph.Graph = Parsing.ParseConfigurationFile(rootNodes, Parsing.ParseBackground((SolidColorBrush)Background));
+ Graph newGraph = Parsing.ParseConfigurationFile(rootNodes);
+ newGraph.Attr.BackgroundColor = pipeline.background;
+ newGraph.Attr.LayerDirection = graphDirection;
+ graph.Graph = newGraph;
}
}
}
diff --git a/CavernSamples/FilterStudio/MainWindow.xaml b/CavernSamples/FilterStudio/MainWindow.xaml
index 7e6a90dc..1005e473 100644
--- a/CavernSamples/FilterStudio/MainWindow.xaml
+++ b/CavernSamples/FilterStudio/MainWindow.xaml
@@ -28,10 +28,17 @@
+
+
@@ -43,7 +50,9 @@
-
+
+
+
diff --git a/CavernSamples/FilterStudio/MainWindow.xaml.cs b/CavernSamples/FilterStudio/MainWindow.xaml.cs
index 7ac6ef5a..95dd7996 100644
--- a/CavernSamples/FilterStudio/MainWindow.xaml.cs
+++ b/CavernSamples/FilterStudio/MainWindow.xaml.cs
@@ -1,11 +1,14 @@
-using Microsoft.Win32;
+using Microsoft.Msagl.Drawing;
+using Microsoft.Win32;
using System;
using System.Windows;
+using System.Windows.Media;
using Cavern;
using Cavern.Filters;
using Cavern.Filters.Utilities;
using Cavern.Format.ConfigurationFile;
+using Cavern.Utilities;
using FilterStudio.Graphs;
using FilterStudio.Resources;
@@ -23,7 +26,7 @@ public partial class MainWindow : Window {
///
/// Each channel's full filter graph.
///
- (string name, FilterGraphNode root)[] rootNodes;
+ FilterGraphNode[] rootNodes;
///
/// Any setting has changed in the application and it should be saved.
@@ -35,6 +38,9 @@ public partial class MainWindow : Window {
///
public MainWindow() {
InitializeComponent();
+ pipeline.OnSplitChanged += SplitChanged;
+ pipeline.background = Parsing.ParseBackground((SolidColorBrush)Background);
+ pipeline.language = language;
graph.OnLeftClick += GraphLeftClick;
graph.OnRightClick += GraphRightClick;
@@ -63,7 +69,7 @@ protected override void OnClosed(EventArgs e) {
/// Create a new empty configuration.
///
void NewConfiguration(object _, RoutedEventArgs e) {
- rootNodes = new CavernFilterStudioConfigurationFile(8).InputChannels;
+ rootNodes = new CavernFilterStudioConfigurationFile((string)language["NSNew"], 8).InputChannels.GetItem2s();
ReloadGraph();
}
@@ -77,16 +83,10 @@ void LoadConfiguration(object _, RoutedEventArgs e) {
if (dialog.ShowDialog().Value) {
ConfigurationFile file = new EqualizerAPOConfigurationFile(dialog.FileName, Listener.DefaultSampleRate);
- rootNodes = file.InputChannels;
- ReloadGraph();
+ pipeline.Source = file;
}
}
- ///
- /// When the user lost the graph because it was moved outside the screen, this function redisplays it in the center of the frame.
- ///
- void Recenter(object _, RoutedEventArgs e) => ReloadGraph();
-
///
/// Delete the currently selected node.
///
@@ -118,6 +118,14 @@ void SetInstructions(object _, RoutedEventArgs e) {
///
void About(object _, RoutedEventArgs e) => MessageBox.Show(Listener.Info, (string)language["HAbou"]);
+ ///
+ /// A different split of the edited file is selected.
+ ///
+ void SplitChanged(FilterGraphNode[] splitRoots) {
+ rootNodes = splitRoots;
+ ReloadGraph();
+ }
+
///
/// Update the name of a filter when any property of it was modified.
///
diff --git a/CavernSamples/FilterStudio/Resources/MainWindowStrings.hu-HU.xaml b/CavernSamples/FilterStudio/Resources/MainWindowStrings.hu-HU.xaml
index 5b93dd30..f07fb23a 100644
--- a/CavernSamples/FilterStudio/Resources/MainWindowStrings.hu-HU.xaml
+++ b/CavernSamples/FilterStudio/Resources/MainWindowStrings.hu-HU.xaml
@@ -5,17 +5,22 @@
_Új konfiguráció
_Konfigurációs fájl megnyitása
- _Szűrő hozzáadása
+ _Szűrők
_Párhuzamos hozzáadás (Shift nyomva tartásával azonos)
_Címke
_Erősítés
_Késleltetés
E_gyszerű parametrikus szűrő...
+ Kiválasztott szűrő _törlése
+ _Törlés
_Gráf
- Középre _mozgatás
- _Kiválasztott szűrő törlése
- _Törlés
+ _Irány
+ _Fentről le
+ _Balról jobbra
+ _Lentről fel
+ _Jobbról balra
+ _Középre mozgatás
Sú_gó
_Segítség mutatása
@@ -31,6 +36,10 @@
Kimenetek után nem adható hozzá szűrő.
Egy csatorna bemenete nem törölhető.
Egy csatorna kimenete nem törölhető.
+
+ Új
+ Bemenet
+ Kimenet
Új címke
Névjegy
diff --git a/CavernSamples/FilterStudio/Resources/MainWindowStrings.xaml b/CavernSamples/FilterStudio/Resources/MainWindowStrings.xaml
index 23ab3bf6..c93c9cb4 100644
--- a/CavernSamples/FilterStudio/Resources/MainWindowStrings.xaml
+++ b/CavernSamples/FilterStudio/Resources/MainWindowStrings.xaml
@@ -5,17 +5,22 @@
_New configuration
_Open configuration file
- _Add filter
+ F_ilters
_Add in parallel (same as holding Shift)
_Label
_Gain
_Delay
_Basic parametric filter...
+ _Delete selected filter
+ _Delete
_Graph
+ _Direction
+ _Top to bottom
+ _Left to right
+ _Bottom to top
+ _Right to left
_Recenter
- _Delete selected filter
- _Delete
_Help
_Show instructions
@@ -31,6 +36,10 @@
Filters can't be added after an output.
The input of a channel can't be deleted.
The output of a channel can't be deleted.
+
+ New
+ Input
+ Output
New label
About