commit a85b360b38d8913b8ae19dca151773da37bb698a
parent cb975edc5b414f1a8f0b88bb70fa98f93d4af3d6
Author: Christopher A. Oliver <[email protected]>
Date: Fri, 27 Nov 2015 20:26:26 -0500
PWM support via phase mod
Diffstat:
4 files changed, 115 insertions(+), 47 deletions(-)
diff --git a/src/Params/ADnoteParameters.h b/src/Params/ADnoteParameters.h
@@ -27,7 +27,7 @@
#include "PresetsArray.h"
enum FMTYPE {
- NONE, MORPH, RING_MOD, PHASE_MOD, FREQ_MOD
+ NONE, MORPH, RING_MOD, PHASE_MOD, FREQ_MOD, PW_MOD
};
/*****************************************************************/
diff --git a/src/Synth/ADnote.cpp b/src/Synth/ADnote.cpp
@@ -113,13 +113,24 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars)
unison_stereo_spread[nvoice] =
pars.VoicePar[nvoice].Unison_stereo_spread / 127.0f;
+
int unison = pars.VoicePar[nvoice].Unison_size;
if(unison < 1)
unison = 1;
- // Since noise unison of greater than two is touch goofy...
- if (pars.VoicePar[nvoice].Type != 0 && unison > 2)
- unison = 2;
+ bool is_pwm = pars.VoicePar[nvoice].PFMEnabled == PW_MOD;
+
+ if (pars.VoicePar[nvoice].Type != 0) {
+ // Since noise unison of greater than two is touch goofy...
+ if (unison > 2)
+ unison = 2;
+ } else if (is_pwm) {
+ /* Pulse width mod uses pairs of subvoices. */
+ unison *= 2;
+ // This many is likely to sound like noise anyhow.
+ if (unison > 64)
+ unison = 64;
+ }
//compute unison
unison_size[nvoice] = unison;
@@ -133,8 +144,8 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars)
float unison_vibratto_a =
pars.VoicePar[nvoice].Unison_vibratto / 127.0f; //0.0f .. 1.0f
-
- switch(unison) {
+ int true_unison = unison / (is_pwm ? 2 : 1);
+ switch(true_unison) {
case 1:
unison_base_freq_rap[nvoice][0] = 1.0f; //if the unison is not used, always make the only subvoice to have the default note
break;
@@ -144,11 +155,11 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars)
};
break;
default: { //unison for more than 2 subvoices
- float unison_values[unison];
+ float unison_values[true_unison];
float min = -1e-6, max = 1e-6;
- for(int k = 0; k < unison; ++k) {
- float step = (k / (float) (unison - 1)) * 2.0f - 1.0f; //this makes the unison spread more uniform
- float val = step + (RND * 2.0f - 1.0f) / (unison - 1);
+ for(int k = 0; k < true_unison; ++k) {
+ float step = (k / (float) (true_unison - 1)) * 2.0f - 1.0f; //this makes the unison spread more uniform
+ float val = step + (RND * 2.0f - 1.0f) / (true_unison - 1);
unison_values[k] = val;
if (min > val) {
min = val;
@@ -158,7 +169,7 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars)
}
}
float diff = max - min;
- for(int k = 0; k < unison; ++k) {
+ for(int k = 0; k < true_unison; ++k) {
unison_values[k] =
(unison_values[k] - (max + min) * 0.5f) / diff; //the lowest value will be -1 and the highest will be 1
unison_base_freq_rap[nvoice][k] =
@@ -166,9 +177,16 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars)
}
};
}
+ if (is_pwm)
+ for (int i = true_unison - 1; i >= 0; i--) {
+ unison_base_freq_rap[nvoice][2*i + 1] =
+ unison_base_freq_rap[nvoice][i];
+ unison_base_freq_rap[nvoice][2*i] =
+ unison_base_freq_rap[nvoice][i];
+ }
//unison vibrattos
- if(unison > 1)
+ if(unison > 2 || (!is_pwm && unison > 1))
for(int k = 0; k < unison; ++k) //reduce the frequency difference for larger vibrattos
unison_base_freq_rap[nvoice][k] = 1.0f
+ (unison_base_freq_rap[
@@ -192,12 +210,27 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars)
if(RND < 0.5f)
m = -m;
unison_vibratto[nvoice].step[k] = m;
+
+ // Ugly, but the alternative is likely uglier.
+ if (is_pwm)
+ for (int i = 0; i < unison; i += 2) {
+ unison_vibratto[nvoice].step[i+1] =
+ unison_vibratto[nvoice].step[i];
+ unison_vibratto[nvoice].position[i+1] =
+ unison_vibratto[nvoice].position[i];
+ }
}
- if(unison == 1) { //no vibratto for a single voice
- unison_vibratto[nvoice].step[0] = 0.0f;
- unison_vibratto[nvoice].position[0] = 0.0f;
- unison_vibratto[nvoice].amplitude = 0.0f;
+ if(unison <= 2) { //no vibratto for a single voice
+ if (is_pwm) {
+ unison_vibratto[nvoice].step[1] = 0.0f;
+ unison_vibratto[nvoice].position[1] = 0.0f;
+ }
+ if (is_pwm || unison == 1) {
+ unison_vibratto[nvoice].step[0] = 0.0f;
+ unison_vibratto[nvoice].position[0] = 0.0f;
+ unison_vibratto[nvoice].amplitude = 0.0f;
+ }
}
//phase invert for unison
@@ -314,9 +347,11 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars)
+ i] =
NoteVoicePar[nvoice].OscilSmp[i];
- oscposhi_start +=
+ NoteVoicePar[nvoice].phase_offset =
(int)((pars.VoicePar[nvoice].Poscilphase
- - 64.0f) / 128.0f * synth.oscilsize + synth.oscilsize * 4);
+ - 64.0f) / 128.0f * synth.oscilsize
+ + synth.oscilsize * 4);
+ oscposhi_start += NoteVoicePar[nvoice].phase_offset;
int kth_start = oscposhi_start;
for(int k = 0; k < unison; ++k) {
@@ -347,22 +382,28 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars)
NoteVoicePar[nvoice].filterbypass =
pars.VoicePar[nvoice].Pfilterbypass;
- switch(pars.VoicePar[nvoice].PFMEnabled) {
- case 1:
- NoteVoicePar[nvoice].FMEnabled = MORPH;
- break;
- case 2:
- NoteVoicePar[nvoice].FMEnabled = RING_MOD;
- break;
- case 3:
- NoteVoicePar[nvoice].FMEnabled = PHASE_MOD;
- break;
- case 4:
- NoteVoicePar[nvoice].FMEnabled = FREQ_MOD;
- break;
- default:
- NoteVoicePar[nvoice].FMEnabled = NONE;
- }
+ if (pars.VoicePar[nvoice].Type != 0)
+ NoteVoicePar[nvoice].FMEnabled = NONE;
+ else
+ switch(pars.VoicePar[nvoice].PFMEnabled) {
+ case 1:
+ NoteVoicePar[nvoice].FMEnabled = MORPH;
+ break;
+ case 2:
+ NoteVoicePar[nvoice].FMEnabled = RING_MOD;
+ break;
+ case 3:
+ NoteVoicePar[nvoice].FMEnabled = PHASE_MOD;
+ break;
+ case 4:
+ NoteVoicePar[nvoice].FMEnabled = FREQ_MOD;
+ break;
+ case 5:
+ NoteVoicePar[nvoice].FMEnabled = PW_MOD;
+ break;
+ default:
+ NoteVoicePar[nvoice].FMEnabled = NONE;
+ }
NoteVoicePar[nvoice].FMVoice = pars.VoicePar[nvoice].PFMVoice;
NoteVoicePar[nvoice].FMFreqEnvelope = NULL;
@@ -377,6 +418,7 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars)
- 1.0f);
switch(NoteVoicePar[nvoice].FMEnabled) {
case PHASE_MOD:
+ case PW_MOD:
fmvoldamp =
powf(440.0f / getvoicebasefreq(
nvoice), pars.VoicePar[nvoice].PFMVolumeDamp
@@ -553,6 +595,7 @@ void ADnote::legatonote(LegatoParams lpars)
switch(NoteVoicePar[nvoice].FMEnabled) {
case PHASE_MOD:
+ case PW_MOD:
fmvoldamp =
powf(440.0f / getvoicebasefreq(
nvoice), pars.VoicePar[nvoice].PFMVolumeDamp
@@ -1096,6 +1139,8 @@ void ADnote::computecurrentparameters()
/***************/
/* Modulator */
/***************/
+
+
if(NoteVoicePar[nvoice].FMEnabled != NONE) {
FMrelativepitch = NoteVoicePar[nvoice].FMDetune / 100.0f;
if(NoteVoicePar[nvoice].FMFreqEnvelope)
@@ -1343,7 +1388,11 @@ inline void ADnote::ComputeVoiceOscillatorFrequencyModulation(int nvoice,
for(int k = 0; k < unison_size[nvoice]; ++k) {
float *tw = tmpwave_unison[k];
const float *smps = NoteVoicePar[NoteVoicePar[nvoice].FMVoice].VoiceOut;
- memcpy(tw, smps, synth.bufferbytes);
+ if (FMmode == PW_MOD && (k & 1))
+ for (int i = 0; i < synth.buffersize; ++i)
+ tw[i] = -smps[i];
+ else
+ memcpy(tw, smps, synth.bufferbytes);
}
} else {
//Compute the modulator and store it in tmpwave_unison[][]
@@ -1357,7 +1406,10 @@ inline void ADnote::ComputeVoiceOscillatorFrequencyModulation(int nvoice,
for(int i = 0; i < synth.buffersize; ++i) {
tw[i] = (smps[poshiFM] * ((1<<24) - posloFM)
- + smps[poshiFM + 1] * posloFM) / (1.0f*(1<<24));
+ + smps[poshiFM + 1] * posloFM) / (1.0f*(1<<24));
+ if (FMmode == PW_MOD && (k & 1))
+ tw[i] = -tw[i];
+
posloFM += freqloFM;
if(posloFM >= (1<<24)) {
posloFM &= 0xffffff;//fmod(posloFM, 1.0f);
@@ -1391,7 +1443,7 @@ inline void ADnote::ComputeVoiceOscillatorFrequencyModulation(int nvoice,
//normalize: makes all sample-rates, oscil_sizes to produce same sound
- if(FMmode != 0) { //Frequency modulation
+ if(FMmode == FREQ_MOD) { //Frequency modulation
const float normalize = synth.oscilsize_f / 262144.0f * 44100.0f
/ synth.samplerate_f;
for(int k = 0; k < unison_size[nvoice]; ++k) {
@@ -1404,7 +1456,7 @@ inline void ADnote::ComputeVoiceOscillatorFrequencyModulation(int nvoice,
FMoldsmp[nvoice][k] = fmold;
}
}
- else { //Phase modulation
+ else { //Phase or PWM modulation
const float normalize = synth.oscilsize_f / 262144.0f;
for(int k = 0; k < unison_size[nvoice]; ++k) {
float *tw = tmpwave_unison[k];
@@ -1432,6 +1484,8 @@ inline void ADnote::ComputeVoiceOscillatorFrequencyModulation(int nvoice,
//carrier
int carposhi = poshi + FMmodfreqhi;
int carposlo = poslo + FMmodfreqlo;
+ if (FMmode == PW_MOD && (k & 1))
+ carposhi += NoteVoicePar[nvoice].phase_offset;
if(carposlo >= (1<<24)) {
carposhi++;
@@ -1519,11 +1573,11 @@ int ADnote::noteout(float *outl, float *outr)
case RING_MOD:
ComputeVoiceOscillatorRingModulation(nvoice);
break;
- case PHASE_MOD:
- ComputeVoiceOscillatorFrequencyModulation(nvoice, 0);
- break;
case FREQ_MOD:
- ComputeVoiceOscillatorFrequencyModulation(nvoice, 1);
+ case PHASE_MOD:
+ case PW_MOD:
+ ComputeVoiceOscillatorFrequencyModulation(nvoice,
+ NoteVoicePar[nvoice].FMEnabled);
break;
default:
ComputeVoiceOscillator_LinearInterpolation(nvoice);
@@ -1548,10 +1602,17 @@ int ADnote::noteout(float *outl, float *outr)
float *tw = tmpwave_unison[k];
if(stereo) {
float stereo_pos = 0;
- if(unison_size[nvoice] > 1)
+ bool is_pwm = NoteVoicePar[nvoice].FMEnabled == PW_MOD;
+ if (is_pwm) {
+ if(unison_size[nvoice] > 2)
+ stereo_pos = k/2
+ / (float)(unison_size[nvoice]/2
+ - 1) * 2.0f - 1.0f;
+ } else if(unison_size[nvoice] > 1) {
stereo_pos = k
- / (float)(unison_size[nvoice]
- - 1) * 2.0f - 1.0f;
+ / (float)(unison_size[nvoice]
+ - 1) * 2.0f - 1.0f;
+ }
float stereo_spread = unison_stereo_spread[nvoice] * 2.0f; //between 0 and 2.0f
if(stereo_spread > 1.0f) {
float stereo_pos_1 = (stereo_pos >= 0.0f) ? 1.0f : -1.0f;
@@ -1563,7 +1624,8 @@ int ADnote::noteout(float *outl, float *outr)
else
stereo_pos *= stereo_spread;
- if(unison_size[nvoice] == 1)
+ if(unison_size[nvoice] == 1 ||
+ (is_pwm && unison_size[nvoice] == 2))
stereo_pos = 0.0f;
float panning = (stereo_pos + 1.0f) * 0.5f;
diff --git a/src/Synth/ADnote.h b/src/Synth/ADnote.h
@@ -105,7 +105,6 @@ class ADnote:public SynthNote
/**Fadein in a way that removes clicks but keep sound "punchy"*/
inline void fadein(float *smps) const;
-
//GLOBALS
ADnoteParameters &pars;
unsigned char stereo; //if the note is stereo (allows note Panning)
@@ -187,6 +186,9 @@ class ADnote:public SynthNote
/* Waveform of the Voice */
float *OscilSmp;
+ /* preserved for phase mod PWM emulation. */
+ int phase_offset;
+
/* Range of waveform */
float OscilSmpMin, OscilSmpMax;
diff --git a/src/UI/ADnoteUI.fl b/src/UI/ADnoteUI.fl
@@ -513,6 +513,10 @@ o->redraw();}
label FM
xywh {80 80 100 20} labelfont 1
}
+ MenuItem {} {
+ label PWM
+ xywh {80 80 100 20} labelfont 1
+ }
}
Fl_Group {} {
label FREQUENCY open