Skip to content

Commit

Permalink
Added support for the ConvolverNode.
Browse files Browse the repository at this point in the history
  • Loading branch information
KristofferStrube committed Oct 15, 2023
1 parent 1fee903 commit a213543
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
SVGElement._stateRepresentation = "";
StateHasChanged();
}
catch (Exception _)
catch (Exception)
{

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
(157, 200),
(202, 252),
(254, 254),
(285, 343),
(285, 355),
(388, 396),
(446, 456),
(461, 468)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class ChannelMergerNode : AudioNode
}

/// <summary>
/// Creates an <see cref="ChannelMergerNode"/> using the standard constructor.
/// Creates a <see cref="ChannelMergerNode"/> using the standard constructor.
/// </summary>
/// <remarks>
/// It throws an <see cref="IndexSizeErrorException"/> if <see cref="ChannelMergerOptions.NumberOfInputs"/> is less than <c>1</c> or larger than the supported number of channels.
Expand All @@ -39,7 +39,7 @@ public class ChannelMergerNode : AudioNode
/// <param name="context">The <see cref="BaseAudioContext"/> this new <see cref="ChannelMergerNode"/> will be associated with.</param>
/// <param name="options">Optional initial parameter value for this <see cref="ChannelMergerNode"/>.</param>
/// <exception cref="IndexSizeErrorException"></exception>
/// <returns>A new instance of an <see cref="ChannelMergerNode"/>.</returns>
/// <returns>A new instance of a <see cref="ChannelMergerNode"/>.</returns>
public static async Task<ChannelMergerNode> CreateAsync(IJSRuntime jSRuntime, BaseAudioContext context, ChannelMergerOptions? options = null)
{
IJSObjectReference helper = await jSRuntime.GetHelperAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class ChannelSplitterNode : AudioNode
}

/// <summary>
/// Creates an <see cref="ChannelSplitterNode"/> using the standard constructor.
/// Creates a <see cref="ChannelSplitterNode"/> using the standard constructor.
/// </summary>
/// <remarks>
/// It throws an <see cref="IndexSizeErrorException"/> if <see cref="ChannelSplitterOptions.NumberOfInputs"/> is less than <c>1</c> or larger than the supported number of channels.
Expand All @@ -38,7 +38,7 @@ public class ChannelSplitterNode : AudioNode
/// <param name="context">The <see cref="BaseAudioContext"/> this new <see cref="ChannelSplitterNode"/> will be associated with.</param>
/// <param name="options">Optional initial parameter value for this <see cref="ChannelSplitterNode"/>.</param>
/// <exception cref="IndexSizeErrorException"></exception>
/// <returns>A new instance of an <see cref="ChannelSplitterNode"/>.</returns>
/// <returns>A new instance of a <see cref="ChannelSplitterNode"/>.</returns>
public static async Task<ChannelSplitterNode> CreateAsync(IJSRuntime jSRuntime, BaseAudioContext context, ChannelSplitterOptions? options = null)
{
IJSObjectReference helper = await jSRuntime.GetHelperAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class ConstantSourceNode : AudioScheduledSourceNode
}

/// <summary>
/// Creates an <see cref="ConstantSourceNode"/> using the standard constructor.
/// Creates a <see cref="ConstantSourceNode"/> using the standard constructor.
/// </summary>
/// <param name="jSRuntime">An <see cref="IJSRuntime"/> instance.</param>
/// <param name="context">The <see cref="BaseAudioContext"/> this new <see cref="ConstantSourceNode"/> will be associated with.</param>
Expand Down
79 changes: 77 additions & 2 deletions src/KristofferStrube.Blazor.WebAudio/AudioNodes/ConvolverNode.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// This interface represents a processing node which applies a linear convolution effect given an impulse response.<br />
/// 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 (<c>1</c> channel) or stereo (<c>2</c> channels) and cannot be increased.
/// Connections from nodes with more channels will be down-mixed appropriately.<br />
/// There are channelCount constraints and channelCountMode constraints for this node.
/// These constraints ensure that the input to the node is either mono or stereo.
Expand All @@ -23,5 +26,77 @@ public class ConvolverNode : AudioNode
return Task.FromResult(new ConvolverNode(jSRuntime, jSReference));
}

/// <summary>
/// Creates a <see cref="ConvolverNode"/> using the standard constructor.
/// </summary>
/// <param name="jSRuntime">An <see cref="IJSRuntime"/> instance.</param>
/// <param name="context">The <see cref="BaseAudioContext"/> this new <see cref="ConvolverNode"/> will be associated with.</param>
/// <param name="options">Optional initial parameter value for this <see cref="ConvolverNode"/>.</param>
/// <returns>A new instance of a <see cref="ConvolverNode"/>.</returns>
public static async Task<ConvolverNode> CreateAsync(IJSRuntime jSRuntime, BaseAudioContext context, ConvolverOptions? options = null)
{
IJSObjectReference helper = await jSRuntime.GetHelperAsync();
IJSObjectReference jSInstance = await helper.InvokeAsync<IJSObjectReference>("constructConvolverNode", context, options);
return new ConvolverNode(jSRuntime, jSInstance);
}

private ConvolverNode(IJSRuntime jSRuntime, IJSObjectReference jSReference) : base(jSRuntime, jSReference) { }

/// <summary>
/// Gets the buffer that is used for convolution.
/// </summary>
public async Task<AudioBuffer?> GetBufferAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
IJSObjectReference? jSInstance = await helper.InvokeAsync<IJSObjectReference?>("getAttribute", JSReference, "buffer");
return jSInstance is null ? null : await AudioBuffer.CreateAsync(JSRuntime, jSInstance);
}

/// <summary>
/// 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 <see cref="ConvolverNode"/> with this impulse response having the given normalization.
/// The initial value of this attribute is <see langword="null"/>.
/// </summary>
/// <remarks>
/// It throws an <see cref="NotSupportedErrorException"/> if <see cref="AudioBuffer.GetNumberOfChannelsAsync"/> is not <c>1</c>, <c>2</c>, <c>4</c>, or if the <see cref="AudioBuffer.GetSampleRateAsync"/> is not equaivalent to the associated <see cref="BaseAudioContext.GetSampleRateAsync"/>.
/// </remarks>
public async Task SetBufferAsync(AudioBuffer? value)
{
IJSObjectReference helper = await webAudioHelperTask.Value;
await helper.InvokeVoidAsync("setAttribute", JSReference, "buffer", value?.JSReference);
}

/// <summary>
/// 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.<br />
/// </summary>
/// <remarks>
/// Its default value is <see langword="true"/> in order to achieve a more uniform output level from the convolver when loaded with diverse impulse responses.
/// If it is set to <see langword="false"/>, 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.<br />
/// If the it is <see langword="false"/> when the buffer attribute is set then the <see cref="ConvolverNode"/> will perform a linear convolution given the exact impulse response contained within the buffer.<br />
/// Otherwise, if the it is <see langword="true"/> when the buffer attribute is set then the <see cref="ConvolverNode"/> will first perform a scaled RMS-power analysis of the audio data contained within buffer to calculate a normalizationScale.
/// During processing, the <see cref="ConvolverNode"/> 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.
/// </remarks>
public async Task<bool> GetNormalizeAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
return await helper.InvokeAsync<bool>("getAttribute", JSReference, "normalize");
}

/// <summary>
/// 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.<br />
/// </summary>
/// <remarks>
/// Its default value is <see langword="true"/> in order to achieve a more uniform output level from the convolver when loaded with diverse impulse responses.
/// If it is set to <see langword="false"/>, 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.<br />
/// If the it is <see langword="false"/> when the buffer attribute is set then the <see cref="ConvolverNode"/> will perform a linear convolution given the exact impulse response contained within the buffer.<br />
/// Otherwise, if the it is <see langword="true"/> when the buffer attribute is set then the <see cref="ConvolverNode"/> will first perform a scaled RMS-power analysis of the audio data contained within buffer to calculate a normalizationScale.
/// During processing, the <see cref="ConvolverNode"/> 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.
/// </remarks>
public async Task SetNormalizeAsync(bool value)
{
IJSObjectReference helper = await webAudioHelperTask.Value;
await helper.InvokeVoidAsync("setAttribute", JSReference, "normalize", value);
}
}
22 changes: 22 additions & 0 deletions src/KristofferStrube.Blazor.WebAudio/Options/ConvolverOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Text.Json.Serialization;

namespace KristofferStrube.Blazor.WebAudio.Options;

/// <summary>
/// This specifies options for constructing a <see cref="ConvolverNode"/>.
/// </summary>
/// <remarks><see href="https://www.w3.org/TR/webaudio/#ConvolverOptions">See the API definition here</see>.</remarks>
public class ConvolverOptions : AudioNodeOptions
{
/// <summary>
/// The desired buffer for the <see cref="ConvolverNode"/>. This buffer will be normalized according to the value of <see cref="DisableNormalization"/>.
/// </summary>
[JsonPropertyName("buffer")]
public AudioBuffer? Buffer { get; set; }

/// <summary>
/// The opposite of the desired initial value for <see cref="ConvolverNode.GetNormalizeAsync"/>.
/// </summary>
[JsonPropertyName("disableNormalization")]
public bool DisableNormalization { get; set; } = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down

0 comments on commit a213543

Please sign in to comment.