2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #include "modules/webaudio/AudioParamTimeline.h"
32 #include "bindings/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"
43 void AudioParamTimeline::setValueAtTime(float value, double time)
45 insertEvent(ParamEvent(ParamEvent::SetValue, value, time, 0, 0, 0));
48 void AudioParamTimeline::linearRampToValueAtTime(float value, double time)
50 insertEvent(ParamEvent(ParamEvent::LinearRampToValue, value, time, 0, 0, 0));
53 void AudioParamTimeline::exponentialRampToValueAtTime(float value, double time, ExceptionState& exceptionState)
55 ASSERT(isMainThread());
57 exceptionState.throwDOMException(
59 "Target value for exponential ramp must be positive: " + String::number(value));
63 insertEvent(ParamEvent(ParamEvent::ExponentialRampToValue, value, time, 0, 0, 0));
66 void AudioParamTimeline::setTargetAtTime(float target, double time, double timeConstant)
68 insertEvent(ParamEvent(ParamEvent::SetTarget, target, time, timeConstant, 0, 0));
71 void AudioParamTimeline::setValueCurveAtTime(Float32Array* curve, double time, double duration)
73 insertEvent(ParamEvent(ParamEvent::SetValueCurve, 0, time, 0, duration, curve));
76 static bool isValidNumber(float x)
78 return !std::isnan(x) && !std::isinf(x);
81 void AudioParamTimeline::insertEvent(const ParamEvent& event)
83 // Sanity check the event. Be super careful we're not getting infected with NaN or Inf.
84 bool isValid = event.type() < ParamEvent::LastType
85 && isValidNumber(event.value())
86 && isValidNumber(event.time())
87 && isValidNumber(event.timeConstant())
88 && isValidNumber(event.duration())
89 && event.duration() >= 0;
95 MutexLocker locker(m_eventsLock);
98 double insertTime = event.time();
99 for (i = 0; i < m_events.size(); ++i) {
100 // Overwrite same event type and time.
101 if (m_events[i].time() == insertTime && m_events[i].type() == event.type()) {
106 if (m_events[i].time() > insertTime)
110 m_events.insert(i, event);
113 void AudioParamTimeline::cancelScheduledValues(double startTime)
115 MutexLocker locker(m_eventsLock);
117 // Remove all events starting at startTime.
118 for (unsigned i = 0; i < m_events.size(); ++i) {
119 if (m_events[i].time() >= startTime) {
120 m_events.remove(i, m_events.size() - i);
126 float AudioParamTimeline::valueForContextTime(AudioContext* context, float defaultValue, bool& hasValue)
131 MutexTryLocker tryLocker(m_eventsLock);
132 if (!tryLocker.locked() || !context || !m_events.size() || context->currentTime() < m_events[0].time()) {
138 // Ask for just a single value.
140 double sampleRate = context->sampleRate();
141 double startTime = context->currentTime();
142 double endTime = startTime + 1.1 / sampleRate; // time just beyond one sample-frame
143 double controlRate = sampleRate / AudioNode::ProcessingSizeInFrames; // one parameter change per render quantum
144 value = valuesForTimeRange(startTime, endTime, defaultValue, &value, 1, sampleRate, controlRate);
150 float AudioParamTimeline::valuesForTimeRange(
155 unsigned numberOfValues,
159 // We can't contend the lock in the realtime audio thread.
160 MutexTryLocker tryLocker(m_eventsLock);
161 if (!tryLocker.locked()) {
163 for (unsigned i = 0; i < numberOfValues; ++i)
164 values[i] = defaultValue;
169 float value = valuesForTimeRangeImpl(startTime, endTime, defaultValue, values, numberOfValues, sampleRate, controlRate);
174 float AudioParamTimeline::valuesForTimeRangeImpl(
179 unsigned numberOfValues,
187 // Return default value if there are no events matching the desired time range.
188 if (!m_events.size() || endTime <= m_events[0].time()) {
189 for (unsigned i = 0; i < numberOfValues; ++i)
190 values[i] = defaultValue;
194 // Maintain a running time and index for writing the values buffer.
195 double currentTime = startTime;
196 unsigned writeIndex = 0;
198 // If first event is after startTime then fill initial part of values buffer with defaultValue
199 // until we reach the first event time.
200 double firstEventTime = m_events[0].time();
201 if (firstEventTime > startTime) {
202 double fillToTime = min(endTime, firstEventTime);
203 unsigned fillToFrame = AudioUtilities::timeToSampleFrame(fillToTime - startTime, sampleRate);
204 fillToFrame = min(fillToFrame, numberOfValues);
205 for (; writeIndex < fillToFrame; ++writeIndex)
206 values[writeIndex] = defaultValue;
208 currentTime = fillToTime;
211 float value = defaultValue;
213 // Go through each event and render the value buffer where the times overlap,
214 // stopping when we've rendered all the requested values.
215 // FIXME: could try to optimize by avoiding having to iterate starting from the very first event
216 // and keeping track of a "current" event index.
217 int n = m_events.size();
218 for (int i = 0; i < n && writeIndex < numberOfValues; ++i) {
219 ParamEvent& event = m_events[i];
220 ParamEvent* nextEvent = i < n - 1 ? &(m_events[i + 1]) : 0;
222 // Wait until we get a more recent event.
223 if (nextEvent && nextEvent->time() < currentTime)
226 float value1 = event.value();
227 double time1 = event.time();
228 float value2 = nextEvent ? nextEvent->value() : value1;
229 double time2 = nextEvent ? nextEvent->time() : endTime + 1;
231 double deltaTime = time2 - time1;
232 float k = deltaTime > 0 ? 1 / deltaTime : 0;
233 double sampleFrameTimeIncr = 1 / sampleRate;
235 double fillToTime = min(endTime, time2);
236 unsigned fillToFrame = AudioUtilities::timeToSampleFrame(fillToTime - startTime, sampleRate);
237 fillToFrame = min(fillToFrame, numberOfValues);
239 ParamEvent::Type nextEventType = nextEvent ? static_cast<ParamEvent::Type>(nextEvent->type()) : ParamEvent::LastType /* unknown */;
241 // First handle linear and exponential ramps which require looking ahead to the next event.
242 if (nextEventType == ParamEvent::LinearRampToValue) {
243 for (; writeIndex < fillToFrame; ++writeIndex) {
244 float x = (currentTime - time1) * k;
245 value = (1 - x) * value1 + x * value2;
246 values[writeIndex] = value;
247 currentTime += sampleFrameTimeIncr;
249 } else if (nextEventType == ParamEvent::ExponentialRampToValue) {
250 if (value1 <= 0 || value2 <= 0) {
251 // Handle negative values error case by propagating previous value.
252 for (; writeIndex < fillToFrame; ++writeIndex)
253 values[writeIndex] = value;
255 float numSampleFrames = deltaTime * sampleRate;
256 // The value goes exponentially from value1 to value2 in a duration of deltaTime seconds (corresponding to numSampleFrames).
257 // Compute the per-sample multiplier.
258 float multiplier = powf(value2 / value1, 1 / numSampleFrames);
260 // Set the starting value of the exponential ramp. This is the same as multiplier ^
261 // AudioUtilities::timeToSampleFrame(currentTime - time1, sampleRate), but is more
262 // accurate, especially if multiplier is close to 1.
263 value = value1 * powf(value2 / value1,
264 AudioUtilities::timeToSampleFrame(currentTime - time1, sampleRate) / numSampleFrames);
266 for (; writeIndex < fillToFrame; ++writeIndex) {
267 values[writeIndex] = value;
269 currentTime += sampleFrameTimeIncr;
273 // Handle event types not requiring looking ahead to the next event.
274 switch (event.type()) {
275 case ParamEvent::SetValue:
276 case ParamEvent::LinearRampToValue:
277 case ParamEvent::ExponentialRampToValue:
279 currentTime = fillToTime;
281 // Simply stay at a constant value.
282 value = event.value();
283 for (; writeIndex < fillToFrame; ++writeIndex)
284 values[writeIndex] = value;
289 case ParamEvent::SetTarget:
291 currentTime = fillToTime;
293 // Exponential approach to target value with given time constant.
294 float target = event.value();
295 float timeConstant = event.timeConstant();
296 float discreteTimeConstant = static_cast<float>(AudioUtilities::discreteTimeConstantForSampleRate(timeConstant, controlRate));
298 for (; writeIndex < fillToFrame; ++writeIndex) {
299 values[writeIndex] = value;
300 value += (target - value) * discreteTimeConstant;
306 case ParamEvent::SetValueCurve:
308 Float32Array* curve = event.curve();
309 float* curveData = curve ? curve->data() : 0;
310 unsigned numberOfCurvePoints = curve ? curve->length() : 0;
312 // Curve events have duration, so don't just use next event time.
313 float duration = event.duration();
314 float durationFrames = duration * sampleRate;
315 float curvePointsPerFrame = static_cast<float>(numberOfCurvePoints) / durationFrames;
317 if (!curve || !curveData || !numberOfCurvePoints || duration <= 0 || sampleRate <= 0) {
318 // Error condition - simply propagate previous value.
319 currentTime = fillToTime;
320 for (; writeIndex < fillToFrame; ++writeIndex)
321 values[writeIndex] = value;
325 // Save old values and recalculate information based on the curve's duration
326 // instead of the next event time.
327 unsigned nextEventFillToFrame = fillToFrame;
328 float nextEventFillToTime = fillToTime;
329 fillToTime = min(endTime, time1 + duration);
330 fillToFrame = AudioUtilities::timeToSampleFrame(fillToTime - startTime, sampleRate);
331 fillToFrame = min(fillToFrame, numberOfValues);
333 // Index into the curve data using a floating-point value.
334 // We're scaling the number of curve points by the duration (see curvePointsPerFrame).
335 float curveVirtualIndex = 0;
336 if (time1 < currentTime) {
337 // Index somewhere in the middle of the curve data.
338 // Don't use timeToSampleFrame() since we want the exact floating-point frame.
339 float frameOffset = (currentTime - time1) * sampleRate;
340 curveVirtualIndex = curvePointsPerFrame * frameOffset;
343 // Render the stretched curve data using nearest neighbor sampling.
344 // Oversampled curve data can be provided if smoothness is desired.
345 for (; writeIndex < fillToFrame; ++writeIndex) {
346 // Ideally we'd use round() from MathExtras, but we're in a tight loop here
347 // and we're trading off precision for extra speed.
348 unsigned curveIndex = static_cast<unsigned>(0.5 + curveVirtualIndex);
350 curveVirtualIndex += curvePointsPerFrame;
353 if (curveIndex < numberOfCurvePoints)
354 value = curveData[curveIndex];
356 values[writeIndex] = value;
359 // If there's any time left after the duration of this event and the start
360 // of the next, then just propagate the last value.
361 for (; writeIndex < nextEventFillToFrame; ++writeIndex)
362 values[writeIndex] = value;
364 // Re-adjust current time
365 currentTime = nextEventFillToTime;
373 // If there's any time left after processing the last event then just propagate the last value
374 // to the end of the values buffer.
375 for (; writeIndex < numberOfValues; ++writeIndex)
376 values[writeIndex] = value;
381 } // namespace WebCore
383 #endif // ENABLE(WEB_AUDIO)