Revert to earlier timbre.
This commit is contained in:
parent
bca9e0381d
commit
ca9e6acbe8
|
@ -93,7 +93,7 @@ protected:
|
||||||
synth.setPhaseVelocity(val);
|
synth.setPhaseVelocity(val);
|
||||||
break;
|
break;
|
||||||
case ktpay:
|
case ktpay:
|
||||||
synth.setTimbre(val);
|
synth.setPitchOffset(val);
|
||||||
break;
|
break;
|
||||||
case ktpaz:
|
case ktpaz:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -11,7 +11,7 @@ static constexpr double amin = 50;
|
||||||
static constexpr double apiq = 500;
|
static constexpr double apiq = 500;
|
||||||
static constexpr double amax = 10000;
|
static constexpr double amax = 10000;
|
||||||
//Volume of voice as a function of sample rate independent frequency.
|
//Volume of voice as a function of sample rate independent frequency.
|
||||||
static constexpr double getAmplitude( double hz )
|
static constexpr inline float getAmplitude( double hz )
|
||||||
{
|
{
|
||||||
if( hz < amin ) return 0.0;
|
if( hz < amin ) return 0.0;
|
||||||
if( hz < apiq ) {
|
if( hz < apiq ) {
|
||||||
|
@ -24,8 +24,13 @@ static constexpr double getAmplitude( double hz )
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Interpolation function on unit interval with good regularity as a function on S^1.
|
||||||
|
static constexpr inline float smooth( double x )
|
||||||
|
{
|
||||||
|
return x * x * x;//(3.0 * x - 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
//Sanity checks: voices should become silent outside audible frequencies.
|
//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(10.0) );
|
||||||
static_assert( MIN_VOLUME > getAmplitude(20000.0));
|
static_assert( MIN_VOLUME > getAmplitude(20000.0));
|
||||||
|
|
||||||
|
@ -33,54 +38,45 @@ static_assert( MIN_VOLUME > getAmplitude(20000.0));
|
||||||
//Even overtones become plain overtones.
|
//Even overtones become plain overtones.
|
||||||
void Synth::shiftUp()
|
void Synth::shiftUp()
|
||||||
{
|
{
|
||||||
|
tablePhase *= 2.0;
|
||||||
spectrumPhase += 1.0;
|
spectrumPhase += 1.0;
|
||||||
for(uint voice = 0; voice < NUM_VOICES; ++voice){
|
|
||||||
phases[voice] = phases[(2 * voice + 1) & VOICE_MASK];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//New fundamental is half as high as old fundamental.
|
//New fundamental is half as high as old fundamental.
|
||||||
//Overtones become even overtones.
|
//Overtones become even overtones.
|
||||||
void Synth::shiftDown()
|
void Synth::shiftDown()
|
||||||
{
|
{
|
||||||
|
tablePhase /= 2.0;
|
||||||
spectrumPhase -= 1.0;
|
spectrumPhase -= 1.0;
|
||||||
for(uint voice = NUM_VOICES / 2 - 1; voice < NUM_VOICES; --voice){
|
|
||||||
phases[voice * 2 + 1] = phases[voice];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::process(float* output, const uint32_t frames)
|
void Synth::process(float* output, const uint32_t frames)
|
||||||
{
|
{
|
||||||
double hz;
|
|
||||||
|
|
||||||
//Render.
|
//Render.
|
||||||
for(uint32_t i = 0; i < frames; i++){
|
for(uint32_t i = 0; i < frames; i++){
|
||||||
|
|
||||||
//Set pitch.
|
//Set pitch.
|
||||||
hz = hzFund = exp2(spectrumPhase) * fMin;
|
hzFund = exp2(spectrumPhase) * fMin;
|
||||||
|
|
||||||
|
tablePhase += hzFund * sampleInterval;
|
||||||
|
tablePhase = frac(tablePhase);
|
||||||
|
|
||||||
bool isOddHarmonic = true;
|
bool isOddHarmonic = true;
|
||||||
|
|
||||||
for(uint voice = 0; voice < NUM_VOICES; ++voice){
|
for(uint voice = 0; voice < NUM_VOICES; ++voice){
|
||||||
//Get new phase.
|
|
||||||
double phase = phases[voice];
|
|
||||||
phase += hz * sampleInterval;
|
|
||||||
phase = frac(phase);
|
|
||||||
|
|
||||||
|
double voicePhase = 2.0 * M_PI * tablePhase * (voice + 1.0);
|
||||||
//Anti-aliasing: don't bother rendering anything over the Nyquist rate.
|
//Anti-aliasing: don't bother rendering anything over the Nyquist rate.
|
||||||
if( hz > hzNyq ) break;
|
if( hzFund * (voice + 1.0) > hzNyq ) break;
|
||||||
|
|
||||||
output[i] += (!isOddHarmonic + isOddHarmonic * spectrumPhase * spectrumPhase) //Fade in odd harmonics.
|
output[i] += (!isOddHarmonic + isOddHarmonic * smooth(spectrumPhase)) //Fade in odd harmonics.
|
||||||
* getAmplitude(hz) //Frequency response.
|
* getAmplitude(hzFund * (voice + 1.0)) //Frequency response.
|
||||||
* sin(2.0 * M_PI * phase); //Additives.
|
* sinf(static_cast<float>(voicePhase)); //Additives.
|
||||||
|
|
||||||
//Remember phase, move to higher overtone.
|
|
||||||
phases[voice] = phase;
|
|
||||||
hz *= (voice + 2.0) / (voice + 1.0);
|
|
||||||
isOddHarmonic = !isOddHarmonic;
|
isOddHarmonic = !isOddHarmonic;
|
||||||
}
|
}
|
||||||
|
|
||||||
output[i] *= 32 * volume / static_cast<double>(NUM_VOICES);
|
output[i] *= fMin * volume / static_cast<float>(8 * NUM_VOICES);
|
||||||
|
|
||||||
//Wrapping.
|
//Wrapping.
|
||||||
spectrumPhase += spectrumPhaseVelocity * sampleInterval;
|
spectrumPhase += spectrumPhaseVelocity * sampleInterval;
|
||||||
|
@ -95,7 +91,7 @@ void Synth::setSampleRate(double oldRate, double newRate){
|
||||||
hzNyq = newRate / 2;
|
hzNyq = newRate / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Takes value from 0 to 1 representing frequency shift factor.
|
//Takes value from 0 to 1 representing rate of wrapping pitch increase.
|
||||||
//0 : lower one octave per second
|
//0 : lower one octave per second
|
||||||
//1 : raise one octave per second
|
//1 : raise one octave per second
|
||||||
void Synth::setPhaseVelocity(double in){
|
void Synth::setPhaseVelocity(double in){
|
||||||
|
@ -103,11 +99,12 @@ void Synth::setPhaseVelocity(double in){
|
||||||
}
|
}
|
||||||
|
|
||||||
//Vary pitch of fundamental in a non-wrapping kinda way.
|
//Vary pitch of fundamental in a non-wrapping kinda way.
|
||||||
//Current range is six octaves, current bottom is 110.0 Hz (A3)
|
//Current range is six octaves, current bottom is 13.75 Hz (A0)
|
||||||
void Synth::setTimbre(double in){
|
void Synth::setPitchOffset(double in){
|
||||||
fMin = exp2(in * 2.0) * 110.0;
|
fMin = exp2(in * 6.0) * 13.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Synth::setVolume(double in){
|
void Synth::setVolume(double in){
|
||||||
volume = in;
|
volume = in;
|
||||||
}
|
}
|
|
@ -1,12 +1,6 @@
|
||||||
#include "DistrhoPlugin.hpp"
|
#include "DistrhoPlugin.hpp"
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
struct Voice
|
|
||||||
{
|
|
||||||
double hz;
|
|
||||||
double phase;
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr unsigned int NUM_VOICES = 512;
|
constexpr unsigned int NUM_VOICES = 512;
|
||||||
constexpr unsigned int VOICE_MASK = NUM_VOICES - 1;
|
constexpr unsigned int VOICE_MASK = NUM_VOICES - 1;
|
||||||
|
|
||||||
|
@ -16,7 +10,7 @@ public:
|
||||||
explicit Synth(double sampleRate);
|
explicit Synth(double sampleRate);
|
||||||
void process(float *output, uint32_t frames);
|
void process(float *output, uint32_t frames);
|
||||||
void setPhaseVelocity(double in);
|
void setPhaseVelocity(double in);
|
||||||
void setTimbre(double in);
|
void setPitchOffset(double in);
|
||||||
void setSampleRate(double oldRate, double newRate);
|
void setSampleRate(double oldRate, double newRate);
|
||||||
void setVolume(double in);
|
void setVolume(double in);
|
||||||
void shiftUp();
|
void shiftUp();
|
||||||
|
@ -26,9 +20,9 @@ public:
|
||||||
double hzFund = fMin;
|
double hzFund = fMin;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//Phase must persist between run invocation.
|
//Phase of wavetable.
|
||||||
//Phase of each voice, considered a function on the unit circle.
|
double tablePhase = 0.0;
|
||||||
std::array<double, NUM_VOICES> phases = {0};
|
double fmPhase = 0.0;
|
||||||
float volume = 0.0f;
|
float volume = 0.0f;
|
||||||
//Parameter in unit circle controlling pitch (varies by one octave).
|
//Parameter in unit circle controlling pitch (varies by one octave).
|
||||||
double spectrumPhase = 1.0f;
|
double spectrumPhase = 1.0f;
|
||||||
|
@ -37,6 +31,9 @@ private:
|
||||||
//Lowest fundamental frequency of blit.
|
//Lowest fundamental frequency of blit.
|
||||||
double fMin = 55.0;
|
double fMin = 55.0;
|
||||||
|
|
||||||
|
//Parameter stretching the overtone series.
|
||||||
|
double inharmonicity = 1.001;
|
||||||
|
|
||||||
double hzNyq = 24000.0;
|
double hzNyq = 24000.0;
|
||||||
double sampleInterval = 1.0 / 48000.0;
|
double sampleInterval = 1.0 / 48000.0;
|
||||||
};
|
};
|
|
@ -177,7 +177,7 @@ void TabUI::onNanoDisplay()
|
||||||
beginPath();
|
beginPath();
|
||||||
fillColor(200, 200, 200);
|
fillColor(200, 200, 200);
|
||||||
textBox(0.f, 15.f, 250.f,
|
textBox(0.f, 15.f, 250.f,
|
||||||
std::format("Frequency: {:.3f}\nNearest: {}\n",
|
std::format("Frequency: {:.3f}\nNearest: {:.3f}\n",
|
||||||
hz, scale.getNearestNoteNumber(hz))
|
hz, scale.getNearestNoteNumber(hz))
|
||||||
.c_str(),
|
.c_str(),
|
||||||
nullptr);
|
nullptr);
|
||||||
|
|
Loading…
Reference in New Issue