Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / modules / webaudio / AudioParamTimeline.cpp
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         InvalidAccessError,
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         InvalidAccessError,
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         InvalidAccessError,
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         InvalidAccessError,
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)