Skip to content

Commit

Permalink
Added implementation of PannerNode.
Browse files Browse the repository at this point in the history
  • Loading branch information
KristofferStrube committed Jan 2, 2025
1 parent 2ae2c29 commit 33f1a4e
Show file tree
Hide file tree
Showing 7 changed files with 521 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,30 @@
@($"{percentImplemented:00%}") Covered!
</div>

<pre><code>@((MarkupString)compareText)</code></pre>
<pre><code>
@for (int i = 0; i < compareLines.Count; i++)
{
<span style="background-color:@(compareLines[i].color);display:block;min-height:21px;">@($"{new string(' ', 3 - i.ToString().Length)}{i}") @compareLines[i].text</span>
}
</code></pre>

@code {
private string compareText = "";
private List<(string text, string color)> compareLines = [];
private double percentImplemented = 0;
private string color => $"#{(int)(255 - 111 * percentImplemented):X2}{(int)(192 + 46 * percentImplemented):X2}{(int)(203 - 59 * percentImplemented):X2}";

protected override async Task OnInitializedAsync()
{
var compareLines = new List<string>();
var lines = webIDL.Replace("<", "&lt;").Split('\n');
compareLines = [];
var lines = webIDL.Split('\n');
for (int i = 0; i < lines.Count(); i++)
{
var color = supportedRows.Any(interval => i >= interval.start && i <= interval.end) ? "lightgreen" : "pink";
compareLines.Add($"""<span style="background-color:{color};display:block;min-height:21px;">{lines[i]}</span>""");
compareLines.Add((lines[i], color));
}

compareText = string.Join("", compareLines);
var percentImplementedTotal = supportedRows.Sum(r => r.end - r.start + 1) / (float)webIDL.Split('\n').Count();
double delta = 0.00005;
double delta = 0.0005;
while (percentImplemented < percentImplementedTotal)
{
await Task.Delay(1);
Expand All @@ -46,7 +50,8 @@
(46, 266),
(269, 269),
(285, 396),
(421, 468),
(421, 497),
(500, 517),
(582, 583),
(585, 592),
(594, 602)
Expand Down
259 changes: 259 additions & 0 deletions src/KristofferStrube.Blazor.WebAudio/AudioNodes/PannerNode.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using KristofferStrube.Blazor.WebIDL;
using KristofferStrube.Blazor.WebIDL.Exceptions;
using Microsoft.JSInterop;

namespace KristofferStrube.Blazor.WebAudio;
Expand Down Expand Up @@ -27,4 +28,262 @@ public class PannerNode : AudioNode, IJSCreatable<PannerNode>
protected PannerNode(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options)
{
}

/// <summary>
/// Specifies the panning model used by this <see cref="PannerNode"/>. Defaults to <see cref="PanningModelType.EqualPower"/>.
/// </summary>
public async Task<PanningModelType> GetPanningModelAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
return await helper.InvokeAsync<PanningModelType>("getAttribute", JSReference, "panningModel");
}

/// <summary>
/// The x-coordinate position of the audio source in a 3D Cartesian system.
/// </summary>
public async Task<AudioParam> GetPositionXAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
IJSObjectReference jSInstance = await helper.InvokeAsync<IJSObjectReference>("getAttribute", JSReference, "positionX");
return await AudioParam.CreateAsync(JSRuntime, jSInstance, new() { DisposesJSReference = true });
}

/// <summary>
/// The y-coordinate position of the audio source in a 3D Cartesian system.
/// </summary>
public async Task<AudioParam> GetPositionYAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
IJSObjectReference jSInstance = await helper.InvokeAsync<IJSObjectReference>("getAttribute", JSReference, "positionY");
return await AudioParam.CreateAsync(JSRuntime, jSInstance, new() { DisposesJSReference = true });
}

/// <summary>
/// The z-coordinate position of the audio source in a 3D Cartesian system.
/// </summary>
public async Task<AudioParam> GetPositionZAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
IJSObjectReference jSInstance = await helper.InvokeAsync<IJSObjectReference>("getAttribute", JSReference, "positionZ");
return await AudioParam.CreateAsync(JSRuntime, jSInstance, new() { DisposesJSReference = true });
}

/// <summary>
/// Describes the x-component of the vector of the direction the audio source is pointing in 3D Cartesian coordinate space.
/// </summary>
public async Task<AudioParam> GetOrientationXAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
IJSObjectReference jSInstance = await helper.InvokeAsync<IJSObjectReference>("getAttribute", JSReference, "orientationX");
return await AudioParam.CreateAsync(JSRuntime, jSInstance, new() { DisposesJSReference = true });
}

/// <summary>
/// Describes the y-component of the vector of the direction the audio source is pointing in 3D Cartesian coordinate space.
/// </summary>
public async Task<AudioParam> GetOrientationYAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
IJSObjectReference jSInstance = await helper.InvokeAsync<IJSObjectReference>("getAttribute", JSReference, "orientationY");
return await AudioParam.CreateAsync(JSRuntime, jSInstance, new() { DisposesJSReference = true });
}

/// <summary>
/// Describes the z-component of the vector of the direction the audio source is pointing in 3D Cartesian coordinate space.
/// </summary>
public async Task<AudioParam> GetOrientationZAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
IJSObjectReference jSInstance = await helper.InvokeAsync<IJSObjectReference>("getAttribute", JSReference, "orientationZ");
return await AudioParam.CreateAsync(JSRuntime, jSInstance, new() { DisposesJSReference = true });
}

/// <summary>
/// Specifies the distance model used by this <see cref="PannerNode"/>. Defaults to <see cref="DistanceModelType.Inverse"/>.
/// </summary>
public async Task<DistanceModelType> GetDistanceModelAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
return await helper.InvokeAsync<DistanceModelType>("getAttribute", JSReference, "distanceModel");
}

/// <summary>
/// Gets the reference distance for reducing volume as source moves further from the listener.
/// For distances less than this, the volume is not reduced.
/// The default value is <c>1</c>.
/// </summary>
public async Task<double> GetRefDistanceAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
return await helper.InvokeAsync<double>("getAttribute", JSReference, "refDistance");
}

/// <summary>
/// Sets the reference distance for reducing volume as source moves further from the listener.
/// For distances less than this, the volume is not reduced.
/// </summary>
/// <remarks>
/// It will throw a <see cref="RangeErrorException"/> if this is set to a negative value.
/// </remarks>
/// <exception cref="RangeErrorException"/>
public async Task SetRefDistanceAsync(double value)
{
IJSObjectReference helper = await webAudioHelperTask.Value;
ErrorHandlingJSObjectReference errorHandlingHelper = new(JSRuntime, helper);
await errorHandlingHelper.InvokeVoidAsync("setAttribute", JSReference, "refDistance", value);
}

/// <summary>
/// Gets the maximum distance between source and listener, after which the volume will not be reduced any further.
/// The default value is <c>10000</c>.
/// </summary>
public async Task<double> GetMaxDistanceAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
return await helper.InvokeAsync<double>("getAttribute", JSReference, "maxDistance");
}

/// <summary>
/// Sets the maximum distance between source and listener, after which the volume will not be reduced any further.
/// </summary>
/// <remarks>
/// It will throw a <see cref="RangeErrorException"/> if this is set to a negative value.
/// </remarks>
/// <exception cref="RangeErrorException"/>
public async Task SetMaxDistanceAsync(double value)
{
IJSObjectReference helper = await webAudioHelperTask.Value;
ErrorHandlingJSObjectReference errorHandlingHelper = new(JSRuntime, helper);
await errorHandlingHelper.InvokeVoidAsync("setAttribute", JSReference, "maxDistance", value);
}

/// <summary>
/// Describes how quickly the volume is reduced as source moves away from listener.
/// The default value is <c>1</c>.
/// The nominal range for the rolloffFactor specifies the minimum and maximum values the rolloffFactor can have.
/// Values outside the range are clamped to lie within this range. The nominal range depends on <see cref="GetDistanceModelAsync"/> as follows:
/// <list type="table">
/// <item>
/// <term><see cref="DistanceModelType.Linear"/></term>
/// <description>The nominal range is <c>[0,1]</c></description>
/// </item>
/// <item>
/// <term><see cref="DistanceModelType.Inverse"/></term>
/// <description>The nominal range is <c>[0,∞)</c></description>
/// </item>
/// <item>
/// <term><see cref="DistanceModelType.Exponential"/></term>
/// <description>The nominal range is <c>[0,∞)</c></description>
/// </item>
/// </list>
/// Note that the clamping happens as part of the processing of the distance computation.
/// The attribute reflects the value that was set and is not modified.
/// </summary>
public async Task<double> GetRolloffFactorAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
return await helper.InvokeAsync<double>("getAttribute", JSReference, "rolloffFactor");
}

/// <summary>
/// Describes how quickly the volume is reduced as source moves away from listener.
/// The nominal range for the rolloffFactor specifies the minimum and maximum values the rolloffFactor can have.
/// Values outside the range are clamped to lie within this range. The nominal range depends on <see cref="GetDistanceModelAsync"/> as follows:
/// <list type="table">
/// <item>
/// <term><see cref="DistanceModelType.Linear"/></term>
/// <description>The nominal range is <c>[0,1]</c></description>
/// </item>
/// <item>
/// <term><see cref="DistanceModelType.Inverse"/></term>
/// <description>The nominal range is <c>[0,∞)</c></description>
/// </item>
/// <item>
/// <term><see cref="DistanceModelType.Exponential"/></term>
/// <description>The nominal range is <c>[0,∞)</c></description>
/// </item>
/// </list>
/// Note that the clamping happens as part of the processing of the distance computation.
/// The attribute reflects the value that was set and is not modified.
/// </summary>
/// <remarks>
/// It will throw a <see cref="RangeErrorException"/> if this is set to a negative value.
/// </remarks>
/// <exception cref="RangeErrorException"/>
public async Task SetRolloffFactorAsync(double value)
{
IJSObjectReference helper = await webAudioHelperTask.Value;
ErrorHandlingJSObjectReference errorHandlingHelper = new(JSRuntime, helper);
await errorHandlingHelper.InvokeVoidAsync("setAttribute", JSReference, "rolloffFactor", value);
}

/// <summary>
/// Get the parameter for directional audio sources that is an angle, in degrees, inside of which there will be no volume reduction.
/// The default value is <c>360</c>.
/// The behavior is undefined if the angle is outside the interval <c>[0, 360]</c>.
/// </summary>
public async Task<double> GetConeInnerAngleAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
return await helper.InvokeAsync<double>("getAttribute", JSReference, "coneInnerAngle");
}

/// <summary>
/// Sets the parameter for directional audio sources that is an angle, in degrees, inside of which there will be no volume reduction.
/// The behavior is undefined if the angle is outside the interval <c>[0, 360]</c>.
/// </summary>
public async Task SetConeInnerAngleAsync(double value)
{
IJSObjectReference helper = await webAudioHelperTask.Value;
ErrorHandlingJSObjectReference errorHandlingHelper = new(JSRuntime, helper);
await errorHandlingHelper.InvokeVoidAsync("setAttribute", JSReference, "coneInnerAngle", value);
}

/// <summary>
/// Gets the parameter for directional audio sources that is an angle, in degrees, outside of which the volume will be reduced to a constant value of <see cref="GetConeOuterGain"/>.

Check warning on line 243 in src/KristofferStrube.Blazor.WebAudio/AudioNodes/PannerNode.cs

View workflow job for this annotation

GitHub Actions / build

XML comment has cref attribute 'GetConeOuterGain' that could not be resolved
/// The default value is <c>360</c>.
/// The behavior is undefined if the angle is outside the interval <c>[0, 360]</c>.
/// </summary>
public async Task<double> GetConeOuterAngleAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
return await helper.InvokeAsync<double>("getAttribute", JSReference, "coneOuterAngle");
}

/// <summary>
/// Sets the parameter for directional audio sources that is an angle, in degrees, outside of which the volume will be reduced to a constant value of <see cref="GetConeOuterGain"/>.

Check warning on line 254 in src/KristofferStrube.Blazor.WebAudio/AudioNodes/PannerNode.cs

View workflow job for this annotation

GitHub Actions / build

XML comment has cref attribute 'GetConeOuterGain' that could not be resolved
/// The default value is <c>360</c>.
/// The behavior is undefined if the angle is outside the interval <c>[0, 360]</c>.
/// </summary>
public async Task SetConeOuterAngleAsync(double value)
{
IJSObjectReference helper = await webAudioHelperTask.Value;
ErrorHandlingJSObjectReference errorHandlingHelper = new(JSRuntime, helper);
await errorHandlingHelper.InvokeVoidAsync("setAttribute", JSReference, "coneOuterAngle", value);
}

/// <summary>
/// A parameter for directional audio sources that is the gain outside of <see cref="GetConeOuterAngleAsync"/>.
/// The default value is <c>0</c>.
/// It is a linear value (not dB) in the range <c>[0, 1]</c>.
/// </summary>
public async Task<double> GetConeOuterGainAsync()
{
IJSObjectReference helper = await webAudioHelperTask.Value;
return await helper.InvokeAsync<double>("getAttribute", JSReference, "coneOuterGain");
}

/// <summary>
/// A parameter for directional audio sources that is the gain outside of the <see cref="GetConeOuterAngleAsync"/>.
/// It is a linear value (not dB) in the range <c>[0, 1]</c>.
/// </summary>
/// <remarks>
/// It will throw a <see cref="InvalidStateErrorException"/> if this is set to a value outside the range <c>[0, 1]</c>
/// </remarks>
public async Task SetConeOuterGainAsync(double value)
{
IJSObjectReference helper = await webAudioHelperTask.Value;
ErrorHandlingJSObjectReference errorHandlingHelper = new(JSRuntime, helper);
await errorHandlingHelper.InvokeVoidAsync("setAttribute", JSReference, "coneOuterGain", value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Text.Json;
using System.Text.Json.Serialization;

namespace KristofferStrube.Blazor.WebAudio.Converters;

internal class DistanceModelTypeConverter : JsonConverter<DistanceModelType>
{
public override DistanceModelType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetString() switch
{
"linear" => DistanceModelType.Linear,
"inverse" => DistanceModelType.Inverse,
"exponential" => DistanceModelType.Exponential,
var value => throw new ArgumentException($"Value '{value}' was not a valid {nameof(DistanceModelType)}.")
};
}

public override void Write(Utf8JsonWriter writer, DistanceModelType value, JsonSerializerOptions options)
{
writer.WriteStringValue(value switch
{
DistanceModelType.Linear => "linear",
DistanceModelType.Inverse => "inverse",
DistanceModelType.Exponential => "exponential",
_ => throw new ArgumentException($"Value '{value}' was not a valid {nameof(DistanceModelType)}.")
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Text.Json;
using System.Text.Json.Serialization;

namespace KristofferStrube.Blazor.WebAudio.Converters;

internal class PanningModelTypeConverter : JsonConverter<PanningModelType>
{
public override PanningModelType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetString() switch
{
"equalpower" => PanningModelType.EqualPower,
"HRTF" => PanningModelType.HRTF,
var value => throw new ArgumentException($"Value '{value}' was not a valid {nameof(PanningModelType)}.")
};
}

public override void Write(Utf8JsonWriter writer, PanningModelType value, JsonSerializerOptions options)
{
writer.WriteStringValue(value switch
{
PanningModelType.EqualPower => "equalpower",
PanningModelType.HRTF => "HRTF",
_ => throw new ArgumentException($"Value '{value}' was not a valid {nameof(PanningModelType)}.")
});
}
}
Loading

0 comments on commit 33f1a4e

Please sign in to comment.