Changeset 116201 in webkit


Ignore:
Timestamp:
May 4, 2012 6:28:38 PM (12 years ago)
Author:
crogers@google.com
Message:

Oscillator must implement noteOn() and noteOff()
https://bugs.webkit.org/show_bug.cgi?id=85236

Reviewed by Kenneth Russell.

Source/WebCore:

Test: webaudio/oscillator-scheduling.html
to be landed separately to get proper platform baselines

  • Modules/webaudio/AudioBufferSourceNode.cpp:

(WebCore::AudioBufferSourceNode::process):
Simplify/remove zeroing-out silence at end of buffer, since it's now handled in the base-class AudioScheduledSourceNode::updateSchedulingInfo().

  • Modules/webaudio/AudioContext.cpp:

(WebCore::AudioContext::createBufferSource):
Improve comment about ownership and dynamic-lifetime of AudioBufferSourceNode.

(WebCore::AudioContext::createOscillator):
AudioContext keeps a reference to the Oscillator and that reference is released in AudioScheduledSourceNode,
when it has finished playing.

  • Modules/webaudio/AudioScheduledSourceNode.h:
  • Modules/webaudio/AudioScheduledSourceNode.cpp:

(WebCore::AudioScheduledSourceNode::updateSchedulingInfo):
updateSchedulingInfo() is now responsible for zeroing out the very start (before a note starts)
and the very end (after note ends) of the output AudioBus. We've also simplified the number
of arguments passed to this method, because of this. It now handles playbackState transition to FINISHED_STATE.

  • Modules/webaudio/Oscillator.cpp:

(WebCore::Oscillator::Oscillator):
(WebCore::Oscillator::calculateSampleAccuratePhaseIncrements):
The frequency value needs to snap immediately to its correct value the very first time.
This bug needs to be fixed here so that the Oscillator layout scheduling test works correctly.

(WebCore::Oscillator::process):
Since Oscillator in now changing to be a AudioScheduledSourceNode, we need to call AudioScheduledSourceNode::updateSchedulingInfo()
to handle playbackState for us.

(WebCore::Oscillator::propagatesSilence):
Add scheduling logic for propagatesSilence().

(Oscillator):

  • Modules/webaudio/Oscillator.idl:

Add noteOn(), noteOff() methods and playbackState according to specification.

LayoutTests:

  • webaudio/oscillator-scheduling-expected.wav: Added.
  • webaudio/resources/oscillator-testing.js:

(generateExponentialOscillatorSweep):

Location:
trunk
Files:
1 added
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r116197 r116201  
     12012-05-04  Chris Rogers  <crogers@google.com>
     2
     3        Oscillator must implement noteOn() and noteOff()
     4        https://bugs.webkit.org/show_bug.cgi?id=85236
     5
     6        Reviewed by Kenneth Russell.
     7
     8        * webaudio/oscillator-scheduling-expected.wav: Added.
     9        * webaudio/resources/oscillator-testing.js:
     10        (generateExponentialOscillatorSweep):
     11
    1122012-05-04  David Barr  <davidbarr@chromium.org>
    213
  • trunk/LayoutTests/webaudio/resources/oscillator-testing.js

    r115140 r116201  
    4848    gainNode.connect(context.destination);
    4949
     50    osc.noteOn(0);
     51
    5052    var nyquist = 0.5 * sampleRate;
    5153    osc.frequency.setValueAtTime(10, 0);
  • trunk/Source/WebCore/ChangeLog

    r116198 r116201  
     12012-05-04  Chris Rogers  <crogers@google.com>
     2
     3        Oscillator must implement noteOn() and noteOff()
     4        https://bugs.webkit.org/show_bug.cgi?id=85236
     5
     6        Reviewed by Kenneth Russell.
     7
     8        Test: webaudio/oscillator-scheduling.html
     9        to be landed separately to get proper platform baselines
     10
     11        * Modules/webaudio/AudioBufferSourceNode.cpp:
     12        (WebCore::AudioBufferSourceNode::process):
     13        Simplify/remove zeroing-out silence at end of buffer, since it's now handled in the base-class AudioScheduledSourceNode::updateSchedulingInfo().
     14
     15        * Modules/webaudio/AudioContext.cpp:
     16        (WebCore::AudioContext::createBufferSource):
     17        Improve comment about ownership and dynamic-lifetime of AudioBufferSourceNode.
     18
     19        (WebCore::AudioContext::createOscillator):
     20        AudioContext keeps a reference to the Oscillator and that reference is released in AudioScheduledSourceNode,
     21        when it has finished playing.
     22
     23        * Modules/webaudio/AudioScheduledSourceNode.h:
     24        * Modules/webaudio/AudioScheduledSourceNode.cpp:
     25        (WebCore::AudioScheduledSourceNode::updateSchedulingInfo):
     26        updateSchedulingInfo() is now responsible for zeroing out the very start (before a note starts)
     27        and the very end (after note ends) of the output AudioBus.  We've also simplified the number
     28        of arguments passed to this method, because of this. It now handles playbackState transition to FINISHED_STATE.
     29
     30        * Modules/webaudio/Oscillator.cpp:
     31        (WebCore::Oscillator::Oscillator):
     32        (WebCore::Oscillator::calculateSampleAccuratePhaseIncrements):
     33        The frequency value needs to snap immediately to its correct value the very first time.
     34        This bug needs to be fixed here so that the Oscillator layout scheduling test works correctly.
     35
     36        (WebCore::Oscillator::process):
     37        Since Oscillator in now changing to be a AudioScheduledSourceNode, we need to call AudioScheduledSourceNode::updateSchedulingInfo()
     38        to handle playbackState for us.
     39
     40        (WebCore::Oscillator::propagatesSilence):
     41        Add scheduling logic for propagatesSilence().
     42
     43        (Oscillator):
     44        * Modules/webaudio/Oscillator.idl:
     45        Add noteOn(), noteOff() methods and playbackState according to specification.
     46
    1472012-05-04  Andy Estes  <aestes@apple.com>
    248
  • trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp

    r115485 r116201  
    102102        }
    103103
    104         size_t quantumStartFrame;
    105         size_t quantumEndFrame;
    106         size_t startFrame;
    107         size_t endFrame;
    108104        size_t quantumFrameOffset;
    109105        size_t bufferFramesToProcess;
    110106
    111107        updateSchedulingInfo(framesToProcess,
    112                              quantumStartFrame,
    113                              quantumEndFrame,
    114                              startFrame,
    115                              endFrame,
     108                             outputBus,
    116109                             quantumFrameOffset,
    117110                             bufferFramesToProcess);
     
    131124        float totalGain = gain()->value() * m_buffer->gain();
    132125        outputBus->copyWithGainFrom(*outputBus, &m_lastGain, totalGain);
    133 
    134         // If the end time is somewhere in the middle of this time quantum, then simply zero out the
    135         // frames starting at the end time.
    136         if (m_endTime != UnknownTime && endFrame >= quantumStartFrame && endFrame < quantumEndFrame) {
    137             size_t zeroStartFrame = endFrame - quantumStartFrame;
    138             size_t framesToZero = framesToProcess - zeroStartFrame;
    139 
    140             bool isSafe = zeroStartFrame < framesToProcess && framesToZero <= framesToProcess && zeroStartFrame + framesToZero <= framesToProcess;
    141             ASSERT(isSafe);
    142            
    143             if (isSafe) {
    144                 for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i)
    145                     memset(m_destinationChannels[i] + zeroStartFrame, 0, sizeof(float) * framesToZero);
    146             }
    147 
    148             m_virtualReadIndex = 0;
    149 
    150             finish();
    151         }
    152 
    153126        outputBus->clearSilentFlag();
    154127    } else {
  • trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp

    r115787 r116201  
    351351    RefPtr<AudioBufferSourceNode> node = AudioBufferSourceNode::create(this, m_destinationNode->sampleRate());
    352352
    353     refNode(node.get()); // context keeps reference until source has finished playing
     353    // Because this is an AudioScheduledSourceNode, the context keeps a reference until it has finished playing.
     354    // When this happens, AudioScheduledSourceNode::finish() calls AudioContext::notifyNodeFinishedProcessing().
     355    refNode(node.get());
     356
    354357    return node;
    355358}
     
    517520    ASSERT(isMainThread());
    518521    lazyInitialize();
    519     return Oscillator::create(this, m_destinationNode->sampleRate());
     522
     523    RefPtr<Oscillator> node = Oscillator::create(this, m_destinationNode->sampleRate());
     524
     525    // Because this is an AudioScheduledSourceNode, the context keeps a reference until it has finished playing.
     526    // When this happens, AudioScheduledSourceNode::finish() calls AudioContext::notifyNodeFinishedProcessing().
     527    refNode(node.get());
     528
     529    return node;
    520530}
    521531
  • trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp

    r115485 r116201  
    4949
    5050void AudioScheduledSourceNode::updateSchedulingInfo(size_t quantumFrameSize,
    51                                                     size_t& quantumStartFrame,
    52                                                     size_t& quantumEndFrame,
    53                                                     size_t& startFrame,
    54                                                     size_t& endFrame,
     51                                                    AudioBus* outputBus,
    5552                                                    size_t& quantumFrameOffset,
    5653                                                    size_t& nonSilentFramesToProcess)
    5754{
     55    ASSERT(outputBus);
     56    if (!outputBus)
     57        return;
     58
    5859    ASSERT(quantumFrameSize == AudioNode::ProcessingSizeInFrames);
    5960    if (quantumFrameSize != AudioNode::ProcessingSizeInFrames)
    6061        return;
    61    
    62     // Check if it's time to start playing.
     62
    6363    double sampleRate = this->sampleRate();
    64     quantumStartFrame = context()->currentSampleFrame();
    65     quantumEndFrame = quantumStartFrame + quantumFrameSize;
    66     startFrame = AudioUtilities::timeToSampleFrame(m_startTime, sampleRate);
    67     endFrame = m_endTime == UnknownTime ? 0 : AudioUtilities::timeToSampleFrame(m_endTime, sampleRate);
     64
     65    // quantumStartFrame     : Start frame of the current time quantum.
     66    // quantumEndFrame       : End frame of the current time quantum.
     67    // startFrame            : Start frame for this source.
     68    // endFrame              : End frame for this source.
     69    size_t quantumStartFrame = context()->currentSampleFrame();
     70    size_t quantumEndFrame = quantumStartFrame + quantumFrameSize;
     71    size_t startFrame = AudioUtilities::timeToSampleFrame(m_startTime, sampleRate);
     72    size_t endFrame = m_endTime == UnknownTime ? 0 : AudioUtilities::timeToSampleFrame(m_endTime, sampleRate);
    6873
    6974    // If we know the end time and it's already passed, then don't bother doing any more rendering this cycle.
     
    7378    if (m_playbackState == UNSCHEDULED_STATE || m_playbackState == FINISHED_STATE || startFrame >= quantumEndFrame) {
    7479        // Output silence.
     80        outputBus->zero();
    7581        nonSilentFramesToProcess = 0;
    7682        return;
    7783    }
    7884
     85    // Check if it's time to start playing.
    7986    if (m_playbackState == SCHEDULED_STATE) {
    8087        // Increment the active source count only if we're transitioning from SCHEDULED_STATE to PLAYING_STATE.
     
    8693    quantumFrameOffset = min(quantumFrameOffset, quantumFrameSize); // clamp to valid range
    8794    nonSilentFramesToProcess = quantumFrameSize - quantumFrameOffset;
     95
     96    if (!nonSilentFramesToProcess) {
     97        // Output silence.
     98        outputBus->zero();
     99        return;
     100    }
     101
     102    // Handle silence before we start playing.
     103    // Zero any initial frames representing silence leading up to a rendering start time in the middle of the quantum.
     104    if (quantumFrameOffset) {
     105        for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i)
     106            memset(outputBus->channel(i)->mutableData(), 0, sizeof(float) * quantumFrameOffset);
     107    }
     108
     109    // Handle silence after we're done playing.
     110    // If the end time is somewhere in the middle of this time quantum, then zero out the
     111    // frames from the end time to the very end of the quantum.
     112    if (m_endTime != UnknownTime && endFrame >= quantumStartFrame && endFrame < quantumEndFrame) {
     113        size_t zeroStartFrame = endFrame - quantumStartFrame;
     114        size_t framesToZero = quantumFrameSize - zeroStartFrame;
     115
     116        bool isSafe = zeroStartFrame < quantumFrameSize && framesToZero <= quantumFrameSize && zeroStartFrame + framesToZero <= quantumFrameSize;
     117        ASSERT(isSafe);
     118
     119        if (isSafe) {
     120            nonSilentFramesToProcess -= framesToZero;
     121            for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i)
     122                memset(outputBus->channel(i)->mutableData() + zeroStartFrame, 0, sizeof(float) * framesToZero);
     123        }
     124
     125        finish();
     126    }
    88127
    89128    return;
  • trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.h

    r115485 r116201  
    3434namespace WebCore {
    3535
     36class AudioBus;
     37
    3638class AudioScheduledSourceNode : public AudioSourceNode {
    3739public:
     
    6567protected:
    6668    // Get frame information for the current time quantum.
     69    // We handle the transition into PLAYING_STATE and FINISHED_STATE here,
     70    // zeroing out portions of the outputBus which are outside the range of startFrame and endFrame.
     71    //
    6772    // Each frame time is relative to the context's currentSampleFrame().
    68     // quantumStartFrame     : Start frame of the current time quantum.
    69     // quantumEndFrame       : End frame of the current time quantum.
    70     // startFrame            : Start frame for this source.
    71     // endFrame              : End frame for this source.
    7273    // quantumFrameOffset    : Offset frame in this time quantum to start rendering.
    7374    // nonSilentFramesToProcess : Number of frames rendering non-silence (will be <= quantumFrameSize).
    7475    void updateSchedulingInfo(size_t quantumFrameSize,
    75                               size_t& quantumStartFrame,
    76                               size_t& quantumEndFrame,
    77                               size_t& startFrame,
    78                               size_t& endFrame,
     76                              AudioBus* outputBus,
    7977                              size_t& quantumFrameOffset,
    8078                              size_t& nonSilentFramesToProcess);
  • trunk/Source/WebCore/Modules/webaudio/Oscillator.cpp

    r114988 r116201  
    4949
    5050Oscillator::Oscillator(AudioContext* context, float sampleRate)
    51     : AudioSourceNode(context, sampleRate)
     51    : AudioScheduledSourceNode(context, sampleRate)
    5252    , m_type(SINE)
     53    , m_firstRender(true)
    5354    , m_virtualReadIndex(0)
    5455    , m_phaseIncrements(AudioNode::ProcessingSizeInFrames)
     
    112113    if (!isGood)
    113114        return false;
     115
     116    if (m_firstRender) {
     117        m_firstRender = false;
     118        m_frequency->resetSmoothedValue();
     119        m_detune->resetSmoothedValue();
     120    }
    114121
    115122    bool hasSampleAccurateValues = false;
     
    193200    }
    194201
     202    size_t quantumFrameOffset;
     203    size_t nonSilentFramesToProcess;
     204
     205    updateSchedulingInfo(framesToProcess,
     206                         outputBus,
     207                         quantumFrameOffset,
     208                         nonSilentFramesToProcess);
     209
     210    if (!nonSilentFramesToProcess) {
     211        outputBus->zero();
     212        return;
     213    }
     214
    195215    unsigned waveTableSize = m_waveTable->waveTableSize();
    196216    double invWaveTableSize = 1.0 / waveTableSize;
     
    198218    float* destP = outputBus->channel(0)->mutableData();
    199219
    200     int n = framesToProcess;
     220    ASSERT(quantumFrameOffset <= framesToProcess);
    201221
    202222    // We keep virtualReadIndex double-precision since we're accumulating values.
     
    225245    unsigned readIndexMask = waveTableSize - 1;
    226246
     247    // Start rendering at the correct offset.
     248    destP += quantumFrameOffset;
     249    int n = nonSilentFramesToProcess;
     250
    227251    while (n--) {
    228252        unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
     
    261285
    262286    m_virtualReadIndex = virtualReadIndex;
     287
     288    outputBus->clearSilentFlag();
    263289}
    264290
     
    278304}
    279305
     306bool Oscillator::propagatesSilence() const
     307{
     308    return !isPlayingOrScheduled() || hasFinished() || !m_waveTable.get();
     309}
     310
    280311} // namespace WebCore
    281312
  • trunk/Source/WebCore/Modules/webaudio/Oscillator.h

    r113728 r116201  
    2828#include "AudioBus.h"
    2929#include "AudioParam.h"
    30 #include "AudioSourceNode.h"
     30#include "AudioScheduledSourceNode.h"
    3131#include <wtf/OwnArrayPtr.h>
    3232#include <wtf/PassRefPtr.h>
     
    4141// Oscillator is an audio generator of periodic waveforms.
    4242
    43 class Oscillator : public AudioSourceNode {
     43class Oscillator : public AudioScheduledSourceNode {
    4444public:
    4545    // The waveform type.
     
    7575    bool calculateSampleAccuratePhaseIncrements(size_t framesToProcess);
    7676
    77     // As a pure generator, Oscillator will never propagate silence.
    78     virtual bool propagatesSilence() const OVERRIDE { return false; }
     77    virtual bool propagatesSilence() const OVERRIDE;
    7978
    8079    // One of the waveform types defined in the enum.
     
    8685    // Detune value (deviating from the frequency) in Cents.
    8786    RefPtr<AudioParam> m_detune;
     87
     88    bool m_firstRender;
    8889
    8990    // m_virtualReadIndex is a sample-frame index into our buffer representing the current playback position.
  • trunk/Source/WebCore/Modules/webaudio/Oscillator.idl

    r112938 r116201  
    3636        const unsigned short TRIANGLE = 3;
    3737        const unsigned short CUSTOM = 4;
    38        
     38
    3939        attribute unsigned short type;
     40
     41        // Playback state constants.
     42        const unsigned short UNSCHEDULED_STATE = 0;
     43        const unsigned short SCHEDULED_STATE = 1;
     44        const unsigned short PLAYING_STATE = 2;
     45        const unsigned short FINISHED_STATE = 3;
     46
     47        readonly attribute unsigned short playbackState;
    4048
    4149        readonly attribute AudioParam frequency; // in Hertz
    4250        readonly attribute AudioParam detune; // in Cents
    4351
     52        void noteOn(in double when);
     53        void noteOff(in double when);
    4454        void setWaveTable(in WaveTable waveTable);
    4555
Note: See TracChangeset for help on using the changeset viewer.