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 "bindings/core/v8/ExceptionState.h"
     33 #include "core/dom/ExceptionCode.h"
     34 #include "platform/audio/AudioUtilities.h"
     35 #include "platform/FloatConversion.h"
     36 #include "wtf/MathExtras.h"
     37 #include <algorithm>
     38 
     39 namespace blink {
     40 
     41 static bool isValidAudioParamValue(float value, ExceptionState& exceptionState)
     42 {
     43     if (std::isfinite(value))
     44         return true;
     45 
     46     exceptionState.throwDOMException(
     47         InvalidStateError,
     48         "Target value must be a finite number: " + String::number(value));
     49     return false;
     50 }
     51 
     52 static bool isPositiveAudioParamValue(float value, ExceptionState& exceptionState)
     53 {
     54     if (std::isfinite(value) && (value > 0))
     55         return true;
     56 
     57     exceptionState.throwDOMException(
     58         InvalidStateError,
     59         "Target value must be a finite positive number: " + String::number(value));
     60     return false;
     61 }
     62 
     63 static bool isValidAudioParamTime(double time, ExceptionState& exceptionState, String message)
     64 {
     65     if (std::isfinite(time) && (time >= 0))
     66         return true;
     67 
     68     exceptionState.throwDOMException(
     69         InvalidStateError,
     70         message + " must be a finite non-negative number: " + String::number(time));
     71     return false;
     72 }
     73 
     74 static bool isPositiveAudioParamTime(double time, ExceptionState& exceptionState, String message)
     75 {
     76     if (std::isfinite(time) && (time > 0))
     77         return true;
     78 
     79     exceptionState.throwDOMException(
     80         InvalidStateError,
     81         message + " must be a finite positive number: " + String::number(time));
     82     return false;
     83 }
     84 
     85 static bool isValidAudioParamTime(double time, ExceptionState& exceptionState)
     86 {
     87     return isValidAudioParamTime(time, exceptionState, "Time");
     88 }
     89 
     90 void AudioParamTimeline::setValueAtTime(float value, double time, ExceptionState& exceptionState)
     91 {
     92     ASSERT(isMainThread());
     93 
     94     if (!isValidAudioParamValue(value, exceptionState)
     95         || !isValidAudioParamTime(time, exceptionState))
     96         return;
     97 
     98     insertEvent(ParamEvent(ParamEvent::SetValue, value, time, 0, 0, nullptr));
     99 }
    100 
    101 void AudioParamTimeline::linearRampToValueAtTime(float value, double time, ExceptionState& exceptionState)
    102 {
    103     ASSERT(isMainThread());
    104 
    105     if (!isValidAudioParamValue(value, exceptionState)
    106         || !isValidAudioParamTime(time, exceptionState))
    107         return;
    108 
    109     insertEvent(ParamEvent(ParamEvent::LinearRampToValue, value, time, 0, 0, nullptr));
    110 }
    111 
    112 void AudioParamTimeline::exponentialRampToValueAtTime(float value, double time, ExceptionState& exceptionState)
    113 {
    114     ASSERT(isMainThread());
    115 
    116     if (!isPositiveAudioParamValue(value, exceptionState)
    117         || !isValidAudioParamTime(time, exceptionState))
    118         return;
    119 
    120     insertEvent(ParamEvent(ParamEvent::ExponentialRampToValue, value, time, 0, 0, nullptr));
    121 }
    122 
    123 void AudioParamTimeline::setTargetAtTime(float target, double time, double timeConstant, ExceptionState& exceptionState)
    124 {
    125     ASSERT(isMainThread());
    126 
    127     if (!isValidAudioParamValue(target, exceptionState)
    128         || !isValidAudioParamTime(time, exceptionState)
    129         || !isValidAudioParamTime(timeConstant, exceptionState, "Time constant"))
    130         return;
    131 
    132     insertEvent(ParamEvent(ParamEvent::SetTarget, target, time, timeConstant, 0, nullptr));
    133 }
    134 
    135 void AudioParamTimeline::setValueCurveAtTime(Float32Array* curve, double time, double duration, ExceptionState& exceptionState)
    136 {
    137     ASSERT(isMainThread());
    138 
    139     if (!isValidAudioParamTime(time, exceptionState)
    140         || !isPositiveAudioParamTime(duration, exceptionState, "Duration"))
    141         return;
    142 
    143     insertEvent(ParamEvent(ParamEvent::SetValueCurve, 0, time, 0, duration, curve));
    144 }
    145 
    146 void AudioParamTimeline::insertEvent(const ParamEvent& event)
    147 {
    148     // Sanity check the event. Be super careful we're not getting infected with NaN or Inf. These
    149     // should have been handled by the caller.
    150     bool isValid = event.type() < ParamEvent::LastType
    151         && std::isfinite(event.value())
    152         && std::isfinite(event.time())
    153         && std::isfinite(event.timeConstant())
    154         && std::isfinite(event.duration())
    155         && event.duration() >= 0;
    156 
    157     ASSERT(isValid);
    158     if (!isValid)
    159         return;
    160 
    161     MutexLocker locker(m_eventsLock);
    162 
    163     unsigned i = 0;
    164     double insertTime = event.time();
    165     for (i = 0; i < m_events.size(); ++i) {
    166         // Overwrite same event type and time.
    167         if (m_events[i].time() == insertTime && m_events[i].type() == event.type()) {
    168             m_events[i] = event;
    169             return;
    170         }
    171 
    172         if (m_events[i].time() > insertTime)
    173             break;
    174     }
    175 
    176     m_events.insert(i, event);
    177 }
    178 
    179 void AudioParamTimeline::cancelScheduledValues(double startTime, ExceptionState& exceptionState)
    180 {
    181     ASSERT(isMainThread());
    182 
    183     if (!std::isfinite(startTime)) {
    184         exceptionState.throwDOMException(
    185             InvalidStateError,
    186             "Time must be a finite number: " + String::number(startTime));
    187     }
    188 
    189     MutexLocker locker(m_eventsLock);
    190 
    191     // Remove all events starting at startTime.
    192     for (unsigned i = 0; i < m_events.size(); ++i) {
    193         if (m_events[i].time() >= startTime) {
    194             m_events.remove(i, m_events.size() - i);
    195             break;
    196         }
    197     }
    198 }
    199 
    200 float AudioParamTimeline::valueForContextTime(AudioContext* context, float defaultValue, bool& hasValue)
    201 {
    202     ASSERT(context);
    203 
    204     {
    205         MutexTryLocker tryLocker(m_eventsLock);
    206         if (!tryLocker.locked() || !context || !m_events.size() || context->currentTime() < m_events[0].time()) {
    207             hasValue = false;
    208             return defaultValue;
    209         }
    210     }
    211 
    212     // Ask for just a single value.
    213     float value;
    214     double sampleRate = context->sampleRate();
    215     double startTime = context->currentTime();
    216     double endTime = startTime + 1.1 / sampleRate; // time just beyond one sample-frame
    217     double controlRate = sampleRate / AudioNode::ProcessingSizeInFrames; // one parameter change per render quantum
    218     value = valuesForTimeRange(startTime, endTime, defaultValue, &value, 1, sampleRate, controlRate);
    219 
    220     hasValue = true;
    221     return value;
    222 }
    223 
    224 float AudioParamTimeline::valuesForTimeRange(
    225     double startTime,
    226     double endTime,
    227     float defaultValue,
    228     float* values,
    229     unsigned numberOfValues,
    230     double sampleRate,
    231     double controlRate)
    232 {
    233     // We can't contend the lock in the realtime audio thread.
    234     MutexTryLocker tryLocker(m_eventsLock);
    235     if (!tryLocker.locked()) {
    236         if (values) {
    237             for (unsigned i = 0; i < numberOfValues; ++i)
    238                 values[i] = defaultValue;
    239         }
    240         return defaultValue;
    241     }
    242 
    243     float value = valuesForTimeRangeImpl(startTime, endTime, defaultValue, values, numberOfValues, sampleRate, controlRate);
    244 
    245     return value;
    246 }
    247 
    248 float AudioParamTimeline::valuesForTimeRangeImpl(
    249     double startTime,
    250     double endTime,
    251     float defaultValue,
    252     float* values,
    253     unsigned numberOfValues,
    254     double sampleRate,
    255     double controlRate)
    256 {
    257     ASSERT(values);
    258     if (!values)
    259         return defaultValue;
    260 
    261     // Return default value if there are no events matching the desired time range.
    262     if (!m_events.size() || endTime <= m_events[0].time()) {
    263         for (unsigned i = 0; i < numberOfValues; ++i)
    264             values[i] = defaultValue;
    265         return defaultValue;
    266     }
    267 
    268     // Maintain a running time and index for writing the values buffer.
    269     double currentTime = startTime;
    270     unsigned writeIndex = 0;
    271 
    272     // If first event is after startTime then fill initial part of values buffer with defaultValue
    273     // until we reach the first event time.
    274     double firstEventTime = m_events[0].time();
    275     if (firstEventTime > startTime) {
    276         double fillToTime = std::min(endTime, firstEventTime);
    277         unsigned fillToFrame = AudioUtilities::timeToSampleFrame(fillToTime - startTime, sampleRate);
    278         fillToFrame = std::min(fillToFrame, numberOfValues);
    279         for (; writeIndex < fillToFrame; ++writeIndex)
    280             values[writeIndex] = defaultValue;
    281 
    282         currentTime = fillToTime;
    283     }
    284 
    285     float value = defaultValue;
    286 
    287     // Go through each event and render the value buffer where the times overlap,
    288     // stopping when we've rendered all the requested values.
    289     // FIXME: could try to optimize by avoiding having to iterate starting from the very first event
    290     // and keeping track of a "current" event index.
    291     int n = m_events.size();
    292     for (int i = 0; i < n && writeIndex < numberOfValues; ++i) {
    293         ParamEvent& event = m_events[i];
    294         ParamEvent* nextEvent = i < n - 1 ? &(m_events[i + 1]) : 0;
    295 
    296         // Wait until we get a more recent event.
    297         if (nextEvent && nextEvent->time() < currentTime)
    298             continue;
    299 
    300         float value1 = event.value();
    301         double time1 = event.time();
    302         float value2 = nextEvent ? nextEvent->value() : value1;
    303         double time2 = nextEvent ? nextEvent->time() : endTime + 1;
    304 
    305         double deltaTime = time2 - time1;
    306         float k = deltaTime > 0 ? 1 / deltaTime : 0;
    307         double sampleFrameTimeIncr = 1 / sampleRate;
    308 
    309         double fillToTime = std::min(endTime, time2);
    310         unsigned fillToFrame = AudioUtilities::timeToSampleFrame(fillToTime - startTime, sampleRate);
    311         fillToFrame = std::min(fillToFrame, numberOfValues);
    312 
    313         ParamEvent::Type nextEventType = nextEvent ? static_cast<ParamEvent::Type>(nextEvent->type()) : ParamEvent::LastType /* unknown */;
    314 
    315         // First handle linear and exponential ramps which require looking ahead to the next event.
    316         if (nextEventType == ParamEvent::LinearRampToValue) {
    317             for (; writeIndex < fillToFrame; ++writeIndex) {
    318                 float x = (currentTime - time1) * k;
    319                 value = (1 - x) * value1 + x * value2;
    320                 values[writeIndex] = value;
    321                 currentTime += sampleFrameTimeIncr;
    322             }
    323         } else if (nextEventType == ParamEvent::ExponentialRampToValue) {
    324             if (value1 <= 0 || value2 <= 0) {
    325                 // Handle negative values error case by propagating previous value.
    326                 for (; writeIndex < fillToFrame; ++writeIndex)
    327                     values[writeIndex] = value;
    328             } else {
    329                 float numSampleFrames = deltaTime * sampleRate;
    330                 // The value goes exponentially from value1 to value2 in a duration of deltaTime seconds (corresponding to numSampleFrames).
    331                 // Compute the per-sample multiplier.
    332                 float multiplier = powf(value2 / value1, 1 / numSampleFrames);
    333 
    334                 // Set the starting value of the exponential ramp. This is the same as multiplier ^
    335                 // AudioUtilities::timeToSampleFrame(currentTime - time1, sampleRate), but is more
    336                 // accurate, especially if multiplier is close to 1.
    337                 value = value1 * powf(value2 / value1,
    338                                       AudioUtilities::timeToSampleFrame(currentTime - time1, sampleRate) / numSampleFrames);
    339 
    340                 for (; writeIndex < fillToFrame; ++writeIndex) {
    341                     values[writeIndex] = value;
    342                     value *= multiplier;
    343                     currentTime += sampleFrameTimeIncr;
    344                 }
    345             }
    346         } else {
    347             // Handle event types not requiring looking ahead to the next event.
    348             switch (event.type()) {
    349             case ParamEvent::SetValue:
    350             case ParamEvent::LinearRampToValue:
    351             case ParamEvent::ExponentialRampToValue:
    352                 {
    353                     currentTime = fillToTime;
    354 
    355                     // Simply stay at a constant value.
    356                     value = event.value();
    357                     for (; writeIndex < fillToFrame; ++writeIndex)
    358                         values[writeIndex] = value;
    359 
    360                     break;
    361                 }
    362 
    363             case ParamEvent::SetTarget:
    364                 {
    365                     currentTime = fillToTime;
    366 
    367                     // Exponential approach to target value with given time constant.
    368                     float target = event.value();
    369                     float timeConstant = event.timeConstant();
    370                     float discreteTimeConstant = static_cast<float>(AudioUtilities::discreteTimeConstantForSampleRate(timeConstant, controlRate));
    371 
    372                     for (; writeIndex < fillToFrame; ++writeIndex) {
    373                         values[writeIndex] = value;
    374                         value += (target - value) * discreteTimeConstant;
    375                     }
    376 
    377                     break;
    378                 }
    379 
    380             case ParamEvent::SetValueCurve:
    381                 {
    382                     Float32Array* curve = event.curve();
    383                     float* curveData = curve ? curve->data() : 0;
    384                     unsigned numberOfCurvePoints = curve ? curve->length() : 0;
    385 
    386                     // Curve events have duration, so don't just use next event time.
    387                     float duration = event.duration();
    388                     float durationFrames = duration * sampleRate;
    389                     float curvePointsPerFrame = static_cast<float>(numberOfCurvePoints) / durationFrames;
    390 
    391                     if (!curve || !curveData || !numberOfCurvePoints || duration <= 0 || sampleRate <= 0) {
    392                         // Error condition - simply propagate previous value.
    393                         currentTime = fillToTime;
    394                         for (; writeIndex < fillToFrame; ++writeIndex)
    395                             values[writeIndex] = value;
    396                         break;
    397                     }
    398 
    399                     // Save old values and recalculate information based on the curve's duration
    400                     // instead of the next event time.
    401                     unsigned nextEventFillToFrame = fillToFrame;
    402                     float nextEventFillToTime = fillToTime;
    403                     fillToTime = std::min(endTime, time1 + duration);
    404                     fillToFrame = AudioUtilities::timeToSampleFrame(fillToTime - startTime, sampleRate);
    405                     fillToFrame = std::min(fillToFrame, numberOfValues);
    406 
    407                     // Index into the curve data using a floating-point value.
    408                     // We're scaling the number of curve points by the duration (see curvePointsPerFrame).
    409                     float curveVirtualIndex = 0;
    410                     if (time1 < currentTime) {
    411                         // Index somewhere in the middle of the curve data.
    412                         // Don't use timeToSampleFrame() since we want the exact floating-point frame.
    413                         float frameOffset = (currentTime - time1) * sampleRate;
    414                         curveVirtualIndex = curvePointsPerFrame * frameOffset;
    415                     }
    416 
    417                     // Render the stretched curve data using nearest neighbor sampling.
    418                     // Oversampled curve data can be provided if smoothness is desired.
    419                     for (; writeIndex < fillToFrame; ++writeIndex) {
    420                         // Ideally we'd use round() from MathExtras, but we're in a tight loop here
    421                         // and we're trading off precision for extra speed.
    422                         unsigned curveIndex = static_cast<unsigned>(0.5 + curveVirtualIndex);
    423 
    424                         curveVirtualIndex += curvePointsPerFrame;
    425 
    426                         // Bounds check.
    427                         if (curveIndex < numberOfCurvePoints)
    428                             value = curveData[curveIndex];
    429 
    430                         values[writeIndex] = value;
    431                     }
    432 
    433                     // If there's any time left after the duration of this event and the start
    434                     // of the next, then just propagate the last value.
    435                     for (; writeIndex < nextEventFillToFrame; ++writeIndex)
    436                         values[writeIndex] = value;
    437 
    438                     // Re-adjust current time
    439                     currentTime = nextEventFillToTime;
    440 
    441                     break;
    442                 }
    443             }
    444         }
    445     }
    446 
    447     // If there's any time left after processing the last event then just propagate the last value
    448     // to the end of the values buffer.
    449     for (; writeIndex < numberOfValues; ++writeIndex)
    450         values[writeIndex] = value;
    451 
    452     return value;
    453 }
    454 
    455 } // namespace blink
    456 
    457 #endif // ENABLE(WEB_AUDIO)
    458