#include <Arduino.h>
#include <dbAudio.h>
#include <dbConfig.h>
#include <dbMIDICommands.h>
#include <dbUtility.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
// GUItool: begin automatically generated code
AudioSynthWaveform waveform3; // xy=121.17647171020508,789.7479152679443
AudioSynthWaveform waveform0; // xy=123.6470947265625,682.8235549926758
AudioSynthWaveform waveform1; // xy=123.6470947265625,717.8235549926758
AudioSynthWaveform waveform2; // xy=123.6470947265625,752.8235549926758
AudioInputI2S i2sIn; // xy=124.6470947265625,590.8235549926758
AudioMixer4 mixerVS1053; // xy=336.6470947265625,602.8235549926758
AudioMixer4 mixer_Waves; // xy=338.6470947265625,692.8235549926758
AudioSynthWaveformDc dc_CutoffFreq; // xy=340.6470947265625,751.8235549926758
AudioSynthWaveformDc dc_Resonance; // xy=341.6470947265625,790.8235549926758
AudioFilterLadder ladder1; // xy=510.6470947265625,742.8235549926758
AudioMixer4 mixerSynths; // xy=692.5042152404785,614.2520980834961
AudioEffectFreeverbStereo freeverbs1; // xy=1305.0757331848145,597.5376958847046
AudioMixer4 mixerLeft; // xy=1335.0756072998047,235.39498710632324
AudioMixer4 mixerRight; // xy=1346.5042686462402,995.8235321044922
AudioOutputUSB usbOut; // xy=1629.6470642089844,657.8235473632812
AudioOutputI2S i2sOut; // xy=1630.6470642089844,607.8235473632812
AudioConnection patchCord1(waveform3, 0, mixer_Waves, 3);
AudioConnection patchCord2(waveform0, 0, mixer_Waves, 0);
AudioConnection patchCord3(waveform1, 0, mixer_Waves, 1);
AudioConnection patchCord4(waveform2, 0, mixer_Waves, 2);
AudioConnection patchCord5(i2sIn, 0, mixerVS1053, 0);
AudioConnection patchCord6(i2sIn, 1, mixerVS1053, 1);
AudioConnection patchCord7(mixerVS1053, 0, mixerSynths, 0);
AudioConnection patchCord8(mixer_Waves, 0, ladder1, 0);
AudioConnection patchCord9(dc_CutoffFreq, 0, ladder1, 1);
AudioConnection patchCord10(dc_Resonance, 0, ladder1, 2);
AudioConnection patchCord11(ladder1, 0, mixerSynths, 1);
AudioConnection patchCord12(mixerSynths, 0, mixerLeft, 0);
AudioConnection patchCord13(mixerSynths, 0, mixerRight, 0);
AudioConnection patchCord14(mixerSynths, freeverbs1);
AudioConnection patchCord15(freeverbs1, 0, mixerLeft, 1);
AudioConnection patchCord16(freeverbs1, 1, mixerRight, 1);
AudioConnection patchCord17(mixerLeft, 0, i2sOut, 0);
AudioConnection patchCord18(mixerLeft, 0, usbOut, 0);
AudioConnection patchCord19(mixerRight, 0, i2sOut, 1);
AudioConnection patchCord20(mixerRight, 0, usbOut, 1);
AudioControlSGTL5000 sgtl5000_1; // xy=117.6470947265625,469.8235549926758
// GUItool: end automatically generated code
SGTL5000Audio audio;
TeensyWaveformTypes getTeensyWaveformType(dbAudioWaveformTypes waveformType)
{
switch (waveformType)
{
case dbWAVEFORM_BANDLIMIT_SAWTOOTH:
return TEENSY_WAVEFORM_BANDLIMIT_SAWTOOTH;
case dbWAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE:
return TEENSY_WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE;
case dbWAVEFORM_BANDLIMIT_SQUARE:
return TEENSY_WAVEFORM_BANDLIMIT_SQUARE;
case dbWAVEFORM_TRIANGLE_VARIABLE:
return TEENSY_WAVEFORM_TRIANGLE_VARIABLE;
case dbWAVEFORM_SINE:
return TEENSY_WAVEFORM_SINE;
case dbWAVEFORM_BANDLIMIT_PULSE:
return TEENSY_WAVEFORM_BANDLIMIT_PULSE;
default:
return TEENSY_WAVEFORM_UNKNOWN;
}
}
////////////////////////////////////////////////////////////
// SGTL5000 codec (headphone / line output)
////////////////////////////////////////////////////////////
#define DEFAULT_AUDIO_LEVEL 0.5
void SGTL5000Audio::setup(void)
{
for (size_t i = 0; i < 4; i++) {
mixerLeft.gain(i, 0);
mixerRight.gain(i, 0);
}
AudioMemory(50);
sgtl5000_1.enable();
setVolume();
sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN);
delay(1000); // this apears to avoid a click when the audio is enabled
}
void SGTL5000Audio::setVolume(void)
{
sgtl5000_1.volume(headphoneVolume() / 127.0);
}
void setSoundOutput(bool on)
{
for (size_t i = 0; i < 4; i++) {
mixerLeft.gain(i, on ? DEFAULT_AUDIO_LEVEL : 0);
mixerRight.gain(i, on ? DEFAULT_AUDIO_LEVEL : 0);
}
}
////////////////////////////////////////////////////////////
// Synth voicing: ladder filter, GM/wave mix, reverb
////////////////////////////////////////////////////////////
float _filterResonance = 0.3;
float _filterFrequency = 800;
float _filterOctave = 2.6;
void setFilter(void)
{
_filterOctave = filterSemitones() / 12.0;
_filterResonance = filterResonancePercent() / 100.0;
ladder1.resonance(_filterResonance);
ladder1.frequency(_filterFrequency);
ladder1.octaveControl(_filterOctave);
}
float _waveInputMixFloat = 0;
float _VS1053InputMixFloat = 0;
void setInputMix(uint8_t mixPercent)
{
_waveInputMixFloat = (float)mixPercent / 100.0;
_VS1053InputMixFloat = constrainFloat(1.0 - ((float)mixPercent / 100.0), 0.0, .5); // Constrain VS1053 to 50% volume to avoid hiss
mixerSynths.gain(0, _VS1053InputMixFloat); // VS1053
mixerSynths.gain(1, _waveInputMixFloat); // Waves & Filter
}
float _audioReverbAmount = 0.0;
void setAudioReverbMix(void)
{
freeverbs1.roomsize(_audioReverbAmount);
freeverbs1.damping(_audioReverbAmount);
mixerLeft.gain(0, 1 - _audioReverbAmount);
mixerRight.gain(0, 1 - _audioReverbAmount);
mixerLeft.gain(1, _audioReverbAmount);
mixerRight.gain(1, _audioReverbAmount);
}
void setAudioReverbAmount(float audioReverbAmount)
{
if (audioReverbAmount != _audioReverbAmount)
{
_audioReverbAmount = audioReverbAmount;
setAudioReverbMix();
}
}
void dbAudioSynth::setup(void)
{
if (!isSetUp)
{
for (size_t i = 0; i < 4; i++) {
mixerLeft.gain(i, 0);
mixerRight.gain(i, 0);
}
usbOut.begin();
setPatchParams();
setInputMix(presetSynthMixPercent());
setAudioReverbAmount(0);
isSetUp = true;
}
}
float freq = 0;
int inboundPitchBend = 0;
int lastRPN_MSB = 0;
int lastRPN_LSB = 0;
int lastDataEntry_MSB = 0;
float currentPitchBendRange = 12;
void setWaves(void)
{
waveform0.begin(getTeensyWaveformType((dbAudioWaveformTypes)wave1Form().getValue()));
waveform1.begin(getTeensyWaveformType((dbAudioWaveformTypes)wave2Form().getValue()));
waveform2.begin(getTeensyWaveformType((dbAudioWaveformTypes)wave3Form().getValue()));
waveform3.begin(getTeensyWaveformType((dbAudioWaveformTypes)wave4Form().getValue()));
waveform0.pulseWidth(wave1DutyCycle() / 100.0);
waveform1.pulseWidth(wave2DutyCycle() / 100.0);
waveform2.pulseWidth(wave3DutyCycle() / 100.0);
waveform3.pulseWidth(wave4DutyCycle() / 100.0);
// scale the volume of all waveformes such that the sum does not exceed 1.0
float waveVolumeSum = wave1Volume() + wave2Volume() + wave3Volume() + wave4Volume();
mixer_Waves.gain(0, (float)wave1Volume() / waveVolumeSum);
mixer_Waves.gain(1, (float)wave2Volume() / waveVolumeSum);
mixer_Waves.gain(2, (float)wave3Volume() / waveVolumeSum);
mixer_Waves.gain(3, (float)wave4Volume() / waveVolumeSum);
// consistent, moderate level for all waveforms
waveform0.amplitude(.5);
waveform1.amplitude(.5);
waveform2.amplitude(.5);
waveform3.amplitude(.5);
}
// Pushes every configurable IntValue into the synth objects. Called on any internal/external
// parameter change and on preset/patch change.
// TODO: update only the parameters that actually changed rather than re-pushing all of them.
void setPatchParams(void)
{
setWaves();
setFilter();
setInputMix(presetSynthMixPercent());
setAudioReverbAmount(0);
}
////////////////////////////////////////////////////////////
// MIDI consumer: note / CC / pitch-bend -> synth voices
////////////////////////////////////////////////////////////
void dbAudioSynth::consume(const dbMsg &msg)
{
static int lastNote = 0;
static int lastLevel = 0;
if (!isSetUp)
{
setup();
}
static float ex = 0;
switch (msg.type())
{
case CONTROL_CHANGE:
switch (msg.value1())
{
case BREATH_CONTROLLER:
case EXPRESSION_CONTROLLER:
ex = (float)msg.value2() / 127.0;
ex = ex * 2.0 - 1.0; // now a value between -1 and +1
dc_CutoffFreq.amplitude(ex, 5);
dc_Resonance.amplitude(ex / 20, 5);
lastLevel = msg.value2();
break;
case EFFECT_DEPTH_1:
setAudioReverbAmount((float)msg.value2() / 128.0);
break;
case RPN_MSB:
lastRPN_MSB = msg.value2();
break;
case RPN_LSB:
lastRPN_LSB = msg.value2();
break;
case DATA_ENTRY_MSB:
lastDataEntry_MSB = msg.value2();
if (lastRPN_MSB == 0 && lastRPN_LSB == 0)
{
currentPitchBendRange = lastDataEntry_MSB;
}
break;
case DATA_ENTRY_LSB:
// LSB fine-tunes the pitch-bend range; MSB resolution (whole semitones) is sufficient here.
break;
}
break;
case NOTE_ON:
lastNote = msg.value1();
freq = dbMIDI::getFrequency(lastNote);
_filterFrequency = freq;
setFilter();
break;
case NOTE_OFF:
break;
case PITCH_BEND:
inboundPitchBend = ((msg.value2() * 128) + msg.value1()) - 8192; // MSB * 128 + LSB - 8192 to create a value between -8192 and 8191
break;
case PROGRAM_CHANGE:
setPatchParams();
break;
}
if (lastLevel > 0)
{
mixer_Waves.gain(0, wave1Volume() / 100.0);
mixer_Waves.gain(1, wave2Volume() / 100.0);
mixer_Waves.gain(2, wave3Volume() / 100.0);
mixer_Waves.gain(3, wave4Volume() / 100.0);
float transposedFreq = freq * pow(2, ((float)wave1Semitones().getValue() + ((float)wave1Cents().getValue() / 100.0)) / 12.0 );
float pitchBendSemitones = (inboundPitchBend / (16383.0f / 2.0f)) * currentPitchBendRange;
float bentFreq = transposedFreq * pow(2, pitchBendSemitones / 12.0f);
waveform0.frequency(bentFreq);
waveform1.frequency(bentFreq * pow(2, (wave2Semitones() + (wave2Cents() / 100.0)) / 12.0));
waveform2.frequency(bentFreq * pow(2, (wave3Semitones() + (wave3Cents() / 100.0)) / 12.0));
waveform3.frequency(bentFreq * pow(2, (wave4Semitones() + (wave4Cents() / 100.0)) / 12.0));
}
else
{
mixer_Waves.gain(0, 0);
mixer_Waves.gain(1, 0);
mixer_Waves.gain(2, 0);
mixer_Waves.gain(3, 0);
}
}
static const char *dbAudioWaveformNames[] = {
"Saw",
"Reverse Saw",
"Square",
"Triangle",
"Sine",
"Pulse Mod",
};
char _badWaveFormString[20];
const char *getWaveformName(uint8_t waveformType)
{
if (waveformType < sizeof(dbAudioWaveformNames) / sizeof(dbAudioWaveformNames[0]))
{
return dbAudioWaveformNames[waveformType];
}
else
{
snprintf(_badWaveFormString, sizeof(_badWaveFormString), "Unknown Wave %d", waveformType);
return _badWaveFormString;
}
}
////////////////////////////////////////////////////////////
// PRESET IntValues
////////////////////////////////////////////////////////////
static void _presetPatchNumberChanged(int newValue, Cause cause)
{
if (cause == Cause::Edit)
{
loadCurrentPatch(newValue);
setPatchParams();
} else {
#if defined(ERROR_TRACKING)
Serial.printf("Unexpected cause: %d\n", cause);
#endif
}
}
static IntValue _presetPatchNumber = IntValue(0).range(0, 8).onChange(_presetPatchNumberChanged);
IntValue &presetPatchNumber(void) { return _presetPatchNumber; }
// Preset Synth Mix Percent
static void _describePresetSynthMixPercent(int amt, char *buffer, size_t capacity)
{
String s = String(100 - amt) + " : " + String(amt);
strncpy(buffer, s.c_str(), capacity);
}
static void _presetSynthMixPercentChanged(int newValue, Cause cause)
{
if (cause == Cause::Edit)
{
setInputMix(newValue);
}
}
static IntValue _presetSynthMixPercent = IntValue(100).range(0, 100).step(10).describeWith(_describePresetSynthMixPercent).onChange(_presetSynthMixPercentChanged);
IntValue &presetSynthMixPercent(void) { return _presetSynthMixPercent; }
////////////////////////////////////////////////////////////
// SYNTH PATCH IntValues
////////////////////////////////////////////////////////////
// Filter Semitones
static void _describeFilterSemitones(int amt, char *buffer, size_t capacity)
{
String s = String(amt) + " semitones";
strncpy(buffer, s.c_str(), capacity);
}
static IntValue _filterSemitones = IntValue(30).range(0, 36).describeWith(_describeFilterSemitones);
IntValue &filterSemitones(void) { return _filterSemitones; }
// Filter Resonance Tenths
static void _describeFilterResonancePercent(int amt, char *buffer, size_t capacity)
{
String s = String(amt) + "%";
strncpy(buffer, s.c_str(), capacity);
}
static IntValue _filterResonancePercent = IntValue(5).range(0, 100).describeWith(_describeFilterResonancePercent);
IntValue &filterResonancePercent(void) { return _filterResonancePercent; }
static IntValue _wave1Form = IntValue(0).range(0, 5).describeWith(dbAudioWaveformNames);
IntValue &wave1Form(void) { return _wave1Form; }
static IntValue _wave2Form = IntValue(0).range(0, 5).describeWith(dbAudioWaveformNames);
IntValue &wave2Form(void) { return _wave2Form; }
static IntValue _wave3Form = IntValue(0).range(0, 5).describeWith(dbAudioWaveformNames);
IntValue &wave3Form(void) { return _wave3Form; }
static IntValue _wave4Form = IntValue(0).range(0, 5).describeWith(dbAudioWaveformNames);
IntValue &wave4Form(void) { return _wave4Form; }
// Wave Volume
static void _describeWaveVolume(int amt, char *buffer, size_t capacity)
{
String wave1VolumeString = String(amt) + "%";
strncpy(buffer, wave1VolumeString.c_str(), capacity);
}
static IntValue _wave1Volume = IntValue(100).range(0, 100).describeWith(_describeWaveVolume);
IntValue &wave1Volume(void) { return _wave1Volume; }
static IntValue _wave2Volume = IntValue(0).range(0, 100).describeWith(_describeWaveVolume);
IntValue &wave2Volume(void) { return _wave2Volume; }
static IntValue _wave3Volume = IntValue(0).range(0, 100).describeWith(_describeWaveVolume);
IntValue &wave3Volume(void) { return _wave3Volume; }
static IntValue _wave4Volume = IntValue(0).range(0, 100).describeWith(_describeWaveVolume);
IntValue &wave4Volume(void) { return _wave4Volume; }
// Wave Semitone
static void _describeWaveSemitones(int amt, char *buffer, size_t capacity)
{
String wave1SemitonesString = String(amt) + " semitones";
strncpy(buffer, wave1SemitonesString.c_str(), capacity);
}
static IntValue _wave1Semitones = IntValue(0).range(-36, 36).describeWith(_describeWaveSemitones);
IntValue &wave1Semitones(void) { return _wave1Semitones; }
static IntValue _wave2Semitones = IntValue(0).range(-36, 36).describeWith(_describeWaveSemitones);
IntValue &wave2Semitones(void) { return _wave2Semitones; }
static IntValue _wave3Semitones = IntValue(0).range(-36, 36).describeWith(_describeWaveSemitones);
IntValue &wave3Semitones(void) { return _wave3Semitones; }
static IntValue _wave4Semitones = IntValue(0).range(-36, 36).describeWith(_describeWaveSemitones);
IntValue &wave4Semitones(void) { return _wave4Semitones; }
// Wave Cents
static void _describeWaveCents(int amt, char *buffer, size_t capacity)
{
String wave1CentsString = String(amt) + " cents";
strncpy(buffer, wave1CentsString.c_str(), capacity);
}
static IntValue _wave1Cents = IntValue(0).range(-99, 99).describeWith(_describeWaveCents);
IntValue &wave1Cents(void) { return _wave1Cents; }
static IntValue _wave2Cents = IntValue(0).range(-99, 99).describeWith(_describeWaveCents);
IntValue &wave2Cents(void) { return _wave2Cents; }
static IntValue _wave3Cents = IntValue(0).range(-99, 99).describeWith(_describeWaveCents);
IntValue &wave3Cents(void) { return _wave3Cents; }
static IntValue _wave4Cents = IntValue(0).range(-99, 99).describeWith(_describeWaveCents);
IntValue &wave4Cents(void) { return _wave4Cents; }
// Wave Duty Cycle
static void _describeWaveDutyCycle(int amt, char *buffer, size_t capacity)
{
String wave1DutyCycleString = String(amt) + "%";
strncpy(buffer, wave1DutyCycleString.c_str(), capacity);
}
static IntValue _wave1DutyCycle = IntValue(50).range(10, 90).step(10).describeWith(_describeWaveDutyCycle);
IntValue &wave1DutyCycle(void) { return _wave1DutyCycle; }
static IntValue _wave2DutyCycle = IntValue(50).range(10, 90).step(10).describeWith(_describeWaveDutyCycle);
IntValue &wave2DutyCycle(void) { return _wave2DutyCycle; }
static IntValue _wave3DutyCycle = IntValue(50).range(10, 90).step(10).describeWith(_describeWaveDutyCycle);
IntValue &wave3DutyCycle(void) { return _wave3DutyCycle; }
static IntValue _wave4DutyCycle = IntValue(50).range(10, 90).step(10).describeWith(_describeWaveDutyCycle);
IntValue &wave4DutyCycle(void) { return _wave4DutyCycle; }
void printAudioParams(void)
{
#if defined(UTILITY_TRACKING)
Serial.printf("Patch: %d\n", presetPatchNumber().getValue());
Serial.printf("Synth Mix: %d\n", presetSynthMixPercent().getValue());
Serial.printf("\twave1Form: %d\twave1Volume: %d\twave1Semitones: %d\twave1Cents: %d\twave1DutyCycle: %d\n"
"\twave2Form: %d\twave2Volume: %d\twave2Semitones: %d\twave2Cents: %d\twave2DutyCycle: %d\n"
"\twave3Form: %d\twave3Volume: %d\twave3Semitones: %d\twave3Cents: %d\twave3DutyCycle: %d\n"
"\twave4Form: %d\twave4Volume: %d\twave4Semitones: %d\twave4Cents: %d\twave4DutyCycle: %d\n",
wave1Form().getValue(), wave1Volume().getValue(), wave1Semitones().getValue(), wave1Cents().getValue(), wave1DutyCycle().getValue(),
wave2Form().getValue(), wave2Volume().getValue(), wave2Semitones().getValue(), wave2Cents().getValue(), wave2DutyCycle().getValue(),
wave3Form().getValue(), wave3Volume().getValue(), wave3Semitones().getValue(), wave3Cents().getValue(), wave3DutyCycle().getValue(),
wave4Form().getValue(), wave4Volume().getValue(), wave4Semitones().getValue(), wave4Cents().getValue(), wave4DutyCycle().getValue());
Serial.printf("\n\tfilterSemitones: %d\tfilterResonanceTenths: %d\n", filterSemitones().getValue(), filterResonancePercent().getValue());
Serial.printf("\taudioReverbAmount: %1.2f (reverbAmount: %d)\n", _audioReverbAmount, reverbAmount().getValue());
Serial.printf("\tfreq: %1.2f\tfilterFrequency: %1.2f\tfilterOctave: %1.2f\tfilterResonance: %1.2f\n", freq , _filterFrequency, _filterOctave, _filterResonance);
Serial.printf("\n");
#endif
}