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) + { + + } +
+ + + + + + + + + + +
+
+ +@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); }