Skip to content

Instantly share code, notes, and snippets.

@LeoAdamek
Created May 7, 2017 19:24
Show Gist options
  • Save LeoAdamek/8ed24b8e3322037b9a7adb7546b705f1 to your computer and use it in GitHub Desktop.
Save LeoAdamek/8ed24b8e3322037b9a7adb7546b705f1 to your computer and use it in GitHub Desktop.
polysynth
#include <Audio.h>
#include <MIDI.h>
// Each concurrent note is assigned to a voice.
// The voice tracks which note is being played so
// when it is relased the correct voice can be stopped.
typedef struct {
// A pointer to the oscillator so it can be controlled.
AudioSynthWaveformSine* osc;
// MIDI Note
byte note;
// Note Frequency
float freq;
// Note amplitude.
float amp;
} Voice;
// The actual oscillators for the voices
AudioSynthWaveformSine voice0, voice1, voice2, voice3, voice4, voice5;
// A non-USB output is required for the audio engine to run.
AudioOutputAnalog uselessOutput;
// Return audio over USB
AudioOutputUSB audioRet;
// 3 Mixes are used so downmix the 6 voices to a dual-mono output.
AudioMixer4 mixA, mixB, mixC;
Voice* voices;
// activeVoices is a bitmask of the currently playing voices.
unsigned char activeVoices, i;
// Connect the first 3 voices to mixA
AudioConnection v0mA(voice0, 0, mixA, 0);
AudioConnection v1mA(voice1, 0, mixA, 1);
AudioConnection v2mA(voice2, 0, mixA, 2);
// Connect the last 3 voices to mixB
AudioConnection v3mB(voice3, 0, mixB, 0);
AudioConnection v4mB(voice4, 0, mixB, 1);
AudioConnection v5mB(voice5, 0, mixB, 2);
// Connect mix A and B to mix C
AudioConnection mAmC(mixA, 0, mixC, 0);
AudioConnection mBmC(mixB, 0, mixC, 1);
// Connect mix C to Output L and R
AudioConnection mCoL(mixC, 0, audioRet, 0);
AudioConnection mCoR(mixC, 0, audioRet, 1);
void setup() {
AudioMemory(32);
activeVoices = 0x00;
voices = (Voice*)malloc(6 * sizeof(Voice));
// Serial will be used for debugging messages.
Serial.begin(9600);
// Callbacks for note handling.
usbMIDI.setHandleNoteOff(OnNoteOff);
usbMIDI.setHandleNoteOn(OnNoteOn);
Serial.println("Teensynth (teensy-unth) v0 @ " __FILE__);
//AudioSynthWaveformSine* v0 = &voice0;
//Serial.printf("v0 = %0x\n", voices[0].osc);
// Doesn't like this for some reason... not sure why :(
// voices[0].osc = &voice0;
// voices[1].osc = &voice1;
// voices[2].osc = &voice2;
// voices[3].osc = &voice3;e
// voices[4].osc = &voice4;
// voices[5].osc = &voice5;
pinMode(13, OUTPUT);
}
void loop() {
// Continuously read and handle MIDI messages.
usbMIDI.read();
// Turn the LED on if any notes are playing
digitalWrite(13, activeVoices);
}
void OnNoteOn(byte channel, byte note, byte vel) {
// If all 6 voices are playing just return.
if (activeVoices == 0x3f) return;
for(i = 0; i < 6; i++) {
// Serial.printf("i = %d \t v = %01x \t a = %01x\n", i, (1<<i), (activeVoices & (1<<i)));
if ( (activeVoices & (1<<i)) == 0) {
//Serial.printf("Voice %0d on.\n", i);
activeVoices |= (1<<i);
voices[i].amp = 1;
voices[i].note = note;
voices[i].freq = noteToFreq(note);
/*
AudioSynthWaveformSine* osc = voices[i]->osc;
osc->amplitude(1);
osc->frequency(voices[i]->freq);
*/
break;
}
}
showActiveNotes();
//Serial.printf("Active Voices:\t\t %02x\n", activeVoices);
//Serial.printf("Audio Memory:\t\t %02x\n", AudioMemoryUsage());
}
void OnNoteOff(byte channel, byte note, byte vel) {
for(i = 0; i < 6; i++) {
//Serial.printf("i = %0d \t v = %02x \t a = %02x\n", i, (1<<i), (activeVoices & (1<<i)));
//Serial.printf("v[i].note = %02x\n", voices[i].note);
if(voices[i].note == note && (activeVoices & (1<<i)) != 0 ) {
//Serial.printf("Voice %0d Off.", i);
voices[i].note = 0xff;
voices[i].amp = 0;
/*
AudioSynthWaveformSine* osc;
osc->amplitude(0);
*/
activeVoices &= ~(1<<i);
break;
}
}
showActiveNotes();
//Serial.printf("Active Voices:\t\t %02x\n", activeVoices);
}
// Debugging function which shows the currently playing notes.
void showActiveNotes() {
Serial.printf("[%10lu] Notes:\t\t", millis());
for(i = 0; i < 6; i++) {
if ( (activeVoices & (1<<i)) != 0 ) {
Serial.printf("%02x ", voices[i].note);
} else {
Serial.print("-- ");
}
};
Serial.print("\n");
}
// Convert MIDI note to tonal freq.
//
// TODO: Replace this function with a lookup table of frequencies
// mapping the midi notes to frequencies.
//
// Formula: 440 * 2^( midi_note_val - 69) / 12)
float noteToFreq(byte note) {
float freq = 440 * pow(2, (float(note - 69) / 12));
// Serial.printf("Note: %x // Freq: %.2fHz\n", note, freq);
return freq;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment