Diff
Modified: trunk/Source/WebCore/ChangeLog (113727 => 113728)
--- trunk/Source/WebCore/ChangeLog 2012-04-10 16:40:46 UTC (rev 113727)
+++ trunk/Source/WebCore/ChangeLog 2012-04-10 17:04:41 UTC (rev 113728)
@@ -1,3 +1,67 @@
+2011-12-14 Jer Noble <jer.no...@apple.com>
+
+ WebAudio: propagate a silence hint through the AudioNode graph.
+ https://bugs.webkit.org/show_bug.cgi?id=74553
+
+ Reviewed by Chris Rogers.
+
+ No new tests; optimization of existing code path, so covered by existing tests.
+
+ Introduce the concept of a "silent" channel:
+ * platform/audio/AudioChannel.h:
+ (WebCore::AudioChannel::AudioChannel):
+ (WebCore::AudioChannel::set): Clear silent bit.
+ (WebCore::AudioChannel::zero): Set silent bit.
+ (WebCore::AudioChannel::clearSilentFlag): Clear silent bit.
+ (WebCore::AudioChannel::isSilent): Accessor.
+
+ Optimize a few channel operations when the source or destination channels are silent.
+ * platform/audio/AudioChannel.cpp:
+ (WebCore::AudioChannel::scale): No-op on a silent channel.
+ (WebCore::AudioChannel::copyFrom): zero() when source is silent.
+ (WebCore::AudioChannel::copyFromRange): possibly zero() when source is silent.
+ (WebCore::AudioChannel::sumFrom): No-op when source is silent.
+ (WebCore::AudioChannel::maxAbsValue): 0 on a silent channel.
+
+ Optimize a few bus operations when the source or destination channels are silent.
+ * platform/audio/AudioBus.cpp:
+ (WebCore::AudioBus::processWithGainFromMonoStereo): No-op if source is silent and either
+ the destination bus is silent, or the output is not summed to the destination.
+ (WebCore::AudioBus::copyWithSampleAccurateGainValuesFrom): zero() if the source is silent
+ and the lengths of the gain values, source, and destination match.
+ (WebCore::AudioBus::createBySampleRateConverting): Return an empty bus if the source is silent.
+ (WebCore::AudioBus::createByMixingToMono): Ditto; clear the destination's silent bit otherwise.
+ (WebCore::AudioBus::isSilent): Return whether all channels are silent.
+ (WebCore::AudioBus::clearSilentFlag): Clear silent bit of constituent channels.
+ * platform/audio/AudioBus.h:
+
+ Make sure that classes which generate audio clear their busses' silent bit.
+ * webaudio/AudioBufferSourceNode.cpp:
+ (WebCore::AudioBufferSourceNode::process): Ditto.
+ (WebCore::AudioBufferSourceNode::renderFromBuffer): Ditto.
+ (WebCore::AudioBufferSourceNode::propagatesSilence): Propagate silence only when the source node
+ has nothing left to render.
+ * webaudio/AudioBufferSourceNode.h:
+ (WebCore::AudioBufferSourceNode::playbackState): Made const correct.
+ (WebCore::AudioBufferSourceNode::isPlaying): Added simple accessor.
+ (WebCore::AudioBufferSourceNode::hasFinished): Ditto.
+ * webaudio/AudioDestinationNode.h:
+ (WebCore::AudioDestinationNode::currentSampleFrame): Made const correct.
+ (WebCore::AudioDestinationNode::currentTime): Ditto.
+
+ Audio nodes should not process audio data when the input is silent and the nodes will propagate silences.
+ * webaudio/AudioNode.cpp:
+ (WebCore::AudioNode::processIfNecessary):
+ (WebCore::AudioNode::inputsAreSilent): Convenience function which walk over the node's inputs.
+ (WebCore::AudioNode::silenceOutputs): Ditto.
+ (WebCore::AudioNode::unsilenceOutputs): Ditto.
+ * webaudio/AudioNode.h:
+ (WebCore::AudioNode::propagatesSilence):
+
+ These Nodes can generate audio when given silent input, so return false from propagatesSilence.
+ * Modules/webaudio/Oscillator.h:
+ (WebCore::Oscillator::propagatesSilence): Added.
+
2012-04-10 Yi Shen <yi.4.s...@nokia.com>
Pressing enter on blank line after bullet deletes entire bulleted line.
Modified: trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp (113727 => 113728)
--- trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp 2012-04-10 16:40:46 UTC (rev 113727)
+++ trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp 2012-04-10 17:04:41 UTC (rev 113728)
@@ -158,6 +158,10 @@
finish();
}
+
+ outputBus->clearSilentFlag();
+
+ m_processLock.unlock();
} else {
// Too bad - the tryLock() failed. We must be in the middle of changing buffers and were already outputting silence anyway.
outputBus->zero();
@@ -331,7 +335,10 @@
break;
}
}
- }
+ }
+
+ bus->clearSilentFlag();
+
m_virtualReadIndex = virtualReadIndex;
}
@@ -499,6 +506,12 @@
m_isLooping = looping;
}
+
+bool AudioBufferSourceNode::propagatesSilence() const
+{
+ return !isPlayingOrScheduled() || hasFinished() || !m_buffer;
+}
+
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO)
Modified: trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.h (113727 => 113728)
--- trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.h 2012-04-10 16:40:46 UTC (rev 113727)
+++ trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.h 2012-04-10 17:04:41 UTC (rev 113728)
@@ -84,7 +84,9 @@
void noteGrainOn(double when, double grainOffset, double grainDuration);
void noteOff(double when);
- unsigned short playbackState() { return static_cast<unsigned short>(m_playbackState); }
+ unsigned short playbackState() const { return static_cast<unsigned short>(m_playbackState); }
+ bool isPlayingOrScheduled() const { return m_playbackState == PLAYING_STATE || m_playbackState == SCHEDULED_STATE; }
+ bool hasFinished() const { return m_playbackState == FINISHED_STATE; }
// Note: the attribute was originally exposed as .looping, but to be more consistent in naming with <audio>
// and with how it's described in the specification, the proper attribute name is .loop
@@ -102,6 +104,9 @@
// If a panner node is set, then we can incorporate doppler shift into the playback pitch rate.
void setPannerNode(PassRefPtr<AudioPannerNode> pannerNode) { m_pannerNode = pannerNode; }
+ // If we are no longer playing, propogate silence ahead to downstream nodes.
+ virtual bool propagatesSilence() const;
+
private:
AudioBufferSourceNode(AudioContext*, float sampleRate);
Modified: trunk/Source/WebCore/Modules/webaudio/AudioContext.h (113727 => 113728)
--- trunk/Source/WebCore/Modules/webaudio/AudioContext.h 2012-04-10 16:40:46 UTC (rev 113727)
+++ trunk/Source/WebCore/Modules/webaudio/AudioContext.h 2012-04-10 17:04:41 UTC (rev 113728)
@@ -93,10 +93,10 @@
bool hasDocument();
AudioDestinationNode* destination() { return m_destinationNode.get(); }
- size_t currentSampleFrame() { return m_destinationNode->currentSampleFrame(); }
- double currentTime() { return m_destinationNode->currentTime(); }
- float sampleRate() { return m_destinationNode->sampleRate(); }
- unsigned long activeSourceCount() { return static_cast<unsigned long>(m_activeSourceCount); }
+ size_t currentSampleFrame() const { return m_destinationNode->currentSampleFrame(); }
+ double currentTime() const { return m_destinationNode->currentTime(); }
+ float sampleRate() const { return m_destinationNode->sampleRate(); }
+ unsigned long activeSourceCount() const { return static_cast<unsigned long>(m_activeSourceCount); }
void incrementActiveSourceCount();
void decrementActiveSourceCount();
Modified: trunk/Source/WebCore/Modules/webaudio/AudioDestinationNode.h (113727 => 113728)
--- trunk/Source/WebCore/Modules/webaudio/AudioDestinationNode.h 2012-04-10 16:40:46 UTC (rev 113727)
+++ trunk/Source/WebCore/Modules/webaudio/AudioDestinationNode.h 2012-04-10 17:04:41 UTC (rev 113728)
@@ -46,8 +46,8 @@
// The audio hardware calls here periodically to gets its input stream.
virtual void provideInput(AudioBus*, size_t numberOfFrames);
- size_t currentSampleFrame() { return m_currentSampleFrame; }
- double currentTime() { return currentSampleFrame() / static_cast<double>(sampleRate()); }
+ size_t currentSampleFrame() const { return m_currentSampleFrame; }
+ double currentTime() const { return currentSampleFrame() / static_cast<double>(sampleRate()); }
virtual unsigned numberOfChannels() const { return 2; } // FIXME: update when multi-channel (more than stereo) is supported
Modified: trunk/Source/WebCore/Modules/webaudio/AudioNode.cpp (113727 => 113728)
--- trunk/Source/WebCore/Modules/webaudio/AudioNode.cpp 2012-04-10 16:40:46 UTC (rev 113727)
+++ trunk/Source/WebCore/Modules/webaudio/AudioNode.cpp 2012-04-10 17:04:41 UTC (rev 113728)
@@ -42,7 +42,8 @@
, m_nodeType(NodeTypeUnknown)
, m_context(context)
, m_sampleRate(sampleRate)
- , m_lastProcessingTime(-1.0)
+ , m_lastProcessingTime(-1)
+ , m_lastNonSilentTime(-1)
, m_normalRefCount(1) // start out with normal refCount == 1 (like WTF::RefCounted class)
, m_connectionRefCount(0)
, m_disabledRefCount(0)
@@ -177,8 +178,19 @@
double currentTime = context()->currentTime();
if (m_lastProcessingTime != currentTime) {
m_lastProcessingTime = currentTime; // important to first update this time because of feedback loops in the rendering graph
+
pullInputs(framesToProcess);
- process(framesToProcess);
+
+ bool silentInputs = inputsAreSilent();
+ if (!silentInputs)
+ m_lastNonSilentTime = (context()->currentSampleFrame() + framesToProcess) / static_cast<double>(m_sampleRate);
+
+ if (silentInputs && propagatesSilence())
+ silenceOutputs();
+ else {
+ process(framesToProcess);
+ unsilenceOutputs();
+ }
}
}
@@ -193,6 +205,11 @@
input->updateInternalBus();
}
+bool AudioNode::propagatesSilence() const
+{
+ return m_lastNonSilentTime + latencyTime() + tailTime() < context()->currentTime();
+}
+
void AudioNode::pullInputs(size_t framesToProcess)
{
ASSERT(context()->isAudioThread());
@@ -202,6 +219,27 @@
input(i)->pull(0, framesToProcess);
}
+bool AudioNode::inputsAreSilent()
+{
+ for (unsigned i = 0; i < m_inputs.size(); ++i) {
+ if (!input(i)->bus()->isSilent())
+ return false;
+ }
+ return true;
+}
+
+void AudioNode::silenceOutputs()
+{
+ for (unsigned i = 0; i < m_outputs.size(); ++i)
+ output(i)->bus()->zero();
+}
+
+void AudioNode::unsilenceOutputs()
+{
+ for (unsigned i = 0; i < m_outputs.size(); ++i)
+ output(i)->bus()->clearSilentFlag();
+}
+
void AudioNode::ref(RefType refType)
{
switch (refType) {
Modified: trunk/Source/WebCore/Modules/webaudio/AudioNode.h (113727 => 113728)
--- trunk/Source/WebCore/Modules/webaudio/AudioNode.h 2012-04-10 16:40:46 UTC (rev 113727)
+++ trunk/Source/WebCore/Modules/webaudio/AudioNode.h 2012-04-10 17:04:41 UTC (rev 113728)
@@ -54,6 +54,7 @@
virtual ~AudioNode();
AudioContext* context() { return m_context.get(); }
+ const AudioContext* context() const { return m_context.get(); }
enum NodeType {
NodeTypeUnknown,
@@ -143,6 +144,13 @@
// example, a "delay" effect is expected to delay the signal, and thus would not be considered latency.
virtual double latencyTime() const = 0;
+ // propagatesSilence() should return true if the node will generate silent output when given silent input. By default, AudioNode
+ // will take tailTime() and latencyTime() into account when determining whether the node will propagate silence.
+ virtual bool propagatesSilence() const;
+ bool inputsAreSilent();
+ void silenceOutputs();
+ void unsilenceOutputs();
+
protected:
// Inputs and outputs must be created before the AudioNode is initialized.
void addInput(PassOwnPtr<AudioNodeInput>);
@@ -162,6 +170,7 @@
Vector<OwnPtr<AudioNodeOutput> > m_outputs;
double m_lastProcessingTime;
+ double m_lastNonSilentTime;
// Ref-counting
volatile int m_normalRefCount;
Modified: trunk/Source/WebCore/Modules/webaudio/Oscillator.h (113727 => 113728)
--- trunk/Source/WebCore/Modules/webaudio/Oscillator.h 2012-04-10 16:40:46 UTC (rev 113727)
+++ trunk/Source/WebCore/Modules/webaudio/Oscillator.h 2012-04-10 17:04:41 UTC (rev 113728)
@@ -74,6 +74,9 @@
// Returns true if there are sample-accurate timeline parameter changes.
bool calculateSampleAccuratePhaseIncrements(size_t framesToProcess);
+ // As a pure generator, Oscillator will never propagate silence.
+ virtual bool propagatesSilence() const OVERRIDE { return false; }
+
// One of the waveform types defined in the enum.
unsigned short m_type;
Modified: trunk/Source/WebCore/platform/audio/AudioBus.cpp (113727 => 113728)
--- trunk/Source/WebCore/platform/audio/AudioBus.cpp 2012-04-10 16:40:46 UTC (rev 113727)
+++ trunk/Source/WebCore/platform/audio/AudioBus.cpp 2012-04-10 17:04:41 UTC (rev 113728)
@@ -424,6 +424,12 @@
const float* sourceL = sourceBusSafe.channelByType(ChannelLeft)->data();
const float* sourceR = numberOfSourceChannels > 1 ? sourceBusSafe.channelByType(ChannelRight)->data() : 0;
+ if (sourceBusSafe.isSilent()) {
+ if (!sumToBus)
+ zero();
+ return;
+ }
+
float* destinationL = channelByType(ChannelLeft)->mutableData();
float* destinationR = numberOfDestinationChannels > 1 ? channelByType(ChannelRight)->mutableData() : 0;
@@ -510,6 +516,11 @@
return;
}
+ if (sourceBus.length() == numberOfGainValues && sourceBus.length() == length() && sourceBus.isSilent()) {
+ zero();
+ return;
+ }
+
// We handle both the 1 -> N and N -> N case here.
const float* source = sourceBus.channel(0)->data();
for (unsigned channelIndex = 0; channelIndex < numberOfChannels(); ++channelIndex) {
@@ -539,6 +550,7 @@
double sourceSampleRate = sourceBus->sampleRate();
double destinationSampleRate = newSampleRate;
+ double sampleRateRatio = sourceSampleRate / destinationSampleRate;
unsigned numberOfSourceChannels = sourceBus->numberOfChannels();
if (numberOfSourceChannels == 1)
@@ -552,7 +564,13 @@
// Return exact copy.
return AudioBus::createBufferFromRange(sourceBus, 0, sourceBus->length());
}
-
+
+ if (sourceBus->isSilent()) {
+ OwnPtr<AudioBus> silentBus = adoptPtr(new AudioBus(numberOfSourceChannels, sourceBus->length() / sampleRateRatio));
+ silentBus->setSampleRate(newSampleRate);
+ return silentBus.release();
+ }
+
// First, mix to mono (if necessary) then sample-rate convert.
const AudioBus* resamplerSourceBus;
OwnPtr<AudioBus> mixedMonoBus;
@@ -565,7 +583,6 @@
}
// Calculate destination length based on the sample-rates.
- double sampleRateRatio = sourceSampleRate / destinationSampleRate;
int sourceLength = resamplerSourceBus->length();
int destinationLength = sourceLength / sampleRateRatio;
@@ -582,12 +599,16 @@
resampler.process(source, destination, sourceLength);
}
+ destinationBus->clearSilentFlag();
destinationBus->setSampleRate(newSampleRate);
return destinationBus.release();
}
PassOwnPtr<AudioBus> AudioBus::createByMixingToMono(const AudioBus* sourceBus)
{
+ if (sourceBus->isSilent())
+ return adoptPtr(new AudioBus(1, sourceBus->length()));
+
switch (sourceBus->numberOfChannels()) {
case 1:
// Simply create an exact copy.
@@ -605,6 +626,7 @@
for (unsigned i = 0; i < n; ++i)
destination[i] = (sourceL[i] + sourceR[i]) / 2;
+ destinationBus->clearSilentFlag();
destinationBus->setSampleRate(sourceBus->sampleRate());
return destinationBus.release();
}
@@ -614,6 +636,21 @@
return nullptr;
}
+bool AudioBus::isSilent() const
+{
+ for (size_t i = 0; i < m_channels.size(); ++i) {
+ if (!m_channels[i]->isSilent())
+ return false;
+ }
+ return true;
+}
+
+void AudioBus::clearSilentFlag()
+{
+ for (size_t i = 0; i < m_channels.size(); ++i)
+ m_channels[i]->clearSilentFlag();
+}
+
} // WebCore
#endif // ENABLE(WEB_AUDIO)
Modified: trunk/Source/WebCore/platform/audio/AudioBus.h (113727 => 113728)
--- trunk/Source/WebCore/platform/audio/AudioBus.h 2012-04-10 16:40:46 UTC (rev 113727)
+++ trunk/Source/WebCore/platform/audio/AudioBus.h 2012-04-10 17:04:41 UTC (rev 113728)
@@ -83,6 +83,12 @@
// Zeroes all channels.
void zero();
+ // Clears the silent flag on all channels.
+ void clearSilentFlag();
+
+ // Returns true if the silent bit is set on all channels.
+ bool isSilent() const;
+
// Returns true if the channel count and frame-size match.
bool topologyMatches(const AudioBus &sourceBus) const;
Modified: trunk/Source/WebCore/platform/audio/AudioChannel.cpp (113727 => 113728)
--- trunk/Source/WebCore/platform/audio/AudioChannel.cpp 2012-04-10 16:40:46 UTC (rev 113727)
+++ trunk/Source/WebCore/platform/audio/AudioChannel.cpp 2012-04-10 17:04:41 UTC (rev 113728)
@@ -43,6 +43,9 @@
void AudioChannel::scale(float scale)
{
+ if (isSilent())
+ return;
+
vsmul(data(), 1, &scale, mutableData(), 1, length());
}
@@ -53,11 +56,18 @@
if (!isSafe)
return;
+ if (sourceChannel->isSilent()) {
+ zero();
+ return;
+ }
memcpy(mutableData(), sourceChannel->data(), sizeof(float) * length());
}
void AudioChannel::copyFromRange(const AudioChannel* sourceChannel, unsigned startFrame, unsigned endFrame)
{
+ if (sourceChannel->isSilent() && isSilent())
+ return;
+
// Check that range is safe for reading from sourceChannel.
bool isRangeSafe = sourceChannel && startFrame < endFrame && endFrame <= sourceChannel->length();
ASSERT(isRangeSafe);
@@ -73,23 +83,39 @@
const float* source = sourceChannel->data();
float* destination = mutableData();
- memcpy(destination, source + startFrame, sizeof(float) * rangeLength);
+
+ if (sourceChannel->isSilent()) {
+ if (rangeLength == length())
+ zero();
+ else
+ memset(destination, 0, sizeof(float) * rangeLength);
+ } else
+ memcpy(destination, source + startFrame, sizeof(float) * rangeLength);
}
void AudioChannel::sumFrom(const AudioChannel* sourceChannel)
{
+ if (sourceChannel->isSilent())
+ return;
+
bool isSafe = sourceChannel && sourceChannel->length() >= length();
ASSERT(isSafe);
if (!isSafe)
return;
- vadd(data(), 1, sourceChannel->data(), 1, mutableData(), 1, length());
+ if (isSilent())
+ copyFrom(sourceChannel);
+ else
+ vadd(data(), 1, sourceChannel->data(), 1, mutableData(), 1, length());
}
float AudioChannel::maxAbsValue() const
{
- float max = 0.0f;
+ if (isSilent())
+ return 0;
+ float max = 0;
+
vmaxmgv(data(), 1, &max, length());
return max;
Modified: trunk/Source/WebCore/platform/audio/AudioChannel.h (113727 => 113728)
--- trunk/Source/WebCore/platform/audio/AudioChannel.h 2012-04-10 16:40:46 UTC (rev 113727)
+++ trunk/Source/WebCore/platform/audio/AudioChannel.h 2012-04-10 17:04:41 UTC (rev 113728)
@@ -43,12 +43,17 @@
// Reference an external buffer.
AudioChannel(float* storage, size_t length)
- : m_length(length), m_rawPointer(storage) { }
+ : m_length(length)
+ , m_rawPointer(storage)
+ , m_silent(false)
+ {
+ }
// Manage storage for us.
explicit AudioChannel(size_t length)
: m_length(length)
, m_rawPointer(0)
+ , m_silent(true)
{
m_memBuffer = adoptPtr(new AudioFloatArray(length));
}
@@ -57,6 +62,7 @@
AudioChannel()
: m_length(0)
, m_rawPointer(0)
+ , m_silent(true)
{
}
@@ -67,24 +73,40 @@
m_memBuffer.clear(); // cleanup managed storage
m_rawPointer = storage;
m_length = length;
+ m_silent = false;
}
// How many sample-frames do we contain?
size_t length() const { return m_length; }
- // Direct access to PCM sample data
- float* mutableData() { return m_rawPointer ? m_rawPointer : m_memBuffer->data(); }
+ // Direct access to PCM sample data. Non-const accessor clears silent flag.
+ float* mutableData()
+ {
+ clearSilentFlag();
+ return m_rawPointer ? m_rawPointer : m_memBuffer->data();
+ }
+
const float* data() const { return m_rawPointer ? m_rawPointer : m_memBuffer->data(); }
// Zeroes out all sample values in buffer.
void zero()
{
+ if (m_silent)
+ return;
+
+ m_silent = true;
+
if (m_memBuffer.get())
m_memBuffer->zero();
else
memset(m_rawPointer, 0, sizeof(float) * m_length);
}
+ // Clears the silent flag.
+ void clearSilentFlag() { m_silent = false; }
+
+ bool isSilent() const { return m_silent; }
+
// Scales all samples by the same amount.
void scale(float scale);
@@ -105,6 +127,7 @@
float* m_rawPointer;
OwnPtr<AudioFloatArray> m_memBuffer;
+ bool m_silent;
};
} // WebCore