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