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 "AudioParamTimeline.h"
31
32#include "AudioUtilities.h"
33#include "FloatConversion.h"
34#include <algorithm>
35#include <wtf/MathExtras.h>
36
37using namespace std;
38
39namespace WebCore {
40
41void AudioParamTimeline::setValueAtTime(float value, float time)
42{
43    insertEvent(ParamEvent(ParamEvent::SetValue, value, time, 0, 0, 0));
44}
45
46void AudioParamTimeline::linearRampToValueAtTime(float value, float time)
47{
48    insertEvent(ParamEvent(ParamEvent::LinearRampToValue, value, time, 0, 0, 0));
49}
50
51void AudioParamTimeline::exponentialRampToValueAtTime(float value, float time)
52{
53    insertEvent(ParamEvent(ParamEvent::ExponentialRampToValue, value, time, 0, 0, 0));
54}
55
56void AudioParamTimeline::setTargetAtTime(float target, float time, float timeConstant)
57{
58    insertEvent(ParamEvent(ParamEvent::SetTarget, target, time, timeConstant, 0, 0));
59}
60
61void AudioParamTimeline::setValueCurveAtTime(Float32Array* curve, float time, float duration)
62{
63    insertEvent(ParamEvent(ParamEvent::SetValueCurve, 0, time, 0, duration, curve));
64}
65
66static bool isValidNumber(float x)
67{
68    return !std::isnan(x) && !std::isinf(x);
69}
70
71void 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    float 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
103void AudioParamTimeline::cancelScheduledValues(float 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
116float 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
140float 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
164float 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