Created
March 1, 2021 02:05
-
-
Save wcalvert/2a5b5f1c9d0d4066cab27fa6ac526a60 to your computer and use it in GitHub Desktop.
Mutable Instruments envelopes adapted to Audio library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#include "envlut.h" | |
#include "arm_math.h" | |
// Todo: handle noteOff occurring before reaching SUSTAIN. | |
void AudioEffectEnvelopeLUT::noteOn(void) { | |
__disable_irq(); | |
sample = 0; | |
count = 0; | |
state = ATTACK; | |
__enable_irq(); | |
} | |
void AudioEffectEnvelopeLUT::noteOff(void) { | |
__disable_irq(); | |
if(state != IDLE) { | |
count = 0; | |
sample = SAMPLES-1.0f; | |
state = RELEASE; | |
} | |
__enable_irq(); | |
} | |
void AudioEffectEnvelopeLUT::update(void) { | |
audio_block_t *block = receiveWritable(); | |
if (!block) return; | |
if (state == IDLE) { | |
AudioStream::release(block); | |
return; | |
} | |
float32_t level[AUDIO_BLOCK_SAMPLES] = {0.0f}; | |
for(int i=0; i<AUDIO_BLOCK_SAMPLES; i++) { | |
switch(state) { | |
case IDLE: | |
level[i] = 0; | |
break; | |
case ATTACK: | |
if(count < a_samples) { | |
level[i] = (float32_t)block->data[i] * LUT[(uint32_t)sample]; | |
sample += a_inc; | |
} else { | |
// duplicate code - we have to do the decay processing here or it will be skipped due to loop | |
level[i] = (float32_t)block->data[i] * (sustain + ((1.0f-sustain) * LUT[(uint32_t)sample])); | |
// now setup for next state | |
count = 0; | |
sample = SAMPLES-1.0f; | |
state = DECAY; | |
} | |
break; | |
case DECAY: | |
if(count < d_samples) { | |
level[i] = (float32_t)block->data[i] * (sustain + ((1.0f-sustain) * LUT[(uint32_t)sample])); | |
sample -= d_inc; | |
} else { | |
// duplicate code - we have to do the sustian processing here or it will be skipped due to loop | |
level[i] = (float32_t)block->data[i] * sustain; | |
// now setup for next state | |
sample = 0; | |
count = 0; | |
state = SUSTAIN; | |
} | |
break; | |
case SUSTAIN: | |
level[i] = (float32_t)block->data[i] * sustain; | |
break; | |
case RELEASE: | |
if(count < r_samples) { | |
level[i] = (float32_t)block->data[i] * sustain * LUT[(uint32_t)sample]; | |
sample -= r_inc; | |
} else { | |
sample = 0; | |
state = IDLE; | |
} | |
break; | |
} | |
count++; | |
} | |
// Hack alert, I was getting an output glitch when trying to cast the calculations to int16 without this intermediate step. | |
// Maybe the compiler got confused and let something wrap around negative or something? | |
for(int i=0; i<AUDIO_BLOCK_SAMPLES; i++) { | |
//Serial.println(level[i]); | |
block->data[i] = (int16_t)level[i]; | |
} | |
transmit(block); | |
AudioStream::release(block); | |
} | |
bool AudioEffectEnvelopeLUT::isActive() { | |
EnvelopeState current_state = *(volatile EnvelopeState *)&state; | |
if (current_state == IDLE) return false; | |
return true; | |
} | |
bool AudioEffectEnvelopeLUT::isSustain() { | |
EnvelopeState current_state = *(volatile EnvelopeState *)&state; | |
if (current_state == SUSTAIN) return true; | |
return false; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef effect_envelope_h_ | |
#define effect_envelope_h_ | |
#include "Arduino.h" | |
#include "Audio.h" | |
#include "arm_math.h" | |
const float32_t lut_env_expo[] = { | |
0.00000, 0.01550, 0.03077, 0.04579, | |
0.06059, 0.07515, 0.08949, 0.10361, | |
0.11750, 0.13118, 0.14465, 0.15792, | |
0.17097, 0.18382, 0.19648, 0.20893, | |
0.22120, 0.23327, 0.24516, 0.25686, | |
0.26838, 0.27973, 0.29089, 0.30189, | |
0.31271, 0.32337, 0.33386, 0.34418, | |
0.35435, 0.36436, 0.37422, 0.38392, | |
0.39347, 0.40287, 0.41213, 0.42124, | |
0.43022, 0.43905, 0.44775, 0.45631, | |
0.46474, 0.47304, 0.48121, 0.48925, | |
0.49717, 0.50496, 0.51264, 0.52019, | |
0.52763, 0.53496, 0.54217, 0.54926, | |
0.55625, 0.56313, 0.56991, 0.57657, | |
0.58314, 0.58960, 0.59596, 0.60223, | |
0.60839, 0.61447, 0.62044, 0.62633, | |
0.63212, 0.63782, 0.64344, 0.64897, | |
0.65441, 0.65977, 0.66504, 0.67024, | |
0.67535, 0.68038, 0.68534, 0.69021, | |
0.69502, 0.69975, 0.70440, 0.70898, | |
0.71350, 0.71794, 0.72231, 0.72662, | |
0.73085, 0.73503, 0.73913, 0.74318, | |
0.74716, 0.75108, 0.75494, 0.75874, | |
0.76248, 0.76616, 0.76979, 0.77336, | |
0.77687, 0.78033, 0.78373, 0.78709, | |
0.79039, 0.79364, 0.79684, 0.79999, | |
0.80309, 0.80614, 0.80915, 0.81211, | |
0.81502, 0.81789, 0.82071, 0.82349, | |
0.82623, 0.82892, 0.83157, 0.83418, | |
0.83675, 0.83929, 0.84178, 0.84423, | |
0.84665, 0.84902, 0.85136, 0.85367, | |
0.85594, 0.85817, 0.86037, 0.86253, | |
0.86466, 0.86676, 0.86883, 0.87086, | |
0.87286, 0.87484, 0.87678, 0.87869, | |
0.88057, 0.88242, 0.88424, 0.88604, | |
0.88780, 0.88954, 0.89126, 0.89294, | |
0.89460, 0.89623, 0.89784, 0.89943, | |
0.90099, 0.90252, 0.90403, 0.90552, | |
0.90699, 0.90843, 0.90985, 0.91124, | |
0.91262, 0.91398, 0.91531, 0.91662, | |
0.91792, 0.91919, 0.92044, 0.92167, | |
0.92289, 0.92408, 0.92526, 0.92642, | |
0.92756, 0.92868, 0.92979, 0.93088, | |
0.93195, 0.93300, 0.93404, 0.93507, | |
0.93607, 0.93706, 0.93804, 0.93900, | |
0.93995, 0.94088, 0.94179, 0.94270, | |
0.94358, 0.94446, 0.94532, 0.94617, | |
0.94700, 0.94782, 0.94863, 0.94943, | |
0.95021, 0.95098, 0.95174, 0.95249, | |
0.95323, 0.95395, 0.95467, 0.95537, | |
0.95606, 0.95674, 0.95741, 0.95808, | |
0.95873, 0.95936, 0.95999, 0.96062, | |
0.96123, 0.96183, 0.96242, 0.96300, | |
0.96358, 0.96414, 0.96470, 0.96524, | |
0.96578, 0.96631, 0.96683, 0.96735, | |
0.96786, 0.96835, 0.96884, 0.96933, | |
0.96980, 0.97027, 0.97073, 0.97119, | |
0.97163, 0.97207, 0.97250, 0.97293, | |
0.97335, 0.97376, 0.97417, 0.97457, | |
0.97497, 0.97535, 0.97574, 0.97611, | |
0.97648, 0.97685, 0.97721, 0.97756, | |
0.97791, 0.97825, 0.97859, 0.97892, | |
0.97925, 0.97957, 0.97988, 0.98020, | |
0.98050, 0.98081, 0.98110, 0.98140, | |
0.98140 | |
}; | |
const float32_t lut_env_quartic[] = { | |
0.00000, 0.00000, 0.00000, 0.00000, | |
0.00000, 0.00000, 0.00000, 0.00001, | |
0.00001, 0.00001, 0.00002, 0.00003, | |
0.00004, 0.00005, 0.00006, 0.00008, | |
0.00010, 0.00012, 0.00015, 0.00018, | |
0.00021, 0.00025, 0.00029, 0.00034, | |
0.00039, 0.00044, 0.00050, 0.00057, | |
0.00064, 0.00072, 0.00081, 0.00090, | |
0.00100, 0.00111, 0.00123, 0.00135, | |
0.00148, 0.00163, 0.00178, 0.00194, | |
0.00211, 0.00229, 0.00248, 0.00268, | |
0.00289, 0.00311, 0.00335, 0.00360, | |
0.00386, 0.00413, 0.00442, 0.00472, | |
0.00503, 0.00536, 0.00570, 0.00606, | |
0.00644, 0.00683, 0.00723, 0.00765, | |
0.00809, 0.00855, 0.00902, 0.00952, | |
0.01003, 0.01056, 0.01111, 0.01167, | |
0.01226, 0.01287, 0.01350, 0.01415, | |
0.01482, 0.01552, 0.01624, 0.01698, | |
0.01774, 0.01853, 0.01934, 0.02017, | |
0.02103, 0.02192, 0.02283, 0.02377, | |
0.02473, 0.02572, 0.02674, 0.02779, | |
0.02886, 0.02997, 0.03110, 0.03226, | |
0.03345, 0.03467, 0.03593, 0.03721, | |
0.03853, 0.03988, 0.04126, 0.04267, | |
0.04412, 0.04560, 0.04712, 0.04867, | |
0.05026, 0.05188, 0.05354, 0.05523, | |
0.05697, 0.05874, 0.06054, 0.06239, | |
0.06428, 0.06620, 0.06817, 0.07017, | |
0.07222, 0.07431, 0.07643, 0.07861, | |
0.08082, 0.08308, 0.08538, 0.08773, | |
0.09012, 0.09255, 0.09503, 0.09756, | |
0.10013, 0.10275, 0.10542, 0.10814, | |
0.11090, 0.11372, 0.11658, 0.11950, | |
0.12246, 0.12547, 0.12854, 0.13166, | |
0.13483, 0.13805, 0.14133, 0.14466, | |
0.14805, 0.15149, 0.15499, 0.15854, | |
0.16215, 0.16581, 0.16954, 0.17332, | |
0.17716, 0.18106, 0.18502, 0.18904, | |
0.19312, 0.19726, 0.20146, 0.20572, | |
0.21005, 0.21444, 0.21889, 0.22341, | |
0.22799, 0.23264, 0.23736, 0.24214, | |
0.24698, 0.25190, 0.25688, 0.26193, | |
0.26705, 0.27224, 0.27750, 0.28283, | |
0.28823, 0.29371, 0.29925, 0.30487, | |
0.31056, 0.31633, 0.32217, 0.32808, | |
0.33407, 0.34014, 0.34628, 0.35250, | |
0.35880, 0.36517, 0.37163, 0.37816, | |
0.38477, 0.39147, 0.39824, 0.40510, | |
0.41203, 0.41906, 0.42616, 0.43335, | |
0.44062, 0.44798, 0.45542, 0.46295, | |
0.47056, 0.47826, 0.48605, 0.49393, | |
0.50190, 0.50995, 0.51810, 0.52633, | |
0.53466, 0.54308, 0.55159, 0.56019, | |
0.56889, 0.57768, 0.58657, 0.59555, | |
0.60463, 0.61380, 0.62307, 0.63243, | |
0.64190, 0.65146, 0.66112, 0.67089, | |
0.68075, 0.69071, 0.70078, 0.71094, | |
0.72121, 0.73159, 0.74206, 0.75264, | |
0.76333, 0.77412, 0.78502, 0.79602, | |
0.80713, 0.81835, 0.82968, 0.84112, | |
0.85266, 0.86432, 0.87609, 0.88797, | |
0.89996, 0.91206, 0.92428, 0.93661, | |
0.94906, 0.96162, 0.97430, 0.98709, | |
0.98709 | |
}; | |
const float32_t lut_raised_cosine[] = { | |
0.00000, 0.00004, 0.00015, 0.00034, | |
0.00060, 0.00094, 0.00135, 0.00184, | |
0.00241, 0.00305, 0.00376, 0.00455, | |
0.00541, 0.00635, 0.00736, 0.00845, | |
0.00961, 0.01084, 0.01215, 0.01353, | |
0.01498, 0.01651, 0.01811, 0.01978, | |
0.02153, 0.02335, 0.02524, 0.02720, | |
0.02923, 0.03133, 0.03350, 0.03575, | |
0.03806, 0.04044, 0.04290, 0.04542, | |
0.04801, 0.05066, 0.05339, 0.05618, | |
0.05904, 0.06196, 0.06496, 0.06801, | |
0.07114, 0.07432, 0.07757, 0.08089, | |
0.08427, 0.08771, 0.09121, 0.09477, | |
0.09840, 0.10208, 0.10583, 0.10963, | |
0.11349, 0.11742, 0.12140, 0.12543, | |
0.12952, 0.13367, 0.13788, 0.14213, | |
0.14645, 0.15081, 0.15523, 0.15970, | |
0.16422, 0.16879, 0.17341, 0.17808, | |
0.18280, 0.18757, 0.19238, 0.19724, | |
0.20215, 0.20710, 0.21210, 0.21713, | |
0.22221, 0.22734, 0.23250, 0.23771, | |
0.24295, 0.24823, 0.25355, 0.25891, | |
0.26430, 0.26973, 0.27519, 0.28069, | |
0.28622, 0.29179, 0.29738, 0.30300, | |
0.30866, 0.31434, 0.32005, 0.32579, | |
0.33156, 0.33734, 0.34316, 0.34900, | |
0.35486, 0.36074, 0.36664, 0.37257, | |
0.37851, 0.38447, 0.39045, 0.39644, | |
0.40245, 0.40848, 0.41452, 0.42057, | |
0.42663, 0.43271, 0.43879, 0.44489, | |
0.45099, 0.45710, 0.46322, 0.46934, | |
0.47547, 0.48160, 0.48773, 0.49386, | |
0.50000, 0.50614, 0.51227, 0.51840, | |
0.52453, 0.53066, 0.53678, 0.54290, | |
0.54901, 0.55511, 0.56121, 0.56729, | |
0.57337, 0.57943, 0.58548, 0.59152, | |
0.59755, 0.60356, 0.60955, 0.61553, | |
0.62149, 0.62743, 0.63336, 0.63926, | |
0.64514, 0.65100, 0.65684, 0.66266, | |
0.66844, 0.67421, 0.67995, 0.68566, | |
0.69134, 0.69700, 0.70262, 0.70821, | |
0.71378, 0.71931, 0.72481, 0.73027, | |
0.73570, 0.74109, 0.74645, 0.75177, | |
0.75705, 0.76229, 0.76750, 0.77266, | |
0.77779, 0.78287, 0.78790, 0.79290, | |
0.79785, 0.80276, 0.80762, 0.81243, | |
0.81720, 0.82192, 0.82659, 0.83121, | |
0.83578, 0.84030, 0.84477, 0.84919, | |
0.85355, 0.85787, 0.86212, 0.86633, | |
0.87048, 0.87457, 0.87860, 0.88258, | |
0.88651, 0.89037, 0.89417, 0.89792, | |
0.90160, 0.90523, 0.90879, 0.91229, | |
0.91573, 0.91911, 0.92243, 0.92568, | |
0.92886, 0.93199, 0.93504, 0.93804, | |
0.94096, 0.94382, 0.94661, 0.94934, | |
0.95199, 0.95458, 0.95710, 0.95956, | |
0.96194, 0.96425, 0.96650, 0.96867, | |
0.97077, 0.97280, 0.97476, 0.97665, | |
0.97847, 0.98022, 0.98189, 0.98349, | |
0.98502, 0.98647, 0.98785, 0.98916, | |
0.99039, 0.99155, 0.99264, 0.99365, | |
0.99459, 0.99545, 0.99624, 0.99695, | |
0.99759, 0.99816, 0.99865, 0.99906, | |
0.99940, 0.99966, 0.99985, 0.99996, | |
0.99996 | |
}; | |
#define SAMPLES 257.0f | |
class AudioEffectEnvelopeLUT : public AudioStream { | |
public: | |
enum LUTType { | |
EXPO, | |
QUARTIC, | |
RAISED_COSINE | |
}; | |
enum EnvelopeState { | |
IDLE, | |
ATTACK, | |
DECAY, | |
SUSTAIN, | |
RELEASE | |
}; | |
AudioEffectEnvelopeLUT() : AudioStream(1, inputQueueArray) { | |
updateVariables(); | |
} | |
void noteOn(void); | |
void noteOff(void); | |
bool isActive(); | |
bool isSustain(); | |
virtual void update(void); | |
void setAttack(float32_t attack_) { | |
attack = attack_; | |
updateVariables(); | |
} | |
void setDecay(float32_t decay_) { | |
decay = decay_; | |
updateVariables(); | |
} | |
void setSustain(float32_t sustain_) { | |
sustain = sustain_; | |
updateVariables(); | |
} | |
void setRelease(float32_t release_) { | |
release = release_; | |
updateVariables(); | |
} | |
void setLUT(LUTType lutType) { | |
switch(lutType) { | |
case EXPO: | |
LUT = lut_env_expo; | |
break; | |
case QUARTIC: | |
LUT = lut_env_quartic; | |
break; | |
case RAISED_COSINE: | |
LUT = lut_raised_cosine; | |
break; | |
} | |
} | |
private: | |
audio_block_t *inputQueueArray[1]; | |
EnvelopeState state = IDLE; | |
float32_t attack = 50.0f; | |
float32_t decay = 50.0f; | |
float32_t sustain = .7f; | |
float32_t release = 1000.0; | |
float32_t sample = 0; // index into the LUT | |
int32_t count=0; // counter for required number of samples in each state. | |
int32_t a_samples, d_samples, r_samples; | |
float32_t a_inc, d_inc, r_inc; | |
const float32_t *LUT = lut_raised_cosine; | |
void updateVariables(void) { | |
a_samples = (attack/1000.0f) * AUDIO_SAMPLE_RATE_EXACT; | |
d_samples = (decay/1000.0f) * AUDIO_SAMPLE_RATE_EXACT; | |
r_samples = (release/1000.0f) * AUDIO_SAMPLE_RATE_EXACT; | |
a_inc = SAMPLES / a_samples; | |
d_inc = SAMPLES / d_samples; | |
r_inc = SAMPLES / r_samples; | |
} | |
}; | |
#undef SAMPLES_PER_MSEC | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Arduino.h> | |
#include "envlut.h" | |
#include <Audio.h> | |
#include <Wire.h> | |
#include <SPI.h> | |
#include <SD.h> | |
#include <SerialFlash.h> | |
// GUItool: begin automatically generated code | |
AudioSynthWaveform waveform1; //xy=171,365 | |
AudioEffectEnvelopeLUT envelope1; //xy=356,351 | |
AudioOutputI2S i2s1; //xy=373,400 | |
AudioOutputAnalogStereo dacs1; //xy=553,351 | |
AudioConnection patchCord1(waveform1, envelope1); | |
AudioConnection patchCord2(envelope1, 0, i2s1, 0); | |
AudioConnection patchCord3(envelope1, 0, i2s1, 1); | |
AudioConnection patchCord4(envelope1, 0, dacs1, 0); | |
AudioConnection patchCord5(envelope1, 0, dacs1, 1); | |
AudioControlWM8731 wm8731_1; //xy=507,500 | |
// GUItool: end automatically generated code | |
elapsedMillis debounce = 0; | |
#define BUTTON 2 | |
void isr(void) { | |
if(debounce<300) { | |
return; | |
} | |
debounce = 0; | |
static int lutnum = 0; | |
if(envelope1.isSustain()) { | |
envelope1.noteOff(); | |
if(++lutnum == 3) { | |
lutnum = 0; | |
} | |
switch(lutnum) { | |
case 0: | |
envelope1.setLUT(AudioEffectEnvelopeLUT::EXPO); | |
break; | |
case 1: | |
envelope1.setLUT(AudioEffectEnvelopeLUT::QUARTIC); | |
break; | |
case 2: | |
envelope1.setLUT(AudioEffectEnvelopeLUT::RAISED_COSINE); | |
break; | |
} | |
} else { | |
envelope1.noteOn(); | |
} | |
} | |
void setup() { | |
AudioMemory(30); | |
wm8731_1.enable(); | |
wm8731_1.volume(1.0f); | |
waveform1.begin(.5f, 80, WAVEFORM_SQUARE); | |
envelope1.setAttack(100); | |
envelope1.setDecay(100); | |
envelope1.setSustain(.7f); | |
envelope1.setRelease(500.0f); | |
Serial.begin(115200); | |
pinMode(BUTTON, INPUT_PULLUP); | |
attachInterrupt(BUTTON, isr, CHANGE); | |
} | |
void loop() { | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment