/**************************************************************************
Copyright (C) 2019 Arnaud Champenois arthelion@free.fr
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/
#pragma once
#include <vector>
#define _USE_MATH_DEFINES
#include <math.h>
#include <mutex>
#include "Utils.h"
//===============================================================================================
/*!
TableGenerator
Generator based on a sample table of size sampleRate (which means that it is sampled at 1 Hz period)
*/
class TableGenerator {
public:
TableGenerator(const std::vector<double>&table) :
mTable(table) {
}
static inline double interpolateBilinear(const double*tableData, size_t tableSize, double rank) {
int rank1 = int(rank);
int rank2 = rank1 + 1;
if (rank2 == int(tableSize))
rank2 = 0;
double pond2 = double(rank) - double(rank1);
double pond1 = 1.0f - pond2;
return (tableData[rank1] * pond1 + tableData[rank2] * pond2);
}
static inline double next(double freq, const double*tableData, size_t tableSize, double&phase) {
double value = interpolateBilinear(tableData, tableSize, phase);
phase += freq;
while (phase >= double(tableSize))
phase -= double(tableSize);
while (phase < 0)
phase += double(tableSize);
return value;
}
inline double next(double freq) {
return next(freq, mTableData, mTableSize, phase);
}
inline void move(double freq) {
phase += freq;
while (phase >= double(mTableSize))
phase -= double(mTableSize);
}
double getSamplingFreq() const { return double(mTableSize); }
virtual void init(double samplingFreq) = 0;
inline double getPhase() const { return phase / double(mTableSize); }
inline double getRawPhase() const { return phase; }
inline void setPhase(double phaseIn) {
if (phaseIn >= 1.0)
phaseIn -= 1.0;
phase = phaseIn * double(mTableSize);
}
protected:
size_t mTableSize;
double* mTableData;
private:
const std::vector<double>&mTable;
double phase = 0.0f;
};
/*!
* Delay line is a custom table generator that implements
* a fractional delay expressed in seconds.
*/
class DelayLine : private TableGenerator
{
public:
DelayLine(double maxDelay) : TableGenerator(mDelayTable),
mMaxDelay(maxDelay) {}
virtual void init(double samplingFreq);
inline void setMaxDelay(double maxDelay) {
mMaxDelay = maxDelay;
init(mSamplingFreq);
}
/*!
* Pushes a new value in the delay line
*
* \param value The value to push
*/
inline void push(double value) {
mTableData[mCurIndex] = value;
++mCurIndex;
if (mCurIndex == mDelayTable.size())
mCurIndex = 0;
}
/*!
* Gets a new value, with a step (which can be
* modulated by a LFO typically for a chorus effect).
* The step can be non-integer. Beware that the mean step
* must be 1.0 to ensure that reading goes at the same pace
* than writing (else there will be buffer overflow).
* Max delay setting should take care of the possible variations and
* be setted at least to <max step>*<actual max delay> to prevent overflow.
*
* \param step step to go forward (or 1.0f by default)
* \return the delayed value
*/
inline double next(double step = 1.0f) {
return TableGenerator::next(step);
}
inline void setDelay(double delayInSec) {
// If we overflow the max, reinit with a security margin...
if (delayInSec > mMaxDelay)
setMaxDelay(delayInSec*2.0f);
if (mDelayTable.size() < 2) {
return;
}
double phaseIn = (double(mCurIndex)-delayInSec * mSamplingFreq) / double(mDelayTable.size() - 1);
while (phaseIn < 0.0f)
phaseIn += 1.0f;
setPhase(phaseIn);
}
inline void reinit() {
std::fill(mDelayTable.begin(), mDelayTable.end(), 0.0);
}
private:
std::vector<double> mDelayTable;
size_t mCurIndex = 0;
double mMaxDelay;
double mSamplingFreq = 44100.0f;
};
//===============================================================================================
/*!
* MultiTableGenerator
* Generator based on several tables sorted in decreasing Nyquist frequency (with more and more harmonics)
* so to prevent aliasing while keeping as much as harmonics as possible.
*/
class MultiTableGenerator {
public:
class TableDescriptor {
public:
std::vector<double> &getTable() { return mTable; }
TableDescriptor(double maxFreq) {
mMaxFreq = maxFreq;
}
TableDescriptor(const TableDescriptor&in) {
mTable = in.mTable;
mTableData = mTable.data();
mTableSize = mTable.size();
mMaxFreq = in.mMaxFreq;
}
TableDescriptor(TableDescriptor&&in) :
mTable(std::move(in.mTable))
{
mTableData = mTable.data();
mTableSize = mTable.size();
mMaxFreq = in.mMaxFreq;
}
TableDescriptor& operator=(const TableDescriptor&in) {
mTable = in.mTable;
mTableData = mTable.data();
mTableSize = mTable.size();
mMaxFreq = in.mMaxFreq;
return *this;
}
inline const double*getData() const {
return mTableData;
}
inline size_t getSize() const { return mTableSize; }
/*!
* Max frequency threshold to prevent aliasing
*
* \return max possible frequency
*/
inline double getMaxFreq() const { return mMaxFreq; }
private:
std::vector<double> mTable;
const double* mTableData;
size_t mTableSize;
double mMaxFreq;
};
MultiTableGenerator(std::vector<TableDescriptor>& tableDescriptors) :
mTableDescriptors(tableDescriptors) {
}
virtual void init(double samplingFreq) = 0;
/*!
* Gets the next sample for a given frequency, choosing the suited table.
*
* \param freq The frequency
* \return The next sample.
*/
inline double next(double freq) {
for (TableDescriptor&desc : mTableDescriptors) {
// We found the best
if (freq < desc.getMaxFreq()) {
return TableGenerator::next(freq, desc.getData(),desc.getSize(), phase);
}
}
// We take the last if no one fits
return TableGenerator::next(freq, mTableDescriptors.back().getData(),
mTableSize, phase);
}
double getSamplingFreq() const { return double(mTableSize); }
inline double getPhase() const { return phase / mPhaseMax; }
inline void setPhase(double phaseIn) {
if (phaseIn > 1.0)
phaseIn -= 1.0;
phase = phaseIn * mPhaseMax;
}
inline void move(double freq) {
phase += freq;
if (phase > mPhaseMax)
phase -= mPhaseMax;
}
protected:
int mTableSize = 0;
double mPhaseMax = 0.0f;
private:
std::vector<TableDescriptor>& mTableDescriptors;
double phase = 0;
};
//===============================================================================================
/*!
* Fine SawTooth generator
*/
class SawToothGenerator : public MultiTableGenerator {
public:
SawToothGenerator() : MultiTableGenerator(sSawTables) {}
void init(double samplingFreq) override;
static void initSawTable(std::vector<double>& table, double samplingFreq, double harmonics);
private:
static std::vector<TableDescriptor> sSawTables;
};
//===============================================================================================
/**
* Fine reverted SawTooth generator
*/
class RevertSawToothGenerator : public MultiTableGenerator {
public:
RevertSawToothGenerator() : MultiTableGenerator(sRSawTables) {}
void init(double samplingFreq) override;
static void initRSawTable(std::vector<double>& table, double samplingFreq, double harmonics, double deltaPhase = 0);
private:
static std::vector<TableDescriptor> sRSawTables;
};
//===============================================================================================
/**
FinePWMGenerator
*/
class PWMGenerator {
public:
PWMGenerator() {}
void init(double samplingFreq);
inline double next(double frequency, double pwm) {
mRSawGenerator.setPhase(mSawGenerator.getPhase() + pwm);
return double(mSawGenerator.next(frequency) + mRSawGenerator.next(frequency)+0.5);
}
inline void move(double freq) {
mSawGenerator.move(freq);
mRSawGenerator.move(freq);
}
inline void setPhase(double phase) {
mSawGenerator.setPhase(phase);
}
private:
SawToothGenerator mSawGenerator;
RevertSawToothGenerator mRSawGenerator;
};
//===============================================================================================
/**
FineSquareGenerator
*/
class SquareGenerator : public MultiTableGenerator {
public:
SquareGenerator() : MultiTableGenerator(sSquareTables) {}
void init(double samplingFreq) override;
static void initSquareTable(std::vector<double>& table, double samplingFreq, double harmonics);
private:
static std::vector<TableDescriptor> sSquareTables;
};
//===============================================================================================
/**
SineGenerator
*/
class SineGenerator : public TableGenerator {
public:
SineGenerator() : TableGenerator(sSineTable) {}
void init(double samplingFreq) override;
private:
static std::vector<double> sSineTable;
};
//===============================================================================================
/**
HarmonicsGenerator
*/
class OddHarmonicsGenerator : public MultiTableGenerator {
public:
OddHarmonicsGenerator() : MultiTableGenerator(sHarmonicsTables) {}
void init(double samplingFreq) override;
static void initHarmonicsTable(std::vector<double>& table, double samplingFreq, double harmonicsIn);
private:
static std::vector<TableDescriptor> sHarmonicsTables;
};
//===============================================================================================
/**
HarmonicsGenerator
*/
class EvenHarmonicsGenerator : public MultiTableGenerator {
public:
EvenHarmonicsGenerator() : MultiTableGenerator(sHarmonicsTables) {}
void init(double samplingFreq) override;
static void initHarmonicsTable(std::vector<double>& table, double samplingFreq, double harmonicsIn);
private:
static std::vector<TableDescriptor> sHarmonicsTables;
};
/*!
* White noise generator
*
*/
class NoiseGenerator {
public:
NoiseGenerator() {}
void init(double samplingFreq);
inline double next() {
double value = mTableData[phase];
phase++;
if (phase >= mTableSize)
phase = 0;
return value;
}
private:
int phase = 0;
static std::vector<double> sNoiseTable;
int mTableSize;
const double* mTableData;
};
/*!
* Aliased triangle generator of amplitude 2 between -1 and 1. Useful for LFO.
*/
class TriangleAliased {
public:
TriangleAliased() { }
inline void init(double samplingFreq) {
mSamplingFreq = samplingFreq;
}
inline void setPhase(double phaseIn) {
phase = phaseIn * mSamplingFreq;
}
inline double next(double freq) {
if (phase < 0.5*mSamplingFreq)
currentValue = phase * 4.0 / mSamplingFreq - 1.0;
else
currentValue = 1.0 - (phase - 0.5*mSamplingFreq) * 4.0 / mSamplingFreq;
move(freq);
return double(currentValue);
}
inline void move(double freq) {
phase += freq;
if (phase >= mSamplingFreq)
phase -= mSamplingFreq;
}
inline void move(double freq, double samples) {
phase += freq * samples;
if (phase >= mSamplingFreq)
phase = fmod(phase,mSamplingFreq);
}
inline void resetPhase() { phase = 0.0; }
inline double getSamplingFreq() const { return mSamplingFreq; }
inline double current() { return currentValue; }
private:
double phase = 0.0;
double mSamplingFreq = 44100.0;
double currentValue = 0.0;
};
#ifdef CAN_USE_ALIASED_WG
/**
SawtoothGeneratorAliased
*/
class SawtoothGeneratorAliased {
public:
SawtoothGeneratorAliased() { }
void init(double samplingFreq) {
mSamplingFreq = samplingFreq;
}
double next(double freq) {
double slope = 2.0*freq;
double modulo = 1.0 / freq;
double value = double(phase*slope - 1.0);
phase += 1.0 / mSamplingFreq;
if (phase > modulo)
phase -= modulo;
return value;
}
private:
double phase = 0;
double mSamplingFreq = 44100.0;
};
/**
SquareGeneratorAliased
*/
class SquareGeneratorAliased {
public:
SquareGeneratorAliased() { }
void init(double samplingFreq) {
mSamplingFreq = samplingFreq;
}
double next(double freq) {
double modulo = 1.0 / freq;
double mid = modulo * 0.5;
double value = (phase < mid) ? -1.0f : 1.0f;
phase += 1.0 / mSamplingFreq;
if (phase > modulo)
phase -= modulo;
return value;
}
private:
double phase = 0;
double mSamplingFreq = 44100.0;
};
#endif