Hello,
I am having a seemingly simple issue trying get polyphony from the Notes and Volts synth using the MidiSynth example. The issue I am running into is assigning AudioSynthWaveforms to an array of them... when I try to using classes from members of the array, which should be exactly the same as using the classes from the waveform object outside the array, it just stops working. I have attached the code in question. In the oscPlay() function you can comment out the section of code that is active and uncomment the inactive to see the problem.
C++ isn't my native language, but I can't for the life of me figure what is causing it to break. Any help would be appreciated.
Thanks for your time!
I am having a seemingly simple issue trying get polyphony from the Notes and Volts synth using the MidiSynth example. The issue I am running into is assigning AudioSynthWaveforms to an array of them... when I try to using classes from members of the array, which should be exactly the same as using the classes from the waveform object outside the array, it just stops working. I have attached the code in question. In the oscPlay() function you can comment out the section of code that is active and uncomment the inactive to see the problem.
C++ isn't my native language, but I can't for the life of me figure what is causing it to break. Any help would be appreciated.
Thanks for your time!
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
const int TOTAL_VOICES = 4;
// GUItool: begin automatically generated code
AudioSynthNoisePink pink1; //xy=55,87
AudioSynthNoisePink pink2; //xy=55,212
AudioSynthNoisePink pink3; //xy=55,339
AudioSynthNoisePink pink4; //xy=55,464
AudioSynthWaveform waveform1; //xy=64,21
AudioSynthWaveform waveform2; //xy=64,54
AudioSynthWaveform waveform4; //xy=64,146
AudioSynthWaveform waveform3; //xy=64,179
AudioSynthWaveform waveform5; //xy=64,273
AudioSynthWaveform waveform6; //xy=64,306
AudioSynthWaveform waveform7; //xy=64,398
AudioSynthWaveform waveform8; //xy=64,431
AudioMixer4 mixer1; //xy=199,61
AudioMixer4 mixer2; //xy=199,185
AudioMixer4 mixer3; //xy=199,313
AudioMixer4 mixer4; //xy=199,437
AudioFilterStateVariable filter1; //xy=320,67
AudioFilterStateVariable filter3; //xy=319,319
AudioFilterStateVariable filter2; //xy=323,191
AudioFilterStateVariable filter4; //xy=323,443
AudioEffectEnvelope envelope1; //xy=460,54
AudioEffectEnvelope envelope2; //xy=460,307
AudioEffectEnvelope envelope3; //xy=461,178
AudioEffectEnvelope envelope4; //xy=461,430
AudioMixer4 mixer5; //xy=653,244
AudioOutputI2S i2s1; //xy=779,245
AudioConnection patchCord1(pink1, 0, mixer1, 2);
AudioConnection patchCord2(pink2, 0, mixer2, 2);
AudioConnection patchCord3(pink3, 0, mixer3, 2);
AudioConnection patchCord4(pink4, 0, mixer4, 2);
AudioConnection patchCord5(waveform1, 0, mixer1, 0);
AudioConnection patchCord6(waveform2, 0, mixer1, 1);
AudioConnection patchCord7(waveform4, 0, mixer2, 0);
AudioConnection patchCord8(waveform3, 0, mixer2, 1);
AudioConnection patchCord9(waveform5, 0, mixer3, 0);
AudioConnection patchCord10(waveform6, 0, mixer3, 1);
AudioConnection patchCord11(waveform7, 0, mixer4, 0);
AudioConnection patchCord12(waveform8, 0, mixer4, 1);
AudioConnection patchCord13(mixer1, 0, filter1, 0);
AudioConnection patchCord14(mixer2, 0, filter2, 0);
AudioConnection patchCord15(mixer3, 0, filter3, 0);
AudioConnection patchCord16(mixer4, 0, filter4, 0);
AudioConnection patchCord17(filter1, 0, envelope1, 0);
AudioConnection patchCord18(filter3, 0, envelope3, 0);
AudioConnection patchCord19(filter2, 0, envelope2, 0);
AudioConnection patchCord20(filter4, 0, envelope4, 0);
AudioConnection patchCord21(envelope1, 0, mixer5, 0);
AudioConnection patchCord22(envelope3, 0, mixer5, 2);
AudioConnection patchCord23(envelope2, 0, mixer5, 1);
AudioConnection patchCord24(envelope4, 0, mixer5, 3);
AudioConnection patchCord25(mixer5, 0, i2s1, 0);
AudioConnection patchCord26(mixer5, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000_1; //xy=1023,20
// GUItool: end automatically generated code
AudioSynthNoisePink noiseBank[] = {pink1, pink2, pink3, pink4};
AudioSynthWaveform oscBank1[] = {waveform1, waveform3, waveform5, waveform7};
AudioSynthWaveform oscBank2[] = {waveform2, waveform4, waveform6, waveform8};
AudioFilterStateVariable filterBank[] = {filter1, filter2, filter3, filter4};
AudioEffectEnvelope envelopeBank[] = {envelope1, envelope2, envelope3, envelope4};
AudioMixer4 oscMixerBank[] = {mixer1, mixer2, mixer3, mixer4};
//from midisynth
struct voice_t {
int wavetable_id;
byte channel;
byte note;
};
voice_t voices[TOTAL_VOICES];
const byte BUFFER = 8; //Size of keyboard buffer
const float noteFreqs[128] = {8.176, 8.662, 9.177, 9.723, 10.301, 10.913, 11.562, 12.25, 12.978, 13.75, 14.568, 15.434, 16.352, 17.324, 18.354, 19.445, 20.602, 21.827, 23.125, 24.5, 25.957, 27.5, 29.135, 30.868, 32.703, 34.648, 36.708, 38.891, 41.203, 43.654, 46.249, 48.999, 51.913, 55, 58.27, 61.735, 65.406, 69.296, 73.416, 77.782, 82.407, 87.307, 92.499, 97.999, 103.826, 110, 116.541, 123.471, 130.813, 138.591, 146.832, 155.563, 164.814, 174.614, 184.997, 195.998, 207.652, 220, 233.082, 246.942, 261.626, 277.183, 293.665, 311.127, 329.628, 349.228, 369.994, 391.995, 415.305, 440, 466.164, 493.883, 523.251, 554.365, 587.33, 622.254, 659.255, 698.456, 739.989, 783.991, 830.609, 880, 932.328, 987.767, 1046.502, 1108.731, 1174.659, 1244.508, 1318.51, 1396.913, 1479.978, 1567.982, 1661.219, 1760, 1864.655, 1975.533, 2093.005, 2217.461, 2349.318, 2489.016, 2637.02, 2793.826, 2959.955, 3135.963, 3322.438, 3520, 3729.31, 3951.066, 4186.009, 4434.922, 4698.636, 4978.032, 5274.041, 5587.652, 5919.911, 6271.927, 6644.875, 7040, 7458.62, 7902.133, 8372.018, 8869.844, 9397.273, 9956.063, 10548.08, 11175.3, 11839.82, 12543.85};
byte globalNote = 0;
byte globalVelocity = 0;
int octave = 0;
const float DIV127 = (1.0 / 127.0);
float detuneFactor = 1;
float bendFactor = 1;
int bendRange = 12;
unsigned int LFOspeed = 2000;
float LFOpitch = 1;
float LFOdepth = 0;
byte LFOmodeSelect = 0;
int FILfreq = 10000;
float FILfactor = 1;
void setup() {
Init();
}
void loop() {
usbMIDI.read();
LFOupdate(false, LFOmodeSelect, FILfactor, LFOdepth);
}
int allocateVoice(byte channel, byte note);
int findVoice(byte channel, byte note);
void freeVoices();
int used_voices = 0;
int stopped_voices = 0;
int evict_voice = 0;
int notes_played = 0;
void myNoteOn(byte channel, byte note, byte velocity) {
if ( note > 23 && note < 108 ) {
globalNote = note;
globalVelocity = velocity;
//keyBuff(note, true);
oscPlay(note);
LFOupdate(true, LFOmodeSelect, FILfactor, LFOdepth);
}
}
void myNoteOff(byte channel, byte note, byte velocity) {
if ( note > 23 && note < 108 ) {
//keyBuff(note, false);
oscStop();
}
}
void myPitchBend(byte channel, int bend) {
float bendF = bend;
bendF = bendF / 8192;
bendF = bendF * bendRange;
bendF = bendF / 12;
bendFactor = pow(2, bendF);
oscSet();
}
void keyBuff(byte note, bool playNote) {
static byte buff[BUFFER];
static byte buffSize = 0;
/*
// Add Note
if (playNote == true && (buffSize < BUFFER) ) {
oscPlay(note);
buff[buffSize] = note;
buffSize++;
return;
}
// Remove Note
else if (playNote == false && buffSize != 0) {
for (byte found = 0; found < buffSize; found++) {
if (buff[found] == note) {
for (byte gap = found; gap < (buffSize - 1); gap++) {
buff[gap] = buff[gap + 1];
}
buffSize--;
buff[buffSize] = 255;
if (buffSize != 0) {
oscPlay(buff[buffSize - 1]);
return;
}
else {
oscStop();
return;
}
}
}
}
*/
}
void oscPlay(byte note) {
waveform1.frequency(noteFreqs[note] * bendFactor * LFOpitch);
waveform2.frequency(noteFreqs[note + octave] * detuneFactor * bendFactor * LFOpitch);
float velo = (globalVelocity * DIV127);
waveform1.amplitude(velo);
waveform2.amplitude(velo);
pink1.amplitude(velo);
envelope1.noteOn();
/*
oscBank1[0].frequency(noteFreqs[note] * bendFactor * LFOpitch);
oscBank2[0].frequency(noteFreqs[note + octave] * detuneFactor * bendFactor * LFOpitch);
float velo = (globalVelocity * DIV127);
oscBank1[0].amplitude(velo);
oscBank2[0].amplitude(velo);
noiseBank[0].amplitude(velo);
envelopeBank[0].noteOn();
Serial.print("Velocity in OscPlay: ");
Serial.print(velo);
Serial.println();
Serial.print("envelope is active after noteOn: ");
Serial.print(envelopeBank[0].isActive());
Serial.println();
*/
}
void oscStop() {
envelope1.noteOff();
//envelopeBank[0].noteOff();
}
void oscSet() {
oscBank1[0].frequency(noteFreqs[globalNote] * bendFactor * LFOpitch);
oscBank2[0].frequency(noteFreqs[globalNote + octave] * detuneFactor * bendFactor * LFOpitch);
}
void myControlChange(byte channel, byte control, byte value) {
switch (control) {
case 100:
mixer1.gain(0, (value * DIV127));
break;
case 101:
mixer1.gain(1, (value * DIV127));
break;
case 102:
mixer1.gain(2, (value * DIV127));
break;
case 103:
switch (value) {
case 0:
octave = 24;
break;
case 1:
octave = 12;
break;
case 2:
octave = 0;
break;
case 3:
octave = -12;
break;
case 4:
octave = -24;
break;
}
oscSet();
break;
case 104:
envelope1.attack(3000 * (value * DIV127));
break;
case 105:
envelope1.decay(3000 * (value * DIV127));
break;
case 106:
envelope1.sustain(value * DIV127);
break;
case 107:
envelope1.release(3000 * (value * DIV127));
break;
case 108:
switch (value) {
case 0:
waveform1.begin(WAVEFORM_SINE);
break;
case 1:
waveform1.begin(WAVEFORM_TRIANGLE);
break;
case 2:
waveform1.begin(WAVEFORM_SAWTOOTH);
break;
case 3:
waveform1.begin(WAVEFORM_PULSE);
break;
}
break;
case 109:
switch (value) {
case 0:
waveform2.begin(WAVEFORM_SINE);
break;
case 1:
waveform2.begin(WAVEFORM_TRIANGLE);
break;
case 2:
waveform2.begin(WAVEFORM_SAWTOOTH);
break;
case 3:
waveform2.begin(WAVEFORM_PULSE);
break;
}
case 110:
detuneFactor = 1 - (0.05 * (value * DIV127));
oscSet();
break;
case 111:
FILfactor = value * DIV127;
FILfreq = 10000 * (value * DIV127);
if (LFOmodeSelect < 1 || LFOmodeSelect > 5)filter1.frequency(FILfreq);
break;
case 112:
filter1.resonance((4.3 * (value * DIV127)) + 0.7);
break;
case 113:
if (value <= 12 && value > 0) bendRange = value;
break;
case 114:
{
float xSpeed = value * DIV127;
xSpeed = pow(100, (xSpeed - 1));
LFOspeed = (70000 * xSpeed);
break;
}
case 115:
LFOdepth = value * DIV127;
break;
case 116:
LFOmodeSelect = value;
break;
}
}
void LFOupdate(bool retrig, byte mode, float FILtop, float FILbottom) {
static float LFO = 0;
static unsigned long LFOtime = 0;
static bool LFOdirection = false;
unsigned long currentMicros = micros();
static bool LFOstop = false;
static float LFOrange = 0;
static byte oldMode = 0;
static bool retriggered = false;
if (retrig == true) retriggered = true;
if (currentMicros - LFOtime >= LFOspeed) {
LFOtime = currentMicros;
if (mode != oldMode) {
if (mode == 0 || mode == 8) {
LFOpitch = 1;
oscSet();
filter1.frequency(FILfreq);
}
else if (mode >= 1 || mode <= 7) {
LFOpitch = 1;
oscSet();
}
else if (mode >= 9 || mode <= 13) {
filter1.frequency(FILfreq);
}
oldMode = mode;
}
LFOrange = FILtop - FILbottom;
if (LFOrange < 0) LFOrange = 0;
// LFO Modes
switch (mode) {
case 0: //Filter OFF
return;
break;
case 1: //Filter FREE
filter1.frequency(10000 * ((LFOrange * LFO) + LFOdepth));
break;
case 2: //Filter DOWN
if (retriggered == true) {
LFOdirection = true;
LFO = 1.0;
}
filter1.frequency(10000 * ((LFOrange * LFO) + LFOdepth));
break;
case 3: //Filter UP
if (retriggered == true) {
LFOdirection = false;
LFO = 0;
}
filter1.frequency(10000 * ((LFOrange * LFO) + LFOdepth));
break;
case 4: //Filter 1-DN
if (retriggered == true) {
LFOstop = false;
LFOdirection = true;
LFO = 1.0;
}
if (LFOstop == false) filter1.frequency(10000 * ((LFOrange * LFO) + LFOdepth));
break;
case 5: //Filter 1-UP
if (retriggered == true) {
LFOstop = false;
LFOdirection = false;
LFO = 0;
}
if (LFOstop == false) filter1.frequency(10000 * ((LFOrange * LFO) + LFOdepth));
break;
case 8: //Pitch OFF
return;
break;
case 9: //Pitch FREE
LFOpitch = (LFO * LFOdepth) + 1;
oscSet();
break;
case 10: //Pitch DOWN
if (retriggered == true) {
LFOdirection = true;
LFO = 1.0;
}
LFOpitch = (LFO * LFOdepth) + 1;
oscSet();
break;
case 11: //Pitch UP
if (retriggered == true) {
LFOdirection = false;
LFO = 0;
}
LFOpitch = (LFO * LFOdepth) + 1;
oscSet();
break;
case 12: //Pitch 1-DN
if (retriggered == true) {
LFOstop = false;
LFOdirection = true;
LFO = 1.0;
}
if (LFOstop == false) {
LFOpitch = (LFO * LFOdepth) + 1;
oscSet();
}
break;
case 13: //Pitch 1-UP
if (retriggered == true) {
LFOstop = false;
LFOdirection = false;
LFO = 0;
}
if (LFOstop == false) {
LFOpitch = (LFO * LFOdepth) + 1;
oscSet();
}
break;
}
retriggered = false;
// Update LFO
if (LFOdirection == false) { //UP
LFO = (LFO + 0.01);
if (LFO >= 1) {
LFOdirection = true;
LFOstop = true;
}
}
if (LFOdirection == true) { //Down
LFO = (LFO - 0.01);
if (LFO <= 0) {
LFOdirection = false;
LFOstop = true;
}
}
}
}
int allocateVoice(byte channel, byte note) {
int i;
int nonfree_voices = stopped_voices + used_voices;
if (nonfree_voices < TOTAL_VOICES) {
for (i = nonfree_voices; i < TOTAL_VOICES && voices[i].channel != channel; ++i);
if (i < TOTAL_VOICES) {
voice_t temp = voices[i];
voices[i] = voices[nonfree_voices];
voices[nonfree_voices] = temp;
}
i = nonfree_voices;
used_voices++;
}
else {
if (stopped_voices) {
i = evict_voice % stopped_voices;
voice_t temp = voices[i];
stopped_voices--;
voices[i] = voices[stopped_voices];
voices[stopped_voices] = temp;
used_voices++;
i = stopped_voices;
}
else
i = evict_voice;
}
voices[i].channel = channel;
voices[i].note = note;
evict_voice++;
evict_voice %= TOTAL_VOICES;
return voices[i].wavetable_id;
}
int findVoice(byte channel, byte note) {
int i;
//find match
int nonfree_voices = stopped_voices + used_voices;
for (i = stopped_voices; i < nonfree_voices && !(voices[i].channel == channel && voices[i].note == note); ++i);
//return TOTAL_VOICES if no match
if (i == (nonfree_voices)) return TOTAL_VOICES;
voice_t temp = voices[i];
voices[i] = voices[stopped_voices];
voices[stopped_voices] = temp;
--used_voices;
return voices[stopped_voices++].wavetable_id;
}
void freeVoices() {
for (int i = 0; i < stopped_voices; i++)
if (!true/*wavetable[voices[i].wavetable_id].isPlaying() == false*/) {
voice_t temp = voices[i];
--stopped_voices;
voices[i] = voices[stopped_voices];
int nonfree_voices = stopped_voices + used_voices;
voices[stopped_voices] = voices[nonfree_voices];
voices[nonfree_voices] = temp;
}
}
const char* note_map[] = {
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
};
void printVoices() {
static int last_notes_played = notes_played;
if (last_notes_played == notes_played)
return;
last_notes_played = notes_played;
int usage = AudioProcessorUsage();
Serial.printf("\nCPU:%03i voices:%02i CPU/Voice:%02i evict:%02i", usage, used_voices, usage / used_voices, evict_voice);
for (int i = 0; i < used_voices; ++i)
Serial.printf(" %02hhu %-2s", voices[i].channel, note_map[voices[i].note % 12]);
}
void Init()
{
AudioMemory(120);
usbMIDI.setHandleControlChange(myControlChange);
usbMIDI.setHandleNoteOff(myNoteOff);
usbMIDI.setHandleNoteOn(myNoteOn);
usbMIDI.setHandlePitchChange(myPitchBend);
sgtl5000_1.enable();
sgtl5000_1.volume(0.52);
sgtl5000_1.dacVolumeRamp();
envelopeBank[0] = envelope1;
envelopeBank[1] = envelope2;
envelopeBank[2] = envelope3;
envelopeBank[3] = envelope4;
for (int i = 0; i < TOTAL_VOICES; i++)
{
oscBank1[i].begin(WAVEFORM_SINE);
oscBank1[i].amplitude(1.0);
oscBank1[i].frequency(82.41);
oscBank1[i].pulseWidth(0.15);
oscBank2[i].begin(WAVEFORM_SINE);
oscBank2[i].amplitude(1.0);
oscBank2[i].frequency(82.41);
oscBank2[i].pulseWidth(0.15);
noiseBank[i].amplitude(0.3);
for (int j = 0; j < 4; j++)
{
oscMixerBank[i].gain(j, 1.0);
}
envelopeBank[i].delay(0);
envelopeBank[i].attack(0);
envelopeBank[i].decay(0);
envelopeBank[i].sustain(1);
envelopeBank[i].release(500.0);
envelopeBank[i].releaseNoteOn(7.0);
mixer5.gain(i, 1.0);
}
}