Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
AudioParam must support connections from audio-rate signals
https://bugs.webkit.org/show_bug.cgi?id=83524

Source/WebCore:

Reviewed by Eric Carlson.

In the Web Audio API, it's possible to connect one AudioNode to another AudioNode.
Similary we should allow an AudioNode to connect to an AudioParam, thus controlling
a parameter with an audio-rate signal.  This is important in many audio processing
applications.

Test: webaudio/audioparam-connect-audioratesignal.html

Simple method name change of AudioParam::hasTimelineValues() to AudioParam::hasSampleAccurateValues().
* Modules/webaudio/AudioGainNode.cpp:
(WebCore::AudioGainNode::process):

* Modules/webaudio/AudioNode.cpp:
(WebCore::AudioNode::connect): Add connect() method from AudioNode -> AudioParam.
(WebCore):
(WebCore::AudioNode::disconnect):
(WebCore::AudioNode::finishDeref):
Use AudioNodeOutput::disconnectAll() instead of AudioNodeOutput::disconnectAllInputs().
* Modules/webaudio/AudioNode.h: Add connect() method from AudioNode -> AudioParam.
(WebCore):
(AudioNode):
* Modules/webaudio/AudioNode.idl: Add connect() method from AudioNode -> AudioParam.

Implement support for an AudioNodeOutput to fanout to multiple AudioParams.
* Modules/webaudio/AudioNodeOutput.cpp:
(WebCore::AudioNodeOutput::AudioNodeOutput):
(WebCore::AudioNodeOutput::updateRenderingState): Update rendering state related to AudioParams.
(WebCore::AudioNodeOutput::pull): pull() must now take into account fanout to AudioParams for in-place processing.
(WebCore::AudioNodeOutput::fanOutCount):
(WebCore):
(WebCore::AudioNodeOutput::paramFanOutCount): New method keeping track of number of connections to AudioParams.
(WebCore::AudioNodeOutput::renderingParamFanOutCount): New method keeping track of number of connections to AudioParams for rendering.
(WebCore::AudioNodeOutput::addParam): Add a connection to an AudioParam.
(WebCore::AudioNodeOutput::removeParam): Remove a connection to an AudioParam.
(WebCore::AudioNodeOutput::disconnectAllParams): Remove all connections to AudioParams.
(WebCore::AudioNodeOutput::disconnectAll): New method to disconnect all AudioNodeInputs and AudioParams.
* Modules/webaudio/AudioNodeOutput.h:
(AudioNodeOutput):

Allow an AudioParam to accept a connection from an AudioNodeOutput, thus being controlled
by an audio-rate signal.
* Modules/webaudio/AudioParam.cpp:
(WebCore::AudioParam::calculateSampleAccurateValues): Calculates sample-accurate values from timeline or an AudioNode.
(WebCore):
(WebCore::AudioParam::calculateAudioRateSignalValues): Calculates sample-accurate values from an AudioNode.
(WebCore::AudioParam::calculateTimelineValues): Calculates sample-accurate values scheduled on the timeline.
(WebCore::AudioParam::connect): Connect from an AudioNodeOutput for control from an audio-rate signal.
(WebCore::AudioParam::disconnect): Disconnect from an AudioNodeOutput.
* Modules/webaudio/AudioParam.h:
(WebCore):
(WebCore::AudioParam::AudioParam):
(WebCore::AudioParam::hasSampleAccurateValues): Change name from hasTimelineValues() and return true
either if we have timeline values or if we've been connected from an AudioNode.
(AudioParam):

Simple method name change of AudioParam::hasTimelineValues() to AudioParam::hasSampleAccurateValues().
* Modules/webaudio/Oscillator.cpp:
(WebCore::Oscillator::calculateSampleAccuratePhaseIncrements):
(WebCore::Oscillator::process):

LayoutTests:

Reviewed by Eric Carlson.

* webaudio/audioparam-connect-audioratesignal-expected.txt: Added.
* webaudio/audioparam-connect-audioratesignal.html: Added.
* webaudio/resources/audio-testing.js:
(createLinearRampBuffer):
(createConstantBuffer):

Canonical link: https://commits.webkit.org/101068@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@113769 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
Chris Rogers committed Apr 10, 2012
1 parent 2a760a0 commit cb7e833
Show file tree
Hide file tree
Showing 14 changed files with 432 additions and 32 deletions.
13 changes: 13 additions & 0 deletions LayoutTests/ChangeLog
@@ -1,3 +1,16 @@
2012-04-10 Chris Rogers <crogers@google.com>

AudioParam must support connections from audio-rate signals
https://bugs.webkit.org/show_bug.cgi?id=83524

Reviewed by Eric Carlson.

* webaudio/audioparam-connect-audioratesignal-expected.txt: Added.
* webaudio/audioparam-connect-audioratesignal.html: Added.
* webaudio/resources/audio-testing.js:
(createLinearRampBuffer):
(createConstantBuffer):

2012-04-10 Abhishek Arya <inferno@chromium.org>

Crash due to captions list not updated after section recalc.
Expand Down
@@ -0,0 +1,6 @@
PASS Rendered signal is of correct length.
PASS Rendered signal exactly matches the audio-rate gain changing signal.
PASS successfullyParsed is true

TEST COMPLETE

112 changes: 112 additions & 0 deletions LayoutTests/webaudio/audioparam-connect-audioratesignal.html
@@ -0,0 +1,112 @@
<!DOCTYPE html>

<!--
Tests that an audio-rate signal (AudioNode output) can be connected to an AudioParam.
Specifically, this tests that an audio-rate signal coming from an AudioBufferSourceNode
playing an AudioBuffer containing a specific curve can be connected to an AudioGainNode's
.gain attribute (an AudioParam). Another AudioBufferSourceNode will be the audio source
having its gain changed. We load this one with an AudioBuffer containing a constant value of 1.
Thus it's easy to check that the resultant signal should be equal to the gain-scaling curve.
-->

<html>
<head>
<link rel="stylesheet" href="../fast/js/resources/js-test-style.css"/>
<script src="resources/audio-testing.js"></script>
<script src="../fast/js/resources/js-test-pre.js"></script>

</head>
<body>

<script>

var sampleRate = 44100.0;
var lengthInSeconds = 1;

var context = 0;
var constantOneBuffer = 0;
var linearRampBuffer = 0;

function checkResult(event) {
var renderedBuffer = event.renderedBuffer;
var renderedData = renderedBuffer.getChannelData(0);
var expectedData = linearRampBuffer.getChannelData(0);
var n = renderedBuffer.length;

if (n == linearRampBuffer.length) {
testPassed("Rendered signal is of correct length.");
} else {
testFailed("Rendered signal is not of correct length.");
}

// Check that the rendered result exactly matches the buffer used to control gain.
// This is because we're changing the gain of a signal having constant value 1.
var success = true;
for (var i = 0; i < n; ++i) {
if (renderedData[i] != expectedData[i]) {
success = false;
break;
}
}

if (success) {
testPassed("Rendered signal exactly matches the audio-rate gain changing signal.");
} else {
testFailed("Rendered signal differs from the audio-rate gain changing signal.");
}

finishJSTest();
}

function runTest() {
if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}

window.jsTestIsAsync = true;

var sampleFrameLength = sampleRate * lengthInSeconds;

// Create offline audio context.
context = new webkitAudioContext(1, sampleFrameLength, sampleRate);

// Create buffer used by the source which will have its gain controlled.
constantOneBuffer = createConstantBuffer(context, sampleFrameLength, 1);

// Create buffer used to control gain.
linearRampBuffer = createLinearRampBuffer(context, sampleFrameLength);

// Create the two sources.

var constantSource = context.createBufferSource();
constantSource.buffer = constantOneBuffer;

var gainChangingSource = context.createBufferSource();
gainChangingSource.buffer = linearRampBuffer;

// Create a gain node controlling the gain of constantSource and make the connections.
var gainNode = context.createGainNode();
constantSource.connect(gainNode);
gainNode.connect(context.destination);

// Connect an audio-rate signal to control the .gain AudioParam.
// This is the heart of what is being tested.
gainChangingSource.connect(gainNode.gain);

// Start both sources at time 0.
constantSource.noteOn(0);
gainChangingSource.noteOn(0);

context.oncomplete = checkResult;
context.startRendering();
}

runTest();
successfullyParsed = true;

</script>
<script src="../fast/js/resources/js-test-post.js"></script>

</body>
</html>
24 changes: 24 additions & 0 deletions LayoutTests/webaudio/resources/audio-testing.js
Expand Up @@ -115,6 +115,30 @@ function createImpulseBuffer(context, sampleFrameLength) {
return audioBuffer;
}

// Create a buffer of the given length with a linear ramp having values 0 <= x < 1.
function createLinearRampBuffer(context, sampleFrameLength) {
var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate);
var n = audioBuffer.length;
var dataL = audioBuffer.getChannelData(0);

for (var i = 0; i < n; ++i)
dataL[i] = i / n;

return audioBuffer;
}

// Create a buffer of the given length having a constant value.
function createConstantBuffer(context, sampleFrameLength, constantValue) {
var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate);
var n = audioBuffer.length;
var dataL = audioBuffer.getChannelData(0);

for (var i = 0; i < n; ++i)
dataL[i] = constantValue;

return audioBuffer;
}

// Convert time (in seconds) to sample frames.
function timeToSampleFrame(time, sampleRate) {
return Math.floor(0.5 + time * sampleRate);
Expand Down
66 changes: 66 additions & 0 deletions Source/WebCore/ChangeLog
@@ -1,3 +1,69 @@
2012-04-10 Chris Rogers <crogers@google.com>

AudioParam must support connections from audio-rate signals
https://bugs.webkit.org/show_bug.cgi?id=83524

Reviewed by Eric Carlson.

In the Web Audio API, it's possible to connect one AudioNode to another AudioNode.
Similary we should allow an AudioNode to connect to an AudioParam, thus controlling
a parameter with an audio-rate signal. This is important in many audio processing
applications.

Test: webaudio/audioparam-connect-audioratesignal.html

Simple method name change of AudioParam::hasTimelineValues() to AudioParam::hasSampleAccurateValues().
* Modules/webaudio/AudioGainNode.cpp:
(WebCore::AudioGainNode::process):

* Modules/webaudio/AudioNode.cpp:
(WebCore::AudioNode::connect): Add connect() method from AudioNode -> AudioParam.
(WebCore):
(WebCore::AudioNode::disconnect):
(WebCore::AudioNode::finishDeref):
Use AudioNodeOutput::disconnectAll() instead of AudioNodeOutput::disconnectAllInputs().
* Modules/webaudio/AudioNode.h: Add connect() method from AudioNode -> AudioParam.
(WebCore):
(AudioNode):
* Modules/webaudio/AudioNode.idl: Add connect() method from AudioNode -> AudioParam.

Implement support for an AudioNodeOutput to fanout to multiple AudioParams.
* Modules/webaudio/AudioNodeOutput.cpp:
(WebCore::AudioNodeOutput::AudioNodeOutput):
(WebCore::AudioNodeOutput::updateRenderingState): Update rendering state related to AudioParams.
(WebCore::AudioNodeOutput::pull): pull() must now take into account fanout to AudioParams for in-place processing.
(WebCore::AudioNodeOutput::fanOutCount):
(WebCore):
(WebCore::AudioNodeOutput::paramFanOutCount): New method keeping track of number of connections to AudioParams.
(WebCore::AudioNodeOutput::renderingParamFanOutCount): New method keeping track of number of connections to AudioParams for rendering.
(WebCore::AudioNodeOutput::addParam): Add a connection to an AudioParam.
(WebCore::AudioNodeOutput::removeParam): Remove a connection to an AudioParam.
(WebCore::AudioNodeOutput::disconnectAllParams): Remove all connections to AudioParams.
(WebCore::AudioNodeOutput::disconnectAll): New method to disconnect all AudioNodeInputs and AudioParams.
* Modules/webaudio/AudioNodeOutput.h:
(AudioNodeOutput):

Allow an AudioParam to accept a connection from an AudioNodeOutput, thus being controlled
by an audio-rate signal.
* Modules/webaudio/AudioParam.cpp:
(WebCore::AudioParam::calculateSampleAccurateValues): Calculates sample-accurate values from timeline or an AudioNode.
(WebCore):
(WebCore::AudioParam::calculateAudioRateSignalValues): Calculates sample-accurate values from an AudioNode.
(WebCore::AudioParam::calculateTimelineValues): Calculates sample-accurate values scheduled on the timeline.
(WebCore::AudioParam::connect): Connect from an AudioNodeOutput for control from an audio-rate signal.
(WebCore::AudioParam::disconnect): Disconnect from an AudioNodeOutput.
* Modules/webaudio/AudioParam.h:
(WebCore):
(WebCore::AudioParam::AudioParam):
(WebCore::AudioParam::hasSampleAccurateValues): Change name from hasTimelineValues() and return true
either if we have timeline values or if we've been connected from an AudioNode.
(AudioParam):

Simple method name change of AudioParam::hasTimelineValues() to AudioParam::hasSampleAccurateValues().
* Modules/webaudio/Oscillator.cpp:
(WebCore::Oscillator::calculateSampleAccuratePhaseIncrements):
(WebCore::Oscillator::process):

2012-04-10 Patrick Gansterer <paroga@webkit.org>

[CMake] Enable USE_FOLDERS property
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/Modules/webaudio/AudioGainNode.cpp
Expand Up @@ -64,7 +64,7 @@ void AudioGainNode::process(size_t framesToProcess)
else {
AudioBus* inputBus = input(0)->bus();

if (gain()->hasTimelineValues()) {
if (gain()->hasSampleAccurateValues()) {
// Apply sample-accurate gain scaling for precise envelopes, grain windows, etc.
ASSERT(framesToProcess <= m_sampleAccurateGainValues.size());
if (framesToProcess <= m_sampleAccurateGainValues.size()) {
Expand Down
29 changes: 27 additions & 2 deletions Source/WebCore/Modules/webaudio/AudioNode.cpp
Expand Up @@ -31,6 +31,7 @@
#include "AudioContext.h"
#include "AudioNodeInput.h"
#include "AudioNodeOutput.h"
#include "AudioParam.h"
#include "ExceptionCode.h"
#include <wtf/Atomics.h>
#include <wtf/MainThread.h>
Expand Down Expand Up @@ -149,6 +150,30 @@ void AudioNode::connect(AudioNode* destination, unsigned outputIndex, unsigned i
context()->incrementConnectionCount();
}

void AudioNode::connect(AudioParam* param, unsigned outputIndex, ExceptionCode& ec)
{
ASSERT(isMainThread());
AudioContext::AutoLocker locker(context());

if (!param) {
ec = SYNTAX_ERR;
return;
}

if (outputIndex >= numberOfOutputs()) {
ec = INDEX_SIZE_ERR;
return;
}

if (context() != param->context()) {
ec = SYNTAX_ERR;
return;
}

AudioNodeOutput* output = this->output(outputIndex);
param->connect(output);
}

void AudioNode::disconnect(unsigned outputIndex, ExceptionCode& ec)
{
ASSERT(isMainThread());
Expand All @@ -161,7 +186,7 @@ void AudioNode::disconnect(unsigned outputIndex, ExceptionCode& ec)
}

AudioNodeOutput* output = this->output(outputIndex);
output->disconnectAllInputs();
output->disconnectAll();
}

void AudioNode::processIfNecessary(size_t framesToProcess)
Expand Down Expand Up @@ -337,7 +362,7 @@ void AudioNode::finishDeref(RefType refType)
if (!m_isMarkedForDeletion) {
// All references are gone - we need to go away.
for (unsigned i = 0; i < m_outputs.size(); ++i)
output(i)->disconnectAllInputs(); // this will deref() nodes we're connected to...
output(i)->disconnectAll(); // This will deref() nodes we're connected to.

// Mark for deletion at end of each render quantum or when context shuts down.
context()->markForDeletion(this);
Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/Modules/webaudio/AudioNode.h
Expand Up @@ -37,6 +37,7 @@ namespace WebCore {
class AudioContext;
class AudioNodeInput;
class AudioNodeOutput;
class AudioParam;

typedef int ExceptionCode;

Expand Down Expand Up @@ -116,6 +117,7 @@ class AudioNode {

// Called from main thread by corresponding JavaScript methods.
void connect(AudioNode*, unsigned outputIndex, unsigned inputIndex, ExceptionCode&);
void connect(AudioParam*, unsigned outputIndex, ExceptionCode&);
void disconnect(unsigned outputIndex, ExceptionCode&);

virtual float sampleRate() const { return m_sampleRate; }
Expand Down
3 changes: 3 additions & 0 deletions Source/WebCore/Modules/webaudio/AudioNode.idl
Expand Up @@ -33,6 +33,9 @@ module audio {
void connect(in AudioNode destination, in [Optional=DefaultIsUndefined] unsigned long output, in [Optional=DefaultIsUndefined] unsigned long input)
raises(DOMException);

void connect(in AudioParam destination, in [Optional=DefaultIsUndefined] unsigned long output)
raises(DOMException);

void disconnect(in [Optional=DefaultIsUndefined] unsigned long output)
raises(DOMException);
};
Expand Down

0 comments on commit cb7e833

Please sign in to comment.