#include "config.h"
#if ENABLE(WEB_AUDIO)
#include "Oscillator.h"
#include "AudioContext.h"
#include "AudioNodeOutput.h"
#include "AudioUtilities.h"
#include "VectorMath.h"
#include "WaveTable.h"
#include <algorithm>
#include <wtf/MathExtras.h>
using namespace std;
namespace WebCore {
using namespace VectorMath;
PassRefPtr<Oscillator> Oscillator::create(AudioContext* context, float sampleRate)
{
return adoptRef(new Oscillator(context, sampleRate));
}
Oscillator::Oscillator(AudioContext* context, float sampleRate)
: AudioScheduledSourceNode(context, sampleRate)
, m_type(SINE)
, m_firstRender(true)
, m_virtualReadIndex(0)
, m_phaseIncrements(AudioNode::ProcessingSizeInFrames)
, m_detuneValues(AudioNode::ProcessingSizeInFrames)
{
setNodeType(NodeTypeOscillator);
m_frequency = AudioParam::create("frequency", 440, 0, 100000);
m_detune = AudioParam::create("detune", 0, -4800, 4800);
m_frequency->setContext(context);
m_detune->setContext(context);
setType(m_type);
addOutput(adoptPtr(new AudioNodeOutput(this, 1)));
initialize();
}
Oscillator::~Oscillator()
{
uninitialize();
}
void Oscillator::setType(unsigned short type)
{
RefPtr<WaveTable> waveTable;
float sampleRate = this->sampleRate();
switch (type) {
case SINE:
waveTable = WaveTable::createSine(sampleRate);
break;
case SQUARE:
waveTable = WaveTable::createSquare(sampleRate);
break;
case SAWTOOTH:
waveTable = WaveTable::createSawtooth(sampleRate);
break;
case TRIANGLE:
waveTable = WaveTable::createTriangle(sampleRate);
break;
case CUSTOM:
return;
break;
}
m_type = type;
setWaveTable(waveTable.get());
}
bool Oscillator::calculateSampleAccuratePhaseIncrements(size_t framesToProcess)
{
bool isGood = framesToProcess <= m_phaseIncrements.size() && framesToProcess <= m_detuneValues.size();
ASSERT(isGood);
if (!isGood)
return false;
if (m_firstRender) {
m_firstRender = false;
m_frequency->resetSmoothedValue();
m_detune->resetSmoothedValue();
}
bool hasSampleAccurateValues = false;
bool hasFrequencyChanges = false;
float* phaseIncrements = m_phaseIncrements.data();
float finalScale = m_waveTable->rateScale();
if (m_frequency->hasSampleAccurateValues()) {
hasSampleAccurateValues = true;
hasFrequencyChanges = true;
m_frequency->calculateSampleAccurateValues(phaseIncrements, framesToProcess);
} else {
m_frequency->smooth();
float frequency = m_frequency->smoothedValue();
finalScale *= frequency;
}
if (m_detune->hasSampleAccurateValues()) {
hasSampleAccurateValues = true;
float* detuneValues = hasFrequencyChanges ? m_detuneValues.data() : phaseIncrements;
m_detune->calculateSampleAccurateValues(detuneValues, framesToProcess);
float k = 1.0 / 1200;
vsmul(detuneValues, 1, &k, detuneValues, 1, framesToProcess);
for (unsigned i = 0; i < framesToProcess; ++i)
detuneValues[i] = powf(2, detuneValues[i]);
if (hasFrequencyChanges) {
vmul(detuneValues, 1, phaseIncrements, 1, phaseIncrements, 1, framesToProcess);
}
} else {
m_detune->smooth();
float detune = m_detune->smoothedValue();
float detuneScale = powf(2, detune / 1200);
finalScale *= detuneScale;
}
if (hasSampleAccurateValues) {
vsmul(phaseIncrements, 1, &finalScale, phaseIncrements, 1, framesToProcess);
}
return hasSampleAccurateValues;
}
void Oscillator::process(size_t framesToProcess)
{
AudioBus* outputBus = output(0)->bus();
if (!isInitialized() || !outputBus->numberOfChannels()) {
outputBus->zero();
return;
}
ASSERT(framesToProcess <= m_phaseIncrements.size());
if (framesToProcess > m_phaseIncrements.size())
return;
MutexTryLocker tryLocker(m_processLock);
if (!tryLocker.locked()) {
outputBus->zero();
return;
}
if (!m_waveTable.get()) {
outputBus->zero();
return;
}
size_t quantumFrameOffset;
size_t nonSilentFramesToProcess;
updateSchedulingInfo(framesToProcess,
outputBus,
quantumFrameOffset,
nonSilentFramesToProcess);
if (!nonSilentFramesToProcess) {
outputBus->zero();
return;
}
unsigned waveTableSize = m_waveTable->waveTableSize();
double invWaveTableSize = 1.0 / waveTableSize;
float* destP = outputBus->channel(0)->mutableData();
ASSERT(quantumFrameOffset <= framesToProcess);
double virtualReadIndex = m_virtualReadIndex;
float rateScale = m_waveTable->rateScale();
float invRateScale = 1 / rateScale;
bool hasSampleAccurateValues = calculateSampleAccuratePhaseIncrements(framesToProcess);
float frequency = 0;
float* higherWaveData = 0;
float* lowerWaveData = 0;
float tableInterpolationFactor;
if (!hasSampleAccurateValues) {
frequency = m_frequency->smoothedValue();
float detune = m_detune->smoothedValue();
float detuneScale = powf(2, detune / 1200);
frequency *= detuneScale;
m_waveTable->waveDataForFundamentalFrequency(frequency, lowerWaveData, higherWaveData, tableInterpolationFactor);
}
float incr = frequency * rateScale;
float* phaseIncrements = m_phaseIncrements.data();
unsigned readIndexMask = waveTableSize - 1;
destP += quantumFrameOffset;
int n = nonSilentFramesToProcess;
while (n--) {
unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
unsigned readIndex2 = readIndex + 1;
readIndex = readIndex & readIndexMask;
readIndex2 = readIndex2 & readIndexMask;
if (hasSampleAccurateValues) {
incr = *phaseIncrements++;
frequency = invRateScale * incr;
m_waveTable->waveDataForFundamentalFrequency(frequency, lowerWaveData, higherWaveData, tableInterpolationFactor);
}
float sample1Lower = lowerWaveData[readIndex];
float sample2Lower = lowerWaveData[readIndex2];
float sample1Higher = higherWaveData[readIndex];
float sample2Higher = higherWaveData[readIndex2];
float interpolationFactor = static_cast<float>(virtualReadIndex) - readIndex;
float sampleHigher = (1 - interpolationFactor) * sample1Higher + interpolationFactor * sample2Higher;
float sampleLower = (1 - interpolationFactor) * sample1Lower + interpolationFactor * sample2Lower;
float sample = (1 - tableInterpolationFactor) * sampleHigher + tableInterpolationFactor * sampleLower;
*destP++ = sample;
virtualReadIndex += incr;
virtualReadIndex -= floor(virtualReadIndex * invWaveTableSize) * waveTableSize;
}
m_virtualReadIndex = virtualReadIndex;
outputBus->clearSilentFlag();
}
void Oscillator::reset()
{
m_virtualReadIndex = 0;
}
void Oscillator::setWaveTable(WaveTable* waveTable)
{
ASSERT(isMainThread());
MutexLocker processLocker(m_processLock);
m_waveTable = waveTable;
m_type = CUSTOM;
}
bool Oscillator::propagatesSilence() const
{
return !isPlayingOrScheduled() || hasFinished() || !m_waveTable.get();
}
}
#endif // ENABLE(WEB_AUDIO)