Home | History | Annotate | Download | only in webaudio
      1 /*
      2  * Copyright (C) 2012, Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1.  Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2.  Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
     20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     23  */
     24 
     25 #include "config.h"
     26 
     27 #if ENABLE(WEB_AUDIO)
     28 
     29 #include "modules/webaudio/OscillatorNode.h"
     30 
     31 #include "platform/audio/AudioUtilities.h"
     32 #include "platform/audio/VectorMath.h"
     33 #include "modules/webaudio/AudioContext.h"
     34 #include "modules/webaudio/AudioNodeOutput.h"
     35 #include "modules/webaudio/PeriodicWave.h"
     36 #include "wtf/MathExtras.h"
     37 #include "wtf/StdLibExtras.h"
     38 #include <algorithm>
     39 
     40 using namespace std;
     41 
     42 namespace WebCore {
     43 
     44 using namespace VectorMath;
     45 
     46 PassRefPtr<OscillatorNode> OscillatorNode::create(AudioContext* context, float sampleRate)
     47 {
     48     return adoptRef(new OscillatorNode(context, sampleRate));
     49 }
     50 
     51 OscillatorNode::OscillatorNode(AudioContext* context, float sampleRate)
     52     : AudioScheduledSourceNode(context, sampleRate)
     53     , m_type(SINE)
     54     , m_firstRender(true)
     55     , m_virtualReadIndex(0)
     56     , m_phaseIncrements(AudioNode::ProcessingSizeInFrames)
     57     , m_detuneValues(AudioNode::ProcessingSizeInFrames)
     58 {
     59     ScriptWrappable::init(this);
     60     setNodeType(NodeTypeOscillator);
     61 
     62     // Use musical pitch standard A440 as a default.
     63     m_frequency = AudioParam::create(context, "frequency", 440, 0, 100000);
     64     // Default to no detuning.
     65     m_detune = AudioParam::create(context, "detune", 0, -4800, 4800);
     66 
     67     // Sets up default wavetable.
     68     setType(m_type);
     69 
     70     // An oscillator is always mono.
     71     addOutput(adoptPtr(new AudioNodeOutput(this, 1)));
     72 
     73     initialize();
     74 }
     75 
     76 OscillatorNode::~OscillatorNode()
     77 {
     78     uninitialize();
     79 }
     80 
     81 String OscillatorNode::type() const
     82 {
     83     switch (m_type) {
     84     case SINE:
     85         return "sine";
     86     case SQUARE:
     87         return "square";
     88     case SAWTOOTH:
     89         return "sawtooth";
     90     case TRIANGLE:
     91         return "triangle";
     92     case CUSTOM:
     93         return "custom";
     94     default:
     95         ASSERT_NOT_REACHED();
     96         return "custom";
     97     }
     98 }
     99 
    100 void OscillatorNode::setType(const String& type)
    101 {
    102     if (type == "sine")
    103         setType(SINE);
    104     else if (type == "square")
    105         setType(SQUARE);
    106     else if (type == "sawtooth")
    107         setType(SAWTOOTH);
    108     else if (type == "triangle")
    109         setType(TRIANGLE);
    110     else
    111         ASSERT_NOT_REACHED();
    112 }
    113 
    114 bool OscillatorNode::setType(unsigned type)
    115 {
    116     PeriodicWave* periodicWave = 0;
    117     float sampleRate = this->sampleRate();
    118 
    119     switch (type) {
    120     case SINE: {
    121         DEFINE_STATIC_REF(PeriodicWave, periodicWaveSine, (PeriodicWave::createSine(sampleRate)));
    122         periodicWave = periodicWaveSine;
    123         break;
    124     }
    125     case SQUARE: {
    126         DEFINE_STATIC_REF(PeriodicWave, periodicWaveSquare, (PeriodicWave::createSquare(sampleRate)));
    127         periodicWave = periodicWaveSquare;
    128         break;
    129     }
    130     case SAWTOOTH: {
    131         DEFINE_STATIC_REF(PeriodicWave, periodicWaveSawtooth, (PeriodicWave::createSawtooth(sampleRate)));
    132         periodicWave = periodicWaveSawtooth;
    133         break;
    134     }
    135     case TRIANGLE: {
    136         DEFINE_STATIC_REF(PeriodicWave, periodicWaveTriangle, (PeriodicWave::createTriangle(sampleRate)));
    137         periodicWave = periodicWaveTriangle;
    138         break;
    139     }
    140     case CUSTOM:
    141     default:
    142         // Return error for invalid types, including CUSTOM since setPeriodicWave() method must be
    143         // called explicitly.
    144         return false;
    145     }
    146 
    147     setPeriodicWave(periodicWave);
    148     m_type = type;
    149     return true;
    150 }
    151 
    152 bool OscillatorNode::calculateSampleAccuratePhaseIncrements(size_t framesToProcess)
    153 {
    154     bool isGood = framesToProcess <= m_phaseIncrements.size() && framesToProcess <= m_detuneValues.size();
    155     ASSERT(isGood);
    156     if (!isGood)
    157         return false;
    158 
    159     if (m_firstRender) {
    160         m_firstRender = false;
    161         m_frequency->resetSmoothedValue();
    162         m_detune->resetSmoothedValue();
    163     }
    164 
    165     bool hasSampleAccurateValues = false;
    166     bool hasFrequencyChanges = false;
    167     float* phaseIncrements = m_phaseIncrements.data();
    168 
    169     float finalScale = m_periodicWave->rateScale();
    170 
    171     if (m_frequency->hasSampleAccurateValues()) {
    172         hasSampleAccurateValues = true;
    173         hasFrequencyChanges = true;
    174 
    175         // Get the sample-accurate frequency values and convert to phase increments.
    176         // They will be converted to phase increments below.
    177         m_frequency->calculateSampleAccurateValues(phaseIncrements, framesToProcess);
    178     } else {
    179         // Handle ordinary parameter smoothing/de-zippering if there are no scheduled changes.
    180         m_frequency->smooth();
    181         float frequency = m_frequency->smoothedValue();
    182         finalScale *= frequency;
    183     }
    184 
    185     if (m_detune->hasSampleAccurateValues()) {
    186         hasSampleAccurateValues = true;
    187 
    188         // Get the sample-accurate detune values.
    189         float* detuneValues = hasFrequencyChanges ? m_detuneValues.data() : phaseIncrements;
    190         m_detune->calculateSampleAccurateValues(detuneValues, framesToProcess);
    191 
    192         // Convert from cents to rate scalar.
    193         float k = 1.0 / 1200;
    194         vsmul(detuneValues, 1, &k, detuneValues, 1, framesToProcess);
    195         for (unsigned i = 0; i < framesToProcess; ++i)
    196             detuneValues[i] = powf(2, detuneValues[i]); // FIXME: converting to expf() will be faster.
    197 
    198         if (hasFrequencyChanges) {
    199             // Multiply frequencies by detune scalings.
    200             vmul(detuneValues, 1, phaseIncrements, 1, phaseIncrements, 1, framesToProcess);
    201         }
    202     } else {
    203         // Handle ordinary parameter smoothing/de-zippering if there are no scheduled changes.
    204         m_detune->smooth();
    205         float detune = m_detune->smoothedValue();
    206         float detuneScale = powf(2, detune / 1200);
    207         finalScale *= detuneScale;
    208     }
    209 
    210     if (hasSampleAccurateValues) {
    211         // Convert from frequency to wavetable increment.
    212         vsmul(phaseIncrements, 1, &finalScale, phaseIncrements, 1, framesToProcess);
    213     }
    214 
    215     return hasSampleAccurateValues;
    216 }
    217 
    218 void OscillatorNode::process(size_t framesToProcess)
    219 {
    220     AudioBus* outputBus = output(0)->bus();
    221 
    222     if (!isInitialized() || !outputBus->numberOfChannels()) {
    223         outputBus->zero();
    224         return;
    225     }
    226 
    227     ASSERT(framesToProcess <= m_phaseIncrements.size());
    228     if (framesToProcess > m_phaseIncrements.size())
    229         return;
    230 
    231     // The audio thread can't block on this lock, so we call tryLock() instead.
    232     MutexTryLocker tryLocker(m_processLock);
    233     if (!tryLocker.locked()) {
    234         // Too bad - the tryLock() failed. We must be in the middle of changing wave-tables.
    235         outputBus->zero();
    236         return;
    237     }
    238 
    239     // We must access m_periodicWave only inside the lock.
    240     if (!m_periodicWave.get()) {
    241         outputBus->zero();
    242         return;
    243     }
    244 
    245     size_t quantumFrameOffset;
    246     size_t nonSilentFramesToProcess;
    247 
    248     updateSchedulingInfo(framesToProcess, outputBus, quantumFrameOffset, nonSilentFramesToProcess);
    249 
    250     if (!nonSilentFramesToProcess) {
    251         outputBus->zero();
    252         return;
    253     }
    254 
    255     unsigned periodicWaveSize = m_periodicWave->periodicWaveSize();
    256     double invPeriodicWaveSize = 1.0 / periodicWaveSize;
    257 
    258     float* destP = outputBus->channel(0)->mutableData();
    259 
    260     ASSERT(quantumFrameOffset <= framesToProcess);
    261 
    262     // We keep virtualReadIndex double-precision since we're accumulating values.
    263     double virtualReadIndex = m_virtualReadIndex;
    264 
    265     float rateScale = m_periodicWave->rateScale();
    266     float invRateScale = 1 / rateScale;
    267     bool hasSampleAccurateValues = calculateSampleAccuratePhaseIncrements(framesToProcess);
    268 
    269     float frequency = 0;
    270     float* higherWaveData = 0;
    271     float* lowerWaveData = 0;
    272     float tableInterpolationFactor;
    273 
    274     if (!hasSampleAccurateValues) {
    275         frequency = m_frequency->smoothedValue();
    276         float detune = m_detune->smoothedValue();
    277         float detuneScale = powf(2, detune / 1200);
    278         frequency *= detuneScale;
    279         m_periodicWave->waveDataForFundamentalFrequency(frequency, lowerWaveData, higherWaveData, tableInterpolationFactor);
    280     }
    281 
    282     float incr = frequency * rateScale;
    283     float* phaseIncrements = m_phaseIncrements.data();
    284 
    285     unsigned readIndexMask = periodicWaveSize - 1;
    286 
    287     // Start rendering at the correct offset.
    288     destP += quantumFrameOffset;
    289     int n = nonSilentFramesToProcess;
    290 
    291     while (n--) {
    292         unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
    293         unsigned readIndex2 = readIndex + 1;
    294 
    295         // Contain within valid range.
    296         readIndex = readIndex & readIndexMask;
    297         readIndex2 = readIndex2 & readIndexMask;
    298 
    299         if (hasSampleAccurateValues) {
    300             incr = *phaseIncrements++;
    301 
    302             frequency = invRateScale * incr;
    303             m_periodicWave->waveDataForFundamentalFrequency(frequency, lowerWaveData, higherWaveData, tableInterpolationFactor);
    304         }
    305 
    306         float sample1Lower = lowerWaveData[readIndex];
    307         float sample2Lower = lowerWaveData[readIndex2];
    308         float sample1Higher = higherWaveData[readIndex];
    309         float sample2Higher = higherWaveData[readIndex2];
    310 
    311         // Linearly interpolate within each table (lower and higher).
    312         float interpolationFactor = static_cast<float>(virtualReadIndex) - readIndex;
    313         float sampleHigher = (1 - interpolationFactor) * sample1Higher + interpolationFactor * sample2Higher;
    314         float sampleLower = (1 - interpolationFactor) * sample1Lower + interpolationFactor * sample2Lower;
    315 
    316         // Then interpolate between the two tables.
    317         float sample = (1 - tableInterpolationFactor) * sampleHigher + tableInterpolationFactor * sampleLower;
    318 
    319         *destP++ = sample;
    320 
    321         // Increment virtual read index and wrap virtualReadIndex into the range 0 -> periodicWaveSize.
    322         virtualReadIndex += incr;
    323         virtualReadIndex -= floor(virtualReadIndex * invPeriodicWaveSize) * periodicWaveSize;
    324     }
    325 
    326     m_virtualReadIndex = virtualReadIndex;
    327 
    328     outputBus->clearSilentFlag();
    329 }
    330 
    331 void OscillatorNode::reset()
    332 {
    333     m_virtualReadIndex = 0;
    334 }
    335 
    336 void OscillatorNode::setPeriodicWave(PeriodicWave* periodicWave)
    337 {
    338     ASSERT(isMainThread());
    339 
    340     // This synchronizes with process().
    341     MutexLocker processLocker(m_processLock);
    342     m_periodicWave = periodicWave;
    343     m_type = CUSTOM;
    344 }
    345 
    346 bool OscillatorNode::propagatesSilence() const
    347 {
    348     return !isPlayingOrScheduled() || hasFinished() || !m_periodicWave.get();
    349 }
    350 
    351 } // namespace WebCore
    352 
    353 #endif // ENABLE(WEB_AUDIO)
    354