Home | History | Annotate | Download | only in webaudio
      1 /*
      2  * Copyright (C) 2011 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  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 
     28 #if ENABLE(WEB_AUDIO)
     29 
     30 #include "modules/webaudio/AudioParamTimeline.h"
     31 
     32 #include "platform/audio/AudioUtilities.h"
     33 #include "platform/FloatConversion.h"
     34 #include "wtf/MathExtras.h"
     35 #include <algorithm>
     36 
     37 using namespace std;
     38 
     39 namespace WebCore {
     40 
     41 void AudioParamTimeline::setValueAtTime(float value, double time)
     42 {
     43     insertEvent(ParamEvent(ParamEvent::SetValue, value, time, 0, 0, 0));
     44 }
     45 
     46 void AudioParamTimeline::linearRampToValueAtTime(float value, double time)
     47 {
     48     insertEvent(ParamEvent(ParamEvent::LinearRampToValue, value, time, 0, 0, 0));
     49 }
     50 
     51 void AudioParamTimeline::exponentialRampToValueAtTime(float value, double time)
     52 {
     53     insertEvent(ParamEvent(ParamEvent::ExponentialRampToValue, value, time, 0, 0, 0));
     54 }
     55 
     56 void AudioParamTimeline::setTargetAtTime(float target, double time, double timeConstant)
     57 {
     58     insertEvent(ParamEvent(ParamEvent::SetTarget, target, time, timeConstant, 0, 0));
     59 }
     60 
     61 void AudioParamTimeline::setValueCurveAtTime(Float32Array* curve, double time, double duration)
     62 {
     63     insertEvent(ParamEvent(ParamEvent::SetValueCurve, 0, time, 0, duration, curve));
     64 }
     65 
     66 static bool isValidNumber(float x)
     67 {
     68     return !std::isnan(x) && !std::isinf(x);
     69 }
     70 
     71 void AudioParamTimeline::insertEvent(const ParamEvent& event)
     72 {
     73     // Sanity check the event. Be super careful we're not getting infected with NaN or Inf.
     74     bool isValid = event.type() < ParamEvent::LastType
     75         && isValidNumber(event.value())
     76         && isValidNumber(event.time())
     77         && isValidNumber(event.timeConstant())
     78         && isValidNumber(event.duration())
     79         && event.duration() >= 0;
     80 
     81     ASSERT(isValid);
     82     if (!isValid)
     83         return;
     84 
     85     MutexLocker locker(m_eventsLock);
     86 
     87     unsigned i = 0;
     88     double insertTime = event.time();
     89     for (i = 0; i < m_events.size(); ++i) {
     90         // Overwrite same event type and time.
     91         if (m_events[i].time() == insertTime && m_events[i].type() == event.type()) {
     92             m_events[i] = event;
     93             return;
     94         }
     95 
     96         if (m_events[i].time() > insertTime)
     97             break;
     98     }
     99 
    100     m_events.insert(i, event);
    101 }
    102 
    103 void AudioParamTimeline::cancelScheduledValues(double startTime)
    104 {
    105     MutexLocker locker(m_eventsLock);
    106 
    107     // Remove all events starting at startTime.
    108     for (unsigned i = 0; i < m_events.size(); ++i) {
    109         if (m_events[i].time() >= startTime) {
    110             m_events.remove(i, m_events.size() - i);
    111             break;
    112         }
    113     }
    114 }
    115 
    116 float AudioParamTimeline::valueForContextTime(AudioContext* context, float defaultValue, bool& hasValue)
    117 {
    118     ASSERT(context);
    119 
    120     {
    121         MutexTryLocker tryLocker(m_eventsLock);
    122         if (!tryLocker.locked() || !context || !m_events.size() || context->currentTime() < m_events[0].time()) {
    123             hasValue = false;
    124             return defaultValue;
    125         }
    126     }
    127 
    128     // Ask for just a single value.
    129     float value;
    130     double sampleRate = context->sampleRate();
    131     double startTime = context->currentTime();
    132     double endTime = startTime + 1.1 / sampleRate; // time just beyond one sample-frame
    133     double controlRate = sampleRate / AudioNode::ProcessingSizeInFrames; // one parameter change per render quantum
    134     value = valuesForTimeRange(startTime, endTime, defaultValue, &value, 1, sampleRate, controlRate);
    135 
    136     hasValue = true;
    137     return value;
    138 }
    139 
    140 float AudioParamTimeline::valuesForTimeRange(
    141     double startTime,
    142     double endTime,
    143     float defaultValue,
    144     float* values,
    145     unsigned numberOfValues,
    146     double sampleRate,
    147     double controlRate)
    148 {
    149     // We can't contend the lock in the realtime audio thread.
    150     MutexTryLocker tryLocker(m_eventsLock);
    151     if (!tryLocker.locked()) {
    152         if (values) {
    153             for (unsigned i = 0; i < numberOfValues; ++i)
    154                 values[i] = defaultValue;
    155         }
    156         return defaultValue;
    157     }
    158 
    159     float value = valuesForTimeRangeImpl(startTime, endTime, defaultValue, values, numberOfValues, sampleRate, controlRate);
    160 
    161     return value;
    162 }
    163 
    164 float AudioParamTimeline::valuesForTimeRangeImpl(
    165     double startTime,
    166     double endTime,
    167     float defaultValue,
    168     float* values,
    169     unsigned numberOfValues,
    170     double sampleRate,
    171     double controlRate)
    172 {
    173     ASSERT(values);
    174     if (!values)
    175         return defaultValue;
    176 
    177     // Return default value if there are no events matching the desired time range.
    178     if (!m_events.size() || endTime <= m_events[0].time()) {
    179         for (unsigned i = 0; i < numberOfValues; ++i)
    180             values[i] = defaultValue;
    181         return defaultValue;
    182     }
    183 
    184     // Maintain a running time and index for writing the values buffer.
    185     double currentTime = startTime;
    186     unsigned writeIndex = 0;
    187 
    188     // If first event is after startTime then fill initial part of values buffer with defaultValue
    189     // until we reach the first event time.
    190     double firstEventTime = m_events[0].time();
    191     if (firstEventTime > startTime) {
    192         double fillToTime = min(endTime, firstEventTime);
    193         unsigned fillToFrame = AudioUtilities::timeToSampleFrame(fillToTime - startTime, sampleRate);
    194         fillToFrame = min(fillToFrame, numberOfValues);
    195         for (; writeIndex < fillToFrame; ++writeIndex)
    196             values[writeIndex] = defaultValue;
    197 
    198         currentTime = fillToTime;
    199     }
    200 
    201     float value = defaultValue;
    202 
    203     // Go through each event and render the value buffer where the times overlap,
    204     // stopping when we've rendered all the requested values.
    205     // FIXME: could try to optimize by avoiding having to iterate starting from the very first event
    206     // and keeping track of a "current" event index.
    207     int n = m_events.size();
    208     for (int i = 0; i < n && writeIndex < numberOfValues; ++i) {
    209         ParamEvent& event = m_events[i];
    210         ParamEvent* nextEvent = i < n - 1 ? &(m_events[i + 1]) : 0;
    211 
    212         // Wait until we get a more recent event.
    213         if (nextEvent && nextEvent->time() < currentTime)
    214             continue;
    215 
    216         float value1 = event.value();
    217         double time1 = event.time();
    218         float value2 = nextEvent ? nextEvent->value() : value1;
    219         double time2 = nextEvent ? nextEvent->time() : endTime + 1;
    220 
    221         double deltaTime = time2 - time1;
    222         float k = deltaTime > 0 ? 1 / deltaTime : 0;
    223         double sampleFrameTimeIncr = 1 / sampleRate;
    224 
    225         double fillToTime = min(endTime, time2);
    226         unsigned fillToFrame = AudioUtilities::timeToSampleFrame(fillToTime - startTime, sampleRate);
    227         fillToFrame = min(fillToFrame, numberOfValues);
    228 
    229         ParamEvent::Type nextEventType = nextEvent ? static_cast<ParamEvent::Type>(nextEvent->type()) : ParamEvent::LastType /* unknown */;
    230 
    231         // First handle linear and exponential ramps which require looking ahead to the next event.
    232         if (nextEventType == ParamEvent::LinearRampToValue) {
    233             for (; writeIndex < fillToFrame; ++writeIndex) {
    234                 float x = (currentTime - time1) * k;
    235                 value = (1 - x) * value1 + x * value2;
    236                 values[writeIndex] = value;
    237                 currentTime += sampleFrameTimeIncr;
    238             }
    239         } else if (nextEventType == ParamEvent::ExponentialRampToValue) {
    240             if (value1 <= 0 || value2 <= 0) {
    241                 // Handle negative values error case by propagating previous value.
    242                 for (; writeIndex < fillToFrame; ++writeIndex)
    243                     values[writeIndex] = value;
    244             } else {
    245                 float numSampleFrames = deltaTime * sampleRate;
    246                 // The value goes exponentially from value1 to value2 in a duration of deltaTime seconds (corresponding to numSampleFrames).
    247                 // Compute the per-sample multiplier.
    248                 float multiplier = powf(value2 / value1, 1 / numSampleFrames);
    249 
    250                 // Set the starting value of the exponential ramp. This is the same as multiplier ^
    251                 // AudioUtilities::timeToSampleFrame(currentTime - time1, sampleRate), but is more
    252                 // accurate, especially if multiplier is close to 1.
    253                 value = value1 * powf(value2 / value1,
    254                                       AudioUtilities::timeToSampleFrame(currentTime - time1, sampleRate) / numSampleFrames);
    255 
    256                 for (; writeIndex < fillToFrame; ++writeIndex) {
    257                     values[writeIndex] = value;
    258                     value *= multiplier;
    259                     currentTime += sampleFrameTimeIncr;
    260                 }
    261             }
    262         } else {
    263             // Handle event types not requiring looking ahead to the next event.
    264             switch (event.type()) {
    265             case ParamEvent::SetValue:
    266             case ParamEvent::LinearRampToValue:
    267             case ParamEvent::ExponentialRampToValue:
    268                 {
    269                     currentTime = fillToTime;
    270 
    271                     // Simply stay at a constant value.
    272                     value = event.value();
    273                     for (; writeIndex < fillToFrame; ++writeIndex)
    274                         values[writeIndex] = value;
    275 
    276                     break;
    277                 }
    278 
    279             case ParamEvent::SetTarget:
    280                 {
    281                     currentTime = fillToTime;
    282 
    283                     // Exponential approach to target value with given time constant.
    284                     float target = event.value();
    285                     float timeConstant = event.timeConstant();
    286                     float discreteTimeConstant = static_cast<float>(AudioUtilities::discreteTimeConstantForSampleRate(timeConstant, controlRate));
    287 
    288                     for (; writeIndex < fillToFrame; ++writeIndex) {
    289                         values[writeIndex] = value;
    290                         value += (target - value) * discreteTimeConstant;
    291                     }
    292 
    293                     break;
    294                 }
    295 
    296             case ParamEvent::SetValueCurve:
    297                 {
    298                     Float32Array* curve = event.curve();
    299                     float* curveData = curve ? curve->data() : 0;
    300                     unsigned numberOfCurvePoints = curve ? curve->length() : 0;
    301 
    302                     // Curve events have duration, so don't just use next event time.
    303                     float duration = event.duration();
    304                     float durationFrames = duration * sampleRate;
    305                     float curvePointsPerFrame = static_cast<float>(numberOfCurvePoints) / durationFrames;
    306 
    307                     if (!curve || !curveData || !numberOfCurvePoints || duration <= 0 || sampleRate <= 0) {
    308                         // Error condition - simply propagate previous value.
    309                         currentTime = fillToTime;
    310                         for (; writeIndex < fillToFrame; ++writeIndex)
    311                             values[writeIndex] = value;
    312                         break;
    313                     }
    314 
    315                     // Save old values and recalculate information based on the curve's duration
    316                     // instead of the next event time.
    317                     unsigned nextEventFillToFrame = fillToFrame;
    318                     float nextEventFillToTime = fillToTime;
    319                     fillToTime = min(endTime, time1 + duration);
    320                     fillToFrame = AudioUtilities::timeToSampleFrame(fillToTime - startTime, sampleRate);
    321                     fillToFrame = min(fillToFrame, numberOfValues);
    322 
    323                     // Index into the curve data using a floating-point value.
    324                     // We're scaling the number of curve points by the duration (see curvePointsPerFrame).
    325                     float curveVirtualIndex = 0;
    326                     if (time1 < currentTime) {
    327                         // Index somewhere in the middle of the curve data.
    328                         // Don't use timeToSampleFrame() since we want the exact floating-point frame.
    329                         float frameOffset = (currentTime - time1) * sampleRate;
    330                         curveVirtualIndex = curvePointsPerFrame * frameOffset;
    331                     }
    332 
    333                     // Render the stretched curve data using nearest neighbor sampling.
    334                     // Oversampled curve data can be provided if smoothness is desired.
    335                     for (; writeIndex < fillToFrame; ++writeIndex) {
    336                         // Ideally we'd use round() from MathExtras, but we're in a tight loop here
    337                         // and we're trading off precision for extra speed.
    338                         unsigned curveIndex = static_cast<unsigned>(0.5 + curveVirtualIndex);
    339 
    340                         curveVirtualIndex += curvePointsPerFrame;
    341 
    342                         // Bounds check.
    343                         if (curveIndex < numberOfCurvePoints)
    344                             value = curveData[curveIndex];
    345 
    346                         values[writeIndex] = value;
    347                     }
    348 
    349                     // If there's any time left after the duration of this event and the start
    350                     // of the next, then just propagate the last value.
    351                     for (; writeIndex < nextEventFillToFrame; ++writeIndex)
    352                         values[writeIndex] = value;
    353 
    354                     // Re-adjust current time
    355                     currentTime = nextEventFillToTime;
    356 
    357                     break;
    358                 }
    359             }
    360         }
    361     }
    362 
    363     // If there's any time left after processing the last event then just propagate the last value
    364     // to the end of the values buffer.
    365     for (; writeIndex < numberOfValues; ++writeIndex)
    366         values[writeIndex] = value;
    367 
    368     return value;
    369 }
    370 
    371 } // namespace WebCore
    372 
    373 #endif // ENABLE(WEB_AUDIO)
    374