Added synthesis.
This commit is contained in:
parent
42337e08cd
commit
9f50e9d40e
|
@ -2,22 +2,15 @@
|
|||
#define DISTRHO_PLUGIN_INFO_H_INCLUDED
|
||||
|
||||
#define DISTRHO_PLUGIN_BRAND "yaw-audio"
|
||||
#define DISTRHO_PLUGIN_NAME "yaw-tab"
|
||||
#define DISTRHO_PLUGIN_URI "https://yaw.man/plugins/yaw-tab"
|
||||
#define DISTRHO_PLUGIN_NAME "yaw-shepard"
|
||||
#define DISTRHO_PLUGIN_URI "https://yaw.man/plugins/yaw-shepard"
|
||||
|
||||
#define DISTRHO_PLUGIN_HAS_UI 1
|
||||
#define DISTRHO_PLUGIN_IS_RT_SAFE 1
|
||||
#define DISTRHO_PLUGIN_NUM_INPUTS 0
|
||||
#define DISTRHO_PLUGIN_NUM_OUTPUTS 2
|
||||
#define DISTRHO_PLUGIN_NUM_OUTPUTS 1
|
||||
#define DISTRHO_UI_USE_NANOVG 1
|
||||
|
||||
// only checking if supported, not actually used
|
||||
#define DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST 1
|
||||
|
||||
#ifdef __MOD_DEVICES__
|
||||
#define DISTRHO_PLUGIN_USES_MODGUI 1
|
||||
#endif
|
||||
|
||||
enum Parameters {
|
||||
ktpax = 0,
|
||||
ktpay,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "DistrhoPlugin.hpp"
|
||||
#include "synth.h"
|
||||
|
||||
START_NAMESPACE_DISTRHO
|
||||
|
||||
|
@ -7,19 +8,18 @@ class TabPlugin : public Plugin
|
|||
public:
|
||||
TabPlugin()
|
||||
: Plugin(kParameterCount, 0, 0),
|
||||
sampleRate(getSampleRate())
|
||||
sampleRate(getSampleRate()),
|
||||
synth(sampleRate),
|
||||
fParameters { 0 }
|
||||
{
|
||||
// clear all parameters
|
||||
std::memset(fParameters, 0, sizeof(float) * kParameterCount);
|
||||
|
||||
}
|
||||
|
||||
protected:
|
||||
const char* getLabel() const override { return "yaw-tab-shepard"; }
|
||||
const char* getDescription() const override { return "Drawing tablet Shepard buzz tone"; }
|
||||
const char* getLabel() const override { return "yaw-shepard"; }
|
||||
const char* getDescription() const override { return "Generalized Shepard tone, tablet UI"; }
|
||||
const char* getMaker() const override { return "yaw-audio"; }
|
||||
const char* getHomePage() const override { return "https://yaw.man/plugins/yaw-tab"; }
|
||||
const char* getLicense() const override { return "ISC"; }
|
||||
const char* getLicense() const override { return "Fuck You Pay Me"; }
|
||||
uint32_t getVersion() const override { return d_version(1, 0, 0); }
|
||||
int64_t getUniqueId() const override { return d_cconst('y', 's', 'p', 'd'); }
|
||||
|
||||
|
@ -69,6 +69,7 @@ protected:
|
|||
|
||||
void sampleRateChanged(double newRate) override
|
||||
{
|
||||
synth.setSampleRate(sampleRate, newRate);
|
||||
sampleRate = newRate;
|
||||
}
|
||||
|
||||
|
@ -82,14 +83,15 @@ protected:
|
|||
fParameters[idx] = val;
|
||||
switch (idx) {
|
||||
case ktpax:
|
||||
period = 0.02f * val * static_cast<float>(sampleRate);
|
||||
synth.setFrequencyShift(val);
|
||||
break;
|
||||
case ktpay:
|
||||
synth.setTargetRatio(val);
|
||||
break;
|
||||
case ktpaz:
|
||||
break;
|
||||
case ktpap:
|
||||
volume = val;
|
||||
synth.setVolume(val);
|
||||
break;
|
||||
case kParameterButtonA:
|
||||
break;
|
||||
|
@ -100,30 +102,17 @@ protected:
|
|||
|
||||
void run(const float** inputs, float** outputs, uint32_t frames) override
|
||||
{
|
||||
|
||||
for (uint32_t i = 0; i < frames; ++i) {
|
||||
counter++;
|
||||
if (counter > period) {
|
||||
outputs[0][i] = outputs[1][i] = volume;
|
||||
counter = 0;
|
||||
}
|
||||
else {
|
||||
outputs[0][i] = outputs[1][i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
synth.process(*outputs, frames);
|
||||
parity = !parity;
|
||||
fParameters[kParameterTime] += parity ? 1 : -1;
|
||||
}
|
||||
|
||||
private:
|
||||
float fParameters[kParameterCount];
|
||||
float period = 0.f;
|
||||
float counter = 0.f;
|
||||
float volume = 0.f;
|
||||
double sampleRate;
|
||||
bool parity = false;
|
||||
|
||||
Synth synth;
|
||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TabPlugin)
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
#include "synth.h"
|
||||
|
||||
Synth::Synth(double sampleRate){
|
||||
setSampleRate(48000.0, sampleRate);
|
||||
}
|
||||
|
||||
#define frac(x) ((x) - ((long)x))
|
||||
constexpr double MIN_VOLUME = 0.00001;
|
||||
|
||||
//Volume of voice as a function of sample rate independent frequency.
|
||||
static constexpr double getAmplitude( double hz )
|
||||
{
|
||||
if( hz < 20.0 ) return 0.0;
|
||||
if( hz < 440.0 ) return hz / 440.0;
|
||||
if( hz < 16000.0 ) return (16000.0 - hz) / 15560.0;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
//Sanity checks: voices should become silent outside audible frequencies.
|
||||
//Voice index code will loop infinitely if these fail.
|
||||
static_assert( MIN_VOLUME > getAmplitude(10.0) );
|
||||
static_assert( MIN_VOLUME > getAmplitude(20000.0));
|
||||
|
||||
void Synth::process(float* output, const uint32_t frames)
|
||||
{
|
||||
double hz;
|
||||
|
||||
//Render.
|
||||
for(uint32_t i = 0; i < frames; i++){
|
||||
|
||||
//Set pitch.
|
||||
hz = hzFund *= hzShift;
|
||||
|
||||
//Set timbre.
|
||||
ratio = ratioSlewRate * ratio + (1.0 - ratioSlewRate) * targetRatio;
|
||||
|
||||
for(uint voice = 0; voice < NUM_VOICES; ++voice){
|
||||
//Get new phase.
|
||||
double phase = phases[(voice + idxFund) & VOICE_MASK];
|
||||
phase += hz * sampleInterval;
|
||||
phase = frac(phase);
|
||||
|
||||
//Don't bother rendering anything over the Nyquist rate.
|
||||
if( hz > hzNyq ) break;
|
||||
output[i] += getAmplitude(hz) * sin(2.0 * M_PI * phase);
|
||||
|
||||
//Remember phase, move to higher overtone.
|
||||
phases[(voice + idxFund) & VOICE_MASK] = phase;
|
||||
hz *= ratio;
|
||||
}
|
||||
|
||||
output[i] *= volume;
|
||||
}
|
||||
|
||||
//Make timbre cyclical.
|
||||
if( hzShift < 1.0 )
|
||||
{
|
||||
//Pitch low and decreasing, shift bottom voices to top.
|
||||
while( getAmplitude(hz) > MIN_VOLUME
|
||||
&& getAmplitude(hzFund) < MIN_VOLUME)
|
||||
{
|
||||
++idxFund;
|
||||
hz *= ratio;
|
||||
hzFund *= ratio;
|
||||
}
|
||||
}
|
||||
|
||||
if( (hzShift > 1.0) || (targetRatio > ratio))
|
||||
{
|
||||
//Pitch high and increasing. Shift top voices to bottom.
|
||||
while( getAmplitude(hz) < MIN_VOLUME
|
||||
&& getAmplitude(hzFund) > MIN_VOLUME)
|
||||
{
|
||||
--idxFund;
|
||||
hz /= ratio;
|
||||
hzFund /= ratio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Synth::setSampleRate(double oldRate, double newRate){
|
||||
sampleInterval = 1.0 / newRate;
|
||||
hzNyq = newRate / 2;
|
||||
ratioSlewRate *= newRate / oldRate;
|
||||
hzShift = pow(hzShift, newRate / oldRate);
|
||||
}
|
||||
|
||||
//Takes value from 0 to 1 representing frequency shift factor.
|
||||
//0 : lower one octave per second
|
||||
//1 : raise one octave per second
|
||||
void Synth::setFrequencyShift(double in){
|
||||
hzShift = exp2(2.0 * (in - 0.5) * sampleInterval);
|
||||
}
|
||||
|
||||
//Slew to given ratio.
|
||||
//0 : next voice is one fifth above previous voice.
|
||||
//1 : next voice is one octave above previous voice.
|
||||
void Synth::setTargetRatio(double in){
|
||||
targetRatio = 1.5 + in * 0.5;
|
||||
}
|
||||
|
||||
void Synth::setVolume(double in){
|
||||
volume = in;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#include "DistrhoPlugin.hpp"
|
||||
#include <array>
|
||||
|
||||
struct Voice
|
||||
{
|
||||
double hz;
|
||||
double phase;
|
||||
};
|
||||
|
||||
constexpr int NUM_VOICES = 256;
|
||||
constexpr uchar VOICE_MASK = 0xFF;
|
||||
|
||||
class Synth
|
||||
{
|
||||
public:
|
||||
explicit Synth(double sampleRate);
|
||||
void process(float *output, uint32_t frames);
|
||||
void setFrequencyShift(double in);
|
||||
void setTargetRatio(double in);
|
||||
void setSampleRate(double oldRate, double newRate);
|
||||
void setVolume(double in);
|
||||
|
||||
private:
|
||||
//Phase must persist between run invocation.
|
||||
//Phase of each voice, considered as a period function on the unit interval.
|
||||
std::array<double, NUM_VOICES> phases = {0};
|
||||
//Index of lowest voice in phase array.
|
||||
uchar idxFund = 0;
|
||||
float volume = 0.0f;
|
||||
double hzFund = 32.7;
|
||||
double hzShift = 1.0;
|
||||
double hzNyq = 24000.0;
|
||||
double ratio = 2.0;
|
||||
double targetRatio = 2.0;
|
||||
double ratioSlewRate = 0.99999;
|
||||
double sampleInterval = 1.0 / 48000.0;
|
||||
};
|
Loading…
Reference in New Issue