From 2e77e8e5318dac06b92a8794709ad5abc400d7c3 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 10 Apr 2019 01:24:52 -0500 Subject: [PATCH] Added 9 --- M0_Simple_Synth_05/README.md | 2 +- M0_Simple_Synth_05/images/circuit.PNG | Bin 0 -> 6730 bytes M0_Simple_Synth_05/images/circuit.svg | 431 ++++++++++++ M0_Simple_Synth_08/M0_Simple_Synth_08.ino | 413 ++++++++++++ M0_Simple_Synth_08/initSequnce.ino | 20 + M0_Simple_Synth_08/logging.ino | 69 ++ M0_Simple_Synth_08/setTimers.ino | 61 ++ M0_Simple_Synth_09/M0_Simple_Synth_09.ino | 435 ++++++++++++ M0_Simple_Synth_09/images/circuit.svg | 774 ++++++++++++++++++++++ M0_Simple_Synth_09/initSequnce.ino | 20 + M0_Simple_Synth_09/logging.ino | 69 ++ M0_Simple_Synth_09/setTimers.ino | 61 ++ README.md | Bin 9018 -> 8998 bytes 13 files changed, 2354 insertions(+), 1 deletion(-) create mode 100644 M0_Simple_Synth_05/images/circuit.PNG create mode 100644 M0_Simple_Synth_05/images/circuit.svg create mode 100644 M0_Simple_Synth_08/M0_Simple_Synth_08.ino create mode 100644 M0_Simple_Synth_08/initSequnce.ino create mode 100644 M0_Simple_Synth_08/logging.ino create mode 100644 M0_Simple_Synth_08/setTimers.ino create mode 100644 M0_Simple_Synth_09/M0_Simple_Synth_09.ino create mode 100644 M0_Simple_Synth_09/images/circuit.svg create mode 100644 M0_Simple_Synth_09/initSequnce.ino create mode 100644 M0_Simple_Synth_09/logging.ino create mode 100644 M0_Simple_Synth_09/setTimers.ino diff --git a/M0_Simple_Synth_05/README.md b/M0_Simple_Synth_05/README.md index 131b10f..a800ced 100644 --- a/M0_Simple_Synth_05/README.md +++ b/M0_Simple_Synth_05/README.md @@ -4,7 +4,7 @@ Nand Synth So this is an approximation of the following circuit - +![Capture1](https://github.com/robstave/trinketM0Synth/blob/master/M0_Simple_Synth_05/images/circuit.PNG) ![Capture1](https://github.com/robstave/trinketM0Synth/blob/master/M0_Simple_Synth_05/images/oneMinusLogPNG.PNG) \ No newline at end of file diff --git a/M0_Simple_Synth_05/images/circuit.PNG b/M0_Simple_Synth_05/images/circuit.PNG new file mode 100644 index 0000000000000000000000000000000000000000..41380a5bf1b8ca4f2671bbd112ca537d576fe7fb GIT binary patch literal 6730 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 literal 0 HcmV?d00001 diff --git a/M0_Simple_Synth_05/images/circuit.svg b/M0_Simple_Synth_05/images/circuit.svg new file mode 100644 index 0000000..f87321c --- /dev/null +++ b/M0_Simple_Synth_05/images/circuit.svg @@ -0,0 +1,431 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + 5v + + + + + + + + + + + + + + + + + + + 5v + + + + + + + + + + + LFO on + LFO CC + HF CC + + + LFO CC + HF CC + + diff --git a/M0_Simple_Synth_08/M0_Simple_Synth_08.ino b/M0_Simple_Synth_08/M0_Simple_Synth_08.ino new file mode 100644 index 0000000..f2200d2 --- /dev/null +++ b/M0_Simple_Synth_08/M0_Simple_Synth_08.ino @@ -0,0 +1,413 @@ + +#include "MIDIUSB.h" +#include +#include "pitchToFrequency.h" + +/** + M0 Trinket Synth - Exercise 08 + + Previously: + make a squarewave sound (2 Cores) with 3 CCs each + HfCC - CC to control pitch + LfoCC - CC to control LFO Frequency + + + Added: + LfoOnCC - CC to turn core on/off/LFO so its always on, always off or squarewave LFO + Also another CC for the mixer + Toggle Mixer to be sum/nand/xor + + +*/ + + +// Setup Onboard Dot +#define NUMPIXELS 1 // Number of LEDs in strip + +// The code is just doing the squarewaves at the moment with very simple counters. +// In the interrupt, we just count the number of ticks here until flipping the bit. + +// LFO Counters. minLFO is actually a high number because its a larger tick to +# define maxLFO 40 +# define minLFO 500 + +# define maxHF 70 +# define minHF 800 + + +# define CORE_COUNT 2 +# define PARAMETER_COUNT 13 + +# define CORE1 0 +# define CORE2 1 + + +typedef struct CoreState { + uint8_t lfoCC; + uint32_t lfoCounter; + uint32_t lfoCounterCompare; + uint8_t lfoState; + uint8_t lfoValueCompare; + + + uint8_t hfCC; + uint32_t hfCounter; + uint32_t hfCounterCompare; + uint8_t hfState; + uint8_t hfValueCompare; + + uint8_t lfoOnCC; + uint8_t lfoSwitchState; + uint8_t color; + + +}; + +// using global memory. You could use local variables in loop as well if your +// good with pointers. + +// Initialize with a random speed and silence count +CoreState coreArray[] = { + + + { + // HF CC = 10; + // initialize at 90 + 10, 0, 90, 0, 91, + + // LFO CC = 74; + 74, 0, 90, 0, 91, + + // LFO ON CC = 71; + 71, 77, 0 + }, + // CORE 2 + { + // HF CC = 114; + // initialize at 130 + 114, 0, 130, 0, 91, + + // LFO CC = 18; + 18, 0, 100, 0, 91, + + // LFO ON CC = 19; + 19, 77, 0 + }, +}; + + +// Here's how to control the LEDs from any two pins: +#define DATAPIN 7 +#define CLOCKPIN 8 + +Adafruit_DotStar strip = Adafruit_DotStar( + NUMPIXELS, DATAPIN, CLOCKPIN, DOTSTAR_BGR); + +// Fix Serial for SAMD +#if defined(ARDUINO_SAMD_ZERO) && defined(SERIAL_PORT_USBVIRTUAL) +// Required for Serial on Zero based boards +#define Serial SERIAL_PORT_USBVIRTUAL +#endif + + +uint8_t MIX_MODE_CC = 7; +volatile uint8_t mix_mode_value = 10; // default on at 50/50 + + +/* + TC4 LFO Interrupt + + Manipulates the LFO state which is either + - Always OFF + - Always ON + - LFO-ing Squarewave on off. Note, this is not a bool, but a value, so later we can use it to manipulate Amplitude +*/ + +void TC4_Handler() // Interrupt Service Routine (ISR) for timer TC4 +{ + // Check for overflow (OVF) interrupt + if (TC4->COUNT16.INTFLAG.bit.OVF && TC4->COUNT16.INTENSET.bit.OVF) + { + // Put your timer overflow (OVF) code here.... + + if (coreArray[CORE1].lfoSwitchState > 100) { + // LFO CODE. Always on + coreArray[CORE1].color = 0xFF; + coreArray[CORE1].lfoState = 127; + } else { + if (coreArray[CORE1].lfoSwitchState > 50) { + // LFO CODE. When we hit the counter, flip the state bool (and the color of the light) + + if (coreArray[CORE1].lfoCounter > coreArray[CORE1].lfoCounterCompare) { + coreArray[CORE1].lfoState++; + if (coreArray[CORE1].lfoState % 2 == 0) { + coreArray[CORE1].color = 0x00; + } else { + coreArray[CORE1].color = 0xFF; + } + + coreArray[CORE1].lfoCounter = 0; + } + coreArray[CORE1].lfoCounter++; + } else { + // LFO CODE. Always off + coreArray[CORE1].color = 0x00; + coreArray[CORE1].lfoState = 0; + } + } + + if (coreArray[CORE2].lfoSwitchState > 100) { + // LFO CODE. Always on + + coreArray[CORE2].color = 0xFF; + coreArray[CORE2].lfoState = 127; + } else { + if (coreArray[CORE2].lfoSwitchState > 50) { + // LFO CODE. When we hit the counter, flip the state bool (and the color of the light) + + if (coreArray[CORE2].lfoCounter > coreArray[CORE2].lfoCounterCompare) { + coreArray[CORE2].lfoState++; + if (coreArray[CORE2].lfoState % 2 == 0) { + + coreArray[CORE2].color = 0x00; + } else { + coreArray[CORE2].color = 0xFF; + } + + coreArray[CORE2].lfoCounter = 0; + } + coreArray[CORE2].lfoCounter++; + } else { + // LFO CODE. Always off + coreArray[CORE2].color = 0x00; + coreArray[CORE2].lfoState = 0; + } + } + + + TC4->COUNT16.INTFLAG.reg = TC_INTFLAG_OVF; // Clear the OVF interrupt flag + } +} + +/* + * High Frequency Interrupt + * This is the interrupt that controls the actual signal + * + * Frequency is determined by countdown timers. The Larger the number, the lower the freq. + * LFO is checked here as well as basically an on/off thing. + * + * Value is finally mixed with one of three strategies + * - NAND + * - SUM + * - XOR + */ + +void TC5_Handler() // Interrupt Service Routine (ISR) for timer TC4 +{ + // Check for overflow (OVF) interrupt + if (TC5->COUNT16.INTFLAG.bit.OVF && TC5->COUNT16.INTENSET.bit.OVF) + { + // Put your timer overflow (OVF) code here.... + + // Higher Frequency Interrupt. + + //CORE 1 + if (coreArray[CORE1].hfCounter > coreArray[CORE1].hfCounterCompare) { + coreArray[CORE1].hfState++; + coreArray[CORE1].hfCounter = 0; + } + coreArray[CORE1].hfCounter++; + + boolean value1 = ( (coreArray[CORE1].lfoState % 2 == 1) && (coreArray[CORE1].hfState % 2 == 1)); + + //CORE 2 + if (coreArray[CORE2].hfCounter > coreArray[CORE2].hfCounterCompare) { + coreArray[CORE2].hfState++; + coreArray[CORE2].hfCounter = 0; + } + coreArray[CORE2].hfCounter++; + + boolean value2 = ( (coreArray[CORE2].lfoState % 2 == 1) && (coreArray[CORE2].hfState % 2 == 1)); + + + if (mix_mode_value < 45) { + + // NAND MIXER + if ( !value1 && !value2) { + analogWrite(A0, 127); + } else { + analogWrite(A0, 0); + } + } else { + + if (mix_mode_value < 100 ) { + + // SUM mixer + + int value = 0; + if (value1 ) { + value = 63; + } + if (value2 ) { + value = value + 64; + } + + analogWrite(A0, value); + + } else { + + // XOR Mixer + + if ( (value1 && !value2) || (!value1 && value2) ) { + analogWrite(A0, 127); + } else { + analogWrite(A0, 0); + } + } + } + + TC5->COUNT16.INTFLAG.reg = TC_INTFLAG_OVF; // Clear the OVF interrupt flag + } +} + + +void setup() { + Serial.begin(115200); + Serial.println("Setting up: Version 05"); + + strip.begin(); // Initialize pins for output + strip.show(); // Turn all LEDs off ASAP + + setupTimer4_5(); + +} + + +// Wrap Map into a function that is a little 1-logish. Simple break it a bit in ranges. +// Assumes a midi note in the range of 0-127 and makes it kinda 1-log(x) without a whole lot of +// processing. +// +// +int mapMidiLowBudget(int value, int v1, int v2) { + int octave = v1 - v2; + int point1 = v1 - (octave / 2) ; + + if (value < 33) { + return map(value, 0, 32, v1, point1); + } else { + return map(value, 32, 127, point1, v2); + } +} + + +// 0xB +void controlChange(byte channel, byte control, byte value) { + + logData(0xB, channel, control, value) ; // optional + + + if (control == coreArray[CORE1].lfoCC) { + + if (coreArray[CORE1].lfoValueCompare == value) { + return; + } + coreArray[CORE1].lfoValueCompare = value; + coreArray[CORE1].lfoCounterCompare = mapMidiLowBudget(value, minLFO, maxLFO); + return; + } + + if (control == coreArray[CORE2].lfoCC) { + if (coreArray[CORE2].lfoValueCompare == value) { + return; + } + coreArray[CORE2].lfoValueCompare = value; + coreArray[CORE2].lfoCounterCompare = mapMidiLowBudget(value, minLFO, maxLFO); + return; + } + + if (control == coreArray[CORE1].hfCC) { + if (coreArray[CORE1].hfValueCompare == value) { + return; + } + coreArray[CORE1].hfValueCompare = value; + coreArray[CORE1].hfCounterCompare = mapMidiLowBudget(value, minHF, maxHF); + return; + } + + + if (control == coreArray[CORE2].hfCC) { + if (coreArray[CORE2].hfValueCompare == value) { + return; + } + coreArray[CORE2].hfValueCompare = value; + coreArray[CORE2].hfCounterCompare = mapMidiLowBudget(value, minHF, maxHF); + return; + } + + + if (control == MIX_MODE_CC) { + Serial.print("mix_mode_value:"); + Serial.println(value); + mix_mode_value = value; + } + + + // CC to control LFO + // 0-50 OFF NO sound + // 50-100 LFO on + // 100 + NO LFO always on + + if (control == coreArray[CORE1].lfoOnCC) { + coreArray[CORE1].lfoSwitchState = value; + } + + if (control == coreArray[CORE2].lfoOnCC) { + coreArray[CORE2].lfoSwitchState = value; + } + +} + + +void loop() { + + Serial.println("Enter Loop:"); + + initSequence(); // Blinky intro + + while (true) { + + strip.setPixelColor(0, coreArray[CORE1].color, coreArray[CORE2].color, 0); //set the pixel colors + + strip.show(); + + + // Midi packet taken from + // midiEventPacket_t rx = MidiUSB.read(); + // + // First parameter is the event type (0x09 = note on, 0x08 = note off). + // Second parameter is note-on/note-off, combined with the channel. + // Channel can be anything between 0-15. Typically reported to the user as 1-16. + // Third parameter is the note number (48 = middle C). + // Fourth parameter is the velocity (64 = normal, 127 = fastest). + + midiEventPacket_t rx = MidiUSB.read(); + switch (rx.header) { + case 0: + break; //No pending events + + case 0xB: + controlChange( + rx.byte1 & 0xF, //channel + rx.byte2, //control + rx.byte3 //value + ); + break; + + default: + // If your curious + //logData(rx.header, byte1, byte2, byte3) ; + + break; + } + } +} diff --git a/M0_Simple_Synth_08/initSequnce.ino b/M0_Simple_Synth_08/initSequnce.ino new file mode 100644 index 0000000..47ed94d --- /dev/null +++ b/M0_Simple_Synth_08/initSequnce.ino @@ -0,0 +1,20 @@ +// Pretty sequence to know it started up. Just Call it from init or the loop code (assuming your loop has a while-true loop in there as well) + +// This is optional. But I like to see a little sequence indicator to know that it uploaded or restarted. + +void initSequence() { + strip.setPixelColor(0, 0x1F0000); // red + strip.show(); + delay(500); + + strip.setPixelColor(0, 0x001F00); // green + strip.show(); + delay(050); + + strip.setPixelColor(0, 0x00001F); // blue + strip.show(); + delay(500); + + strip.setPixelColor(0, 0x000000); // blue + strip.show(); +} diff --git a/M0_Simple_Synth_08/logging.ino b/M0_Simple_Synth_08/logging.ino new file mode 100644 index 0000000..426d1f6 --- /dev/null +++ b/M0_Simple_Synth_08/logging.ino @@ -0,0 +1,69 @@ +// Utils to show the key name and Midi logging in the serial port +// +// +const char* pitch_name(byte pitch) { + static const char* names[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; + return names[pitch % 12]; +} + +int pitch_octave(byte pitch) { + return (pitch / 12) - 1; +} + + +void logData(byte header, byte channel, byte byte2, byte byte3) { + + switch (header) { + case 0: + return; + + + case 0x9: + + Serial.print("Note On: "); + Serial.println(byte2); + Serial.print(pitch_name(byte2)); + Serial.print(pitch_octave(byte2)); + Serial.print(", channel="); + Serial.print(channel); + Serial.print(", velocity="); + Serial.println(byte3); + break; + + case 0x8: + Serial.print("Note Off: "); + Serial.println(byte2); + Serial.print(pitch_name(byte2)); + Serial.print(pitch_octave(byte2)); + Serial.print(", channel="); + Serial.print(channel); + Serial.print(", velocity="); + Serial.println(byte3); + + break; + + case 0xB: + Serial.print("Control change: control="); + Serial.print(byte2); + Serial.print(", value="); + Serial.print(byte3); + Serial.print(", channel="); + Serial.println(channel); + break; + + default: + Serial.print("Unhandled MIDI message: "); + Serial.print(header, HEX); + Serial.print("-"); + Serial.print(channel, HEX); + Serial.print("-"); + Serial.print(byte2, HEX); + Serial.print("-"); + Serial.println(byte3, HEX); + + break; + + } +} + + diff --git a/M0_Simple_Synth_08/setTimers.ino b/M0_Simple_Synth_08/setTimers.ino new file mode 100644 index 0000000..7311807 --- /dev/null +++ b/M0_Simple_Synth_08/setTimers.ino @@ -0,0 +1,61 @@ +// Set timer TC4 and TC5 + +// TC4 is set up with a prescaler of 1024 : 48MHz/1024 = 46.875kHz +// The CC0 is set to 100 so ultimately this timer is happening every 469 HZ +// The Basic strategy for the LFO is to track an ADDITIONAL counter and flip the +// LFO value accordingly. +// # define minLFO 500 means that we flip the LFO bit at +// 469/( 500 * 2) = .46 HZ. +// # define maxLFO 40 means that we flip the LFO bit at +// 469/( 40 * 2) = 5.8625 HZ. + +// TC5 is much faster at +// 48MHz/64 = 750kHz + + +// http://forum.arduino.cc/index.php?topic=599151.0 +void setupTimer4_5() { + // Feed GCLK0 to TC4 and TC5 + GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Enable GCLK0 to TC4 and TC5 + GCLK_CLKCTRL_GEN_GCLK0 | // Select GCLK0 + GCLK_CLKCTRL_ID_TC4_TC5; // Feed the GCLK0 to TC4 and TC5 + while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization + + TC4->COUNT16.CC[0].reg = 100; // Set the TC4 CC0 register as the TOP value in match frequency mode + while (TC4->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization + + NVIC_SetPriority(TC4_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority for TC4 to 0 (highest) + NVIC_EnableIRQ(TC4_IRQn); // Connect TC4 to Nested Vector Interrupt Controller (NVIC) + + TC4->COUNT16.INTFLAG.reg |= TC_INTFLAG_OVF; // Clear the interrupt flags + TC4->COUNT16.INTENSET.reg = TC_INTENSET_OVF; // Enable TC4 interrupts + + TC4->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCSYNC_PRESC | // Reset timer on the next prescaler clock + TC_CTRLA_PRESCALER_DIV1024 | // Set prescaler to 1024, 48MHz/1024 = 46.875kHz + TC_CTRLA_WAVEGEN_MFRQ | // Put the timer TC4 into match frequency (MFRQ) mode + TC_CTRLA_MODE_COUNT16; // Set the timer to 16-bit mode + while (TC4->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization + + TC4->COUNT16.CTRLA.bit.ENABLE = 1; // Enable TC4 + while (TC4->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization + + + + TC5->COUNT16.CC[0].reg = 10; // Set the TC5 CC0 register as the TOP value in match frequency mode + while (TC5->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization + + NVIC_SetPriority(TC5_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority for TC5 to 0 (highest) + NVIC_EnableIRQ(TC5_IRQn); // Connect TC4 to Nested Vector Interrupt Controller (NVIC) + + TC5->COUNT16.INTFLAG.reg |= TC_INTFLAG_OVF; // Clear the interrupt flags + TC5->COUNT16.INTENSET.reg = TC_INTENSET_OVF; // Enable TC5 interrupts + + TC5->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCSYNC_PRESC | // Reset timer on the next prescaler clock + TC_CTRLA_PRESCALER_DIV64 | // Set prescaler to 64, 48MHz/64 = 750 kHz + TC_CTRLA_WAVEGEN_MFRQ | // Put the timer TC5 into match frequency (MFRQ) mode + TC_CTRLA_MODE_COUNT16; // Set the timer to 16-bit mode + while (TC5->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization + + TC5->COUNT16.CTRLA.bit.ENABLE = 1; // Enable TC5 + while (TC5->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization +} diff --git a/M0_Simple_Synth_09/M0_Simple_Synth_09.ino b/M0_Simple_Synth_09/M0_Simple_Synth_09.ino new file mode 100644 index 0000000..8851e87 --- /dev/null +++ b/M0_Simple_Synth_09/M0_Simple_Synth_09.ino @@ -0,0 +1,435 @@ + +#include "MIDIUSB.h" +#include +#include "pitchToFrequency.h" + +/** + M0 Trinket Synth - Exercise 09 + + Previously: + make a squarewave sound (2 Cores) with 3 CCs each + HfCC - CC to control pitch + LfoCC - CC to control LFO Frequency + LfoOnCC - CC to turn core on/off/LFO so its always on, always off or squarewave LFO + + Also another CC for the mixer + Toggle Mixer to be sum/nand/xor + + Added: + a Vibrato effect +*/ + +/* + Note some of the boilerplate/extra stuff as been moved to other .ino files. Arduino IDE just combines them + with the dir filename.ino first and the remaining in alphabetical order. + +*/ + + +// Setup Onboard Dot +#define NUMPIXELS 1 // Number of LEDs in strip + +// The code is just doing the squarewaves at the moment with very simple counters. +// In the interrupt, we just count the number of ticks here until flipping the bit. + +// LFO Counters. minLFO is actually a high number because its a larger tick to +# define maxLFO 40 +# define minLFO 500 + +# define maxHF 70 +# define minHF 800 + + +# define CORE_COUNT 2 +# define PARAMETER_COUNT 13 + +# define CORE1 0 +# define CORE2 1 + + +typedef struct CoreState { + uint8_t lfoCC; + uint32_t lfoCounter; + uint32_t lfoCounterCompare; + uint8_t lfoState; + uint8_t lfoValueCompare; + + + uint8_t hfCC; + uint32_t hfCounter; + uint32_t hfCounterCompare; + uint8_t hfState; + uint8_t hfValueCompare; + uint8_t hfTrill; + + + uint8_t lfoOnCC; + uint8_t lfoSwitchState; + + uint8_t color; + + +}; + +// using global memory. You could use local variables in loop as well if your +// good with pointers. + +// Initialize with a random speed and silence count +CoreState coreArray[] = { + + + { + // HF CC = 10; + // initialize at 90 + 10, 0, 90, 0, 91, + + // LFO CC = 74; + 74, 0, 90, 0, 91, 0, + + // LFO ON CC = 71; + 71, 77, 0 + }, + // CORE 2 + { + // HF CC = 114; + // initialize at 130 + 114, 0, 130, 0, 91, + + // LFO CC = 18; + 18, 0, 100, 0, 91, 0, + + // LFO ON CC = 19; + 19, 77, 0 + }, +}; + + +// Here's how to control the LEDs from any two pins: +#define DATAPIN 7 +#define CLOCKPIN 8 + +Adafruit_DotStar strip = Adafruit_DotStar( + NUMPIXELS, DATAPIN, CLOCKPIN, DOTSTAR_BGR); + +// Fix Serial for SAMD +#if defined(ARDUINO_SAMD_ZERO) && defined(SERIAL_PORT_USBVIRTUAL) +// Required for Serial on Zero based boards +#define Serial SERIAL_PORT_USBVIRTUAL +#endif + + +uint8_t MIX_MODE_CC = 7; +volatile uint8_t mix_mode_value = 10; // default on at 50/50 + + +/* + TC4 LFO Interrupt + + Manipulates the LFO state which is either + - Always OFF + - Always ON + - LFO-ing Squarewave on off. Note, this is not a bool, but a value, so later we can use it to manipulate Amplitude + + DID I just copy paste the code twice...yes + Force of habit. Making it a function only adds a level of indirection to the interrupt code (wasted CPU) + and + Sketch uses 14156 bytes (5%) of program storage space. Maximum is 262144 bytes. + + so its not like we are hurting for program storage +*/ + +void TC4_Handler() // Interrupt Service Routine (ISR) for timer TC4 +{ + // Check for overflow (OVF) interrupt + if (TC4->COUNT16.INTFLAG.bit.OVF && TC4->COUNT16.INTENSET.bit.OVF) + { + // Put your timer overflow (OVF) code here.... + + if (coreArray[CORE1].lfoSwitchState > 100) { + // LFO CODE. Always on + coreArray[CORE1].color = 0xFF; + coreArray[CORE1].lfoState = 127; + } else { + if (coreArray[CORE1].lfoSwitchState > 50) { + // LFO CODE. When we hit the counter, flip the state bool (and the color of the light) + + if (coreArray[CORE1].lfoCounter > coreArray[CORE1].lfoCounterCompare) { + coreArray[CORE1].lfoState++; + if (coreArray[CORE1].lfoState % 2 == 0) { + coreArray[CORE1].color = 0x00; + } else { + coreArray[CORE1].color = 0xFF; + } + + coreArray[CORE1].lfoCounter = 0; + } + coreArray[CORE1].lfoCounter++; + } else { + // LFO CODE. Always off + coreArray[CORE1].color = 0x00; + coreArray[CORE1].lfoState = 0; + } + } + + if (coreArray[CORE2].lfoSwitchState > 100) { + // LFO CODE. Always on + + coreArray[CORE2].color = 0xFF; + coreArray[CORE2].lfoState = 127; + } else { + if (coreArray[CORE2].lfoSwitchState > 50) { + // LFO CODE. When we hit the counter, flip the state bool (and the color of the light) + + if (coreArray[CORE2].lfoCounter > coreArray[CORE2].lfoCounterCompare) { + coreArray[CORE2].lfoState++; + if (coreArray[CORE2].lfoState % 2 == 0) { + + coreArray[CORE2].color = 0x00; + } else { + coreArray[CORE2].color = 0xFF; + } + + coreArray[CORE2].lfoCounter = 0; + } + coreArray[CORE2].lfoCounter++; + } else { + // LFO CODE. Always off + coreArray[CORE2].color = 0x00; + coreArray[CORE2].lfoState = 0; + } + } + + + TC4->COUNT16.INTFLAG.reg = TC_INTFLAG_OVF; // Clear the OVF interrupt flag + } +} + +/* + * High Frequency Interrupt + * This is the interrupt that controls the actual signal + * + * Frequency is determined by countdown timers. The Larger the number, the lower the freq. + * LFO is checked here as well as basically an on/off thing. + * + * Value is finally mixed with one of three strategies + * - NAND + * - SUM + * - XOR + */ + +void TC5_Handler() // Interrupt Service Routine (ISR) for timer TC4 +{ + // Check for overflow (OVF) interrupt + if (TC5->COUNT16.INTFLAG.bit.OVF && TC5->COUNT16.INTENSET.bit.OVF) + { + // Put your timer overflow (OVF) code here.... + + // Higher Frequency Interrupt. + + //CORE 1 + if (coreArray[CORE1].hfCounter > (coreArray[CORE1].hfCounterCompare + coreArray[CORE1].hfTrill)) { + coreArray[CORE1].hfState++; + coreArray[CORE1].hfCounter = 0; + + if (coreArray[CORE1].hfTrill > 8) { + coreArray[CORE1].hfTrill = 0; + } + if (coreArray[CORE1].hfState % 32 == 1) { + coreArray[CORE1].hfTrill++; + } + } + coreArray[CORE1].hfCounter++; + + boolean value1 = ( (coreArray[CORE1].lfoState % 2 == 1) && (coreArray[CORE1].hfState % 2 == 1)); + + //CORE 2 + if (coreArray[CORE2].hfCounter > coreArray[CORE2].hfCounterCompare) { + coreArray[CORE2].hfState++; + coreArray[CORE2].hfCounter = 0; + } + coreArray[CORE2].hfCounter++; + + boolean value2 = ( (coreArray[CORE2].lfoState % 2 == 1) && (coreArray[CORE2].hfState % 2 == 1)); + + + if (mix_mode_value < 45) { + + // NAND MIXER + if ( !value1 && !value2) { + analogWrite(A0, 127); + } else { + analogWrite(A0, 0); + } + } else { + + if (mix_mode_value < 100 ) { + + // SUM mixer + + int value = 0; + if (value1 ) { + value = 63; + } + if (value2 ) { + value = value + 64; + } + + analogWrite(A0, value); + + } else { + + // XOR Mixer + + if ( (value1 && !value2) || (!value1 && value2) ) { + analogWrite(A0, 127); + } else { + analogWrite(A0, 0); + } + } + } + + TC5->COUNT16.INTFLAG.reg = TC_INTFLAG_OVF; // Clear the OVF interrupt flag + } +} + + +void setup() { + Serial.begin(115200); + Serial.println("Setting up: Version 05"); + + strip.begin(); // Initialize pins for output + strip.show(); // Turn all LEDs off ASAP + + setupTimer4_5(); + +} + + +// Wrap Map into a function that is a little 1-logish. Simple break it a bit in ranges. +// Assumes a midi note in the range of 0-127 and makes it kinda 1-log(x) without a whole lot of +// processing. +// +// +int mapMidiLowBudget(int value, int v1, int v2) { + int octave = v1 - v2; + int point1 = v1 - (octave / 2) ; + + if (value < 33) { + return map(value, 0, 32, v1, point1); + } else { + return map(value, 32, 127, point1, v2); + } +} + + +// 0xB +void controlChange(byte channel, byte control, byte value) { + + logData(0xB, channel, control, value) ; // optional + + + if (control == coreArray[CORE1].lfoCC) { + + if (coreArray[CORE1].lfoValueCompare == value) { + return; + } + coreArray[CORE1].lfoValueCompare = value; + coreArray[CORE1].lfoCounterCompare = mapMidiLowBudget(value, minLFO, maxLFO); + return; + } + + if (control == coreArray[CORE2].lfoCC) { + if (coreArray[CORE2].lfoValueCompare == value) { + return; + } + coreArray[CORE2].lfoValueCompare = value; + coreArray[CORE2].lfoCounterCompare = mapMidiLowBudget(value, minLFO, maxLFO); + return; + } + + if (control == coreArray[CORE1].hfCC) { + if (coreArray[CORE1].hfValueCompare == value) { + return; + } + coreArray[CORE1].hfValueCompare = value; + coreArray[CORE1].hfCounterCompare = mapMidiLowBudget(value, minHF, maxHF); + return; + } + + + if (control == coreArray[CORE2].hfCC) { + if (coreArray[CORE2].hfValueCompare == value) { + return; + } + coreArray[CORE2].hfValueCompare = value; + coreArray[CORE2].hfCounterCompare = mapMidiLowBudget(value, minHF, maxHF); + return; + } + + + if (control == MIX_MODE_CC) { + Serial.print("mix_mode_value:"); + Serial.println(value); + mix_mode_value = value; + } + + + // CC to control LFO + // 0-50 OFF NO sound + // 50-100 LFO on + // 100 + NO LFO always on + + if (control == coreArray[CORE1].lfoOnCC) { + coreArray[CORE1].lfoSwitchState = value; + } + + if (control == coreArray[CORE2].lfoOnCC) { + coreArray[CORE2].lfoSwitchState = value; + } + +} + + +void loop() { + + Serial.println("Enter Loop:"); + + initSequence(); // Blinky intro + + while (true) { + + strip.setPixelColor(0, coreArray[CORE1].color, coreArray[CORE2].color, 0); //set the pixel colors + + strip.show(); + + + // Midi packet taken from + // midiEventPacket_t rx = MidiUSB.read(); + // + // First parameter is the event type (0x09 = note on, 0x08 = note off). + // Second parameter is note-on/note-off, combined with the channel. + // Channel can be anything between 0-15. Typically reported to the user as 1-16. + // Third parameter is the note number (48 = middle C). + // Fourth parameter is the velocity (64 = normal, 127 = fastest). + + midiEventPacket_t rx = MidiUSB.read(); + switch (rx.header) { + case 0: + break; //No pending events + + case 0xB: + controlChange( + rx.byte1 & 0xF, //channel + rx.byte2, //control + rx.byte3 //value + ); + break; + + default: + // If your curious + //logData(rx.header, byte1, byte2, byte3) ; + + break; + } + } +} diff --git a/M0_Simple_Synth_09/images/circuit.svg b/M0_Simple_Synth_09/images/circuit.svg new file mode 100644 index 0000000..5c5bc36 --- /dev/null +++ b/M0_Simple_Synth_09/images/circuit.svg @@ -0,0 +1,774 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + 5v + + + + + + + + + + + + + + + + + + + 5v + + + + + + + + + + + LFO on + LFO CC + HF CC + + + + + + + + + + + + + + + + + + + + + + 5v + + + + + + + + + + + + + + + + + + + 5v + + + + + + + + + + + LFO on + LFO CC + HF CC + + + + + + mix + + + + diff --git a/M0_Simple_Synth_09/initSequnce.ino b/M0_Simple_Synth_09/initSequnce.ino new file mode 100644 index 0000000..47ed94d --- /dev/null +++ b/M0_Simple_Synth_09/initSequnce.ino @@ -0,0 +1,20 @@ +// Pretty sequence to know it started up. Just Call it from init or the loop code (assuming your loop has a while-true loop in there as well) + +// This is optional. But I like to see a little sequence indicator to know that it uploaded or restarted. + +void initSequence() { + strip.setPixelColor(0, 0x1F0000); // red + strip.show(); + delay(500); + + strip.setPixelColor(0, 0x001F00); // green + strip.show(); + delay(050); + + strip.setPixelColor(0, 0x00001F); // blue + strip.show(); + delay(500); + + strip.setPixelColor(0, 0x000000); // blue + strip.show(); +} diff --git a/M0_Simple_Synth_09/logging.ino b/M0_Simple_Synth_09/logging.ino new file mode 100644 index 0000000..426d1f6 --- /dev/null +++ b/M0_Simple_Synth_09/logging.ino @@ -0,0 +1,69 @@ +// Utils to show the key name and Midi logging in the serial port +// +// +const char* pitch_name(byte pitch) { + static const char* names[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; + return names[pitch % 12]; +} + +int pitch_octave(byte pitch) { + return (pitch / 12) - 1; +} + + +void logData(byte header, byte channel, byte byte2, byte byte3) { + + switch (header) { + case 0: + return; + + + case 0x9: + + Serial.print("Note On: "); + Serial.println(byte2); + Serial.print(pitch_name(byte2)); + Serial.print(pitch_octave(byte2)); + Serial.print(", channel="); + Serial.print(channel); + Serial.print(", velocity="); + Serial.println(byte3); + break; + + case 0x8: + Serial.print("Note Off: "); + Serial.println(byte2); + Serial.print(pitch_name(byte2)); + Serial.print(pitch_octave(byte2)); + Serial.print(", channel="); + Serial.print(channel); + Serial.print(", velocity="); + Serial.println(byte3); + + break; + + case 0xB: + Serial.print("Control change: control="); + Serial.print(byte2); + Serial.print(", value="); + Serial.print(byte3); + Serial.print(", channel="); + Serial.println(channel); + break; + + default: + Serial.print("Unhandled MIDI message: "); + Serial.print(header, HEX); + Serial.print("-"); + Serial.print(channel, HEX); + Serial.print("-"); + Serial.print(byte2, HEX); + Serial.print("-"); + Serial.println(byte3, HEX); + + break; + + } +} + + diff --git a/M0_Simple_Synth_09/setTimers.ino b/M0_Simple_Synth_09/setTimers.ino new file mode 100644 index 0000000..7311807 --- /dev/null +++ b/M0_Simple_Synth_09/setTimers.ino @@ -0,0 +1,61 @@ +// Set timer TC4 and TC5 + +// TC4 is set up with a prescaler of 1024 : 48MHz/1024 = 46.875kHz +// The CC0 is set to 100 so ultimately this timer is happening every 469 HZ +// The Basic strategy for the LFO is to track an ADDITIONAL counter and flip the +// LFO value accordingly. +// # define minLFO 500 means that we flip the LFO bit at +// 469/( 500 * 2) = .46 HZ. +// # define maxLFO 40 means that we flip the LFO bit at +// 469/( 40 * 2) = 5.8625 HZ. + +// TC5 is much faster at +// 48MHz/64 = 750kHz + + +// http://forum.arduino.cc/index.php?topic=599151.0 +void setupTimer4_5() { + // Feed GCLK0 to TC4 and TC5 + GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Enable GCLK0 to TC4 and TC5 + GCLK_CLKCTRL_GEN_GCLK0 | // Select GCLK0 + GCLK_CLKCTRL_ID_TC4_TC5; // Feed the GCLK0 to TC4 and TC5 + while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization + + TC4->COUNT16.CC[0].reg = 100; // Set the TC4 CC0 register as the TOP value in match frequency mode + while (TC4->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization + + NVIC_SetPriority(TC4_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority for TC4 to 0 (highest) + NVIC_EnableIRQ(TC4_IRQn); // Connect TC4 to Nested Vector Interrupt Controller (NVIC) + + TC4->COUNT16.INTFLAG.reg |= TC_INTFLAG_OVF; // Clear the interrupt flags + TC4->COUNT16.INTENSET.reg = TC_INTENSET_OVF; // Enable TC4 interrupts + + TC4->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCSYNC_PRESC | // Reset timer on the next prescaler clock + TC_CTRLA_PRESCALER_DIV1024 | // Set prescaler to 1024, 48MHz/1024 = 46.875kHz + TC_CTRLA_WAVEGEN_MFRQ | // Put the timer TC4 into match frequency (MFRQ) mode + TC_CTRLA_MODE_COUNT16; // Set the timer to 16-bit mode + while (TC4->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization + + TC4->COUNT16.CTRLA.bit.ENABLE = 1; // Enable TC4 + while (TC4->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization + + + + TC5->COUNT16.CC[0].reg = 10; // Set the TC5 CC0 register as the TOP value in match frequency mode + while (TC5->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization + + NVIC_SetPriority(TC5_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority for TC5 to 0 (highest) + NVIC_EnableIRQ(TC5_IRQn); // Connect TC4 to Nested Vector Interrupt Controller (NVIC) + + TC5->COUNT16.INTFLAG.reg |= TC_INTFLAG_OVF; // Clear the interrupt flags + TC5->COUNT16.INTENSET.reg = TC_INTENSET_OVF; // Enable TC5 interrupts + + TC5->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCSYNC_PRESC | // Reset timer on the next prescaler clock + TC_CTRLA_PRESCALER_DIV64 | // Set prescaler to 64, 48MHz/64 = 750 kHz + TC_CTRLA_WAVEGEN_MFRQ | // Put the timer TC5 into match frequency (MFRQ) mode + TC_CTRLA_MODE_COUNT16; // Set the timer to 16-bit mode + while (TC5->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization + + TC5->COUNT16.CTRLA.bit.ENABLE = 1; // Enable TC5 + while (TC5->COUNT16.STATUS.bit.SYNCBUSY); // Wait for synchronization +} diff --git a/README.md b/README.md index b73ca39e0b1aa2738a0bfe158a83adca36ecdf63..17d7c768cbf2fa710eee4be505aa2347fd9d2b70 100644 GIT binary patch delta 14 Vcmdnxw#;njwt0>6?0G;ay2LJ#7