From a213543f47a3dd9e4735cd3d9fe846bd58b47e3b Mon Sep 17 00:00:00 2001 From: KristofferStrube Date: Sun, 15 Oct 2023 13:29:03 +0200 Subject: [PATCH] Added support for the `ConvolverNode`. --- .../NodeEditors/AnalyserEditor.razor | 2 +- .../Pages/Status.razor | 2 +- .../AudioNodes/ChannelMergerNode.cs | 4 +- .../AudioNodes/ChannelSplitterNode.cs | 4 +- .../AudioNodes/ConstantSourceNode.cs | 2 +- .../AudioNodes/ConvolverNode.cs | 79 ++++++++++++++++++- .../Options/ConvolverOptions.cs | 22 ++++++ .../KristofferStrube.Blazor.WebAudio.js | 4 + 8 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 src/KristofferStrube.Blazor.WebAudio/Options/ConvolverOptions.cs diff --git a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/NodeEditors/AnalyserEditor.razor b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/NodeEditors/AnalyserEditor.razor index e14d6e5..68a29eb 100644 --- a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/NodeEditors/AnalyserEditor.razor +++ b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/AudioEditor/NodeEditors/AnalyserEditor.razor @@ -73,7 +73,7 @@ SVGElement._stateRepresentation = ""; StateHasChanged(); } - catch (Exception _) + catch (Exception) { } diff --git a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Pages/Status.razor b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Pages/Status.razor index 36bacd4..e99dde1 100644 --- a/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Pages/Status.razor +++ b/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Pages/Status.razor @@ -32,7 +32,7 @@ (157, 200), (202, 252), (254, 254), - (285, 343), + (285, 355), (388, 396), (446, 456), (461, 468) diff --git a/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ChannelMergerNode.cs b/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ChannelMergerNode.cs index cc3a90b..d23b95e 100644 --- a/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ChannelMergerNode.cs +++ b/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ChannelMergerNode.cs @@ -30,7 +30,7 @@ public class ChannelMergerNode : AudioNode } /// - /// Creates an using the standard constructor. + /// Creates a using the standard constructor. /// /// /// It throws an if is less than 1 or larger than the supported number of channels. @@ -39,7 +39,7 @@ public class ChannelMergerNode : AudioNode /// The this new will be associated with. /// Optional initial parameter value for this . /// - /// A new instance of an . + /// A new instance of a . public static async Task CreateAsync(IJSRuntime jSRuntime, BaseAudioContext context, ChannelMergerOptions? options = null) { IJSObjectReference helper = await jSRuntime.GetHelperAsync(); diff --git a/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ChannelSplitterNode.cs b/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ChannelSplitterNode.cs index a437a8a..c3b10f3 100644 --- a/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ChannelSplitterNode.cs +++ b/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ChannelSplitterNode.cs @@ -29,7 +29,7 @@ public class ChannelSplitterNode : AudioNode } /// - /// Creates an using the standard constructor. + /// Creates a using the standard constructor. /// /// /// It throws an if is less than 1 or larger than the supported number of channels. @@ -38,7 +38,7 @@ public class ChannelSplitterNode : AudioNode /// The this new will be associated with. /// Optional initial parameter value for this . /// - /// A new instance of an . + /// A new instance of a . public static async Task CreateAsync(IJSRuntime jSRuntime, BaseAudioContext context, ChannelSplitterOptions? options = null) { IJSObjectReference helper = await jSRuntime.GetHelperAsync(); diff --git a/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ConstantSourceNode.cs b/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ConstantSourceNode.cs index 7e2600e..78581a2 100644 --- a/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ConstantSourceNode.cs +++ b/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ConstantSourceNode.cs @@ -24,7 +24,7 @@ public class ConstantSourceNode : AudioScheduledSourceNode } /// - /// Creates an using the standard constructor. + /// Creates a using the standard constructor. /// /// An instance. /// The this new will be associated with. diff --git a/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ConvolverNode.cs b/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ConvolverNode.cs index 6458508..6845b0c 100644 --- a/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ConvolverNode.cs +++ b/src/KristofferStrube.Blazor.WebAudio/AudioNodes/ConvolverNode.cs @@ -1,10 +1,13 @@ -using Microsoft.JSInterop; +using KristofferStrube.Blazor.WebAudio.Extensions; +using KristofferStrube.Blazor.WebAudio.Options; +using KristofferStrube.Blazor.WebIDL.Exceptions; +using Microsoft.JSInterop; namespace KristofferStrube.Blazor.WebAudio; /// /// This interface represents a processing node which applies a linear convolution effect given an impulse response.
-/// The input of this node is either mono (1 channel) or stereo (2 channels) and cannot be increased. +/// The input of this node is either mono (1 channel) or stereo (2 channels) and cannot be increased. /// Connections from nodes with more channels will be down-mixed appropriately.
/// There are channelCount constraints and channelCountMode constraints for this node. /// These constraints ensure that the input to the node is either mono or stereo. @@ -23,5 +26,77 @@ public class ConvolverNode : AudioNode return Task.FromResult(new ConvolverNode(jSRuntime, jSReference)); } + /// + /// Creates a using the standard constructor. + /// + /// An instance. + /// The this new will be associated with. + /// Optional initial parameter value for this . + /// A new instance of a . + public static async Task CreateAsync(IJSRuntime jSRuntime, BaseAudioContext context, ConvolverOptions? options = null) + { + IJSObjectReference helper = await jSRuntime.GetHelperAsync(); + IJSObjectReference jSInstance = await helper.InvokeAsync("constructConvolverNode", context, options); + return new ConvolverNode(jSRuntime, jSInstance); + } + private ConvolverNode(IJSRuntime jSRuntime, IJSObjectReference jSReference) : base(jSRuntime, jSReference) { } + + /// + /// Gets the buffer that is used for convolution. + /// + public async Task GetBufferAsync() + { + IJSObjectReference helper = await webAudioHelperTask.Value; + IJSObjectReference? jSInstance = await helper.InvokeAsync("getAttribute", JSReference, "buffer"); + return jSInstance is null ? null : await AudioBuffer.CreateAsync(JSRuntime, jSInstance); + } + + /// + /// Sets the buffer that will be used for convolution. + /// At the time when this attribute is set, the buffer and the state of the normalize attribute will be used to configure the with this impulse response having the given normalization. + /// The initial value of this attribute is . + /// + /// + /// It throws an if is not 1, 2, 4, or if the is not equaivalent to the associated . + /// + public async Task SetBufferAsync(AudioBuffer? value) + { + IJSObjectReference helper = await webAudioHelperTask.Value; + await helper.InvokeVoidAsync("setAttribute", JSReference, "buffer", value?.JSReference); + } + + /// + /// Gets the flag for whether the impulse response from the buffer will be scaled by an equal-power normalization when the buffer atttribute is set.
+ ///
+ /// + /// Its default value is in order to achieve a more uniform output level from the convolver when loaded with diverse impulse responses. + /// If it is set to , then the convolution will be rendered with no pre-processing/scaling of the impulse response. + /// Changes to this value do not take effect until the next time the buffer attribute is set.
+ /// If the it is when the buffer attribute is set then the will perform a linear convolution given the exact impulse response contained within the buffer.
+ /// Otherwise, if the it is when the buffer attribute is set then the will first perform a scaled RMS-power analysis of the audio data contained within buffer to calculate a normalizationScale. + /// During processing, the will then take this calculated normalizationScale value and multiply it by the result of the linear convolution resulting from processing the input with the impulse response (represented by the buffer) to produce the final output. + ///
+ public async Task GetNormalizeAsync() + { + IJSObjectReference helper = await webAudioHelperTask.Value; + return await helper.InvokeAsync("getAttribute", JSReference, "normalize"); + } + + /// + /// Sets the flag for whether the impulse response from the buffer will be scaled by an equal-power normalization when the buffer atttribute is set.
+ ///
+ /// + /// Its default value is in order to achieve a more uniform output level from the convolver when loaded with diverse impulse responses. + /// If it is set to , then the convolution will be rendered with no pre-processing/scaling of the impulse response. + /// Changes to this value do not take effect until the next time the buffer attribute is set.
+ /// If the it is when the buffer attribute is set then the will perform a linear convolution given the exact impulse response contained within the buffer.
+ /// Otherwise, if the it is when the buffer attribute is set then the will first perform a scaled RMS-power analysis of the audio data contained within buffer to calculate a normalizationScale. + /// During processing, the will then take this calculated normalizationScale value and multiply it by the result of the linear convolution resulting from processing the input with the impulse response (represented by the buffer) to produce the final output. + ///
+ public async Task SetNormalizeAsync(bool value) + { + IJSObjectReference helper = await webAudioHelperTask.Value; + await helper.InvokeVoidAsync("setAttribute", JSReference, "normalize", value); + } } diff --git a/src/KristofferStrube.Blazor.WebAudio/Options/ConvolverOptions.cs b/src/KristofferStrube.Blazor.WebAudio/Options/ConvolverOptions.cs new file mode 100644 index 0000000..27122b1 --- /dev/null +++ b/src/KristofferStrube.Blazor.WebAudio/Options/ConvolverOptions.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; + +namespace KristofferStrube.Blazor.WebAudio.Options; + +/// +/// This specifies options for constructing a . +/// +/// See the API definition here. +public class ConvolverOptions : AudioNodeOptions +{ + /// + /// The desired buffer for the . This buffer will be normalized according to the value of . + /// + [JsonPropertyName("buffer")] + public AudioBuffer? Buffer { get; set; } + + /// + /// The opposite of the desired initial value for . + /// + [JsonPropertyName("disableNormalization")] + public bool DisableNormalization { get; set; } = false; +} diff --git a/src/KristofferStrube.Blazor.WebAudio/wwwroot/KristofferStrube.Blazor.WebAudio.js b/src/KristofferStrube.Blazor.WebAudio/wwwroot/KristofferStrube.Blazor.WebAudio.js index 50d8a22..1e2598e 100644 --- a/src/KristofferStrube.Blazor.WebAudio/wwwroot/KristofferStrube.Blazor.WebAudio.js +++ b/src/KristofferStrube.Blazor.WebAudio/wwwroot/KristofferStrube.Blazor.WebAudio.js @@ -54,6 +54,10 @@ export function constructDelayNode(context, options) { return new DelayNode(context, options); } +export function constructConvolverNode(context, options) { + return new ConvolverNode(context, options); +} + export function constructAudioBuffer(options) { return new AudioBuffer(options); }