Skip to content

Commit a9d520e

Browse files
Added tests for AudioBufferSourceNode.
1 parent afbd6a4 commit a9d520e

File tree

3 files changed

+303
-1
lines changed

3 files changed

+303
-1
lines changed

src/KristofferStrube.Blazor.WebAudio/AudioNodes/AudioBufferSourceNode.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ public async Task SetLoopEndAsync(double value)
195195
/// <param name="offset">The offset parameter supplies a playhead position where playback will begin. If <c>0</c> is passed in for this value, then playback will start from the beginning of the buffer.</param>
196196
/// <param name="duration">The duration parameter describes the duration of sound to be played, expressed as seconds of total buffer content to be output, including any whole or partial loop iterations. The units of duration are independent of the effects of <see cref="GetPlaybackRateAsync"/>. For example, a duration of <c>5</c> seconds with a playback rate of <c>0.5</c> will output <c>5</c> seconds of buffer content at half speed, producing <c>10</c> seconds of audible output.</param>
197197
/// <exception cref="InvalidStateErrorException"></exception>
198+
/// <exception cref="RangeErrorException"></exception>
198199
public async Task StartAsync(double when = 0, double? offset = null, double? duration = null)
199200
{
200201
if (offset is null && duration is null)

src/KristofferStrube.Blazor.WebAudio/Options/AudioBufferSourceOptions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ public class AudioBufferSourceOptions
4141
/// <summary>
4242
/// The initial value for <see cref="AudioBufferSourceNode.GetPlaybackRateAsync"/>
4343
/// </summary>
44+
/// <remarks>
45+
/// Default is <c>1</c>.
46+
/// </remarks>
4447
[JsonPropertyName("playbackRate")]
4548
public float PlaybackRate { get; set; } = 1;
4649
}

tests/IntegrationTests/AudioNodeTests/AudioBufferSourceNodeTest.cs

Lines changed: 299 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using FluentAssertions;
2+
using KristofferStrube.Blazor.DOM;
23

34
namespace IntegrationTests.AudioNodeTests;
45

@@ -25,7 +26,7 @@ public async Task GetBufferAsync_ShouldReturnNull_WhenItHasNoBuffer()
2526
}
2627

2728
[Test]
28-
public async Task GetBufferAsync_ShouldReturnBuffer_WhenItHasBuffer()
29+
public async Task GetBufferAsync_ShouldRetrieveBuffer_WhenItHasBuffer()
2930
{
3031
// Arrange
3132
await using AudioContext context = await AudioContext.CreateAsync(JSRuntime);
@@ -42,4 +43,301 @@ public async Task GetBufferAsync_ShouldReturnBuffer_WhenItHasBuffer()
4243
// Assert
4344
_ = readBuffer.Should().NotBeNull();
4445
}
46+
47+
[Test]
48+
public async Task SetBufferAsync_ShouldUpdateBuffer_WhenItHasNotBeenSetAlready()
49+
{
50+
// Arrange
51+
await using AudioContext context = await AudioContext.CreateAsync(JSRuntime);
52+
53+
await using AudioBufferSourceNode node = await AudioBufferSourceNode.CreateAsync(JSRuntime, context);
54+
await using AudioBuffer buffer = await AudioBuffer.CreateAsync(JSRuntime, new AudioBufferOptions() { Length = 1, SampleRate = 8000 });
55+
56+
// Act
57+
await node.SetBufferAsync(buffer);
58+
59+
// Assert
60+
AudioBuffer? readBuffer = await node.GetBufferAsync();
61+
_ = readBuffer.Should().NotBeNull();
62+
}
63+
64+
[Test]
65+
public async Task SetBufferAsync_WithNullArgument_ShouldUpdateBuffer_WhenItHasAlreadyBeenSet()
66+
{
67+
// Arrange
68+
await using AudioContext context = await AudioContext.CreateAsync(JSRuntime);
69+
70+
await using AudioBuffer buffer = await AudioBuffer.CreateAsync(JSRuntime, new AudioBufferOptions() { Length = 1, SampleRate = 8000 });
71+
await using AudioBufferSourceNode node = await AudioBufferSourceNode.CreateAsync(JSRuntime, context, new()
72+
{
73+
Buffer = buffer
74+
});
75+
76+
// Act
77+
await node.SetBufferAsync(null);
78+
79+
// Assert
80+
AudioBuffer? readBuffer = await node.GetBufferAsync();
81+
_ = readBuffer.Should().BeNull();
82+
}
83+
84+
[Test]
85+
[TestCase(1)]
86+
[TestCase(-2)]
87+
public async Task GetPlaybackRateAsync_RetrievesPlaybackRate(float playbackRate)
88+
{
89+
// Arrange
90+
await using AudioContext context = await AudioContext.CreateAsync(JSRuntime);
91+
92+
await using AudioBufferSourceNode node = await AudioBufferSourceNode.CreateAsync(JSRuntime, context, new()
93+
{
94+
PlaybackRate = playbackRate
95+
});
96+
97+
// Act
98+
await using AudioParam detuneParameter = await node.GetPlaybackRateAsync();
99+
float readPlaybackRate = await detuneParameter.GetValueAsync();
100+
101+
// Assert
102+
_ = readPlaybackRate.Should().Be(playbackRate);
103+
}
104+
105+
[Test]
106+
[TestCase(1)]
107+
[TestCase(-2)]
108+
public async Task GetDetuneAsync_RetrievesDetune(float detune)
109+
{
110+
// Arrange
111+
await using AudioContext context = await AudioContext.CreateAsync(JSRuntime);
112+
113+
await using AudioBufferSourceNode node = await AudioBufferSourceNode.CreateAsync(JSRuntime, context, new()
114+
{
115+
Detune = detune
116+
});
117+
118+
// Act
119+
await using AudioParam detuneParameter = await node.GetDetuneAsync();
120+
float readDetune = await detuneParameter.GetValueAsync();
121+
122+
// Assert
123+
_ = readDetune.Should().Be(detune);
124+
}
125+
126+
[Test]
127+
[TestCase(true)]
128+
[TestCase(false)]
129+
public async Task GetLoopAsync_RetrievesLoop(bool loop)
130+
{
131+
// Arrange
132+
await using AudioContext context = await AudioContext.CreateAsync(JSRuntime);
133+
134+
await using AudioBufferSourceNode node = await AudioBufferSourceNode.CreateAsync(JSRuntime, context, new()
135+
{
136+
Loop = loop
137+
});
138+
139+
// Act
140+
bool readLoop = await node.GetLoopAsync();
141+
142+
// Assert
143+
_ = readLoop.Should().Be(loop);
144+
}
145+
146+
[Test]
147+
[TestCase(true)]
148+
[TestCase(false)]
149+
public async Task SetLoopAsync_UpdatesLoop(bool loop)
150+
{
151+
// Arrange
152+
await using AudioContext context = await AudioContext.CreateAsync(JSRuntime);
153+
154+
await using AudioBufferSourceNode node = await AudioBufferSourceNode.CreateAsync(JSRuntime, context, new()
155+
{
156+
Loop = !loop
157+
});
158+
bool readLoopBeforeSetting = await node.GetLoopAsync();
159+
160+
// Act
161+
await node.SetLoopAsync(loop);
162+
163+
// Assert
164+
bool readLoopAfterSetting = await node.GetLoopAsync();
165+
_ = readLoopBeforeSetting.Should().Be(!loop);
166+
_ = readLoopAfterSetting.Should().Be(loop);
167+
}
168+
169+
[Test]
170+
[TestCase(100)]
171+
[TestCase(-3)]
172+
public async Task GetLoopStartAsync_RetrievesLoopStart(double loopStart)
173+
{
174+
// Arrange
175+
await using AudioContext context = await AudioContext.CreateAsync(JSRuntime);
176+
177+
await using AudioBufferSourceNode node = await AudioBufferSourceNode.CreateAsync(JSRuntime, context, new()
178+
{
179+
LoopStart = loopStart
180+
});
181+
182+
// Act
183+
double readLoopStart = await node.GetLoopStartAsync();
184+
185+
// Assert
186+
_ = readLoopStart.Should().Be(loopStart);
187+
}
188+
189+
[Test]
190+
[TestCase(100)]
191+
[TestCase(-3)]
192+
public async Task SetLoopStartAsync_UpdatesLoopStart(double loopStart)
193+
{
194+
// Arrange
195+
await using AudioContext context = await AudioContext.CreateAsync(JSRuntime);
196+
197+
await using AudioBufferSourceNode node = await AudioBufferSourceNode.CreateAsync(JSRuntime, context);
198+
199+
// Act
200+
await node.SetLoopStartAsync(loopStart);
201+
202+
// Assert
203+
double readLoopStart = await node.GetLoopStartAsync();
204+
_ = readLoopStart.Should().Be(loopStart);
205+
}
206+
207+
[Test]
208+
[TestCase(100)]
209+
[TestCase(-3)]
210+
public async Task GetLoopEndAsync_RetrievesLoopEnd(double loopEnd)
211+
{
212+
// Arrange
213+
await using AudioContext context = await AudioContext.CreateAsync(JSRuntime);
214+
215+
await using AudioBufferSourceNode node = await AudioBufferSourceNode.CreateAsync(JSRuntime, context, new()
216+
{
217+
LoopEnd = loopEnd
218+
});
219+
220+
// Act
221+
double readLoopEnd = await node.GetLoopEndAsync();
222+
223+
// Assert
224+
_ = readLoopEnd.Should().Be(loopEnd);
225+
}
226+
227+
[Test]
228+
[TestCase(100)]
229+
[TestCase(-3)]
230+
public async Task SetLoopEndAsync_UpdatesLoopEnd(double loopEnd)
231+
{
232+
// Arrange
233+
await using AudioContext context = await AudioContext.CreateAsync(JSRuntime);
234+
235+
await using AudioBufferSourceNode node = await AudioBufferSourceNode.CreateAsync(JSRuntime, context);
236+
237+
// Act
238+
await node.SetLoopEndAsync(loopEnd);
239+
240+
// Assert
241+
double readLoopEnd = await node.GetLoopEndAsync();
242+
_ = readLoopEnd.Should().Be(loopEnd);
243+
}
244+
245+
[Test]
246+
public async Task StartAsync_WithNoArguments_StartsNodeImmediately()
247+
{
248+
// Arrange
249+
await using AudioContext context = await AudioContext.CreateAsync(JSRuntime);
250+
251+
await using AudioBuffer buffer = await AudioBuffer.CreateAsync(JSRuntime, new AudioBufferOptions()
252+
{
253+
SampleRate = 8000,
254+
Length = 1
255+
});
256+
await using AudioBufferSourceNode node = await AudioBufferSourceNode.CreateAsync(JSRuntime, context, new()
257+
{
258+
Buffer = buffer
259+
});
260+
261+
bool eventListenerTriggered = false;
262+
await using EventListener<Event> onEndedListener = await EventListener<Event>.CreateAsync(JSRuntime, e => eventListenerTriggered = true);
263+
await node.AddOnEndedEventListenerAsync(onEndedListener);
264+
265+
// Act
266+
await node.StartAsync();
267+
268+
// Assert
269+
await Task.Delay(100);
270+
_ = eventListenerTriggered.Should().BeTrue();
271+
await node.RemoveOnEndedEventListenerAsync(onEndedListener);
272+
}
273+
274+
[Test]
275+
[TestCase(0.2)]
276+
[TestCase(0.4)]
277+
public async Task StartAsync_WithWhenArgument_StartsNodeThen(double whenOffset)
278+
{
279+
// Arrange
280+
await using AudioContext context = await AudioContext.CreateAsync(JSRuntime);
281+
282+
await using AudioBuffer buffer = await AudioBuffer.CreateAsync(JSRuntime, new AudioBufferOptions()
283+
{
284+
SampleRate = 8000,
285+
Length = 1
286+
});
287+
await using AudioBufferSourceNode node = await AudioBufferSourceNode.CreateAsync(JSRuntime, context, new()
288+
{
289+
Buffer = buffer
290+
});
291+
292+
bool eventListenerTriggered = false;
293+
await using EventListener<Event> onEndedListener = await EventListener<Event>.CreateAsync(JSRuntime, e => eventListenerTriggered = true);
294+
await node.AddOnEndedEventListenerAsync(onEndedListener);
295+
296+
// Act
297+
double time = await context.GetCurrentTimeAsync();
298+
await node.StartAsync(when: time + whenOffset);
299+
300+
// Assert
301+
await Task.Delay(TimeSpan.FromSeconds(whenOffset - 0.1));
302+
_ = eventListenerTriggered.Should().BeFalse();
303+
await Task.Delay(TimeSpan.FromSeconds(0.2));
304+
_ = eventListenerTriggered.Should().BeTrue();
305+
await node.RemoveOnEndedEventListenerAsync(onEndedListener);
306+
}
307+
308+
[Test]
309+
[TestCase(0.2)]
310+
[TestCase(0.7)]
311+
public async Task StartAsync_WithOffsetParameter_StartsOffsetInBuffer(double offset)
312+
{
313+
// Arrange
314+
await using AudioContext context = await AudioContext.CreateAsync(JSRuntime);
315+
316+
await using AudioBuffer buffer = await AudioBuffer.CreateAsync(JSRuntime, new AudioBufferOptions()
317+
{
318+
SampleRate = 8000,
319+
Length = 8000 * 1,
320+
});
321+
322+
var a = await buffer.GetDurationAsync();
323+
324+
await using AudioBufferSourceNode node = await AudioBufferSourceNode.CreateAsync(JSRuntime, context, new()
325+
{
326+
Buffer = buffer
327+
});
328+
329+
bool eventListenerTriggered = false;
330+
await using EventListener<Event> onEndedListener = await EventListener<Event>.CreateAsync(JSRuntime, e => eventListenerTriggered = true);
331+
await node.AddOnEndedEventListenerAsync(onEndedListener);
332+
333+
// Act
334+
await node.StartAsync(when: 0, offset: offset);
335+
336+
// Assert
337+
await Task.Delay(TimeSpan.FromSeconds(1 - offset - 0.1));
338+
_ = eventListenerTriggered.Should().BeFalse();
339+
await Task.Delay(TimeSpan.FromSeconds(offset + 0.1));
340+
_ = eventListenerTriggered.Should().BeTrue();
341+
await node.RemoveOnEndedEventListenerAsync(onEndedListener);
342+
}
45343
}

0 commit comments

Comments
 (0)