This source file includes following definitions.
- setValueAtTime
- linearRampToValueAtTime
- exponentialRampToValueAtTime
- setTargetAtTime
- setValueCurveAtTime
- isValidNumber
- insertEvent
- cancelScheduledValues
- valueForContextTime
- valuesForTimeRange
- valuesForTimeRangeImpl
#include "config.h"
#if ENABLE(WEB_AUDIO)
#include "modules/webaudio/AudioParamTimeline.h"
#include "bindings/v8/ExceptionState.h"
#include "core/dom/ExceptionCode.h"
#include "platform/audio/AudioUtilities.h"
#include "platform/FloatConversion.h"
#include "wtf/MathExtras.h"
#include <algorithm>
using namespace std;
namespace WebCore {
void AudioParamTimeline::setValueAtTime(float value, double time)
{
insertEvent(ParamEvent(ParamEvent::SetValue, value, time, 0, 0, nullptr));
}
void AudioParamTimeline::linearRampToValueAtTime(float value, double time)
{
insertEvent(ParamEvent(ParamEvent::LinearRampToValue, value, time, 0, 0, nullptr));
}
void AudioParamTimeline::exponentialRampToValueAtTime(float value, double time, ExceptionState& exceptionState)
{
ASSERT(isMainThread());
if (value <= 0) {
exceptionState.throwDOMException(
InvalidStateError,
"Target value for exponential ramp must be positive: " + String::number(value));
return;
}
insertEvent(ParamEvent(ParamEvent::ExponentialRampToValue, value, time, 0, 0, nullptr));
}
void AudioParamTimeline::setTargetAtTime(float target, double time, double timeConstant)
{
insertEvent(ParamEvent(ParamEvent::SetTarget, target, time, timeConstant, 0, nullptr));
}
void AudioParamTimeline::setValueCurveAtTime(Float32Array* curve, double time, double duration)
{
insertEvent(ParamEvent(ParamEvent::SetValueCurve, 0, time, 0, duration, curve));
}
static bool isValidNumber(float x)
{
return !std::isnan(x) && !std::isinf(x);
}
void AudioParamTimeline::insertEvent(const ParamEvent& event)
{
bool isValid = event.type() < ParamEvent::LastType
&& isValidNumber(event.value())
&& isValidNumber(event.time())
&& isValidNumber(event.timeConstant())
&& isValidNumber(event.duration())
&& event.duration() >= 0;
ASSERT(isValid);
if (!isValid)
return;
MutexLocker locker(m_eventsLock);
unsigned i = 0;
double insertTime = event.time();
for (i = 0; i < m_events.size(); ++i) {
if (m_events[i].time() == insertTime && m_events[i].type() == event.type()) {
m_events[i] = event;
return;
}
if (m_events[i].time() > insertTime)
break;
}
m_events.insert(i, event);
}
void AudioParamTimeline::cancelScheduledValues(double startTime)
{
MutexLocker locker(m_eventsLock);
for (unsigned i = 0; i < m_events.size(); ++i) {
if (m_events[i].time() >= startTime) {
m_events.remove(i, m_events.size() - i);
break;
}
}
}
float AudioParamTimeline::valueForContextTime(AudioContext* context, float defaultValue, bool& hasValue)
{
ASSERT(context);
{
MutexTryLocker tryLocker(m_eventsLock);
if (!tryLocker.locked() || !context || !m_events.size() || context->currentTime() < m_events[0].time()) {
hasValue = false;
return defaultValue;
}
}
float value;
double sampleRate = context->sampleRate();
double startTime = context->currentTime();
double endTime = startTime + 1.1 / sampleRate;
double controlRate = sampleRate / AudioNode::ProcessingSizeInFrames;
value = valuesForTimeRange(startTime, endTime, defaultValue, &value, 1, sampleRate, controlRate);
hasValue = true;
return value;
}
float AudioParamTimeline::valuesForTimeRange(
double startTime,
double endTime,
float defaultValue,
float* values,
unsigned numberOfValues,
double sampleRate,
double controlRate)
{
MutexTryLocker tryLocker(m_eventsLock);
if (!tryLocker.locked()) {
if (values) {
for (unsigned i = 0; i < numberOfValues; ++i)
values[i] = defaultValue;
}
return defaultValue;
}
float value = valuesForTimeRangeImpl(startTime, endTime, defaultValue, values, numberOfValues, sampleRate, controlRate);
return value;
}
float AudioParamTimeline::valuesForTimeRangeImpl(
double startTime,
double endTime,
float defaultValue,
float* values,
unsigned numberOfValues,
double sampleRate,
double controlRate)
{
ASSERT(values);
if (!values)
return defaultValue;
if (!m_events.size() || endTime <= m_events[0].time()) {
for (unsigned i = 0; i < numberOfValues; ++i)
values[i] = defaultValue;
return defaultValue;
}
double currentTime = startTime;
unsigned writeIndex = 0;
double firstEventTime = m_events[0].time();
if (firstEventTime > startTime) {
double fillToTime = min(endTime, firstEventTime);
unsigned fillToFrame = AudioUtilities::timeToSampleFrame(fillToTime - startTime, sampleRate);
fillToFrame = min(fillToFrame, numberOfValues);
for (; writeIndex < fillToFrame; ++writeIndex)
values[writeIndex] = defaultValue;
currentTime = fillToTime;
}
float value = defaultValue;
int n = m_events.size();
for (int i = 0; i < n && writeIndex < numberOfValues; ++i) {
ParamEvent& event = m_events[i];
ParamEvent* nextEvent = i < n - 1 ? &(m_events[i + 1]) : 0;
if (nextEvent && nextEvent->time() < currentTime)
continue;
float value1 = event.value();
double time1 = event.time();
float value2 = nextEvent ? nextEvent->value() : value1;
double time2 = nextEvent ? nextEvent->time() : endTime + 1;
double deltaTime = time2 - time1;
float k = deltaTime > 0 ? 1 / deltaTime : 0;
double sampleFrameTimeIncr = 1 / sampleRate;
double fillToTime = min(endTime, time2);
unsigned fillToFrame = AudioUtilities::timeToSampleFrame(fillToTime - startTime, sampleRate);
fillToFrame = min(fillToFrame, numberOfValues);
ParamEvent::Type nextEventType = nextEvent ? static_cast<ParamEvent::Type>(nextEvent->type()) : ParamEvent::LastType ;
if (nextEventType == ParamEvent::LinearRampToValue) {
for (; writeIndex < fillToFrame; ++writeIndex) {
float x = (currentTime - time1) * k;
value = (1 - x) * value1 + x * value2;
values[writeIndex] = value;
currentTime += sampleFrameTimeIncr;
}
} else if (nextEventType == ParamEvent::ExponentialRampToValue) {
if (value1 <= 0 || value2 <= 0) {
for (; writeIndex < fillToFrame; ++writeIndex)
values[writeIndex] = value;
} else {
float numSampleFrames = deltaTime * sampleRate;
float multiplier = powf(value2 / value1, 1 / numSampleFrames);
value = value1 * powf(value2 / value1,
AudioUtilities::timeToSampleFrame(currentTime - time1, sampleRate) / numSampleFrames);
for (; writeIndex < fillToFrame; ++writeIndex) {
values[writeIndex] = value;
value *= multiplier;
currentTime += sampleFrameTimeIncr;
}
}
} else {
switch (event.type()) {
case ParamEvent::SetValue:
case ParamEvent::LinearRampToValue:
case ParamEvent::ExponentialRampToValue:
{
currentTime = fillToTime;
value = event.value();
for (; writeIndex < fillToFrame; ++writeIndex)
values[writeIndex] = value;
break;
}
case ParamEvent::SetTarget:
{
currentTime = fillToTime;
float target = event.value();
float timeConstant = event.timeConstant();
float discreteTimeConstant = static_cast<float>(AudioUtilities::discreteTimeConstantForSampleRate(timeConstant, controlRate));
for (; writeIndex < fillToFrame; ++writeIndex) {
values[writeIndex] = value;
value += (target - value) * discreteTimeConstant;
}
break;
}
case ParamEvent::SetValueCurve:
{
Float32Array* curve = event.curve();
float* curveData = curve ? curve->data() : 0;
unsigned numberOfCurvePoints = curve ? curve->length() : 0;
float duration = event.duration();
float durationFrames = duration * sampleRate;
float curvePointsPerFrame = static_cast<float>(numberOfCurvePoints) / durationFrames;
if (!curve || !curveData || !numberOfCurvePoints || duration <= 0 || sampleRate <= 0) {
currentTime = fillToTime;
for (; writeIndex < fillToFrame; ++writeIndex)
values[writeIndex] = value;
break;
}
unsigned nextEventFillToFrame = fillToFrame;
float nextEventFillToTime = fillToTime;
fillToTime = min(endTime, time1 + duration);
fillToFrame = AudioUtilities::timeToSampleFrame(fillToTime - startTime, sampleRate);
fillToFrame = min(fillToFrame, numberOfValues);
float curveVirtualIndex = 0;
if (time1 < currentTime) {
float frameOffset = (currentTime - time1) * sampleRate;
curveVirtualIndex = curvePointsPerFrame * frameOffset;
}
for (; writeIndex < fillToFrame; ++writeIndex) {
unsigned curveIndex = static_cast<unsigned>(0.5 + curveVirtualIndex);
curveVirtualIndex += curvePointsPerFrame;
if (curveIndex < numberOfCurvePoints)
value = curveData[curveIndex];
values[writeIndex] = value;
}
for (; writeIndex < nextEventFillToFrame; ++writeIndex)
values[writeIndex] = value;
currentTime = nextEventFillToTime;
break;
}
}
}
}
for (; writeIndex < numberOfValues; ++writeIndex)
values[writeIndex] = value;
return value;
}
}
#endif