5 |
6 |
7 |
8 |
20 |
21 |
26 |
27 |
32 |
33 |
34 |
35 |
36 | @code {
37 | private (double x, double y) anchorPosition =>
38 | (
39 | x: SVGElement.X + SVGElement.Width / 2 + Math.Sin(-SVGElement.Rotation / 180 * Math.PI) * 10,
40 | y: SVGElement.Y + SVGElement.Height / 2 + Math.Cos(-SVGElement.Rotation / 180 * Math.PI) * 10
41 | );
42 |
43 | public string Cone(double angle)
44 | {
45 | List<(double x, double y)> points =
46 | [
47 | (SVGElement.X + SVGElement.Width / 2, SVGElement.Y + SVGElement.Height),
48 | (SVGElement.X + SVGElement.Width / 2 + 10 * Math.Sin(angle / 180 * Math.PI), SVGElement.Y + SVGElement.Height + 10 * Math.Cos(angle / 180 * Math.PI)),
49 | (SVGElement.X + SVGElement.Width / 2 + 10 * Math.Sin(-angle / 180 * Math.PI), SVGElement.Y + SVGElement.Height + 10 * Math.Cos(-angle / 180 * Math.PI)),
50 | ];
51 |
52 | return string.Join(" ", points.Select(point => $"{point.x.AsString()},{point.y.AsString()}"));
53 | }
54 | }
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/DoubleExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio.WasmExample;
4 |
5 | public static class DoubleExtensions
6 | {
7 | public static string AsString(this double value)
8 | {
9 | return value.ToString(CultureInfo.InvariantCulture);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/KristofferStrube.Blazor.WebAudio.WasmExample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | true
6 | enable
7 | enable
8 | preview
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Pages/AudioPlayer.razor.css:
--------------------------------------------------------------------------------
1 | .media-control {
2 | background: none;
3 | border: none;
4 | padding: 0;
5 | outline: inherit;
6 | font-size:40px;
7 | margin:2px;
8 | }
9 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Pages/Keyboard.razor.css:
--------------------------------------------------------------------------------
1 | ::deep .anchor-inner-thin {
2 | stroke-width: 2;
3 | r: 10;
4 | }
5 |
6 | ::deep .anchor-inner-thick {
7 | stroke-width: 4;
8 | r: 10;
9 | }
10 |
11 | ::deep .anchor-inner {
12 | stroke-width: 2;
13 | r: 10;
14 | }
15 |
16 | ::deep .anchor-outer-thin {
17 | stroke-width: 2;
18 | r: 12;
19 | }
20 |
21 | ::deep .anchor-outer-thick {
22 | stroke-width: 4;
23 | r: 14;
24 | }
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Pages/Panning.razor.css:
--------------------------------------------------------------------------------
1 | ::deep .anchor-inner-thin {
2 | stroke-width: 2;
3 | r: 10
4 | }
5 |
6 | ::deep .anchor-inner-thick {
7 | stroke-width: 4;
8 | r: 10
9 | }
10 |
11 | ::deep .anchor-outer-thin {
12 | stroke-width: 2;
13 | r: 12
14 | }
15 |
16 | ::deep .anchor-outer-thick {
17 | stroke-width: 4;
18 | r: 14
19 | }
20 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Pages/Spectrogram.razor:
--------------------------------------------------------------------------------
1 | @page "/Spectrogram"
2 |
3 | @page "/Spectogram"
4 | @using KristofferStrube.Blazor.WebIDL
5 | @implements IAsyncDisposable
6 | @inject IJSRuntime JSRuntime
7 | WebAudio - Spectrogram
8 | Spectrogram
9 |
10 |
11 | On this page we create a Spectrogram by passing the sound of a random OscillatorNode
through an AnalyserNode
.
12 |
13 |
14 | @if (oscillator is null)
15 | {
16 | Play Sound 🔊
17 | }
18 | else
19 | {
20 | Stop Sound 🔊
21 | }
22 |
23 |
24 |
25 | @if (frequency is not 0)
26 | {
27 |
28 | frequency: @frequency Hz
29 |
30 | type: @type
31 |
32 | }
33 |
34 |
35 |
36 | @code {
37 | AudioContext context = default!;
38 | GainNode gainNode = default!;
39 | OscillatorNode? oscillator;
40 | AnalyserNode? analyser;
41 | byte[] timeDomainMeasurements = Array.Empty();
42 | byte[] frequencyMeasurements = Array.Empty();
43 | float frequency;
44 | OscillatorType type;
45 |
46 | protected override async Task OnInitializedAsync()
47 | {
48 | context = await AudioContext.CreateAsync(JSRuntime);
49 | gainNode = await GainNode.CreateAsync(JSRuntime, context, new() { Gain = 0.1f });
50 |
51 | AudioDestinationNode destination = await context.GetDestinationAsync();
52 | await gainNode.ConnectAsync(destination);
53 | }
54 |
55 | public async Task PlaySound()
56 | {
57 | await StopSound();
58 | type = (OscillatorType)Random.Shared.Next(0, 4);
59 | frequency = Random.Shared.Next(100, 500);
60 |
61 | analyser = await context.CreateAnalyserAsync();
62 | await analyser.ConnectAsync(gainNode);
63 |
64 | oscillator = await OscillatorNode.CreateAsync(JSRuntime, context, new() { Type = type, Frequency = frequency });
65 | await oscillator.ConnectAsync(analyser);
66 | await oscillator.StartAsync();
67 | }
68 |
69 | public async Task StopSound()
70 | {
71 | if (oscillator is null) return;
72 | await oscillator.StopAsync();
73 | oscillator = null;
74 | }
75 |
76 | public async ValueTask DisposeAsync()
77 | {
78 | await StopSound();
79 | }
80 | }
81 |
82 |
83 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Program.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.MediaCaptureStreams;
2 | using KristofferStrube.Blazor.SVGEditor.Extensions;
3 | using KristofferStrube.Blazor.WebAudio.WasmExample;
4 | using KristofferStrube.Blazor.WebIDL;
5 | using KristofferStrube.Blazor.Window;
6 | using Microsoft.AspNetCore.Components.Web;
7 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
8 |
9 | var builder = WebAssemblyHostBuilder.CreateDefault(args);
10 | builder.RootComponents.Add("#app");
11 | builder.RootComponents.Add("head::after");
12 |
13 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
14 |
15 | builder.Services.AddMediaDevicesService();
16 | builder.Services.AddSVGEditor();
17 | builder.Services.AddWindowService();
18 |
19 | WebAssemblyHost app = builder.Build();
20 |
21 | await app.Services.SetupErrorHandlingJSInterop();
22 |
23 | await app.RunAsync();
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:40854",
7 | "sslPort": 44371
8 | }
9 | },
10 | "profiles": {
11 | "http": {
12 | "commandName": "Project",
13 | "dotnetRunMessages": true,
14 | "launchBrowser": true,
15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
16 | "applicationUrl": "http://localhost:5126",
17 | "environmentVariables": {
18 | "ASPNETCORE_ENVIRONMENT": "Development"
19 | }
20 | },
21 | "https": {
22 | "commandName": "Project",
23 | "dotnetRunMessages": true,
24 | "launchBrowser": true,
25 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
26 | "applicationUrl": "https://localhost:7240;http://localhost:5126",
27 | "environmentVariables": {
28 | "ASPNETCORE_ENVIRONMENT": "Development"
29 | }
30 | },
31 | "IIS Express": {
32 | "commandName": "IISExpress",
33 | "launchBrowser": true,
34 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
35 | "environmentVariables": {
36 | "ASPNETCORE_ENVIRONMENT": "Development"
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/AmplitudePlot.razor:
--------------------------------------------------------------------------------
1 | @using Excubo.Blazor.Canvas
2 |
3 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/AmplitudePlot.razor.cs:
--------------------------------------------------------------------------------
1 | using Excubo.Blazor.Canvas;
2 | using Excubo.Blazor.Canvas.Contexts;
3 | using KristofferStrube.Blazor.WebIDL;
4 | using Microsoft.AspNetCore.Components;
5 | using Microsoft.JSInterop;
6 |
7 | namespace KristofferStrube.Blazor.WebAudio.WasmExample.Shared;
8 |
9 | public partial class AmplitudePlot : ComponentBase, IDisposable
10 | {
11 | private bool running;
12 | private Canvas canvas = default!;
13 |
14 | private string canvasStyle => $"height:{Height}px;width:100%;";
15 |
16 | [Inject]
17 | public required IJSRuntime JSRuntime { get; set; }
18 |
19 | [Parameter, EditorRequired]
20 | public AnalyserNode? Analyser { get; set; }
21 |
22 | [Parameter]
23 | public int Height { get; set; } = 200;
24 |
25 | [Parameter]
26 | public int Width { get; set; } = 180;
27 |
28 | [Parameter]
29 | public string Color { get; set; } = "#000";
30 |
31 | protected override async Task OnAfterRenderAsync(bool _)
32 | {
33 | if (running || Analyser is null)
34 | {
35 | return;
36 | }
37 |
38 | running = true;
39 |
40 | try
41 | {
42 | int bufferLength = (int)await Analyser.GetFftSizeAsync();
43 | await using Uint8Array timeDomainData = await Uint8Array.CreateAsync(JSRuntime, bufferLength);
44 |
45 | while (running)
46 | {
47 | for (int i = 0; i < Width; i++)
48 | {
49 | if (!running)
50 | {
51 | break;
52 | }
53 |
54 | await Analyser.GetByteTimeDomainDataAsync(timeDomainData);
55 |
56 | byte[] reading = await timeDomainData.GetAsArrayAsync();
57 |
58 | double amplitude = reading.Average(r => Math.Abs(r - 128)) / 128.0;
59 |
60 | await using (Context2D context = await canvas.GetContext2DAsync())
61 | {
62 | if (i == 0)
63 | {
64 | await context.FillAndStrokeStyles.FillStyleAsync($"#fff");
65 | await context.FillRectAsync(0, 0, Width * 10, Height * 10);
66 | }
67 |
68 | await context.FillAndStrokeStyles.FillStyleAsync($"#fff");
69 | await context.FillRectAsync(i * 10, 0, 10, Height * 10);
70 |
71 | await context.FillAndStrokeStyles.FillStyleAsync(Color);
72 | await context.FillRectAsync(i * 10, (Height * 10 / 2.0) - (amplitude * Height * 10), 10, amplitude * 2 * Height * 10);
73 | }
74 |
75 | await Task.Delay(1);
76 | }
77 | }
78 | }
79 | catch (Exception e)
80 | {
81 | Console.WriteLine(e.Message);
82 | }
83 | }
84 |
85 | public void Dispose()
86 | {
87 | running = false;
88 | GC.SuppressFinalize(this);
89 | }
90 | }
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/AudioParamSlider.razor:
--------------------------------------------------------------------------------
1 | @using System.Globalization;
2 |
3 | @Label:
4 |
5 |
14 |
15 |
18 |
19 |
20 |
21 | @code {
22 | private string inputId = $"audioParam_{Guid.NewGuid()}"[..9];
23 | private float value = 0;
24 |
25 | [Parameter, EditorRequired]
26 | public required AudioParam AudioParam { get; set; }
27 |
28 | [Parameter, EditorRequired]
29 | public required string Label { get; set; }
30 |
31 | [Parameter,]
32 | public float Min { get; set; } = 0;
33 |
34 | [Parameter]
35 | public float Max { get; set; } = 100;
36 |
37 | [Parameter]
38 | public float StepSize { get; set; } = 0.01f;
39 |
40 | [Parameter]
41 | public Action? UpdateCallback { get; set; }
42 |
43 | protected override async Task OnParametersSetAsync()
44 | {
45 | if (AudioParam is null) return;
46 | value = await AudioParam.GetValueAsync();
47 | }
48 |
49 | public async Task AudioParamUpdatedAsync()
50 | {
51 | if (AudioParam is null) return;
52 | await AudioParam.SetValueAsync(value);
53 | UpdateCallback?.Invoke(value);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/AudioSlicer.razor:
--------------------------------------------------------------------------------
1 | @using Excubo.Blazor.Canvas
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/DoublePlot.razor:
--------------------------------------------------------------------------------
1 |
2 | @for (int i = 1; i < Data1.Length; i++)
3 | {
4 |
5 | }
6 | @for (int i = 1; i < Data2.Length; i++)
7 | {
8 |
9 | }
10 |
11 |
12 | @code {
13 | [Parameter]
14 | public byte[] Data1 { get; set; } = Array.Empty();
15 |
16 | [Parameter]
17 | public byte[] Data2 { get; set; } = Array.Empty();
18 | }
19 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/EnumSelector.razor:
--------------------------------------------------------------------------------
1 | @typeparam TEnum where TEnum : Enum
2 |
3 |
4 | @foreach (TEnum option in Enum.GetValues(typeof(TEnum)))
5 | {
6 | @option
7 | }
8 |
9 |
10 | @code {
11 | [Parameter]
12 | public bool Disabled { get; set; } = false;
13 |
14 | [Parameter, EditorRequired]
15 | public required TEnum Value { get; set; }
16 |
17 | [Parameter]
18 | public EventCallback ValueChanged { get; set; }
19 |
20 | private async Task OnValueChanged()
21 | {
22 | await ValueChanged.InvokeAsync(Value);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/GainSlider.razor:
--------------------------------------------------------------------------------
1 | Gain
2 |
11 | @Math.Round(Gain*100, 0)%
12 |
13 | @code {
14 | private string inputId = $"gain_{Guid.NewGuid()}"[..9];
15 |
16 | [Parameter, EditorRequired]
17 | public GainNode? GainNode { get; set; }
18 |
19 | public float Gain { get; private set; } = 0.05f;
20 |
21 | protected override async Task OnParametersSetAsync()
22 | {
23 | if (GainNode is null) return;
24 | await using AudioParam audioParam = await GainNode.GetGainAsync();
25 | Gain = await audioParam.GetValueAsync();
26 | }
27 |
28 | public async Task GainUpdatedAsync()
29 | {
30 | if (GainNode is null) return;
31 | await using AudioParam audioParam = await GainNode.GetGainAsync();
32 | await audioParam.SetValueAsync(Gain);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 | @inject NavigationManager NavigationManager
3 |
4 |
5 |
6 |
9 |
10 |
11 |
16 |
17 |
18 | @Body
19 |
20 |
21 |
22 |
23 |
24 | @code {
25 | private string relativeUri => NavigationManager.ToBaseRelativePath(NavigationManager.Uri.Split("?")[0]);
26 |
27 | protected string page => (string.IsNullOrEmpty(relativeUri) ? "Index" : relativeUri) + ".razor";
28 | }
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/MainLayout.razor.css:
--------------------------------------------------------------------------------
1 | .page {
2 | position: relative;
3 | display: flex;
4 | flex-direction: column;
5 | }
6 |
7 | main {
8 | flex: 1;
9 | }
10 |
11 | .sidebar {
12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
13 | }
14 |
15 | .top-row {
16 | background-color: #f7f7f7;
17 | border-bottom: 1px solid #d6d5d5;
18 | justify-content: flex-end;
19 | height: 3.5rem;
20 | display: flex;
21 | align-items: center;
22 | }
23 |
24 | .top-row ::deep a, .top-row ::deep .btn-link {
25 | white-space: nowrap;
26 | margin-left: 1.5rem;
27 | text-decoration: none;
28 | }
29 |
30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
31 | text-decoration: underline;
32 | }
33 |
34 | .top-row ::deep a:first-child {
35 | overflow: hidden;
36 | text-overflow: ellipsis;
37 | }
38 |
39 | @media (max-width: 640.98px) {
40 | .top-row:not(.auth) {
41 | display: none;
42 | }
43 |
44 | .top-row.auth {
45 | justify-content: space-between;
46 | }
47 |
48 | .top-row ::deep a, .top-row ::deep .btn-link {
49 | margin-left: 0;
50 | }
51 | }
52 |
53 | @media (min-width: 641px) {
54 | .page {
55 | flex-direction: row;
56 | }
57 |
58 | .sidebar {
59 | width: 250px;
60 | height: 100vh;
61 | position: sticky;
62 | top: 0;
63 | }
64 |
65 | .top-row {
66 | position: sticky;
67 | top: 0;
68 | z-index: 1;
69 | }
70 |
71 | .top-row.auth ::deep a:first-child {
72 | flex: 1;
73 | text-align: right;
74 | width: 0;
75 | }
76 |
77 | .top-row, article {
78 | padding-left: 2rem !important;
79 | padding-right: 1.5rem !important;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/NavMenu.razor.css:
--------------------------------------------------------------------------------
1 | .navbar-toggler {
2 | background-color: rgba(255, 255, 255, 0.1);
3 | }
4 |
5 | .top-row {
6 | height: 3.5rem;
7 | background-color: rgba(0,0,0,0.4);
8 | }
9 |
10 | .navbar-brand {
11 | font-size: 1.1rem;
12 | }
13 |
14 | .oi {
15 | width: 2rem;
16 | font-size: 1.1rem;
17 | vertical-align: text-top;
18 | top: -2px;
19 | }
20 |
21 | .bi {
22 | display: inline-block;
23 | position: relative;
24 | width: 1.25rem;
25 | height: 1.25rem;
26 | margin-right: 0.75rem;
27 | top: -1px;
28 | background-size: cover;
29 | }
30 |
31 | .nav-item {
32 | font-size: 0.9rem;
33 | padding-bottom: 0.5rem;
34 | }
35 |
36 | .nav-item:first-of-type {
37 | padding-top: 1rem;
38 | }
39 |
40 | .nav-item:last-of-type {
41 | padding-bottom: 1rem;
42 | }
43 |
44 | .nav-item ::deep a {
45 | color: #d7d7d7;
46 | border-radius: 4px;
47 | height: 3rem;
48 | display: flex;
49 | align-items: center;
50 | line-height: 3rem;
51 | }
52 |
53 | .nav-item ::deep a.active {
54 | background-color: rgba(255,255,255,0.25);
55 | color: white;
56 | }
57 |
58 | .nav-item ::deep a:hover {
59 | background-color: rgba(255,255,255,0.1);
60 | color: white;
61 | }
62 |
63 | @media (min-width: 641px) {
64 | .navbar-toggler {
65 | display: none;
66 | }
67 |
68 | .collapse {
69 | /* Never collapse the sidebar for wide screens */
70 | display: block;
71 | }
72 |
73 | .nav-scrollable {
74 | /* Allow sidebar to scroll for tall menus */
75 | height: calc(100vh - 3.5rem);
76 | overflow-y: auto;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/Plot.razor:
--------------------------------------------------------------------------------
1 |
2 | @for (int i = 1; i < Data.Length; i++)
3 | {
4 |
5 | }
6 |
7 |
8 | @code {
9 | [Parameter]
10 | public byte[] Data { get; set; } = Array.Empty();
11 |
12 | [Parameter]
13 | public int Height { get; set; } = 200;
14 |
15 | [Parameter]
16 | public string Color { get; set; } = "red";
17 | }
18 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/SpectrogramPlot.razor:
--------------------------------------------------------------------------------
1 | @using Excubo.Blazor.Canvas
2 |
3 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/SpectrogramPlot.razor.cs:
--------------------------------------------------------------------------------
1 | using Excubo.Blazor.Canvas;
2 | using Excubo.Blazor.Canvas.Contexts;
3 | using KristofferStrube.Blazor.WebIDL;
4 | using Microsoft.AspNetCore.Components;
5 | using Microsoft.JSInterop;
6 |
7 | namespace KristofferStrube.Blazor.WebAudio.WasmExample.Shared;
8 |
9 | public partial class SpectrogramPlot : ComponentBase, IDisposable
10 | {
11 | private bool running;
12 | private Canvas canvas = default!;
13 |
14 | private string canvasStyle => $"height:{Height}px;width:100%;";
15 |
16 | [Inject]
17 | public required IJSRuntime JSRuntime { get; set; }
18 |
19 | [Parameter, EditorRequired]
20 | public AnalyserNode? Analyser { get; set; }
21 |
22 | [Parameter]
23 | public int Height { get; set; } = 200;
24 |
25 | [Parameter]
26 | public int Width { get; set; } = 200;
27 |
28 | protected override async Task OnAfterRenderAsync(bool _)
29 | {
30 | if (running || Analyser is null)
31 | {
32 | return;
33 | }
34 |
35 | running = true;
36 |
37 | int bufferLength = (int)await Analyser.GetFrequencyBinCountAsync();
38 | await using Uint8Array frequencyDataArray = await Uint8Array.CreateAsync(JSRuntime, bufferLength);
39 |
40 | while (running)
41 | {
42 | for (int i = 0; i < Width; i++)
43 | {
44 | if (!running)
45 | {
46 | break;
47 | }
48 |
49 | await Analyser.GetByteFrequencyDataAsync(frequencyDataArray);
50 |
51 | byte[] reading = await frequencyDataArray.GetAsArrayAsync();
52 |
53 | await using (Context2D context = await canvas.GetContext2DAsync())
54 | {
55 | await context.FillAndStrokeStyles.FillStyleAsync($"#fff");
56 | await context.FillRectAsync(i, 0, 1, Height);
57 |
58 | for (int j = 0; j < reading.Length; j++)
59 | {
60 | string color = $"#F{(255 - reading[j]) / 16:X}{(255 - reading[j]) / 16:X}";
61 | await context.FillAndStrokeStyles.FillStyleAsync(color);
62 | await context.FillRectAsync(i, j / (double)reading.Length * Height, 1, 1);
63 | }
64 | }
65 | await Task.Delay(1);
66 | }
67 | }
68 | }
69 |
70 | public void Dispose()
71 | {
72 | running = false;
73 | GC.SuppressFinalize(this);
74 | }
75 | }
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/TimeDomainPlot.razor:
--------------------------------------------------------------------------------
1 | @using Excubo.Blazor.Canvas
2 |
3 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/Shared/TimeDomainPlot.razor.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebIDL;
2 | using Microsoft.AspNetCore.Components;
3 | using Microsoft.JSInterop;
4 |
5 | namespace KristofferStrube.Blazor.WebAudio.WasmExample.Shared;
6 |
7 | public partial class TimeDomainPlot : ComponentBase, IDisposable
8 | {
9 | private bool running;
10 | private byte[] timeDomainMeasurements = Array.Empty();
11 |
12 | [Inject]
13 | public required IJSRuntime JSRuntime { get; set; }
14 |
15 | [Parameter, EditorRequired]
16 | public AnalyserNode? Analyser { get; set; }
17 |
18 | [Parameter]
19 | public int Height { get; set; } = 200;
20 |
21 | [Parameter]
22 | public string Color { get; set; } = "red";
23 |
24 | protected override async Task OnAfterRenderAsync(bool _)
25 | {
26 | if (running || Analyser is null)
27 | {
28 | return;
29 | }
30 |
31 | running = true;
32 |
33 | int bufferLength = (int)await Analyser.GetFrequencyBinCountAsync();
34 | Uint8Array timeDomainDataArray = await Uint8Array.CreateAsync(JSRuntime, bufferLength);
35 |
36 | while (running)
37 | {
38 | await Analyser.GetByteTimeDomainDataAsync(timeDomainDataArray);
39 | try
40 | {
41 | timeDomainMeasurements = await timeDomainDataArray.GetAsArrayAsync();
42 | }
43 | catch
44 | {
45 | // If others try to retrieve a byte array at the same time Blazor will fail, so we simply just do nothing if it fails.
46 | }
47 |
48 | await Task.Delay(20);
49 | StateHasChanged();
50 | }
51 | }
52 |
53 | public void Dispose()
54 | {
55 | running = false;
56 | GC.SuppressFinalize(this);
57 | }
58 | }
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using System.Net.Http
2 | @using System.Net.Http.Json
3 | @using Microsoft.AspNetCore.Components.Forms
4 | @using Microsoft.AspNetCore.Components.Routing
5 | @using Microsoft.AspNetCore.Components.Web
6 | @using Microsoft.AspNetCore.Components.Web.Virtualization
7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http
8 | @using Microsoft.JSInterop
9 | @using KristofferStrube.Blazor.WebAudio.WasmExample
10 | @using KristofferStrube.Blazor.WebAudio.WasmExample.Shared
11 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Blazor Web Audio
6 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/Data/BIG HALL E001 M2S.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.WebAudio/6a0e6ad35fdec6dfa78a14b33fd8285bab67d74f/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/Data/BIG HALL E001 M2S.wav
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/Data/drama-cue-synth-and-cello-114417.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.WebAudio/6a0e6ad35fdec6dfa78a14b33fd8285bab67d74f/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/Data/drama-cue-synth-and-cello-114417.mp3
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/Data/dream-sound-effect-downscale-7134.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.WebAudio/6a0e6ad35fdec6dfa78a14b33fd8285bab67d74f/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/Data/dream-sound-effect-downscale-7134.mp3
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/Data/file_example_MP3_700KB.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.WebAudio/6a0e6ad35fdec6dfa78a14b33fd8285bab67d74f/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/Data/file_example_MP3_700KB.mp3
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/Data/yamaha-psr-16-demo-music-25226.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.WebAudio/6a0e6ad35fdec6dfa78a14b33fd8285bab67d74f/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/Data/yamaha-psr-16-demo-music-25226.mp3
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/css/open-iconic/ICON-LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Waybury
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.WebAudio/6a0e6ad35fdec6dfa78a14b33fd8285bab67d74f/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.eot
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.WebAudio/6a0e6ad35fdec6dfa78a14b33fd8285bab67d74f/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.otf
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.WebAudio/6a0e6ad35fdec6dfa78a14b33fd8285bab67d74f/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.WebAudio/6a0e6ad35fdec6dfa78a14b33fd8285bab67d74f/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/css/open-iconic/font/fonts/open-iconic.woff
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.WebAudio/6a0e6ad35fdec6dfa78a14b33fd8285bab67d74f/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/favicon.png
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KristofferStrube/Blazor.WebAudio/6a0e6ad35fdec6dfa78a14b33fd8285bab67d74f/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/icon-192.png
--------------------------------------------------------------------------------
/samples/KristofferStrube.Blazor.WebAudio.WasmExample/wwwroot/js/white-noise.js:
--------------------------------------------------------------------------------
1 | class WhiteNoiseProcessor extends AudioWorkletProcessor {
2 | process(inputs, outputs, parameters) {
3 | const output = outputs[0];
4 | output.forEach((channel) => {
5 | for (let i = 0; i < channel.length; i++) {
6 | channel[i] = Math.random() * 2 - 1;
7 | }
8 | });
9 | return true;
10 | }
11 | }
12 |
13 | registerProcessor("white-noise", WhiteNoiseProcessor);
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioContextState.cs:
--------------------------------------------------------------------------------
1 | namespace KristofferStrube.Blazor.WebAudio;
2 |
3 | ///
4 | /// The state of a .
5 | ///
6 | /// See the API definition here .
7 | public enum AudioContextState
8 | {
9 | ///
10 | /// This context is currently suspended (context time is not proceeding, audio hardware may be powered down/released).
11 | ///
12 | Suspended,
13 | ///
14 | /// Audio is being processed.
15 | ///
16 | Running,
17 | ///
18 | /// This context has been released, and can no longer be used to process audio. All system audio resources have been released.
19 | ///
20 | Closed
21 | }
22 |
23 | internal static class AudioContextStateExtensions
24 | {
25 | public static AudioContextState Parse(string state)
26 | {
27 | return state switch
28 | {
29 | "suspended" => AudioContextState.Suspended,
30 | "running" => AudioContextState.Running,
31 | "closed" => AudioContextState.Closed,
32 | _ => throw new ArgumentException($"'{state}' was not a valid {nameof(AudioContextState)}")
33 | };
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioNodes/AudioDestinationNode.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebIDL;
2 | using Microsoft.JSInterop;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// This is an representing the final audio destination and is what the user will ultimately hear.
8 | /// It can often be considered as an audio output device which is connected to speakers.
9 | /// All rendered audio to be heard will be routed to this node, a "terminal" node in the 's routing graph.
10 | /// There is only a single per , provided through the method.
11 | ///
12 | /// See the API definition here .
13 | [IJSWrapperConverter]
14 | public class AudioDestinationNode : AudioNode, IJSCreatable
15 | {
16 | ///
17 | public static new async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference)
18 | {
19 | return await CreateAsync(jSRuntime, jSReference, new());
20 | }
21 |
22 | ///
23 | public static new Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options)
24 | {
25 | return Task.FromResult(new AudioDestinationNode(jSRuntime, jSReference, options));
26 | }
27 |
28 | ///
29 | protected AudioDestinationNode(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options)
30 | {
31 | }
32 |
33 | ///
34 | /// The maximum number of channels that the channelCount attribute can be set to.
35 | /// An representing the audio hardware end-point (the normal case) can potentially output more than 2 channels of audio if the audio hardware is multi-channel.
36 | /// maxChannelCount is the maximum number of channels that this hardware is capable of supporting.
37 | ///
38 | public async Task GetMaxChannelCountAsync()
39 | {
40 | IJSObjectReference helper = await webAudioHelperTask.Value;
41 | return await helper.InvokeAsync("getAttribute", JSReference, "maxChannelCount");
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioNodes/GainNode.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Extensions;
2 | using KristofferStrube.Blazor.WebIDL;
3 | using Microsoft.JSInterop;
4 |
5 | namespace KristofferStrube.Blazor.WebAudio;
6 |
7 | ///
8 | /// Changing the gain of an audio signal is a fundamental operation in audio applications. This interface is an with a single input and single output.
9 | ///
10 | /// See the API definition here .
11 | [IJSWrapperConverter]
12 | public class GainNode : AudioNode, IJSCreatable
13 | {
14 | ///
15 | public static new async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference)
16 | {
17 | return await CreateAsync(jSRuntime, jSReference, new());
18 | }
19 |
20 | ///
21 | public static new Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options)
22 | {
23 | return Task.FromResult(new GainNode(jSRuntime, jSReference, options));
24 | }
25 |
26 | ///
27 | /// Creates an using the standard constructor.
28 | ///
29 | /// An instance.
30 | /// The this new will be associated with.
31 | /// Optional initial parameter value for this .
32 | /// A new instance of a .
33 | public static async Task CreateAsync(IJSRuntime jSRuntime, BaseAudioContext context, GainOptions? options = null)
34 | {
35 | IJSObjectReference helper = await jSRuntime.GetHelperAsync();
36 | IJSObjectReference jSInstance = await helper.InvokeAsync("constructGainNode", context, options);
37 | return new GainNode(jSRuntime, jSInstance, new() { DisposesJSReference = true });
38 | }
39 |
40 | ///
41 | protected GainNode(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { }
42 |
43 | ///
44 | /// Represents the amount of gain to apply.
45 | ///
46 | public async Task GetGainAsync()
47 | {
48 | IJSObjectReference helper = await webAudioHelperTask.Value;
49 | IJSObjectReference jSInstance = await helper.InvokeAsync("getAttribute", JSReference, "gain");
50 | return await AudioParam.CreateAsync(JSRuntime, jSInstance, new() { DisposesJSReference = true });
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioNodes/IIRFilterNode.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Extensions;
2 | using KristofferStrube.Blazor.WebIDL;
3 | using Microsoft.JSInterop;
4 |
5 | namespace KristofferStrube.Blazor.WebAudio;
6 |
7 | ///
8 | /// is an processor implementing a general IIR Filter.
9 | /// In general, it is best to use 's to implement higher-order filters for the following reasons:
10 | ///
11 | /// - Generally less sensitive to numeric issues.
12 | /// - Filter parameters can be automated.
13 | /// - Can be used to create all even-ordered IIR filters.
14 | ///
15 | /// However, odd-ordered filters cannot be created, so if such filters are needed or automation is not needed, then IIR filters may be appropriate.
16 | /// Once created, the coefficients of the IIR filter cannot be changed.
17 | ///
18 | /// See the API definition here .
19 | [IJSWrapperConverter]
20 | public class IIRFilterNode : AudioNode, IJSCreatable
21 | {
22 | ///
23 | public static new async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference)
24 | {
25 | return await CreateAsync(jSRuntime, jSReference, new());
26 | }
27 |
28 | ///
29 | public static new Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options)
30 | {
31 | return Task.FromResult(new IIRFilterNode(jSRuntime, jSReference, options));
32 | }
33 |
34 | ///
35 | /// Creates an using the standard constructor.
36 | ///
37 | /// An instance.
38 | /// The this new will be associated with.
39 | /// Initial parameter value for this .
40 | /// A new instance of a .
41 | public static async Task CreateAsync(IJSRuntime jSRuntime, BaseAudioContext context, IIRFilterOptions options)
42 | {
43 | IJSObjectReference helper = await jSRuntime.GetHelperAsync();
44 | IJSObjectReference jSInstance = await helper.InvokeAsync("constructIIRFilterNode", context, options);
45 | return new IIRFilterNode(jSRuntime, jSInstance, new() { DisposesJSReference = true });
46 | }
47 |
48 | ///
49 | protected IIRFilterNode(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { }
50 | }
51 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioNodes/MediaElementAudioSourceNode.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Extensions;
2 | using KristofferStrube.Blazor.WebAudio.Options;
3 | using KristofferStrube.Blazor.WebIDL;
4 | using Microsoft.JSInterop;
5 |
6 | namespace KristofferStrube.Blazor.WebAudio;
7 |
8 | ///
9 | /// This interface represents an audio source from an audio or video element.
10 | ///
11 | /// See the API definition here .
12 | [IJSWrapperConverter]
13 | public class MediaElementAudioSourceNode : AudioNode, IJSCreatable
14 | {
15 | ///
16 | public static new Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference)
17 | {
18 | return CreateAsync(jSRuntime, jSReference, new());
19 | }
20 |
21 | ///
22 | public static new Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options)
23 | {
24 | return Task.FromResult(new MediaElementAudioSourceNode(jSRuntime, jSReference, options));
25 | }
26 |
27 | ///
28 | /// Creates an using the standard constructor.
29 | ///
30 | /// An instance.
31 | /// The this new will be associated with.
32 | /// Initial parameter value for this .
33 | /// A new instance of a .
34 | public static async Task CreateAsync(IJSRuntime jSRuntime, AudioContext context, MediaElementAudioSourceOptions options)
35 | {
36 | IJSObjectReference helper = await jSRuntime.GetHelperAsync();
37 | IJSObjectReference jSInstance = await helper.InvokeAsync("constructMediaElementAudioSourceNode", context, options);
38 | return new MediaElementAudioSourceNode(jSRuntime, jSInstance, new() { DisposesJSReference = true });
39 | }
40 |
41 | ///
42 | protected MediaElementAudioSourceNode(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options)
43 | {
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioNodes/MediaStreamAudioSourceNode.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.MediaCaptureStreams;
2 | using KristofferStrube.Blazor.WebAudio.Extensions;
3 | using KristofferStrube.Blazor.WebIDL;
4 | using Microsoft.JSInterop;
5 |
6 | namespace KristofferStrube.Blazor.WebAudio;
7 |
8 | ///
9 | /// This interface represents an audio source from a .
10 | ///
11 | /// See the API definition here .
12 | [IJSWrapperConverter]
13 | public class MediaStreamAudioSourceNode : AudioNode, IJSCreatable
14 | {
15 | ///
16 | public static new async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference)
17 | {
18 | return await CreateAsync(jSRuntime, jSReference, new());
19 | }
20 |
21 | ///
22 | public static new Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options)
23 | {
24 | return Task.FromResult(new MediaStreamAudioSourceNode(jSRuntime, jSReference, options));
25 | }
26 |
27 | ///
28 | /// Creates a using the standard constructor.
29 | ///
30 | /// An instance.
31 | /// The this new will be associated with.
32 | /// Initial parameter value for this .
33 | /// A new instance of a .
34 | public static async Task CreateAsync(IJSRuntime jSRuntime, AudioContext context, MediaStreamAudioSourceOptions options)
35 | {
36 | IJSObjectReference helper = await jSRuntime.GetHelperAsync();
37 | IJSObjectReference jSInstance = await helper.InvokeAsync("constructMediaStreamAudioSourceNode", context, options);
38 | return new MediaStreamAudioSourceNode(jSRuntime, jSInstance, new() { DisposesJSReference = true });
39 | }
40 |
41 | ///
42 | protected MediaStreamAudioSourceNode(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { }
43 |
44 | ///
45 | /// Gets the used when constructing this
46 | ///
47 | ///
48 | public async Task GetMediaStreamAsync()
49 | {
50 | IJSObjectReference helper = await webAudioHelperTask.Value;
51 | IJSObjectReference jSInstance = await helper.InvokeAsync("getAttribute", JSReference, "mediaStream");
52 | return await MediaStream.CreateAsync(JSRuntime, jSInstance, new() { DisposesJSReference = true });
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioNodes/MediaStreamTrackAudioSourceNode.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.MediaCaptureStreams;
2 | using KristofferStrube.Blazor.WebAudio.Extensions;
3 | using KristofferStrube.Blazor.WebIDL;
4 | using Microsoft.JSInterop;
5 |
6 | namespace KristofferStrube.Blazor.WebAudio;
7 |
8 | ///
9 | /// This interface represents an audio source from a .
10 | ///
11 | /// See the API definition here .
12 | [IJSWrapperConverter]
13 | public class MediaStreamTrackAudioSourceNode : AudioNode, IJSCreatable
14 | {
15 | ///
16 | public static new async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference)
17 | {
18 | return await CreateAsync(jSRuntime, jSReference, new());
19 | }
20 |
21 | ///
22 | public static new Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options)
23 | {
24 | return Task.FromResult(new MediaStreamTrackAudioSourceNode(jSRuntime, jSReference, options));
25 | }
26 |
27 | ///
28 | /// Creates a using the standard constructor.
29 | ///
30 | /// An instance.
31 | /// The this new will be associated with.
32 | /// Initial parameter value for this .
33 | /// A new instance of a .
34 | public static async Task CreateAsync(IJSRuntime jSRuntime, AudioContext context, MediaStreamTrackAudioSourceOptions options)
35 | {
36 | IJSObjectReference helper = await jSRuntime.GetHelperAsync();
37 | IJSObjectReference jSInstance = await helper.InvokeAsync("constructMediaStreamTrackAudioSourceNode", context, options);
38 | return new MediaStreamTrackAudioSourceNode(jSRuntime, jSInstance, new() { DisposesJSReference = true });
39 | }
40 |
41 | ///
42 | protected MediaStreamTrackAudioSourceNode(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options) { }
43 | }
44 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioNodes/WaveShaperNode.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Extensions;
2 | using KristofferStrube.Blazor.WebIDL;
3 | using Microsoft.JSInterop;
4 |
5 | namespace KristofferStrube.Blazor.WebAudio;
6 |
7 | ///
8 | /// is an processor implementing non-linear distortion effects.
9 | /// Non-linear waveshaping distortion is commonly used for both subtle non-linear warming, or more obvious distortion effects.
10 | /// Arbitrary non-linear shaping curves may be specified.
11 | /// The number of channels of the output always equals the number of channels of the input.
12 | ///
13 | /// See the API definition here .
14 | [IJSWrapperConverter]
15 | public class WaveShaperNode : AudioNode, IJSCreatable
16 | {
17 | ///
18 | public static new async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference)
19 | {
20 | return await CreateAsync(jSRuntime, jSReference, new());
21 | }
22 |
23 | ///
24 | public static new Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options)
25 | {
26 | return Task.FromResult(new WaveShaperNode(jSRuntime, jSReference, options));
27 | }
28 |
29 | ///
30 | /// Creates an using the standard constructor.
31 | ///
32 | /// An instance.
33 | /// The this new will be associated with.
34 | /// Optional initial parameter value for this .
35 | /// A new instance of a .
36 | public static async Task CreateAsync(IJSRuntime jSRuntime, BaseAudioContext context, WaveShaperOptions? options = null)
37 | {
38 | IJSObjectReference helper = await jSRuntime.GetHelperAsync();
39 | IJSObjectReference jSInstance = await helper.InvokeAsync("constructWaveShaperNode", context, options);
40 | return new WaveShaperNode(jSRuntime, jSInstance, new() { DisposesJSReference = true });
41 | }
42 |
43 | ///
44 | protected WaveShaperNode(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options)
45 | {
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioTimestamp.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// Holds the current time with regards to the related ( )
7 | /// and the current time with regards to the Performance interface.
8 | ///
9 | /// See the API definition here .
10 | public class AudioTimestamp
11 | {
12 | ///
13 | /// Represents a point in the time coordinate system of ’s currentTime.
14 | ///
15 | [JsonPropertyName("contextTime")]
16 | public double ContextTime { get; set; }
17 |
18 | ///
19 | /// Represents a point in the time coordinate system of a Performance interface implementation (described in the High Resolution Time standard).
20 | ///
21 | [JsonPropertyName("performanceTime")]
22 | public double PerformanceTime { get; set; }
23 | }
24 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioWorklet/AudioParamDescriptor.cs:
--------------------------------------------------------------------------------
1 | namespace KristofferStrube.Blazor.WebAudio;
2 |
3 | ///
4 | /// This specifies the properties for an object that is used in an .
5 | ///
6 | /// See the API definition here .
7 | public class AudioParamDescriptor
8 | {
9 | }
10 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioWorklet/AudioParamMap.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebIDL;
2 | using Microsoft.JSInterop;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// A collection of objects with associated names.
8 | /// This maplike object is populated from a list of s in the class constructor at the instantiation.
9 | ///
10 | /// See the API definition here .
11 | public class AudioParamMap : BaseJSWrapper, IJSCreatable
12 | {
13 | ///
14 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference)
15 | {
16 | return await CreateAsync(jSRuntime, jSReference, new());
17 | }
18 |
19 | ///
20 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options)
21 | {
22 | return Task.FromResult(new AudioParamMap(jSRuntime, jSReference, options));
23 | }
24 |
25 | ///
26 | protected AudioParamMap(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options)
27 | {
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioWorklet/AudioWorklet.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebIDL;
2 | using Microsoft.JSInterop;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// The object allows developers to supply scripts (such as JavaScript or WebAssembly code) to process audio on the rendering thread, supporting custom s.
8 | /// This processing mechanism ensures synchronous execution of the script code with other built-in s in the audio graph.
9 | ///
10 | /// See the API definition here .
11 | public class AudioWorklet : Worklet, IJSCreatable
12 | {
13 | ///
14 | public static new async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference)
15 | {
16 | return await CreateAsync(jSRuntime, jSReference, new());
17 | }
18 |
19 | ///
20 | public static new Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options)
21 | {
22 | return Task.FromResult(new AudioWorklet(jSRuntime, jSReference, options));
23 | }
24 |
25 | ///
26 | protected AudioWorklet(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options)
27 | {
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioWorklet/AudioWorkletGlobalScope.cs:
--------------------------------------------------------------------------------
1 | namespace KristofferStrube.Blazor.WebAudio;
2 |
3 | ///
4 | /// This special execution context is designed to enable the generation, processing, and analysis of audio data directly using a script in the audio rendering thread.
5 | /// The user-supplied script code is evaluated in this scope to define one or more subclasses, which in turn are used to instantiate s, in a 1:1 association with s in the main scope.
6 | /// Exactly one exists for each that contains one or more s.
7 | ///
8 | /// See the API definition here .
9 | public class AudioWorkletGlobalScope
10 | {
11 | }
12 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioWorklet/AudioWorkletProcessors/AudioWorkletProcessor.cs:
--------------------------------------------------------------------------------
1 | namespace KristofferStrube.Blazor.WebAudio;
2 |
3 | ///
4 | /// This interface represents an audio processing code that runs on the audio rendering thread.
5 | /// It lives in the , and the definition of the class manifests the actual audio processing.
6 | ///
7 | /// See the API definition here .
8 | public abstract class AudioWorkletProcessor
9 | {
10 | ///
11 | /// Gets the that is registered from this .
12 | ///
13 | public abstract AudioWorkletNode Node { get; }
14 | }
15 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioWorklet/AudioWorkletProcessors/PullAudioWorkletProcessor.Options.cs:
--------------------------------------------------------------------------------
1 | namespace KristofferStrube.Blazor.WebAudio;
2 |
3 | public partial class PullAudioWorkletProcessor
4 | {
5 | ///
6 | /// Options for creating .
7 | ///
8 | public class Options
9 | {
10 | ///
11 | /// Low tide for when the processor should request more chunks.
12 | ///
13 | public int LowTide { get; set; } = 10;
14 |
15 | ///
16 | /// High tide for when the processor should begin discarding chunks.
17 | ///
18 | public int HighTide { get; set; } = 50;
19 |
20 | ///
21 | /// Size of each individual chunk.
22 | ///
23 | public int BufferRequestSize { get; set; } = 10;
24 |
25 | ///
26 | /// Resolution used for transfering chunks.
27 | ///
28 | public Resolution Resolution { get; set; } = Resolution.Double;
29 |
30 | ///
31 | /// A functions that will be used to pull data to play in the audio processor as mono audio
32 | ///
33 | ///
34 | /// If is supplied then this will be ignored.
35 | ///
36 | public Func? ProduceMono { get; set; }
37 |
38 | ///
39 | /// A functions that will be used to pull data to play in the audio processor as stereo audio.
40 | ///
41 | public Func<(double left, double right)>? ProduceStereo { get; set; }
42 | }
43 |
44 | ///
45 | /// The resolution options for how the quality of the sound of the .
46 | ///
47 | public enum Resolution
48 | {
49 | ///
50 | /// This is the full resolution from the producing methods.
51 | ///
52 | Double,
53 |
54 | ///
55 | /// This down-grades the produced sound to 255 discrete values to make the datatransfer more compact.
56 | ///
57 | Byte,
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioWorklet/MessagePort.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.DOM;
2 | using KristofferStrube.Blazor.WebIDL;
3 | using Microsoft.JSInterop;
4 |
5 | namespace KristofferStrube.Blazor.WebAudio;
6 |
7 | ///
8 | /// Each object can be entangled with another (a symmetric relationship).
9 | /// Each object also has a task source called the port message queue, initially empty.
10 | /// A port message queue can be enabled or disabled, and is initially disabled.
11 | /// Once enabled, a port can never be disabled again (though messages in the queue can get moved to another queue or removed altogether, which has much the same effect).
12 | ///
13 | /// See the API definition here .
14 | [IJSWrapperConverter]
15 | public class MessagePort : EventTarget, IJSCreatable
16 | {
17 | ///
18 | public static new async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference)
19 | {
20 | return await CreateAsync(jSRuntime, jSReference, new());
21 | }
22 |
23 | ///
24 | public static new Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options)
25 | {
26 | return Task.FromResult(new MessagePort(jSRuntime, jSReference, options));
27 | }
28 |
29 | ///
30 | protected internal MessagePort(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options)
31 | {
32 | }
33 |
34 | public async Task PostMessageAsync(object message, ITransferable[]? transfer = null)
35 | {
36 | await JSReference.InvokeVoidAsync("postMessage", message, transfer?.Select(e => e).ToArray());
37 | }
38 |
39 | public async Task StartAsync()
40 | {
41 | await JSReference.InvokeVoidAsync("start");
42 | }
43 |
44 | public async Task CloseAsync()
45 | {
46 | await JSReference.InvokeVoidAsync("close");
47 | }
48 |
49 | ///
50 | public async Task AddOnMessageEventListenerAsync(EventListener callback, AddEventListenerOptions? options = null)
51 | {
52 | await AddEventListenerAsync("message", callback, options);
53 | }
54 |
55 | ///
56 | public async Task RemoveOnMessageEventListenerAsync(EventListener callback, EventListenerOptions? options = null)
57 | {
58 | await RemoveEventListenerAsync("message", callback, options);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioWorklet/RequestCredentials.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Converters;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// Controls the flow of credentials during a fetch.
8 | ///
9 | /// See the API definition here .
10 | [JsonConverter(typeof(RequestCredentialsConverter))]
11 | public enum RequestCredentials
12 | {
13 | ///
14 | /// Excludes credentials from this request, and causes any credentials sent back in the response to be ignored.
15 | ///
16 | Omit,
17 |
18 | ///
19 | /// Include credentials with requests made to same-origin URLs, and use any credentials sent back in responses from same-origin URLs.
20 | ///
21 | SameOrigin,
22 |
23 | ///
24 | /// Always includes credentials with this request, and always use any credentials sent back in the response.
25 | ///
26 | Include
27 | }
28 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioWorklet/Worklet.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebIDL;
2 | using KristofferStrube.Blazor.WebIDL.Exceptions;
3 | using Microsoft.JSInterop;
4 |
5 | namespace KristofferStrube.Blazor.WebAudio;
6 |
7 | ///
8 | /// The class provides the capability to add module scripts into its associated WorkletGlobalScopes.
9 | /// The user agent can then create classes registered on the WorkletGlobalScopes and invoke their methods.
10 | ///
11 | /// See the API definition here .
12 | [IJSWrapperConverter]
13 | public class Worklet : BaseJSWrapper, IJSCreatable
14 | {
15 | ///
16 | /// An error handling reference to the underlying js object.
17 | ///
18 | protected IJSObjectReference ErrorHandlingJSReference { get; set; }
19 |
20 | ///
21 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference)
22 | {
23 | return await CreateAsync(jSRuntime, jSReference, new());
24 | }
25 |
26 | ///
27 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options)
28 | {
29 | return Task.FromResult(new Worklet(jSRuntime, jSReference, options));
30 | }
31 |
32 | ///
33 | protected Worklet(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options)
34 | {
35 | ErrorHandlingJSReference = new ErrorHandlingJSObjectReference(jSRuntime, jSReference);
36 | }
37 |
38 | ///
39 | /// Loads and executes the module script given by into all of worklet's global scopes.
40 | /// It can also create additional global scopes as part of this process, depending on the worklet type.
41 | /// The returned promise will fulfill once the script has been successfully loaded and run in all global scopes.
42 | ///
43 | ///
44 | /// Throws an if it fails to fetch the script.
45 | /// Throws the exception thrown within the module itself if it fails.
46 | ///
47 | ///
48 | ///
49 | ///
50 | public async Task AddModuleAsync(string moduleURL, WorkletOptions? options = null)
51 | {
52 | await ErrorHandlingJSReference.InvokeVoidAsync("addModule", moduleURL, options);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/AudioWorklet/WorkletOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// The options used to add a module via
7 | ///
8 | /// See the API definition here .
9 | public class WorkletOptions
10 | {
11 | ///
12 | /// Can be set to a credentials mode to modify the script-fetching process.
13 | /// It defaults to .
14 | ///
15 | [JsonPropertyName("credentials")]
16 | public RequestCredentials Credentials { get; set; } = RequestCredentials.SameOrigin;
17 | }
18 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/BaseJSWrapper.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Extensions;
2 | using KristofferStrube.Blazor.WebIDL;
3 | using Microsoft.JSInterop;
4 |
5 | namespace KristofferStrube.Blazor.WebAudio;
6 |
7 | ///
8 | /// Base class for wrapping objects in the Blazor.WebAudio library.
9 | ///
10 | [IJSWrapperConverter]
11 | public abstract class BaseJSWrapper : IJSWrapper, IAsyncDisposable
12 | {
13 | ///
14 | /// A lazily evaluated task that gives access to helper methods.
15 | ///
16 | protected readonly Lazy> helperTask;
17 |
18 | ///
19 | public IJSRuntime JSRuntime { get; }
20 |
21 | ///
22 | public IJSObjectReference JSReference { get; }
23 |
24 | ///
25 | public bool DisposesJSReference { get; }
26 |
27 | ///
28 | internal BaseJSWrapper(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options)
29 | {
30 | helperTask = new(jSRuntime.GetHelperAsync);
31 | JSReference = jSReference;
32 | JSRuntime = jSRuntime;
33 | DisposesJSReference = options.DisposesJSReference;
34 | }
35 |
36 | ///
37 | /// Disposes the underlying js object reference.
38 | ///
39 | ///
40 | public async ValueTask DisposeAsync()
41 | {
42 | if (helperTask.IsValueCreated)
43 | {
44 | IJSObjectReference module = await helperTask.Value;
45 | await module.DisposeAsync();
46 | }
47 | await IJSWrapper.DisposeJSReference(this);
48 | GC.SuppressFinalize(this);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Converters/AutomationRateConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio.Converters;
5 |
6 | internal class AutomationRateConverter : JsonConverter
7 | {
8 | public override AutomationRate Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
9 | {
10 | throw new NotImplementedException();
11 | }
12 |
13 | public override void Write(Utf8JsonWriter writer, AutomationRate value, JsonSerializerOptions options)
14 | {
15 | writer.WriteStringValue(value switch
16 | {
17 | AutomationRate.ARate => "a-rate",
18 | AutomationRate.KRate => "k-rate",
19 | _ => throw new ArgumentException($"Value '{value}' was not a valid {nameof(AutomationRate)}.")
20 | });
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Converters/BiquadFilterTypeConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio.Converters;
5 |
6 | internal class BiquadFilterTypeConverter : JsonConverter
7 | {
8 | public override BiquadFilterType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
9 | {
10 | return reader.GetString() switch
11 | {
12 | "highpass" => BiquadFilterType.Highpass,
13 | "lowpass" => BiquadFilterType.Lowpass,
14 | "bandpass" => BiquadFilterType.Bandpass,
15 | "lowshelf" => BiquadFilterType.Lowshelf,
16 | "highshelf" => BiquadFilterType.Highshelf,
17 | "peaking" => BiquadFilterType.Peaking,
18 | "notch" => BiquadFilterType.Notch,
19 | "allpass" => BiquadFilterType.Allpass,
20 | var value => throw new ArgumentException($"Value '{value}' was not a valid {nameof(BiquadFilterType)}."),
21 | };
22 | }
23 |
24 | public override void Write(Utf8JsonWriter writer, BiquadFilterType value, JsonSerializerOptions options)
25 | {
26 | writer.WriteStringValue(value switch
27 | {
28 | BiquadFilterType.Highpass => "highpass",
29 | BiquadFilterType.Lowpass => "lowpass",
30 | BiquadFilterType.Bandpass => "bandpass",
31 | BiquadFilterType.Lowshelf => "lowshelf",
32 | BiquadFilterType.Highshelf => "highshelf",
33 | BiquadFilterType.Peaking => "peaking",
34 | BiquadFilterType.Notch => "notch",
35 | BiquadFilterType.Allpass => "allpass",
36 | _ => throw new ArgumentException($"Value '{value}' was not a valid {nameof(BiquadFilterType)}.")
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Converters/ChannelCountModeConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio.Converters;
5 |
6 | internal class ChannelCountModeConverter : JsonConverter
7 | {
8 | public override ChannelCountMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
9 | {
10 | return reader.GetString() switch
11 | {
12 | "max" => ChannelCountMode.Max,
13 | "clamped-max" => ChannelCountMode.ClampedMax,
14 | "explicit" => ChannelCountMode.Explicit,
15 | var value => throw new ArgumentException($"Value '{value}' was not a valid {nameof(ChannelCountMode)}.")
16 | };
17 | }
18 |
19 | public override void Write(Utf8JsonWriter writer, ChannelCountMode value, JsonSerializerOptions options)
20 | {
21 | writer.WriteStringValue(value switch
22 | {
23 | ChannelCountMode.Max => "max",
24 | ChannelCountMode.ClampedMax => "clamped-max",
25 | ChannelCountMode.Explicit => "explicit",
26 | _ => throw new ArgumentException($"Value '{value}' was not a valid {nameof(ChannelCountMode)}.")
27 | });
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Converters/ChannelInterpretationConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio.Converters;
5 |
6 | internal class ChannelInterpretationConverter : JsonConverter
7 | {
8 | public override ChannelInterpretation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
9 | {
10 | return reader.GetString() switch
11 | {
12 | "speakers" => ChannelInterpretation.Speakers,
13 | "discrete" => ChannelInterpretation.Discrete,
14 | var value => throw new ArgumentException($"Value '{value}' was not a valid {nameof(ChannelInterpretation)}.")
15 | };
16 | }
17 |
18 | public override void Write(Utf8JsonWriter writer, ChannelInterpretation value, JsonSerializerOptions options)
19 | {
20 | writer.WriteStringValue(value switch
21 | {
22 | ChannelInterpretation.Speakers => "speakers",
23 | ChannelInterpretation.Discrete => "discrete",
24 | _ => throw new ArgumentException($"Value '{value}' was not a valid {nameof(ChannelInterpretation)}.")
25 | });
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Converters/DistanceModelTypeConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio.Converters;
5 |
6 | internal class DistanceModelTypeConverter : JsonConverter
7 | {
8 | public override DistanceModelType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
9 | {
10 | return reader.GetString() switch
11 | {
12 | "linear" => DistanceModelType.Linear,
13 | "inverse" => DistanceModelType.Inverse,
14 | "exponential" => DistanceModelType.Exponential,
15 | var value => throw new ArgumentException($"Value '{value}' was not a valid {nameof(DistanceModelType)}.")
16 | };
17 | }
18 |
19 | public override void Write(Utf8JsonWriter writer, DistanceModelType value, JsonSerializerOptions options)
20 | {
21 | writer.WriteStringValue(value switch
22 | {
23 | DistanceModelType.Linear => "linear",
24 | DistanceModelType.Inverse => "inverse",
25 | DistanceModelType.Exponential => "exponential",
26 | _ => throw new ArgumentException($"Value '{value}' was not a valid {nameof(DistanceModelType)}.")
27 | });
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Converters/OscillatorTypeConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio.Converters;
5 |
6 | internal class OscillatorTypeConverter : JsonConverter
7 | {
8 | public override OscillatorType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
9 | {
10 | return reader.GetString() switch
11 | {
12 | "sine" => OscillatorType.Sine,
13 | "square" => OscillatorType.Square,
14 | "sawtooth" => OscillatorType.Sawtooth,
15 | "triangle" => OscillatorType.Triangle,
16 | "custom" => OscillatorType.Custom,
17 | var value => throw new ArgumentException($"Value '{value}' was not a valid {nameof(OscillatorType)}."),
18 | };
19 | }
20 |
21 | public override void Write(Utf8JsonWriter writer, OscillatorType value, JsonSerializerOptions options)
22 | {
23 | writer.WriteStringValue(value switch
24 | {
25 | OscillatorType.Sine => "sine",
26 | OscillatorType.Square => "square",
27 | OscillatorType.Sawtooth => "sawtooth",
28 | OscillatorType.Triangle => "triangle",
29 | OscillatorType.Custom => "custom",
30 | _ => throw new ArgumentException($"Value '{value}' was not a valid {nameof(OscillatorType)}.")
31 | });
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Converters/OverSampleTypeConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio.Converters;
5 |
6 | internal class OverSampleTypeConverter : JsonConverter
7 | {
8 | public override OverSampleType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
9 | {
10 | return reader.GetString() switch
11 | {
12 | "none" => OverSampleType.None,
13 | "2x" => OverSampleType.TwoX,
14 | "4x" => OverSampleType.FourX,
15 | var value => throw new ArgumentException($"Value '{value}' was not a valid {nameof(OverSampleType)}.")
16 | };
17 | }
18 |
19 | public override void Write(Utf8JsonWriter writer, OverSampleType value, JsonSerializerOptions options)
20 | {
21 | writer.WriteStringValue(value switch
22 | {
23 | OverSampleType.None => "none",
24 | OverSampleType.TwoX => "2x",
25 | OverSampleType.FourX => "4x",
26 | _ => throw new ArgumentException($"Value '{value}' was not a valid {nameof(OverSampleType)}.")
27 | });
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Converters/PanningModelTypeConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio.Converters;
5 |
6 | internal class PanningModelTypeConverter : JsonConverter
7 | {
8 | public override PanningModelType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
9 | {
10 | return reader.GetString() switch
11 | {
12 | "equalpower" => PanningModelType.EqualPower,
13 | "HRTF" => PanningModelType.HRTF,
14 | var value => throw new ArgumentException($"Value '{value}' was not a valid {nameof(PanningModelType)}.")
15 | };
16 | }
17 |
18 | public override void Write(Utf8JsonWriter writer, PanningModelType value, JsonSerializerOptions options)
19 | {
20 | writer.WriteStringValue(value switch
21 | {
22 | PanningModelType.EqualPower => "equalpower",
23 | PanningModelType.HRTF => "HRTF",
24 | _ => throw new ArgumentException($"Value '{value}' was not a valid {nameof(PanningModelType)}.")
25 | });
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Converters/RequestCredentialsConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio.Converters;
5 |
6 | internal class RequestCredentialsConverter : JsonConverter
7 | {
8 | public override RequestCredentials Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
9 | {
10 | throw new NotImplementedException();
11 | }
12 |
13 | public override void Write(Utf8JsonWriter writer, RequestCredentials value, JsonSerializerOptions options)
14 | {
15 | writer.WriteStringValue(value switch
16 | {
17 | RequestCredentials.Omit => "omit",
18 | RequestCredentials.SameOrigin => "same-origin",
19 | RequestCredentials.Include => "include",
20 | _ => throw new ArgumentException($"Value '{value}' was not a valid {nameof(RequestCredentials)}.")
21 | });
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Converters/UnionTypeJsonConverter.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.UnionTypes;
2 | using System.Text.Json;
3 | using System.Text.Json.Serialization;
4 | using static System.Text.Json.JsonSerializer;
5 |
6 | namespace KristofferStrube.Blazor.WebAudio.Converters;
7 |
8 | internal class UnionTypeJsonConverter : JsonConverter where T : UnionType
9 | {
10 | public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
11 | {
12 | throw new InvalidOperationException("Can't deserialize UnionTypes from the Blazor.WebAudio library.");
13 | }
14 |
15 | public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
16 | {
17 | if (value.Value is not null)
18 | {
19 | writer.WriteRawValue(Serialize(value.Value, options));
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Events/OfflineAudioCompletionEventInit.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.DOM;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio.Events;
5 |
6 | ///
7 | /// Initialisation options for an .
8 | ///
9 | /// See the API definition here .
10 | public class OfflineAudioCompletionEventInit : EventInit
11 | {
12 | ///
13 | /// Value to be assigned to the of the event.
14 | ///
15 | [JsonPropertyName("renderedBuffer")]
16 | public required AudioBuffer RenderedBuffer { get; set; }
17 | }
18 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Extensions/IJSRuntimeExtensions.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebIDL;
2 | using Microsoft.JSInterop;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio.Extensions;
5 |
6 | internal static class IJSRuntimeExtensions
7 | {
8 | internal static async Task GetHelperAsync(this IJSRuntime jSRuntime)
9 | {
10 | return await jSRuntime.InvokeAsync(
11 | "import", "./_content/KristofferStrube.Blazor.WebAudio/KristofferStrube.Blazor.WebAudio.js");
12 | }
13 | internal static async Task GetInProcessHelperAsync(this IJSRuntime jSRuntime)
14 | {
15 | return await jSRuntime.InvokeAsync(
16 | "import", "./_content/KristofferStrube.Blazor.WebAudio/KristofferStrube.Blazor.WebAudio.js");
17 | }
18 | internal static async Task GetErrorHandlingHelperAsync(this IJSRuntime jSRuntime)
19 | {
20 | IJSObjectReference helper = await jSRuntime.GetHelperAsync();
21 | return new ErrorHandlingJSObjectReference(jSRuntime, helper);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/KristofferStrube.Blazor.WebAudio.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0;net8.0
5 | enable
6 | enable
7 | Blazor Web Audio API wrapper
8 | Web Audio API wrapper implementation for Blazor.
9 | KristofferStrube.Blazor.WebAudio
10 | Blazor;Wasm;Wrapper;Audio;AudioNode;AudioContext;AudioBufffer;Media;Music;Sound;JSInterop
11 | https://github.com/KristofferStrube/Blazor.WebAudio
12 | git
13 | MIT
14 | 0.1.0-alpha.13
15 | Kristoffer Strube
16 | README.md
17 | icon.png
18 | true
19 |
20 |
21 |
22 |
23 | True
24 | \
25 |
26 |
27 | True
28 | \
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/AnalyserOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// This specifies the options to be used when constructing an .
7 | ///
8 | /// See the API definition here .
9 | public class AnalyserOptions : AudioNodeOptions
10 | {
11 | ///
12 | /// The desired initial size of the FFT for frequency-domain analysis.
13 | ///
14 | [JsonPropertyName("fftSize")]
15 | public ulong FftSize { get; set; } = 2048;
16 |
17 | ///
18 | /// The desired initial maximum power in dB for FFT analysis.
19 | ///
20 | [JsonPropertyName("maxDecibels")]
21 | public double MaxDecibels { get; set; } = -30;
22 |
23 | ///
24 | /// The desired initial minimum power in dB for FFT analysis.
25 | ///
26 | [JsonPropertyName("minDecibels")]
27 | public double MinDecibels { get; set; } = -100;
28 |
29 | ///
30 | /// The desired initial smoothing constant for the FFT analysis.
31 | ///
32 | [JsonPropertyName("smoothingTimeConstant")]
33 | public double SmoothingTimeConstant { get; set; } = 0.8;
34 | }
35 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/AudioBufferOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// This specifies the options to use in constructing an .
7 | ///
8 | /// See the API definition here .
9 | public class AudioBufferOptions
10 | {
11 | ///
12 | /// The number of channels for the buffer. Browsers will support at least 32 channels.
13 | ///
14 | [JsonPropertyName("numberOfChannels")]
15 | public ulong NumberOfChannels { get; set; } = 1;
16 |
17 | ///
18 | /// The length in sample frames of the buffer. Must be at least 1 .
19 | ///
20 | [JsonPropertyName("length")]
21 | public required ulong Length { get; set; }
22 |
23 | ///
24 | /// The sample rate in Hz for the buffer.
25 | /// The range is at least from 8000 to 96000 but browsers can support broader ranges.
26 | ///
27 | [JsonPropertyName("sampleRate")]
28 | public required float SampleRate { get; set; }
29 | }
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/AudioBufferSourceOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// This specifies options for constructing an .
7 | ///
8 | /// See the API definition here .
9 | public class AudioBufferSourceOptions
10 | {
11 | ///
12 | /// The audio asset to be played. This is equivalent to calling .
13 | ///
14 | [JsonPropertyName("buffer")]
15 | public AudioBuffer? Buffer { get; set; }
16 |
17 | ///
18 | /// The initial value for .
19 | ///
20 | [JsonPropertyName("detune")]
21 | public float Detune { get; set; } = 0f;
22 |
23 | ///
24 | /// The initial value for
25 | ///
26 | [JsonPropertyName("loop")]
27 | public bool Loop { get; set; } = false;
28 |
29 | ///
30 | /// The initial value for
31 | ///
32 | [JsonPropertyName("loopEnd")]
33 | public double LoopEnd { get; set; } = 0;
34 |
35 | ///
36 | /// The initial value for
37 | ///
38 | [JsonPropertyName("loopStart")]
39 | public double LoopStart { get; set; } = 0;
40 |
41 | ///
42 | /// The initial value for
43 | ///
44 | ///
45 | /// Default is 1 .
46 | ///
47 | [JsonPropertyName("playbackRate")]
48 | public float PlaybackRate { get; set; } = 1;
49 | }
50 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/AudioContextLatencyCategory.cs:
--------------------------------------------------------------------------------
1 | namespace KristofferStrube.Blazor.WebAudio;
2 |
3 | ///
4 | /// An enum for specifying the strategy that the should use for balancing audio output latency and power consumption.
5 | ///
6 | /// See the API definition here .
7 | public enum AudioContextLatencyCategory
8 | {
9 | ///
10 | /// Provide the lowest audio output latency possible without glitching. This is the default.
11 | ///
12 | Interactive,
13 | ///
14 | /// Balance audio output latency and power consumption.
15 | ///
16 | Balanced,
17 | ///
18 | /// Prioritize sustained playback without interruption over audio output latency. Lowest power consumption.
19 | ///
20 | Playback,
21 | }
22 |
23 | internal static class AudioContextLatencyCategoryExtensions
24 | {
25 | public static string AsString(this AudioContextLatencyCategory type)
26 | {
27 | return type switch
28 | {
29 | AudioContextLatencyCategory.Balanced => "balanced",
30 | AudioContextLatencyCategory.Interactive => "interactive",
31 | AudioContextLatencyCategory.Playback => "playback",
32 | _ => throw new ArgumentException($"Value '{type}' was not a valid {nameof(AudioContextLatencyCategory)}.")
33 | };
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/AudioContextOptions.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebIDL.Exceptions;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// The is used to specify user-specified options for an .
8 | ///
9 | /// See the API definition here .
10 | public class AudioContextOptions
11 | {
12 | ///
13 | /// Identify the type of playback, which affects tradeoffs between audio output latency and power consumption.
14 | ///
15 | ///
16 | /// The preferred value of the is a value from .
17 | /// However, a can also be specified for the number of seconds of latency for finer control to balance latency and power consumption.
18 | /// It is at the browser’s discretion to interpret the number appropriately.
19 | /// The actual latency used is given by .
20 | ///
21 | [JsonPropertyName("latencyHint")]
22 | public AudioContextLatencyCategoryOrDouble LatencyHint { get; set; } = AudioContextLatencyCategory.Interactive;
23 |
24 | ///
25 | /// Sets the to this value for the that will be created.
26 | /// The supported values are the same as the sample rates for an (at least 8000 to 96000 ).
27 | ///
28 | ///
29 | /// A exception will be thrown if the specified sample rate is not supported.
30 | /// If sampleRate is not specified, the preferred sample rate of the output device for this is used.
31 | ///
32 | ///
33 | [JsonPropertyName("sampleRate")]
34 | public float SampleRate { get; set; }
35 | }
36 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/AudioNodeOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// This specifies the options that can be used in constructing all s.
7 | /// All members are optional. However, the specific values used for each node depends on the actual node.
8 | ///
9 | /// See the API definition here .
10 | public class AudioNodeOptions
11 | {
12 | ///
13 | /// is the number of channels used when up-mixing and down-mixing connections to any inputs to the node.
14 | /// The default value is 2 except for specific nodes where its value is specially determined.
15 | /// This attribute has no effect for nodes with no inputs.
16 | ///
17 | [JsonPropertyName("channelCount")]
18 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
19 | public ulong? ChannelCount { get; set; }
20 |
21 | ///
22 | /// determines how channels will be counted when up-mixing and down-mixing connections to any inputs to the node.
23 | ///
24 | ///
25 | /// The default value is . This attribute has no effect for nodes with no inputs.
26 | ///
27 | [JsonPropertyName("channelCountMode")]
28 | public virtual ChannelCountMode ChannelCountMode { get; set; } = ChannelCountMode.Max;
29 |
30 | ///
31 | /// determines how individual channels will be treated when up-mixing and down-mixing connections to any inputs to the node.
32 | ///
33 | ///
34 | /// The default value is . This attribute has no effect for nodes with no inputs.
35 | ///
36 | [JsonPropertyName("channelInterpretation")]
37 | public virtual ChannelInterpretation ChannelInterpretation { get; set; } = ChannelInterpretation.Speakers;
38 | }
39 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/AudioWorkletNodeOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// This specifies the options to be used when constructing an .
7 | ///
8 | /// See the API definition here .
9 | public class AudioWorkletNodeOptions : AudioNodeOptions
10 | {
11 | ///
12 | /// This is used to initialize the value of .
13 | ///
14 | [JsonPropertyName("numberOfInputs")]
15 | public ulong NumberOfInputs { get; set; } = 1;
16 |
17 | ///
18 | /// This is used to initialize the value of .
19 | ///
20 | [JsonPropertyName("numberOfOutputs")]
21 | public ulong NumberOfOutputs { get; set; } = 1;
22 |
23 | ///
24 | /// This array is used to configure the number of channels in each output.
25 | ///
26 | [JsonPropertyName("outputChannelCount")]
27 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
28 | public ulong[]? OutputChannelCount { get; set; }
29 |
30 | ///
31 | /// This is a list of user-defined key-value pairs that are used to set the initial value of an with the matched name in the .
32 | ///
33 | [JsonPropertyName("parameterData")]
34 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
35 | public Dictionary? ParameterData { get; set; }
36 |
37 | ///
38 | /// This holds any user-defined data that may be used to initialize custom properties in an instance that is associated with the .
39 | ///
40 | [JsonPropertyName("processorOptions")]
41 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
42 | public object? ProcessorOptions { get; set; }
43 | }
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/AutomationRate.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Converters;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// The automation rate of an can be selected calling with one of the following values.
8 | /// However, some s have constraints on whether the automation rate can be changed.
9 | ///
10 | /// See the API definition here .
11 | [JsonConverter(typeof(AutomationRateConverter))]
12 | public enum AutomationRate
13 | {
14 | ///
15 | /// This is set for a-rate processing.
16 | ///
17 | ///
18 | /// a-rate parameters will be sampled for each sample-frame of the block.
19 | ///
20 | ARate,
21 | ///
22 | /// This is set for k-rate processing.
23 | ///
24 | ///
25 | /// k-rate parameter will be sampled at the time of the very first sample-frame, and that value will be used for the entire block.
26 | ///
27 | KRate
28 | }
29 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/BiquadFilterOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// This specifies the options to be used when constructing a .
7 | ///
8 | /// See the API definition here .
9 | public class BiquadFilterOptions : AudioNodeOptions
10 | {
11 | ///
12 | /// The desired initial type of the filter.
13 | ///
14 | ///
15 | /// The default value is .
16 | ///
17 | [JsonPropertyName("type")]
18 | public BiquadFilterType Type { get; set; } = BiquadFilterType.Lowpass;
19 |
20 | ///
21 | /// The desired initial value for Q.
22 | ///
23 | ///
24 | /// The default value is 1 .
25 | ///
26 | [JsonPropertyName("Q")]
27 | public float Q { get; set; } = 1;
28 |
29 | ///
30 | /// The desired initial value for detune.
31 | ///
32 | ///
33 | /// The default value is 0 .
34 | ///
35 | [JsonPropertyName("detune")]
36 | public float Detune { get; set; } = 0;
37 |
38 | ///
39 | /// The desired initial value for frequency.
40 | ///
41 | ///
42 | /// The default value is 350 .
43 | ///
44 | [JsonPropertyName("frequency")]
45 | public float Frequency { get; set; } = 350;
46 |
47 | ///
48 | /// The desired initial value for gain.
49 | ///
50 | ///
51 | /// The default value is 0 .
52 | ///
53 | [JsonPropertyName("gain")]
54 | public float Gain { get; set; } = 0;
55 | }
56 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/ChannelCountMode.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Converters;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// An enum for specifying how channels will be counted when up-mixing and down-mixing connections to any inputs to the node.
8 | ///
9 | /// See the API definition here .
10 | [JsonConverter(typeof(ChannelCountModeConverter))]
11 | public enum ChannelCountMode
12 | {
13 | ///
14 | /// The computed number of channels is the maximum of the number of channels of all connections to an input. In this mode is ignored.
15 | ///
16 | Max,
17 |
18 | ///
19 | /// The computed number of channels is determined as for and then clamped to a maximum value of .
20 | ///
21 | ClampedMax,
22 |
23 | ///
24 | /// The computed number of channels is the exact value as specified by .
25 | ///
26 | Explicit
27 | }
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/ChannelInterpretation.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Converters;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// An enum for specifying how individual channels will be treated when up-mixing and down-mixing connections to any inputs to the node.
8 | ///
9 | /// See the API definition here .
10 | [JsonConverter(typeof(ChannelInterpretationConverter))]
11 | public enum ChannelInterpretation
12 | {
13 | ///
14 | /// Use up-mix equations or down-mix equations.
15 | /// In cases where the number of channels do not match any of these basic speaker layouts, revert to .
16 | ///
17 | Speakers,
18 | ///
19 | /// Up-mix by filling channels until they run out then zero out remaining channels.
20 | /// Down-mix by filling as many channels as possible, then dropping remaining channels.
21 | ///
22 | Discrete,
23 | }
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/ChannelMergerOptions.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebIDL.Exceptions;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 |
7 | ///
8 | /// This specifies the options to use in constructing a .
9 | ///
10 | /// See the API definition here .
11 | public class ChannelMergerOptions : AudioNodeOptions
12 | {
13 | ///
14 | /// The number inputs for the .
15 | ///
16 | ///
17 | /// The default value is 6 .
18 | /// It throws an if it is less than 1 or larger than the supported number of channels when used for constructing a .
19 | ///
20 | [JsonPropertyName("numberOfInputs")]
21 | public ulong NumberOfInputs { get; set; } = 6;
22 |
23 | ///
24 | ///
25 | /// The default value is .
26 | ///
27 | public override ChannelCountMode ChannelCountMode { get; set; } = ChannelCountMode.Explicit;
28 | }
29 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/ChannelSplitterOptions.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebIDL.Exceptions;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// This specifies the options to use in constructing a .
8 | ///
9 | /// See the API definition here .
10 | public class ChannelSplitterOptions : AudioNodeOptions
11 | {
12 | ///
13 | ///
14 | ///
15 | ///
16 | /// The default value is .
17 | ///
18 | [JsonPropertyName("channelCountMode")]
19 | public override ChannelCountMode ChannelCountMode { get; set; } = ChannelCountMode.Explicit;
20 |
21 | ///
22 | ///
23 | /// The default value is .
24 | ///
25 | [JsonPropertyName("channelInterpretation")]
26 | public override ChannelInterpretation ChannelInterpretation { get; set; } = ChannelInterpretation.Discrete;
27 |
28 | ///
29 | /// The number inputs for the .
30 | ///
31 | ///
32 | /// The default value is 6 .
33 | /// It throws an if it is less than 1 or larger than the supported number of channels when used for constructing a .
34 | ///
35 | [JsonPropertyName("numberOfInputs")]
36 | public ulong NumberOfInputs { get; set; } = 6;
37 | }
38 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/ConstantSourceOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// This specifies options for constructing a .
7 | ///
8 | /// See the API definition here .
9 | public class ConstantSourceOptions
10 | {
11 | ///
12 | /// The initial value for the of this node.
13 | ///
14 | ///
15 | /// The default value is 1 .
16 | ///
17 | [JsonPropertyName("offset")]
18 | public float Offset { get; set; } = 1;
19 | }
20 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/ConvolverOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// This specifies options for constructing a .
7 | ///
8 | /// See the API definition here .
9 | public class ConvolverOptions : AudioNodeOptions
10 | {
11 | ///
12 | /// The desired buffer for the . This buffer will be normalized according to the value of .
13 | ///
14 | [JsonPropertyName("buffer")]
15 | public AudioBuffer? Buffer { get; set; }
16 |
17 | ///
18 | /// The opposite of the desired initial value for .
19 | ///
20 | ///
21 | /// The default value is .
22 | ///
23 | [JsonPropertyName("disableNormalization")]
24 | public bool DisableNormalization { get; set; } = false;
25 |
26 | ///
27 | ///
28 | /// The default value is .
29 | ///
30 | public override ChannelCountMode ChannelCountMode { get; set; } = ChannelCountMode.ClampedMax;
31 | }
32 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/DelayOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// This specifies options for constructing a .
7 | ///
8 | /// See the API definition here .
9 | public class DelayOptions : AudioNodeOptions
10 | {
11 | ///
12 | /// The maximum delay time for the node. Time is in seconds and must be greater than 0 and less than 3 minutes (180 seconds).
13 | ///
14 | ///
15 | /// The default value is 1 .
16 | ///
17 | [JsonPropertyName("maxDelayTime")]
18 | public double MaxDelayTime { get; set; } = 1;
19 |
20 | ///
21 | /// The initial delay time for the node.
22 | ///
23 | ///
24 | /// The default value is 0 .
25 | ///
26 | [JsonPropertyName("delayTime")]
27 | public double DelayTime { get; set; } = 0;
28 | }
29 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/DistanceModelType.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Converters;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// The enum determines which algorithm will be used to reduce the volume of an audio source as it moves away from the listener. The default is "inverse".
8 | ///
9 | /// See the API definition here .
10 | [JsonConverter(typeof(DistanceModelTypeConverter))]
11 | public enum DistanceModelType
12 | {
13 | ///
14 | /// The volume decreases proportionally to the distance between the listener and the sound source, creating a uniform and predictable attenuation effect.
15 | ///
16 | Linear,
17 |
18 | ///
19 | /// The volume decreases sharply as the distance increases initially but tapers off at greater distances, closely resembling real-world acoustic behavior.
20 | ///
21 | Inverse,
22 |
23 | ///
24 | /// The volume decreases exponentially as the distance increases, resulting in a rapid attenuation of sound over shorter distances.
25 | ///
26 | Exponential,
27 | }
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/DynamicsCompressorOptions.cs:
--------------------------------------------------------------------------------
1 |
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// This specifies the options to use in constructing a .
8 | ///
9 | /// See the API definition here .
10 | public class DynamicsCompressorOptions : AudioNodeOptions
11 | {
12 | ///
13 | ///
14 | /// The default value is .
15 | ///
16 | [JsonPropertyName("channelCountMode")]
17 | public override ChannelCountMode ChannelCountMode { get; set; } = ChannelCountMode.ClampedMax;
18 |
19 | ///
20 | /// The initial value for the AudioParam.
21 | ///
22 | [JsonPropertyName("attack")]
23 | public float Attack { get; set; } = 0.003f;
24 |
25 | ///
26 | /// The initial value for the AudioParam.
27 | ///
28 | [JsonPropertyName("knee")]
29 | public float Knee { get; set; } = 30f;
30 |
31 | ///
32 | /// The initial value for the AudioParam.
33 | ///
34 | [JsonPropertyName("ratio")]
35 | public float Ratio { get; set; } = 12f;
36 |
37 | ///
38 | /// The initial value for the AudioParam.
39 | ///
40 | [JsonPropertyName("release")]
41 | public float Release { get; set; } = 0.25f;
42 |
43 | ///
44 | /// The initial value for the AudioParam.
45 | ///
46 | [JsonPropertyName("threshold")]
47 | public float Threshold { get; set; } = -24f;
48 | }
49 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/GainOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// This specifies options for constructing a .
7 | ///
8 | /// See the API definition here .
9 | public class GainOptions : AudioNodeOptions
10 | {
11 | ///
12 | /// The initial value for
13 | ///
14 | [JsonPropertyName("gain")]
15 | public float Gain { get; set; } = 1;
16 | }
17 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/IIRFilterOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | public class IIRFilterOptions : AudioNodeOptions
6 | {
7 | [JsonPropertyName("feedforward")]
8 | public required double[] Feedforward { get; set; }
9 |
10 | [JsonPropertyName("feedback")]
11 | public required double[] Feedback { get; set; }
12 | }
13 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/MediaElementAudioSourceOptions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio.Options;
5 |
6 | ///
7 | /// This specifies the options to use in constructing a .
8 | ///
9 | ///
10 | /// See the API definition here .
11 | ///
12 | public class MediaElementAudioSourceOptions
13 | {
14 | ///
15 | /// The media element that will be re-routed.
16 | ///
17 | [JsonPropertyName("mediaElement")]
18 | public required IJSObjectReference MediaElement { get; set; }
19 | }
20 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/MediaStreamAudioDestinationOptions.cs:
--------------------------------------------------------------------------------
1 | namespace KristofferStrube.Blazor.WebAudio.Options;
2 |
3 | ///
4 | /// This specifies the options to use in constructing a .
5 | ///
6 | ///
7 | /// This type doesn't exist in the specs, but the has a non-standard value for the attribute which is why this type is added in this wrapper.
8 | /// See the API definition here .
9 | ///
10 | public class MediaStreamAudioDestinationOptions : AudioNodeOptions
11 | {
12 | ///
13 | ///
14 | /// The default value is .
15 | ///
16 | public override ChannelCountMode ChannelCountMode { get; set; } = ChannelCountMode.Explicit;
17 | }
18 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/MediaStreamAudioSourceOptions.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.MediaCaptureStreams;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// This specifies the options to use in constructing a .
8 | ///
9 | /// See the API definition here .
10 | public class MediaStreamAudioSourceOptions
11 | {
12 | ///
13 | /// The media stream that will act as a source.
14 | ///
15 | [JsonPropertyName("mediaStream")]
16 | public required MediaStream MediaStream { get; set; }
17 | }
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/MediaStreamTrackAudioSourceOptions.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.MediaCaptureStreams;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// This specifies the options to use in constructing a .
8 | ///
9 | /// See the API definition here .
10 | public class MediaStreamTrackAudioSourceOptions
11 | {
12 | ///
13 | /// The media stream track that will act as a source.
14 | ///
15 | [JsonPropertyName("mediaStreamTrack")]
16 | public required MediaStreamTrack MediaStreamTrack { get; set; }
17 | }
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/OfflineAudioContextOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// This specifies the options to use in constructing an .
7 | ///
8 | /// See the API definition here .
9 | public class OfflineAudioContextOptions
10 | {
11 | ///
12 | /// The number of channels for this .
13 | ///
14 | [JsonPropertyName("numberOfChannelse")]
15 | public ulong NumberOfChannelse { get; set; } = 1;
16 |
17 | ///
18 | /// The length of the rendered in sample-frames.
19 | ///
20 | [JsonPropertyName("length")]
21 | public required ulong Length { get; set; }
22 |
23 | ///
24 | /// The sample rate for this .
25 | ///
26 | [JsonPropertyName("sampleRate")]
27 | public required float SampleRate { get; set; }
28 | }
29 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/OscillatorOptions.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebIDL.Exceptions;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// This specifies the options to be used when constructing an .
8 | ///
9 | /// See the API definition here .
10 | public class OscillatorOptions : AudioNodeOptions
11 | {
12 | ///
13 | /// The type of oscillator to be constructed.
14 | /// If periodicWave is specified, then any valid value for type is ignored; it is treated as if it were set to "custom".
15 | ///
16 | ///
17 | /// It throws an if this is set to without a being provided.
18 | ///
19 | [JsonPropertyName("type")]
20 | public OscillatorType Type { get; set; } = OscillatorType.Sine;
21 |
22 | ///
23 | /// The initial frequency for the .
24 | ///
25 | [JsonPropertyName("frequency")]
26 | public float Frequency { get; set; } = 440;
27 |
28 | ///
29 | /// The initial detune value for the .
30 | ///
31 | [JsonPropertyName("detune")]
32 | public float Detune { get; set; } = 0;
33 |
34 | ///
35 | /// The PeriodicWave for the .
36 | /// If this is specified, then any valid value for is ignored; it is treated as if were specified.
37 | ///
38 | [JsonPropertyName("periodicWave")]
39 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
40 | public PeriodicWave? PeriodicWave { get; set; }
41 | }
42 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/OscillatorType.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Converters;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// Changing the gain of an audio signal is a fundamental operation in audio applications. This interface is an with a single input and single output.
8 | ///
9 | /// See the API definition here .
10 | [JsonConverter(typeof(OscillatorTypeConverter))]
11 | public enum OscillatorType
12 | {
13 | ///
14 | /// A sine wave.
15 | ///
16 | Sine,
17 | ///
18 | /// A square wave of duty period 0.5 .
19 | ///
20 | Square,
21 | ///
22 | /// A sawtooth wave.
23 | ///
24 | Sawtooth,
25 | ///
26 | /// A triangle wave.
27 | ///
28 | Triangle,
29 | ///
30 | /// A custom periodic wave.
31 | ///
32 | Custom
33 | }
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/OverSampleType.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Converters;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// The enum determines which the type of oversampling to use when shaping a curve.
8 | ///
9 | /// See the API definition here .
10 | [JsonConverter(typeof(OverSampleTypeConverter))]
11 | public enum OverSampleType
12 | {
13 | ///
14 | /// Don’t oversample
15 | ///
16 | None,
17 |
18 | ///
19 | /// Oversample two times
20 | ///
21 | TwoX,
22 |
23 | ///
24 | /// Oversample four times
25 | ///
26 | FourX,
27 | }
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/PanningModelType.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Converters;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// The enum determines which spatialization algorithm will be used to position the audio in 3D space. The default is .
8 | ///
9 | /// See the API definition here .
10 | [JsonConverter(typeof(PanningModelTypeConverter))]
11 | public enum PanningModelType
12 | {
13 | ///
14 | /// A simple and efficient spatialization algorithm using equal-power panning.
15 | ///
16 | ///
17 | /// When this panning model is used, all the s used to compute the output of this node are .
18 | ///
19 | EqualPower,
20 |
21 | ///
22 | /// A higher quality spatialization algorithm using a convolution with measured impulse responses from human subjects. This panning method renders stereo output.
23 | ///
24 | ///
25 | /// When this panning model is used, all the s used to compute the output of this node are .
26 | ///
27 | HRTF,
28 | }
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/PeriodicWaveConstraints.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// is used to specify how the waveform is normalized.
7 | ///
8 | /// See the API definition here .
9 | public class PeriodicWaveConstraints
10 | {
11 | ///
12 | /// Controls whether the periodic wave is normalized or not.
13 | /// If , the waveform is not normalized; otherwise, the waveform is normalized.
14 | ///
15 | [JsonPropertyName("disableNormalization")]
16 | public bool DisableNormalization { get; set; } = false;
17 | }
18 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/PeriodicWaveOptions.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebIDL.Exceptions;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio;
5 |
6 | ///
7 | /// is used to specify how the waveform is constructed.
8 | /// If only one of real or imag is specified.
9 | /// The other is treated as if it were an array of all zeroes of the same length.
10 | /// If neither is given, a is created that will be equivalent to an with .
11 | /// If both are given, the sequences must have the same length; otherwise an error of type will be thrown.
12 | ///
13 | /// See the API definition here .
14 | public class PeriodicWaveOptions : PeriodicWaveConstraints
15 | {
16 | ///
17 | /// The imag parameter represents an array of sine terms.
18 | /// The first element (index 0 ) does not exist in the Fourier series.
19 | /// The second element (index 1 ) represents the fundamental frequency.
20 | /// The third represents the first overtone and so on.
21 | ///
22 | [JsonPropertyName("imag")]
23 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
24 | public float[]? Imag { get; set; }
25 |
26 | ///
27 | /// The real parameter represents an array of cosine terms.
28 | /// The first element (index 0 ) is the DC-offset of the periodic waveform.
29 | /// The second element (index 1 ) represents the fundmental frequency.
30 | /// The third represents the first overtone and so on.
31 | ///
32 | [JsonPropertyName("real")]
33 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
34 | public float[]? Real { get; set; }
35 | }
36 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/StereoPannerOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// This specifies options for constructing a .
7 | ///
8 | /// See the API definition here .
9 | public class StereoPannerOptions : AudioNodeOptions
10 | {
11 | ///
12 | ///
13 | /// The default value is .
14 | ///
15 | public override ChannelCountMode ChannelCountMode { get; set; } = ChannelCountMode.ClampedMax;
16 |
17 | ///
18 | /// The initial value for
19 | ///
20 | [JsonPropertyName("pan")]
21 | public float Pan { get; set; } = 0;
22 | }
23 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/Options/WaveShaperOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace KristofferStrube.Blazor.WebAudio;
4 |
5 | ///
6 | /// This specifies options for constructing a .
7 | ///
8 | /// See the API definition here .
9 | public class WaveShaperOptions : AudioNodeOptions
10 | {
11 | ///
12 | /// The initial value for
13 | ///
14 | [JsonPropertyName("curve")]
15 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
16 | public float[]? Curve { get; set; }
17 |
18 | ///
19 | /// The initial value for
20 | ///
21 | [JsonPropertyName("oversample")]
22 | public OverSampleType Oversample { get; set; } = OverSampleType.None;
23 | }
24 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/PeriodicWave.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Extensions;
2 | using KristofferStrube.Blazor.WebIDL;
3 | using Microsoft.JSInterop;
4 |
5 | namespace KristofferStrube.Blazor.WebAudio;
6 |
7 | ///
8 | /// represents an arbitrary periodic waveform to be used with an .
9 | ///
10 | /// See the API definition here .
11 | [IJSWrapperConverter]
12 | public class PeriodicWave : BaseJSWrapper, IJSCreatable
13 | {
14 | ///
15 | public static async Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference)
16 | {
17 | return await CreateAsync(jSRuntime, jSReference, new());
18 | }
19 |
20 | ///
21 | public static Task CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options)
22 | {
23 | return Task.FromResult(new PeriodicWave(jSRuntime, jSReference, options));
24 | }
25 |
26 | ///
27 | /// Creates a using the standard constructor.
28 | ///
29 | /// An instance.
30 | /// The this new will be associated with.
31 | /// Initial parameter value for this .
32 | /// A new instance of a .
33 | public static async Task CreateAsync(IJSRuntime jSRuntime, BaseAudioContext context, PeriodicWaveOptions options)
34 | {
35 | IJSObjectReference helper = await jSRuntime.GetHelperAsync();
36 | IJSObjectReference jSInstance = await helper.InvokeAsync("constructPeriodicWave", context, options);
37 | return new PeriodicWave(jSRuntime, jSInstance, new() { DisposesJSReference = true });
38 | }
39 |
40 | ///
41 | protected PeriodicWave(IJSRuntime jSRuntime, IJSObjectReference jSReference, CreationOptions options) : base(jSRuntime, jSReference, options)
42 | {
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/UnionTypes/AudioContextLatencyCategoryOrDouble.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Converters;
2 | using KristofferStrube.Blazor.WebAudio.UnionTypes;
3 | using System.Text.Json.Serialization;
4 |
5 | namespace KristofferStrube.Blazor.WebAudio;
6 |
7 | ///
8 | /// A value that is either a or a .
9 | ///
10 | [JsonConverter(typeof(UnionTypeJsonConverter))]
11 | public class AudioContextLatencyCategoryOrDouble : UnionType
12 | {
13 | ///
14 | /// Creates an from an explicitly instead of using the implicit converter.
15 | ///
16 | /// A .
17 | public AudioContextLatencyCategoryOrDouble(AudioContextLatencyCategory value) : base(value) { }
18 |
19 | ///
20 | /// Creates an from a explicitly instead of using the implicit converter.
21 | ///
22 | /// A .
23 | public AudioContextLatencyCategoryOrDouble(double value) : base(value) { }
24 |
25 | internal AudioContextLatencyCategoryOrDouble(object value) : base(value) { }
26 |
27 | ///
28 | /// Creates an from an .
29 | ///
30 | /// An .
31 | public static implicit operator AudioContextLatencyCategoryOrDouble(AudioContextLatencyCategory value)
32 | {
33 | return new(value);
34 | }
35 |
36 | ///
37 | /// Creates an from a .
38 | ///
39 | /// A .
40 | public static implicit operator AudioContextLatencyCategoryOrDouble(double value)
41 | {
42 | return new(value);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/UnionTypes/UnionType.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Converters;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace KristofferStrube.Blazor.WebAudio.UnionTypes;
5 |
6 | ///
7 | /// A common Union Type class.
8 | ///
9 | [JsonConverter(typeof(UnionTypeJsonConverter))]
10 | public class UnionType
11 | {
12 | ///
13 | /// Creates a Union Type class from a value.
14 | ///
15 | ///
16 | protected UnionType(object value)
17 | {
18 | Value = value;
19 | }
20 |
21 | ///
22 | /// The value of the Union Type.
23 | ///
24 | public object Value { get; }
25 | }
--------------------------------------------------------------------------------
/src/KristofferStrube.Blazor.WebAudio/wwwroot/KristofferStrube.Blazor.WebAudio.PullAudioProcessor.js:
--------------------------------------------------------------------------------
1 | class PullAudioProcessor extends AudioWorkletProcessor {
2 | queue = [];
3 | backIndex = 0;
4 | frontIndex = 0;
5 | dataRequested = 0;
6 |
7 | static get parameterDescriptors() {
8 | return [{
9 | name: 'lowTide',
10 | defaultValue: 10,
11 | minValue: 1,
12 | maxValue: 10000,
13 | automationRate: "k-rate"
14 | },
15 | {
16 | name: 'highTide',
17 | defaultValue: 50,
18 | minValue: 1,
19 | maxValue: 10000,
20 | automationRate: "k-rate"
21 | },
22 | {
23 | name: 'bufferRequestSize',
24 | defaultValue: 10,
25 | minValue: 1,
26 | maxValue: 10000,
27 | automationRate: "k-rate"
28 | },
29 | {
30 | name: 'resolution',
31 | defaultValue: 1,
32 | minValue: 1,
33 | maxValue: 255,
34 | automationRate: "k-rate"
35 | }];
36 | }
37 |
38 | constructor(...args) {
39 | super(...args);
40 | this.queue = [];
41 | this.port.onmessage = (e) => {
42 | for (let i = 0; i < e.data.length / 128; i++) {
43 | this.queue.push(e.data.slice(i * 128, (i + 1) * 128));
44 | this.frontIndex++;
45 | this.dataRequested--;
46 | }
47 | };
48 | }
49 |
50 | process(inputs, outputs, parameters) {
51 | const output = outputs[0];
52 | const lowTide = parameters.lowTide[0];
53 | const highTide = parameters.highTide[0];
54 | const bufferRequestSize = parameters.bufferRequestSize[0];
55 | const resolution = parameters.resolution[0];
56 |
57 | try {
58 | const count = this.frontIndex - this.backIndex;
59 | if (count >= output.length) {
60 | for (let i = 0; i < output.length; i++) {
61 | let data = this.queue[this.backIndex];
62 | this.backIndex++;
63 |
64 | let channel = output[i];
65 | for (let j = 0; j < channel.length; j++) {
66 | if (resolution == 255) {
67 | channel[j] = data[j] / 255 * 2 - 1;
68 | }
69 | else {
70 | channel[j] = data[j];
71 | }
72 | }
73 | }
74 | }
75 | if (count < lowTide && this.dataRequested + bufferRequestSize < highTide) {
76 | this.dataRequested += bufferRequestSize;
77 | this.port.postMessage(bufferRequestSize);
78 | }
79 | }
80 | catch (e) {
81 | //this.port.postMessage(e.message + "-----" + e.stack);
82 | }
83 | return true;
84 | }
85 | }
86 |
87 | registerProcessor("kristoffer-strube-webaudio-pull-audio-processor", PullAudioProcessor);
--------------------------------------------------------------------------------
/tests/BlazorServer/App.razor:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Not found
8 |
9 | Sorry, there's nothing at this address.
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/tests/BlazorServer/BlazorServer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/tests/BlazorServer/EvaluationContext.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace BlazorServer;
3 |
4 | public class EvaluationContext
5 | {
6 | }
7 |
--------------------------------------------------------------------------------
/tests/BlazorServer/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 |
3 | @Body
4 |
--------------------------------------------------------------------------------
/tests/BlazorServer/Pages/Index.razor:
--------------------------------------------------------------------------------
1 | @page "/"
2 |
3 | @result
4 |
5 |
6 |
12 |
13 |
14 | @code {
15 | string? result;
16 |
17 | [Inject]
18 | public required EvaluationContext EvaluationContext { get; set; }
19 |
20 | protected override void OnAfterRender(bool firstRender)
21 | {
22 | if (!firstRender) return;
23 | result = "done";
24 | StateHasChanged();
25 | }
26 | }
--------------------------------------------------------------------------------
/tests/BlazorServer/Pages/_Host.cshtml:
--------------------------------------------------------------------------------
1 | @page "/"
2 | @using Microsoft.AspNetCore.Components.Web
3 | @namespace BlazorServer.Pages
4 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | An error has occurred. This application may no longer respond until reloaded.
20 |
21 |
22 | An unhandled exception has occurred. See browser dev tools for details.
23 |
24 |
Reload
25 |
🗙
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/tests/BlazorServer/Program.cs:
--------------------------------------------------------------------------------
1 | namespace BlazorServer;
2 |
3 | public class Program
4 | {
5 | private static async Task Main(string[] args)
6 | {
7 | var host = BuildWebHost(args, _ => { });
8 | await host.RunAsync();
9 | }
10 |
11 | public static IHost BuildWebHost(string[] args, Action configureServices)
12 | => Host.CreateDefaultBuilder(args)
13 | .ConfigureWebHostDefaults(builder =>
14 | {
15 | builder.UseStaticWebAssets();
16 | builder.UseStartup();
17 | builder.ConfigureServices(configureServices);
18 | })
19 | .Build();
20 | }
--------------------------------------------------------------------------------
/tests/BlazorServer/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "iisExpress": {
4 | "applicationUrl": "http://localhost:1729",
5 | "sslPort": 44325
6 | }
7 | },
8 | "profiles": {
9 | "http": {
10 | "commandName": "Project",
11 | "dotnetRunMessages": true,
12 | "launchBrowser": true,
13 | "applicationUrl": "http://localhost:5151",
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "https": {
19 | "commandName": "Project",
20 | "dotnetRunMessages": true,
21 | "launchBrowser": true,
22 | "applicationUrl": "https://localhost:7131;http://localhost:5151",
23 | "environmentVariables": {
24 | "ASPNETCORE_ENVIRONMENT": "Development"
25 | }
26 | },
27 | "IIS Express": {
28 | "commandName": "IISExpress",
29 | "launchBrowser": true,
30 | "environmentVariables": {
31 | "ASPNETCORE_ENVIRONMENT": "Development"
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tests/BlazorServer/Startup.cs:
--------------------------------------------------------------------------------
1 | namespace BlazorServer;
2 |
3 | public class Startup
4 | {
5 | public void ConfigureServices(IServiceCollection services)
6 | {
7 | services.AddRazorPages();
8 | services.AddServerSideBlazor();
9 | }
10 |
11 | public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env)
12 | {
13 | if (env.IsDevelopment())
14 | {
15 | app.UseExceptionHandler("/Error");
16 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
17 | app.UseHsts();
18 | }
19 |
20 | app.UseHttpsRedirection();
21 |
22 | app.UseStaticFiles();
23 |
24 | app.UseRouting();
25 |
26 | app.UseEndpoints(endpoints =>
27 | {
28 | endpoints.MapBlazorHub();
29 | endpoints.MapFallbackToPage("/_Host");
30 | });
31 | }
32 | }
--------------------------------------------------------------------------------
/tests/BlazorServer/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using Microsoft.AspNetCore.Components.Routing
2 | @using Microsoft.AspNetCore.Components.Web
3 | @using Microsoft.JSInterop
4 | @using BlazorServer
5 |
--------------------------------------------------------------------------------
/tests/BlazorServer/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "DetailedErrors": true,
3 | "Logging": {
4 | "LogLevel": {
5 | "Default": "Information",
6 | "Microsoft.AspNetCore": "Warning"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tests/BlazorServer/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | },
8 | "AllowedHosts": "*"
9 | }
10 |
--------------------------------------------------------------------------------
/tests/BlazorServer/wwwroot/css/site.css:
--------------------------------------------------------------------------------
1 | #blazor-error-ui {
2 | background: lightyellow;
3 | bottom: 0;
4 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
5 | display: none;
6 | left: 0;
7 | padding: 0.6rem 1.25rem 0.7rem 1.25rem;
8 | position: fixed;
9 | width: 100%;
10 | z-index: 1000;
11 | }
12 |
13 | #blazor-error-ui .dismiss {
14 | cursor: pointer;
15 | position: absolute;
16 | right: 3.5rem;
17 | top: 0.5rem;
18 | }
19 |
20 | .blazor-error-boundary {
21 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
22 | padding: 1rem 1rem 1rem 3.7rem;
23 | color: white;
24 | }
25 |
26 | .blazor-error-boundary::after {
27 | content: "An error has occurred."
28 | }
29 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/AudioDestinationNodeTest.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 |
3 | namespace IntegrationTests.AudioNodeTests;
4 |
5 | public class AudioDestinationNodeTest : AudioNodeTest
6 | {
7 | public override async Task GetDefaultInstanceAsync()
8 | {
9 | return await AudioContext.GetDestinationAsync();
10 | }
11 |
12 | [Test]
13 | public async Task GetMaxChannelCountAsync_RetrievesMaxChannelCount()
14 | {
15 | // Arrange
16 | await using AudioDestinationNode destination = await AudioContext.GetDestinationAsync();
17 |
18 | // Act
19 | ulong maxChannelCount = await destination.GetMaxChannelCountAsync();
20 |
21 | // Assert
22 | _ = maxChannelCount.Should().BeGreaterThan(0);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/AudioNodeTest.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using KristofferStrube.Blazor.WebIDL.Exceptions;
3 |
4 | namespace IntegrationTests.AudioNodeTests;
5 |
6 | public abstract class AudioNodeTest : BlazorTest where TAudioNode : AudioNode
7 | {
8 | public abstract Task GetDefaultInstanceAsync();
9 |
10 | public virtual Dictionary UnsupportedChannelCountModes => [];
11 |
12 | public virtual Dictionary UnsupportedChannelInterpretations => [];
13 |
14 | [Test]
15 | public async Task CreateAsync_WithNoOptions_Succeeds()
16 | {
17 | // Act
18 | await using TAudioNode node = await GetDefaultInstanceAsync();
19 |
20 | // Assert
21 | _ = node.Should().BeOfType();
22 | }
23 |
24 | [TestCase(ChannelCountMode.Max)]
25 | [TestCase(ChannelCountMode.Explicit)]
26 | [TestCase(ChannelCountMode.ClampedMax)]
27 | [Test]
28 | public async Task SettingChannelCountMode_SetsChannelCountMode_ExceptForUnsupportedValues(ChannelCountMode mode)
29 | {
30 | // Act
31 | Func> action = async () =>
32 | {
33 | await using TAudioNode node = await GetDefaultInstanceAsync();
34 | await node.SetChannelCountModeAsync(mode);
35 | return await node.GetChannelCountModeAsync();
36 | };
37 |
38 | // Assert
39 | if (UnsupportedChannelCountModes.TryGetValue(mode, out Type? exceptionType))
40 | {
41 | _ = (await action.Should().ThrowAsync()).And.Should().BeOfType(exceptionType);
42 | }
43 | else
44 | {
45 | ChannelCountMode result = await action();
46 | _ = result.Should().Be(mode);
47 | }
48 | }
49 |
50 | [TestCase(ChannelInterpretation.Discrete)]
51 | [TestCase(ChannelInterpretation.Speakers)]
52 | [Test]
53 | public async Task SettingChannelInterpretation_SetsInterpretation_ExceptForUnsupportedValues(ChannelInterpretation interpretation)
54 | {
55 | // Act
56 | Func> action = async () =>
57 | {
58 | await using TAudioNode node = await GetDefaultInstanceAsync();
59 | await node.SetChannelInterpretationAsync(interpretation);
60 | return await node.GetChannelInterpretationAsync();
61 | };
62 |
63 | // Assert
64 | if (UnsupportedChannelInterpretations.TryGetValue(interpretation, out Type? exceptionType))
65 | {
66 | _ = (await action.Should().ThrowAsync()).And.Should().BeOfType(exceptionType);
67 | }
68 | else
69 | {
70 | ChannelInterpretation result = await action();
71 | _ = result.Should().Be(interpretation);
72 | }
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/ChannelMergerNodeTest.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using KristofferStrube.Blazor.WebIDL.Exceptions;
3 | using Microsoft.JSInterop;
4 |
5 | namespace IntegrationTests.AudioNodeTests;
6 |
7 | public class ChannelMergerNodeTest : AudioNodeWithAudioNodeOptions
8 | {
9 | public override async Task CreateAsync(IJSRuntime jSRuntime, AudioContext context, ChannelMergerOptions? options)
10 | => await ChannelMergerNode.CreateAsync(JSRuntime, AudioContext, options);
11 |
12 | public override Dictionary UnsupportedChannelCountModes => new()
13 | {
14 | [ChannelCountMode.Max] = typeof(InvalidStateErrorException),
15 | [ChannelCountMode.ClampedMax] = typeof(InvalidStateErrorException),
16 | };
17 |
18 | [Test]
19 | public async Task CreateAsync_WithNoOptions_DefaultsTo6Inputs()
20 | {
21 | // Arrange
22 | await using ChannelMergerNode node = await ChannelMergerNode.CreateAsync(JSRuntime, AudioContext);
23 |
24 | // Act
25 | ulong numberOfInputs = await node.GetNumberOfInputsAsync();
26 |
27 | // Assert
28 | _ = numberOfInputs.Should().Be(6);
29 | }
30 |
31 | [Test]
32 | public async Task CreateAsync_WithEmptyOptions_DefaultsTo6Inputs()
33 | {
34 | // Arrange
35 | await using ChannelMergerNode node = await ChannelMergerNode.CreateAsync(JSRuntime, AudioContext, new());
36 |
37 | // Act
38 | ulong numberOfInputs = await node.GetNumberOfInputsAsync();
39 |
40 | // Assert
41 | _ = numberOfInputs.Should().Be(6);
42 | }
43 | }
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/ChannelSplitterNodeTest.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using KristofferStrube.Blazor.WebIDL.Exceptions;
3 | using Microsoft.JSInterop;
4 |
5 | namespace IntegrationTests.AudioNodeTests;
6 |
7 | public class ChannelSplitterNodeTest : AudioNodeWithAudioNodeOptions
8 | {
9 | public override async Task CreateAsync(IJSRuntime jSRuntime, AudioContext context, ChannelSplitterOptions? options)
10 | => await ChannelSplitterNode.CreateAsync(JSRuntime, AudioContext, options);
11 |
12 | public override Dictionary UnsupportedChannelCountModes => new()
13 | {
14 | [ChannelCountMode.Max] = typeof(InvalidStateErrorException),
15 | [ChannelCountMode.ClampedMax] = typeof(InvalidStateErrorException),
16 | };
17 |
18 | public override Dictionary UnsupportedChannelInterpretations => new()
19 | {
20 | [ChannelInterpretation.Speakers] = typeof(InvalidStateErrorException),
21 | };
22 |
23 | [Test]
24 | public async Task CreateAsync_WithNoOptions_DefaultsTo6Outputs()
25 | {
26 | // Arrange
27 | await using ChannelSplitterNode node = await ChannelSplitterNode.CreateAsync(JSRuntime, AudioContext);
28 |
29 | // Act
30 | ulong numberOfOutputs = await node.GetNumberOfOutputsAsync();
31 |
32 | // Assert
33 | _ = numberOfOutputs.Should().Be(6);
34 | }
35 |
36 | [Test]
37 | public async Task CreateAsync_WithEmptyOptions_DefaultsTo6Outputs()
38 | {
39 | // Arrange
40 | await using ChannelSplitterNode node = await ChannelSplitterNode.CreateAsync(JSRuntime, AudioContext, new());
41 |
42 | // Act
43 | ulong numberOfOutputs = await node.GetNumberOfOutputsAsync();
44 |
45 | // Assert
46 | _ = numberOfOutputs.Should().Be(6);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/ConstantSourceNodeTest.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 |
3 | namespace IntegrationTests.AudioNodeTests;
4 |
5 | public class ConstantSourceNodeTest : AudioNodeTest
6 | {
7 | public override async Task GetDefaultInstanceAsync()
8 | {
9 | return await ConstantSourceNode.CreateAsync(JSRuntime, AudioContext);
10 | }
11 |
12 | [Test]
13 | [TestCase(1)]
14 | [TestCase(-0.5f)]
15 | public async Task GetOffsetAsync_ShouldRetrieveOffsetParameter(float offset)
16 | {
17 | // Arrange
18 | await using ConstantSourceNode node = await ConstantSourceNode.CreateAsync(JSRuntime, AudioContext, new()
19 | {
20 | Offset = offset
21 | });
22 |
23 | // Act
24 | await using AudioParam offsetParameter = await node.GetOffsetAsync();
25 |
26 | // Assert
27 | float readOffset = await offsetParameter.GetValueAsync();
28 | _ = readOffset.Should().Be(offset);
29 |
30 | await offsetParameter.SetValueAsync(offset + 1);
31 | readOffset = await offsetParameter.GetValueAsync();
32 | _ = readOffset.Should().Be(offset + 1);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/DelayNodeTest.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Microsoft.JSInterop;
3 |
4 | namespace IntegrationTests.AudioNodeTests;
5 |
6 | public class DelayNodeTest : AudioNodeWithAudioNodeOptions
7 | {
8 | public override async Task CreateAsync(IJSRuntime jSRuntime, AudioContext context, DelayOptions? options)
9 | => await DelayNode.CreateAsync(JSRuntime, AudioContext, options);
10 |
11 | [Test]
12 | [TestCase(0)]
13 | [TestCase(10)]
14 | public async Task GetDelayTimeAsync_ShouldRetrieveDelayTimeParameter(float delayTime)
15 | {
16 | // Arrange
17 | await using DelayNode node = await DelayNode.CreateAsync(JSRuntime, AudioContext, new()
18 | {
19 | DelayTime = delayTime,
20 | MaxDelayTime = delayTime + 1,
21 | });
22 |
23 | // Act
24 | await using AudioParam delayTimeParameter = await node.GetDelayTimeAsync();
25 |
26 | // Assert
27 | float readDelayTime = await delayTimeParameter.GetValueAsync();
28 | _ = readDelayTime.Should().Be(delayTime);
29 |
30 | await delayTimeParameter.SetValueAsync(delayTime + 1);
31 | readDelayTime = await delayTimeParameter.GetValueAsync();
32 | _ = readDelayTime.Should().Be(delayTime + 1);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/DynamicsCompressorNodeTest.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebIDL.Exceptions;
2 | using Microsoft.JSInterop;
3 |
4 | namespace IntegrationTests.AudioNodeTests;
5 |
6 | public class DynamicsCompressorNodeTest : AudioNodeWithAudioNodeOptions
7 | {
8 | public override async Task CreateAsync(IJSRuntime jSRuntime, AudioContext context, DynamicsCompressorOptions? options)
9 | => await DynamicsCompressorNode.CreateAsync(JSRuntime, AudioContext, options);
10 |
11 | public override Dictionary UnsupportedChannelCountModes => new()
12 | {
13 | [ChannelCountMode.Max] = typeof(NotSupportedErrorException)
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/GainNodeTest.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 |
3 | namespace IntegrationTests.AudioNodeTests;
4 |
5 | public class GainNodeTest : AudioNodeWithAudioNodeOptions
6 | {
7 | public override async Task CreateAsync(IJSRuntime jSRuntime, AudioContext context, GainOptions? options)
8 | => await GainNode.CreateAsync(JSRuntime, AudioContext, options);
9 | }
10 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/IIRFilterNodeTest.cs:
--------------------------------------------------------------------------------
1 | namespace IntegrationTests.AudioNodeTests;
2 |
3 | public class IIRFilterNodeTest : AudioNodeTest
4 | {
5 | public override async Task GetDefaultInstanceAsync()
6 | {
7 | return await IIRFilterNode.CreateAsync(JSRuntime, AudioContext, new IIRFilterOptions()
8 | {
9 | Feedforward = [1],
10 | Feedback = [1],
11 | });
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/MediaElementAudioSourceNodeTest.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Options;
2 | using Microsoft.JSInterop;
3 |
4 | namespace IntegrationTests.AudioNodeTests;
5 |
6 | public class MediaElementAudioSourceNodeTest : AudioNodeTest
7 | {
8 | public override async Task GetDefaultInstanceAsync()
9 | {
10 | IJSObjectReference element = await EvaluationContext.GetAudioElementAyns();
11 | return await MediaElementAudioSourceNode.CreateAsync(JSRuntime, AudioContext, new MediaElementAudioSourceOptions()
12 | {
13 | MediaElement = element
14 | });
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/MediaStreamAudioDestinationNodeTest.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebAudio.Options;
2 | using Microsoft.JSInterop;
3 |
4 | namespace IntegrationTests.AudioNodeTests;
5 |
6 | public class MediaStreamAudioDestinationNodeTest : AudioNodeWithAudioNodeOptions
7 | {
8 | public override async Task CreateAsync(IJSRuntime jSRuntime, AudioContext context, MediaStreamAudioDestinationOptions? options)
9 | => await MediaStreamAudioDestinationNode.CreateAsync(JSRuntime, AudioContext, options);
10 | }
11 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/MediaStreamAudioSourceNodeTest.cs:
--------------------------------------------------------------------------------
1 | namespace IntegrationTests.AudioNodeTests;
2 |
3 | public class MediaStreamAudioSourceNodeTest : AudioNodeTest
4 | {
5 | public override async Task GetDefaultInstanceAsync()
6 | {
7 | return await MediaStreamAudioSourceNode.CreateAsync(JSRuntime, AudioContext, new MediaStreamAudioSourceOptions()
8 | {
9 | MediaStream = await EvaluationContext.GetMediaStream()
10 | });
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/OscillatorNodeTest.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 |
3 | namespace IntegrationTests.AudioNodeTests;
4 |
5 | public class OscillatorNodeTest : AudioNodeWithAudioNodeOptions
6 | {
7 | public override async Task CreateAsync(IJSRuntime jSRuntime, AudioContext context, OscillatorOptions? options)
8 | => await OscillatorNode.CreateAsync(JSRuntime, AudioContext, options);
9 | }
10 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/PannerNodeTest.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebIDL.Exceptions;
2 | using Microsoft.JSInterop;
3 |
4 | namespace IntegrationTests.AudioNodeTests;
5 |
6 | public class PannerNodeTest : AudioNodeWithAudioNodeOptions
7 | {
8 | public override async Task CreateAsync(IJSRuntime jSRuntime, AudioContext context, PannerOptions? options)
9 | => await PannerNode.CreateAsync(JSRuntime, AudioContext, options);
10 |
11 | public override Dictionary UnsupportedChannelCountModes => new()
12 | {
13 | [ChannelCountMode.Max] = typeof(NotSupportedErrorException)
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/StereoPannerNodeTest.cs:
--------------------------------------------------------------------------------
1 | using KristofferStrube.Blazor.WebIDL.Exceptions;
2 | using Microsoft.JSInterop;
3 |
4 | namespace IntegrationTests.AudioNodeTests;
5 |
6 | public class StereoPannerNodeTest : AudioNodeWithAudioNodeOptions
7 | {
8 | public override async Task CreateAsync(IJSRuntime jSRuntime, AudioContext context, StereoPannerOptions? options)
9 | => await StereoPannerNode.CreateAsync(JSRuntime, AudioContext, options);
10 |
11 | public override Dictionary UnsupportedChannelCountModes => new()
12 | {
13 | [ChannelCountMode.Max] = typeof(NotSupportedErrorException)
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/AudioNodeTests/WaveShaperNodeTest.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.JSInterop;
2 |
3 | namespace IntegrationTests.AudioNodeTests;
4 |
5 | public class WaveShaperNodeTest : AudioNodeWithAudioNodeOptions
6 | {
7 | public override async Task CreateAsync(IJSRuntime jSRuntime, AudioContext context, WaveShaperOptions? options)
8 | => await WaveShaperNode.CreateAsync(JSRuntime, AudioContext, options);
9 | }
10 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/Infrastructure/AudioContextEvaluationContext.cs:
--------------------------------------------------------------------------------
1 | using BlazorServer;
2 | using KristofferStrube.Blazor.MediaCaptureStreams;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using Microsoft.JSInterop;
5 |
6 | namespace IntegrationTests.Infrastructure;
7 |
8 | public class AudioContextEvaluationContext(IJSRuntime jSRuntime, IMediaDevicesService mediaDevicesService) : EvaluationContext
9 | {
10 | public IJSRuntime JSRuntime => jSRuntime;
11 |
12 | public IMediaDevicesService MediaDevicesService => mediaDevicesService;
13 |
14 | public static AudioContextEvaluationContext Create(IServiceProvider provider)
15 | {
16 | IMediaDevicesService mediaDevicesService = provider.GetRequiredService();
17 | IJSRuntime jSRuntime = provider.GetRequiredService();
18 |
19 | return new AudioContextEvaluationContext(jSRuntime, mediaDevicesService);
20 | }
21 |
22 | public async Task GetAudioContext()
23 | {
24 | AudioContext audioContext = await AudioContext.CreateAsync(jSRuntime);
25 | return audioContext;
26 | }
27 |
28 | public async Task GetMediaStream()
29 | {
30 | await using MediaDevices mediaDevices = await mediaDevicesService.GetMediaDevicesAsync();
31 | MediaStream mediaStream = await mediaDevices.GetUserMediaAsync(new() { Audio = true });
32 | return mediaStream;
33 | }
34 |
35 | public async Task GetAudioElementAyns()
36 | {
37 | return await JSRuntime.InvokeAsync("getAudioElement");
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/IntegrationTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | enable
6 | enable
7 | false
8 | true
9 | preview
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | all
21 | runtime; build; native; contentfiles; analyzers; buildtransitive
22 |
23 |
24 | all
25 | runtime; build; native; contentfiles; analyzers; buildtransitive
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/tests/IntegrationTests/Usings.cs:
--------------------------------------------------------------------------------
1 | global using KristofferStrube.Blazor.WebAudio;
2 | global using KristofferStrube.Blazor.WebAudio.IntegrationTests.Infrastructure;
3 | global using NUnit.Framework;
4 |
--------------------------------------------------------------------------------
/tests/tests.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | # Visual Studio Version 17
3 | VisualStudioVersion = 17.5.2.0
4 | MinimumVisualStudioVersion = 10.0.40219.1
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorServer", "BlazorServer\BlazorServer.csproj", "{A54C7275-E823-D105-A0DD-4D35CB594AFD}"
6 | EndProject
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTests", "IntegrationTests\IntegrationTests.csproj", "{59FDAA84-6701-32D0-9E5C-CA30D0B3B987}"
8 | EndProject
9 | Global
10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
11 | Debug|Any CPU = Debug|Any CPU
12 | Release|Any CPU = Release|Any CPU
13 | EndGlobalSection
14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
15 | {A54C7275-E823-D105-A0DD-4D35CB594AFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
16 | {A54C7275-E823-D105-A0DD-4D35CB594AFD}.Debug|Any CPU.Build.0 = Debug|Any CPU
17 | {A54C7275-E823-D105-A0DD-4D35CB594AFD}.Release|Any CPU.ActiveCfg = Release|Any CPU
18 | {A54C7275-E823-D105-A0DD-4D35CB594AFD}.Release|Any CPU.Build.0 = Release|Any CPU
19 | {59FDAA84-6701-32D0-9E5C-CA30D0B3B987}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {59FDAA84-6701-32D0-9E5C-CA30D0B3B987}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {59FDAA84-6701-32D0-9E5C-CA30D0B3B987}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {59FDAA84-6701-32D0-9E5C-CA30D0B3B987}.Release|Any CPU.Build.0 = Release|Any CPU
23 | EndGlobalSection
24 | GlobalSection(SolutionProperties) = preSolution
25 | HideSolutionNode = FALSE
26 | EndGlobalSection
27 | GlobalSection(ExtensibilityGlobals) = postSolution
28 | SolutionGuid = {705E4106-ECBA-47B1-A459-A866AA21E2AE}
29 | EndGlobalSection
30 | EndGlobal
31 |
--------------------------------------------------------------------------------