diff --git a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/AudioEditor.razor b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/AudioEditor.razor
index 5ee36cc..43801a7 100644
--- a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/AudioEditor.razor
+++ b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/AudioEditor.razor
@@ -50,6 +50,7 @@
new(typeof(AudioDestination), element => element.TagName is "RECT" && element.GetAttribute("data-elementtype") == "audio-destination"),
new(typeof(Gain), element => element.TagName is "RECT" && element.GetAttribute("data-elementtype") == "gain"),
new(typeof(Analyser), element => element.TagName is "RECT" && element.GetAttribute("data-elementtype") == "analyser"),
+ new(typeof(BiquadFilter), element => element.TagName is "RECT" && element.GetAttribute("data-elementtype") == "biquad-filter"),
new(typeof(Connector), element => element.TagName is "LINE" && element.GetAttribute("data-elementtype") == "connector"),
};
@@ -60,6 +61,7 @@
new(typeof(AddNewOscillatorMenuItem), (_,_) => true),
new(typeof(AddNewGainMenuItem), (_,_) => true),
new(typeof(AddNewAnalyserMenuItem), (_,_) => true),
+ new(typeof(AddNewBiquadFilterMenuItem), (_,_) => true),
};
[Parameter]
diff --git a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/FloatExtensions.cs b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/FloatExtensions.cs
index 12788b3..30d894c 100644
--- a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/FloatExtensions.cs
+++ b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/FloatExtensions.cs
@@ -6,6 +6,6 @@ internal static class FloatExtensions
{
internal static string AsString(this float f)
{
- return Math.Round(f, 9).ToString(CultureInfo.InvariantCulture);
+ return Math.Round(f, 4).ToString(CultureInfo.InvariantCulture);
}
}
\ No newline at end of file
diff --git a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/MenuItems/AddNewBiquadFilterMenuItem.razor b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/MenuItems/AddNewBiquadFilterMenuItem.razor
new file mode 100644
index 0000000..34ba80e
--- /dev/null
+++ b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/MenuItems/AddNewBiquadFilterMenuItem.razor
@@ -0,0 +1,9 @@
+@using BlazorContextMenu
+🅱
New Biquad Filter
+@code {
+ [CascadingParameter]
+ public required SVGEditor.SVGEditor SVGEditor { get; set; }
+
+ [Parameter]
+ public required object Data { get; set; }
+}
\ No newline at end of file
diff --git a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/NodeEditors/BiquadFilterEditor.razor b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/NodeEditors/BiquadFilterEditor.razor
new file mode 100644
index 0000000..dbc276c
--- /dev/null
+++ b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/NodeEditors/BiquadFilterEditor.razor
@@ -0,0 +1,121 @@
+@using BlazorContextMenu
+@using KristofferStrube.Blazor.SVGEditor.Extensions
+@inherits NodeEditor
+
+
+
+
+
+
+ Biquad Filter
+
+
+
+ @if (QAudioParam is not null && SVGElement.Type
+ is BiquadFilterType.Lowpass
+ or BiquadFilterType.Highpass
+ or BiquadFilterType.Bandpass
+ or BiquadFilterType.Peaking
+ or BiquadFilterType.Notch
+ or BiquadFilterType.Allpass)
+ {
+
+ }
+ @if (DetuneAudioParam is not null)
+ {
+
+ }
+ @if (FrequencyAudioParam is not null)
+ {
+
+ }
+ @if (GainAudioParam is not null && SVGElement.Type
+ is BiquadFilterType.Lowshelf
+ or BiquadFilterType.Highshelf
+ or BiquadFilterType.Peaking)
+ {
+
+ }
+
+
+ SelectPort(0)" cx=@((SVGElement.X).AsString()) cy="@((SVGElement.Y+20).AsString())" r="10" fill="grey">
+
+
+
+
+
+
+
+
+
+
+
+@code {
+ private AudioParam? QAudioParam;
+ private AudioParam? DetuneAudioParam;
+ private AudioParam? FrequencyAudioParam;
+ private AudioParam? GainAudioParam;
+
+ [CascadingParameter]
+ public required AudioContext AudioContext { get; set; }
+
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+ await base.OnAfterRenderAsync(firstRender);
+
+ if (AudioContext is not null)
+ {
+ if (QAudioParam is null)
+ {
+ QAudioParam = await ((BiquadFilterNode)(await SVGElement.AudioNode(AudioContext))).GetQAsync();
+ }
+ if (DetuneAudioParam is null)
+ {
+ DetuneAudioParam = await ((BiquadFilterNode)(await SVGElement.AudioNode(AudioContext))).GetDetuneAsync();
+ }
+ if (FrequencyAudioParam is null)
+ {
+ FrequencyAudioParam = await ((BiquadFilterNode)(await SVGElement.AudioNode(AudioContext))).GetFrequencyAsync();
+ }
+ if (GainAudioParam is null)
+ {
+ GainAudioParam = await ((BiquadFilterNode)(await SVGElement.AudioNode(AudioContext))).GetGainAsync();
+ StateHasChanged();
+ }
+ }
+ }
+
+ private async Task SetType()
+ {
+ if (SVGElement.Type is {} type)
+ {
+ await ((BiquadFilterNode)await SVGElement.AudioNode(AudioContext)).SetTypeAsync(type);
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/Nodes/BiquadFilter.cs b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/Nodes/BiquadFilter.cs
new file mode 100644
index 0000000..9a5ce40
--- /dev/null
+++ b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/Nodes/BiquadFilter.cs
@@ -0,0 +1,162 @@
+using AngleSharp.Dom;
+using KristofferStrube.Blazor.WebAudio.WasmExample.AudioEditor.NodeEditors;
+using System.Globalization;
+using static System.Text.Json.JsonSerializer;
+
+namespace KristofferStrube.Blazor.WebAudio.WasmExample.AudioEditor;
+
+public class BiquadFilter : Node
+{
+ public BiquadFilter(IElement element, SVGEditor.SVGEditor svg) : base(element, svg) { }
+
+ private AudioNode? audioNode;
+ public override Func> AudioNode => async (context) =>
+ {
+ if (audioNode is null)
+ {
+ BiquadFilterOptions options = new();
+ options.Type = Type ?? options.Type;
+ options.Q = Q ?? options.Q;
+ options.Detune = Detune ?? options.Detune;
+ options.Frequency = Frequency ?? options.Frequency;
+ options.Gain = Gain ?? options.Gain;
+
+ BiquadFilterNode oscillator = await BiquadFilterNode.CreateAsync(context.JSRuntime, context, options);
+ audioNode = oscillator;
+ }
+ return audioNode;
+ };
+
+
+ public new float Height
+ {
+ get => 280;
+ set
+ {
+ base.Height = 280;
+ }
+ }
+
+ public BiquadFilterType? Type
+ {
+ get
+ {
+ return Element.GetAttribute("data-type") is { } value ? Deserialize(value) : null;
+ }
+ set
+ {
+ if (value is null)
+ {
+ _ = Element.RemoveAttribute("data-type");
+ }
+ else
+ {
+ Element.SetAttribute("data-type", Serialize(value.Value));
+ }
+ Changed?.Invoke(this);
+ }
+ }
+
+ public float? Q
+ {
+ get
+ {
+ return Element.GetAttribute("data-q") is { } value ? float.Parse(value, CultureInfo.InvariantCulture) : null;
+ }
+ set
+ {
+ if (value is null)
+ {
+ _ = Element.RemoveAttribute("data-q");
+ }
+ else
+ {
+ Element.SetAttribute("data-q", value.Value.AsString());
+ }
+ Changed?.Invoke(this);
+ }
+ }
+
+ public float? Detune
+ {
+ get
+ {
+ return Element.GetAttribute("data-detune") is { } value ? float.Parse(value, CultureInfo.InvariantCulture) : null;
+ }
+ set
+ {
+ if (value is null)
+ {
+ _ = Element.RemoveAttribute("data-detune");
+ }
+ else
+ {
+ Element.SetAttribute("data-detune", value.Value.AsString());
+ }
+ Changed?.Invoke(this);
+ }
+ }
+
+ public float? Frequency
+ {
+ get
+ {
+ return Element.GetAttribute("data-frequnecy") is { } value ? float.Parse(value, CultureInfo.InvariantCulture) : null;
+ }
+ set
+ {
+ if (value is null)
+ {
+ _ = Element.RemoveAttribute("data-frequnecy");
+ }
+ else
+ {
+ Element.SetAttribute("data-frequnecy", value.Value.AsString());
+ }
+ Changed?.Invoke(this);
+ }
+ }
+
+ public float? Gain
+ {
+ get
+ {
+ return Element.GetAttribute("data-gain") is { } value ? float.Parse(value, CultureInfo.InvariantCulture) : null;
+ }
+ set
+ {
+ if (value is null)
+ {
+ _ = Element.RemoveAttribute("data-gain");
+ }
+ else
+ {
+ Element.SetAttribute("data-gain", value.Value.AsString());
+ }
+ Changed?.Invoke(this);
+ }
+ }
+
+ public override Type Presenter => typeof(BiquadFilterEditor);
+
+ public static new void AddNew(SVGEditor.SVGEditor SVG)
+ {
+ IElement element = SVG.Document.CreateElement("RECT");
+ element.SetAttribute("data-elementtype", "biquad-filter");
+
+ BiquadFilter node = new(element, SVG)
+ {
+ Changed = SVG.UpdateInput,
+ Stroke = "#FFEE58",
+ StrokeWidth = "2",
+ Height = 280,
+ Width = 250,
+ };
+
+ (node.X, node.Y) = SVG.LocalDetransform(SVG.LastRightClick);
+
+ SVG.ClearSelectedShapes();
+ SVG.SelectShape(node);
+ SVG.AddElement(node);
+ }
+}
diff --git a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/Nodes/Gain.cs b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/Nodes/Gain.cs
index 8f4cf04..ee5d997 100644
--- a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/Nodes/Gain.cs
+++ b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/Nodes/Gain.cs
@@ -66,12 +66,4 @@ public float? GainValue
SVG.SelectShape(node);
SVG.AddElement(node);
}
-
- public override async void BeforeBeingRemoved()
- {
- if (audioNode is OscillatorNode { } oscillator)
- {
- await oscillator.StopAsync();
- }
- }
}
diff --git a/src/KristofferStrube.Blazor.WebAudio/AudioNodes/BiquadFilterNode.cs b/src/KristofferStrube.Blazor.WebAudio/AudioNodes/BiquadFilterNode.cs
index e973b6c..d9d0877 100644
--- a/src/KristofferStrube.Blazor.WebAudio/AudioNodes/BiquadFilterNode.cs
+++ b/src/KristofferStrube.Blazor.WebAudio/AudioNodes/BiquadFilterNode.cs
@@ -97,7 +97,7 @@ public async Task GetDetuneAsync()
public async Task GetQAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
- IJSObjectReference jSInstance = await helper.InvokeAsync("getAttribute", JSReference, "q");
+ IJSObjectReference jSInstance = await helper.InvokeAsync("getAttribute", JSReference, "Q");
return await AudioParam.CreateAsync(JSRuntime, jSInstance);
}