Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit f4ede88

Browse files
author
Ehsan Akhgari
committed
Bug 864164 - Part 2: Send the AudioBufferSourceNode buffer parameter changes to the stream; r=padenot
1 parent c1c4213 commit f4ede88

6 files changed

Lines changed: 127 additions & 29 deletions

File tree

content/media/webaudio/AudioBufferSourceNode.cpp

Lines changed: 67 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "AudioDestinationNode.h"
1313
#include "PannerNode.h"
1414
#include "speex/speex_resampler.h"
15+
#include <limits>
1516

1617
namespace mozilla {
1718
namespace dom {
@@ -400,10 +401,13 @@ AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* aContext)
400401
: AudioNode(aContext)
401402
, mLoopStart(0.0)
402403
, mLoopEnd(0.0)
404+
, mOffset(0.0)
405+
, mDuration(std::numeric_limits<double>::min())
403406
, mPlaybackRate(new AudioParam(this, SendPlaybackRateToStream, 1.0f))
404407
, mPannerNode(nullptr)
405408
, mLoop(false)
406409
, mStartCalled(false)
410+
, mOffsetAndDurationRemembered(false)
407411
{
408412
mStream = aContext->Graph()->CreateAudioNodeStream(
409413
new AudioBufferSourceNodeEngine(this, aContext->Destination()),
@@ -425,7 +429,7 @@ AudioBufferSourceNode::WrapObject(JSContext* aCx, JSObject* aScope)
425429
}
426430

427431
void
428-
AudioBufferSourceNode::Start(JSContext* aCx, double aWhen, double aOffset,
432+
AudioBufferSourceNode::Start(double aWhen, double aOffset,
429433
const Optional<double>& aDuration, ErrorResult& aRv)
430434
{
431435
if (mStartCalled) {
@@ -435,40 +439,77 @@ AudioBufferSourceNode::Start(JSContext* aCx, double aWhen, double aOffset,
435439
mStartCalled = true;
436440

437441
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
438-
if (!mBuffer || !ns) {
442+
if (!ns) {
439443
// Nothing to play, or we're already dead for some reason
440444
return;
441445
}
442446

443-
float rate = mBuffer->SampleRate();
444-
int32_t lengthSamples = mBuffer->Length();
445-
nsRefPtr<ThreadSharedFloatArrayBufferList> data =
446-
mBuffer->GetThreadSharedChannelsForRate(aCx);
447+
if (mBuffer) {
448+
double duration = aDuration.WasPassed() ?
449+
aDuration.Value() :
450+
std::numeric_limits<double>::min();
451+
SendOffsetAndDurationParametersToStream(ns, aOffset, duration);
452+
} else {
453+
// Remember our argument so that we can use them once we have a buffer
454+
mOffset = aOffset;
455+
mDuration = aDuration.WasPassed() ?
456+
aDuration.Value() :
457+
std::numeric_limits<double>::min();
458+
mOffsetAndDurationRemembered = true;
459+
}
460+
461+
// Don't set parameter unnecessarily
462+
if (aWhen > 0.0) {
463+
ns->SetStreamTimeParameter(START, Context()->DestinationStream(), aWhen);
464+
}
465+
466+
MOZ_ASSERT(!mPlayingRef, "We can only accept a successful start() call once");
467+
mPlayingRef.Take(this);
468+
}
469+
470+
void
471+
AudioBufferSourceNode::SendBufferParameterToStream(JSContext* aCx)
472+
{
473+
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
474+
MOZ_ASSERT(ns, "Why don't we have a stream here?");
475+
476+
if (mBuffer) {
477+
float rate = mBuffer->SampleRate();
478+
nsRefPtr<ThreadSharedFloatArrayBufferList> data =
479+
mBuffer->GetThreadSharedChannelsForRate(aCx);
480+
ns->SetBuffer(data.forget());
481+
ns->SetInt32Parameter(SAMPLE_RATE, rate);
482+
} else {
483+
ns->SetBuffer(nullptr);
484+
}
485+
486+
if (mOffsetAndDurationRemembered) {
487+
SendOffsetAndDurationParametersToStream(ns, mOffset, mDuration);
488+
}
489+
}
490+
491+
void
492+
AudioBufferSourceNode::SendOffsetAndDurationParametersToStream(AudioNodeStream* aStream,
493+
double aOffset,
494+
double aDuration)
495+
{
496+
float rate = mBuffer ? mBuffer->SampleRate() : Context()->SampleRate();
497+
int32_t lengthSamples = mBuffer ? mBuffer->Length() : 0;
447498
double length = double(lengthSamples) / rate;
448499
double offset = std::max(0.0, aOffset);
449-
double endOffset = aDuration.WasPassed() ?
450-
std::min(aOffset + aDuration.Value(), length) : length;
500+
double endOffset = aDuration == std::numeric_limits<double>::min() ?
501+
length : std::min(aOffset + aDuration, length);
451502

452503
if (offset >= endOffset) {
453504
return;
454505
}
455506

456-
ns->SetBuffer(data.forget());
457-
// Don't set parameter unnecessarily
458-
if (aWhen > 0.0) {
459-
ns->SetStreamTimeParameter(START, Context()->DestinationStream(), aWhen);
460-
}
461507
int32_t offsetTicks = NS_lround(offset*rate);
462508
// Don't set parameter unnecessarily
463509
if (offsetTicks > 0) {
464-
ns->SetInt32Parameter(OFFSET, offsetTicks);
510+
aStream->SetInt32Parameter(OFFSET, offsetTicks);
465511
}
466-
ns->SetInt32Parameter(DURATION,
467-
NS_lround(endOffset*rate) - offsetTicks);
468-
ns->SetInt32Parameter(SAMPLE_RATE, rate);
469-
470-
MOZ_ASSERT(!mPlayingRef, "We can only accept a successful start() call once");
471-
mPlayingRef.Take(this);
512+
aStream->SetInt32Parameter(DURATION, NS_lround(endOffset*rate) - offsetTicks);
472513
}
473514

474515
void
@@ -479,6 +520,12 @@ AudioBufferSourceNode::Stop(double aWhen, ErrorResult& aRv)
479520
return;
480521
}
481522

523+
if (!mBuffer) {
524+
// We don't have a buffer, so the stream is never marked as finished.
525+
// Therefore we need to drop our playing ref right now.
526+
mPlayingRef.Drop(this);
527+
}
528+
482529
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
483530
if (!ns) {
484531
// We've already stopped and had our stream shut down

content/media/webaudio/AudioBufferSourceNode.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,32 +55,33 @@ class AudioBufferSourceNode : public AudioNode,
5555

5656
virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope);
5757

58-
void Start(JSContext* aCx, double aWhen, double aOffset,
58+
void Start(double aWhen, double aOffset,
5959
const Optional<double>& aDuration, ErrorResult& aRv);
60-
void NoteOn(JSContext* aCx, double aWhen, ErrorResult& aRv)
60+
void NoteOn(double aWhen, ErrorResult& aRv)
6161
{
62-
Start(aCx, aWhen, 0.0, Optional<double>(), aRv);
62+
Start(aWhen, 0.0, Optional<double>(), aRv);
6363
}
64-
void NoteGrainOn(JSContext* aCx, double aWhen, double aOffset,
64+
void NoteGrainOn(double aWhen, double aOffset,
6565
double aDuration, ErrorResult& aRv)
6666
{
6767
Optional<double> duration;
6868
duration.Construct(aDuration);
69-
Start(aCx, aWhen, aOffset, duration, aRv);
69+
Start(aWhen, aOffset, duration, aRv);
7070
}
7171
void Stop(double aWhen, ErrorResult& aRv);
7272
void NoteOff(double aWhen, ErrorResult& aRv)
7373
{
7474
Stop(aWhen, aRv);
7575
}
7676

77-
AudioBuffer* GetBuffer() const
77+
AudioBuffer* GetBuffer(JSContext* aCx) const
7878
{
7979
return mBuffer;
8080
}
81-
void SetBuffer(AudioBuffer* aBuffer)
81+
void SetBuffer(JSContext* aCx, AudioBuffer* aBuffer)
8282
{
8383
mBuffer = aBuffer;
84+
SendBufferParameterToStream(aCx);
8485
}
8586
AudioParam* PlaybackRate() const
8687
{
@@ -136,17 +137,24 @@ class AudioBufferSourceNode : public AudioNode,
136137
};
137138

138139
void SendLoopParametersToStream();
140+
void SendBufferParameterToStream(JSContext* aCx);
141+
void SendOffsetAndDurationParametersToStream(AudioNodeStream* aStream,
142+
double aOffset,
143+
double aDuration);
139144
static void SendPlaybackRateToStream(AudioNode* aNode);
140145

141146
private:
142147
double mLoopStart;
143148
double mLoopEnd;
149+
double mOffset;
150+
double mDuration;
144151
nsRefPtr<AudioBuffer> mBuffer;
145152
nsRefPtr<AudioParam> mPlaybackRate;
146153
PannerNode* mPannerNode;
147154
SelfReference<AudioBufferSourceNode> mPlayingRef; // a reference to self while playing
148155
bool mLoop;
149156
bool mStartCalled;
157+
bool mOffsetAndDurationRemembered;
150158
};
151159

152160
}

content/media/webaudio/test/Makefile.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ MOCHITEST_FILES := \
2525
test_audioBufferSourceNode.html \
2626
test_audioBufferSourceNodeLoop.html \
2727
test_audioBufferSourceNodeLoopStartEnd.html \
28+
test_audioBufferSourceNodeNullBuffer.html \
2829
test_badConnect.html \
2930
test_biquadFilterNode.html \
3031
test_currentTime.html \

content/media/webaudio/test/test_audioBufferSourceNode.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121
}
2222

2323
var source = context.createBufferSource();
24-
source.buffer = buffer;
2524

2625
var sp = context.createScriptProcessor(2048);
2726
source.start(0);
27+
source.buffer = buffer;
2828
source.connect(sp);
2929
sp.connect(context.destination);
3030
sp.onaudioprocess = function(e) {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<!DOCTYPE HTML>
2+
<html>
3+
<head>
4+
<title>Test AudioBufferSourceNode</title>
5+
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
6+
<script type="text/javascript" src="webaudio.js"></script>
7+
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
8+
</head>
9+
<body>
10+
<pre id="test">
11+
<script class="testbody" type="text/javascript">
12+
13+
SimpleTest.waitForExplicitFinish();
14+
addLoadEvent(function() {
15+
SpecialPowers.setBoolPref("media.webaudio.enabled", true);
16+
17+
var context = new AudioContext();
18+
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate); // silence
19+
20+
var source = context.createBufferSource();
21+
22+
var sp = context.createScriptProcessor(2048);
23+
source.start(0);
24+
source.buffer = null;
25+
is(source.buffer, null, "Try playing back a null buffer");
26+
source.connect(sp);
27+
sp.connect(context.destination);
28+
sp.onaudioprocess = function(e) {
29+
compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
30+
compareBuffers(e.inputBuffer.getChannelData(1), expectedBuffer.getChannelData(0));
31+
32+
sp.onaudioprocess = null;
33+
34+
SpecialPowers.clearUserPref("media.webaudio.enabled");
35+
SimpleTest.finish();
36+
};
37+
});
38+
39+
</script>
40+
</pre>
41+
</body>
42+
</html>

dom/bindings/Bindings.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ DOMInterfaces = {
106106
},
107107

108108
'AudioBufferSourceNode': {
109-
'implicitJSContext': [ 'start', 'noteOn', 'noteGrainOn' ],
109+
'implicitJSContext': [ 'buffer' ],
110110
'resultNotAddRefed': [ 'playbackRate' ],
111111
},
112112

0 commit comments

Comments
 (0)