-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
23 changed files
with
395 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<UserControl x:Class="Cavern.WPF.Controls.GraphRendererControl" | ||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | ||
mc:Ignorable="d" | ||
d:DesignWidth="800" d:DesignHeight="450"> | ||
<Image Name="image"/> | ||
</UserControl> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
using System.Windows; | ||
using System.Windows.Controls; | ||
|
||
using Cavern.QuickEQ.Equalization; | ||
using Cavern.QuickEQ.Graphing; | ||
using Cavern.QuickEQ.Graphing.Overlays; | ||
using Cavern.WPF.Utils; | ||
|
||
namespace Cavern.WPF.Controls { | ||
/// <summary> | ||
/// Displays one or more <see cref="Equalizer"/> filters. | ||
/// </summary> | ||
public partial class GraphRendererControl : UserControl { | ||
/// <summary> | ||
/// The background of the displayed graph. | ||
/// </summary> | ||
public GraphOverlay Overlay { get; set; } = new LogScaleGrid(2, 1, 0xFF000000, 10); | ||
|
||
/// <summary> | ||
/// All displayed curves referencing the current <see cref="renderer"/>. | ||
/// </summary> | ||
readonly List<RenderedCurve> curves = []; | ||
|
||
/// <summary> | ||
/// Cavern's internal graph rendering engine. | ||
/// </summary> | ||
GraphRenderer renderer = new GraphRenderer(1, 1); // Placeholder for initialization, initial invalidation updates it | ||
|
||
/// <summary> | ||
/// Displays one or more <see cref="Equalizer"/> filters. | ||
/// </summary> | ||
public GraphRendererControl() => InitializeComponent(); | ||
|
||
/// <summary> | ||
/// Add a curve with an ARGB color. | ||
/// </summary> | ||
/// <returns>Index of the curve that can be used in <see cref="Invalidate(int)"/>.</returns> | ||
public int AddCurve(Equalizer curve, uint color) { | ||
curves.Add(renderer.AddCurve(curve, color)); | ||
Invalidate(); | ||
return curves.Count - 1; | ||
} | ||
|
||
/// <summary> | ||
/// Remove all displayed curves. | ||
/// </summary> | ||
public void Clear() { | ||
curves.Clear(); | ||
renderer.Clear(); | ||
Invalidate(); | ||
} | ||
|
||
/// <summary> | ||
/// When a curve at a given <paramref name="index"/> has changed, update its drawn curve. | ||
/// </summary> | ||
public void Invalidate(int index) { | ||
curves[index].Update(true); | ||
InvalidateImage(); | ||
} | ||
|
||
/// <summary> | ||
/// Update all data related to the graph and redraw. | ||
/// </summary> | ||
public void Invalidate() { | ||
for (int i = 0, c = curves.Count - 1; i <= c; i++) { | ||
curves[i].Update(i == c); | ||
} | ||
InvalidateImage(); | ||
} | ||
|
||
/// <summary> | ||
/// Update the displayed graph when a curve was added, changed, or removed. | ||
/// </summary> | ||
public void InvalidateImage() => image.Source = renderer.Pixels.ToBitmap(renderer.Width, renderer.Height).ToImageSource(); | ||
|
||
/// <summary> | ||
/// Keep the graph's size at the control resolution. | ||
/// </summary> | ||
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { | ||
base.OnRenderSizeChanged(sizeInfo); | ||
renderer = new((int)(sizeInfo.NewSize.Width + .5), (int)(sizeInfo.NewSize.Height + .5)) { | ||
DynamicRange = 50, | ||
Peak = 25, | ||
Overlay = Overlay | ||
}; | ||
for (int i = 0, c = curves.Count; i < c; i++) { | ||
curves[i] = renderer.AddCurve(curves[i].Curve, curves[i].Color); | ||
} | ||
Invalidate(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<base:OkCancelDialog x:Class="Cavern.WPF.ConvolutionEditor" | ||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
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:base="clr-namespace:Cavern.WPF.BaseClasses" | ||
xmlns:cavern="clr-namespace:Cavern.WPF.Controls" | ||
mc:Ignorable="d" | ||
Title="{StaticResource Title}" Width="600" Height="300"> | ||
<Window.Resources> | ||
<d:ResourceDictionary> | ||
<ResourceDictionary.MergedDictionaries> | ||
<ResourceDictionary Source="Resources/CommonStrings.xaml"/> | ||
<ResourceDictionary Source="Resources/ConvolutionEditorStrings.xaml"/> | ||
</ResourceDictionary.MergedDictionaries> | ||
</d:ResourceDictionary> | ||
</Window.Resources> | ||
<Grid> | ||
<Grid.RowDefinitions> | ||
<RowDefinition Height="*"/> | ||
<RowDefinition Height="*"/> | ||
</Grid.RowDefinitions> | ||
<Grid.ColumnDefinitions> | ||
<ColumnDefinition Width="*"/> | ||
<ColumnDefinition Width="200"/> | ||
</Grid.ColumnDefinitions> | ||
<cavern:GraphRendererControl x:Name="impulseDisplay"/> | ||
<cavern:GraphRendererControl Grid.Row="1" x:Name="fftDisplay"/> | ||
<Grid Grid.Column="1" Grid.RowSpan="2"> | ||
<Button Grid.Column="1" Margin="0,10,10,0" HorizontalAlignment="Right" VerticalAlignment="Top" Width="165" Height="20" | ||
Content="{StaticResource BLoad}" Click="LoadFromFile"/> | ||
<Button Grid.Column="1" Margin="0,0,95,10" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="80" Height="20" | ||
Content="{StaticResource BtnOk}" Click="OK"/> | ||
<Button Grid.Column="1" Margin="0,0,10,10" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="80" Height="20" | ||
Content="{StaticResource BtnCa}" Click="Cancel"/> | ||
<TextBlock x:Name="polarity" Margin="25,35,0,0" HorizontalAlignment="Left" VerticalAlignment="Top"/> | ||
<TextBlock x:Name="phaseDisplay" Margin="25,55,0,0" HorizontalAlignment="Left" VerticalAlignment="Top"/> | ||
<TextBlock x:Name="delay" Margin="25,75,0,0" HorizontalAlignment="Left" VerticalAlignment="Top"/> | ||
</Grid> | ||
</Grid> | ||
</base:OkCancelDialog> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
using Microsoft.Win32; | ||
using System.Windows; | ||
|
||
using Cavern.QuickEQ; | ||
using Cavern.QuickEQ.Equalization; | ||
using Cavern.QuickEQ.Graphing.Overlays; | ||
using Cavern.QuickEQ.Utilities; | ||
using Cavern.Utilities; | ||
using Cavern.WPF.BaseClasses; | ||
using Cavern.Format; | ||
|
||
namespace Cavern.WPF { | ||
/// <summary> | ||
/// Displays properties of a convolution's impulse response and allows loading a new set of samples. | ||
/// </summary> | ||
public partial class ConvolutionEditor : OkCancelDialog { | ||
/// <summary> | ||
/// Last displayed/loaded impulse response of a convolution filter. | ||
/// </summary> | ||
public float[] Impulse { | ||
get => impulse; | ||
set { | ||
impulse = value; | ||
Reset(); | ||
} | ||
} | ||
float[] impulse; | ||
|
||
/// <summary> | ||
/// The initial value of <see cref="impulse"/> as received in the constructor. When the editing is cancelled, | ||
/// or no new convolution samples are loaded, <see cref="Impulse"/> will return its original reference. | ||
/// </summary> | ||
readonly float[] originalImpulse; | ||
|
||
/// <summary> | ||
/// Sample rate of the <see cref="impulse"/>. | ||
/// </summary> | ||
readonly int sampleRate; | ||
|
||
/// <summary> | ||
/// Source of language strings. | ||
/// </summary> | ||
readonly ResourceDictionary language = Consts.Language.GetConvolutionEditorStrings(); | ||
|
||
/// <summary> | ||
/// Source of common language strings. | ||
/// </summary> | ||
readonly ResourceDictionary common = Consts.Language.GetCommonStrings(); | ||
|
||
/// <summary> | ||
/// Displays properties of a convolution's impulse response and allows loading a new set of samples. | ||
/// </summary> | ||
public ConvolutionEditor(float[] impulse, int sampleRate) { | ||
Resources.MergedDictionaries.Add(language); | ||
Resources.MergedDictionaries.Add(common); | ||
InitializeComponent(); | ||
impulseDisplay.Overlay = new Grid(2, 1, 0xFFAAAAAA, 10, 10); | ||
fftDisplay.Overlay = new LogScaleGrid(2, 1, 0xFFAAAAAA, 10); | ||
this.sampleRate = sampleRate; | ||
Impulse = impulse; | ||
originalImpulse = impulse; | ||
} | ||
|
||
/// <inheritdoc/> | ||
protected override void Cancel(object _, RoutedEventArgs e) { | ||
impulse = originalImpulse; | ||
base.Cancel(_, e); | ||
} | ||
|
||
/// <summary> | ||
/// Overwrite the convolution from an impulse response from a file, only allowing the system sample rate. | ||
/// </summary> | ||
void LoadFromFile(object _, RoutedEventArgs e) { | ||
OpenFileDialog dialog = new OpenFileDialog() { | ||
Filter = string.Format((string)common["ImFmt"], AudioReader.filter) | ||
}; | ||
if (dialog.ShowDialog().Value) { | ||
AudioReader file = AudioReader.Open(dialog.FileName); | ||
file.ReadHeader(); | ||
if (file.SampleRate != sampleRate) { | ||
Consts.Language.Error(string.Format((string)language["ESRat"], file.SampleRate, sampleRate)); | ||
} else if (file.ChannelCount != 1) { | ||
Consts.Language.Error((string)language["EMono"]); | ||
} else { | ||
Impulse = file.Read(); | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Reanalyze the <see cref="impulse"/> and redraw the layout. | ||
/// </summary> | ||
void Reset() { | ||
impulseDisplay.Clear(); | ||
fftDisplay.Clear(); | ||
if (impulse == null) { | ||
polarity.Text = string.Empty; | ||
phaseDisplay.Text = string.Empty; | ||
delay.Text = string.Empty; | ||
return; | ||
} | ||
|
||
impulseDisplay.AddCurve(EQGenerator.FromGraph(impulse, 20, 20000), 0xFF0000FF); | ||
Complex[] fft = impulse.FFT(); | ||
fftDisplay.AddCurve(EQGenerator.FromTransferFunction(fft, sampleRate), 0xFF00FF00); | ||
float[] phase = Measurements.GetPhase(fft); | ||
float[] phaseGraph = GraphUtils.ConvertToGraph(phase, 20, 20000, sampleRate, 1024); | ||
WaveformUtils.Gain(phaseGraph, 25 / MathF.PI); | ||
fftDisplay.AddCurve(EQGenerator.FromGraph(phaseGraph, 20, 20000), 0xFFFF0000); | ||
|
||
VerboseImpulseResponse verbose = new(fft); | ||
polarity.Text = string.Format((string)language["VPola"], verbose.Polarity ? '+' : '-'); | ||
phaseDisplay.Text = string.Format((string)language["VPhas"], (180 / Math.PI * verbose.Phase).ToString("0.00")); | ||
delay.Text = string.Format((string)language["VDela"], verbose.Delay); | ||
} | ||
} | ||
} |
Oops, something went wrong.