SYNTEREMINO v.1.0
Ultrasonic Distance-Controlled Granular Synthesizer
Components and supplies
Ultrasonic Sensor - HC-SR04
Display LCD 16x2 (I2C)
MAX7219 8x8 LED matrix
Speaker (generic) 8Ohm
6.35mm Audio Jacks
PAM-8610 12VDC Audio Amplifier
Potentiometer
ARDUINO UNO R3
push buttons
12VDC Power Supply
Apps and platforms
Arduino IDE 2.0 (beta)
Project description
Code
Synteremino v 1.0
c
1// SYNTEREMINO v1.0 Arduino-based granular synthesizer with ultrasonic distance control 2// This code is released under GNU Lesser General Public License https://www.gnu.org/licenses/lgpl-3.0.html 3// The code is by Salvatore Mecca 4// Waveform management is taken from 5// Auduino, the Lo-Fi granular synthesiser 6// 7// by Peter Knight, 8// 9// Help: http://code.google.com/p/tinkerit/wiki/Auduino 10// More help: http://groups.google.com/group/auduino 11// 12// Analog in 1: Grain 2 decay 13// Analog in 2: Grain 1 decay 14// Analog in 3: Grain 2 pitch 15// Analog in 4: Grain repetition frequency 16 17#include <avr/pgmspace.h> 18#include <avr/io.h> 19#include <avr/interrupt.h> 20#include <LedControl.h> 21#include <LiquidCrystal_I2C.h> 22#include <SR04.h> 23 24// LED Matrix configuration 25#define DIN 10 26#define CS 9 27#define CLK 8 28LedControl lc = LedControl(DIN, CLK, CS, 0); 29 30// LCD configuration 31LiquidCrystal_I2C lcd(0x27, 16, 2); 32 33// Note definitions 34const int notes[] = { 35 31, 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, 65, 69, 73, 78, 82, 87, 93, 98, 104, 110, 117, 123, 131, 36 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 37 494, 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 38 1661, 1760, 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 4186, 4435, 4699, 4978 39}; 40 41const char* notes_name[] = { 42 "B0", "C1", "CS1", "D1", "DS1", "E1", "F1", "FS1", "G1", "GS1", "A1", "AS1", "B1", "C2", "CS2", "D2", "DS2", "E2", 43 "F2", "FS2", "G2", "GS2", "A2", "AS2", "B2", "C3", "CS3", "D3", "DS3", "E3", "F3", "FS3", "G3", "GS3", "A3", "AS3", 44 "B3", "C4", "CS4", "D4", "DS4", "E4", "F4", "FS4", "G4", "GS4", "A4", "AS4", "B4", "C5", "CS5", "D5", "DS5", "E5", 45 "F5", "FS5", "G5", "GS5", "A5", "AS5", "B5", "C6", "CS6", "D6", "DS6", "E6", "F6", "FS6", "G6", "GS6", "A6", "AS6", 46 "B6", "C7", "CS7", "D7", "DS7", "E7", "F7", "FS7", "G7", "GS7", "A7", "AS7", "B7", "C8", "CS8", "D8", "DS8" 47}; 48 49// LED matrix configuration 50const int MATRIX_ROWS = 8; 51const int MATRIX_COLS = 8; 52const byte matrix_notes[][MATRIX_ROWS] PROGMEM = { 53 { 0xE7, 0x95, 0xF5, 0x95, 0xE7, 0x00, 0x00, 0x00 }, { 0xF1, 0x83, 0x81, 0x81, 0xF1, 0x00, 0x00, 0x00 }, 54 { 0xF1, 0x83, 0x81, 0x81, 0xF1, 0x12, 0x3C, 0x48 }, { 0xE1, 0x93, 0x91, 0x91, 0xE1, 0x00, 0x00, 0x00 }, 55 { 0xE1, 0x93, 0x91, 0x91, 0xE1, 0x12, 0x3C, 0x48 }, { 0xF1, 0x83, 0xE1, 0x81, 0xF1, 0x00, 0x00, 0x00 }, 56 { 0xF1, 0x83, 0xF1, 0x81, 0x81, 0x00, 0x00, 0x00 }, { 0xF1, 0x83, 0xF1, 0x81, 0x81, 0x12, 0x3C, 0x48 }, 57 { 0xF1, 0x83, 0xB1, 0x91, 0xF1, 0x00, 0x00, 0x00 }, { 0xF1, 0x83, 0xB1, 0x91, 0xF1, 0x12, 0x3C, 0x48 }, 58 { 0x61, 0x93, 0x91, 0xF1, 0x91, 0x00, 0x00, 0x00 }, { 0x61, 0x93, 0xF1, 0x91, 0x91, 0x12, 0x3C, 0x48 }, 59 { 0xE1, 0x93, 0xE1, 0x91, 0xE1, 0x00, 0x00, 0x00 }, { 0xF7, 0x81, 0x87, 0x84, 0xF7, 0x00, 0x00, 0x00 }, 60 { 0xF7, 0x81, 0x87, 0x84, 0xF7, 0x12, 0x3C, 0x48 }, { 0xE7, 0x91, 0x97, 0x94, 0xE7, 0x00, 0x00, 0x00 }, 61 { 0xE7, 0x91, 0x97, 0x94, 0xE7, 0x12, 0x3C, 0x48 }, { 0xF7, 0x81, 0xE7, 0x84, 0xF7, 0x00, 0x00, 0x00 }, 62 { 0xF7, 0x81, 0xF7, 0x84, 0x87, 0x00, 0x00, 0x00 }, { 0xF7, 0x81, 0xF7, 0x84, 0x87, 0x12, 0x3C, 0x48 }, 63 { 0xF7, 0x81, 0xB7, 0x94, 0xF7, 0x00, 0x00, 0x00 }, { 0xF7, 0x81, 0xB7, 0x94, 0xF7, 0x12, 0x3C, 0x48 }, 64 { 0x67, 0x91, 0x97, 0xF4, 0x97, 0x00, 0x00, 0x00 }, { 0x67, 0x91, 0xF7, 0x94, 0x97, 0x12, 0x3C, 0x48 }, 65 { 0xE7, 0x91, 0xE7, 0x94, 0xE7, 0x00, 0x00, 0x00 }, { 0xF7, 0x81, 0x87, 0x81, 0xF7, 0x00, 0x00, 0x00 }, 66 { 0xF7, 0x81, 0x87, 0x81, 0xF7, 0x12, 0x3C, 0x48 }, { 0xE7, 0x91, 0x97, 0x91, 0xE7, 0x00, 0x00, 0x00 }, 67 { 0xE7, 0x91, 0x97, 0x91, 0xE7, 0x12, 0x3C, 0x48 }, { 0xF7, 0x81, 0xE7, 0x81, 0xF7, 0x00, 0x00, 0x00 }, 68 { 0xF7, 0x81, 0xF7, 0x81, 0x87, 0x00, 0x00, 0x00 }, { 0xF7, 0x81, 0xF7, 0x81, 0x87, 0x12, 0x3C, 0x48 }, 69 { 0xF7, 0x81, 0xB7, 0x91, 0xF7, 0x00, 0x00, 0x00 }, { 0xF7, 0x81, 0xB7, 0x91, 0xF7, 0x12, 0x3C, 0x48 }, 70 { 0x67, 0x91, 0x97, 0xF1, 0x97, 0x00, 0x00, 0x00 }, { 0x67, 0x91, 0xF7, 0x91, 0x97, 0x12, 0x3C, 0x48 }, 71 { 0xE7, 0x91, 0xE7, 0x91, 0xE7, 0x00, 0x00, 0x00 }, { 0xF1, 0x83, 0x87, 0x81, 0xF1, 0x00, 0x00, 0x00 }, 72 { 0xF1, 0x83, 0x87, 0x81, 0xF1, 0x12, 0x3C, 0x48 }, { 0xE1, 0x93, 0x97, 0x91, 0xE1, 0x00, 0x00, 0x00 }, 73 { 0xE1, 0x93, 0x97, 0x91, 0xE1, 0x12, 0x3C, 0x48 }, { 0xF1, 0x83, 0xE7, 0x81, 0xF1, 0x00, 0x00, 0x00 }, 74 { 0xF1, 0x83, 0xF7, 0x81, 0x81, 0x00, 0x00, 0x00 }, { 0xF1, 0x83, 0xF7, 0x81, 0x81, 0x12, 0x3C, 0x48 }, 75 { 0xF1, 0x83, 0xB7, 0x91, 0xF1, 0x00, 0x00, 0x00 }, { 0xF1, 0x83, 0xB7, 0x91, 0xF1, 0x12, 0x3C, 0x48 }, 76 { 0x61, 0x93, 0x97, 0xF1, 0x91, 0x00, 0x00, 0x00 }, { 0x61, 0x93, 0xF7, 0x91, 0x91, 0x12, 0x3C, 0x48 }, 77 { 0xE1, 0x93, 0xE7, 0x91, 0xE1, 0x00, 0x00, 0x00 }, { 0xF7, 0x84, 0x87, 0x81, 0xF7, 0x00, 0x00, 0x00 }, 78 { 0xF7, 0x84, 0x87, 0x81, 0xF7, 0x12, 0x3C, 0x48 }, { 0xE7, 0x94, 0x97, 0x91, 0xE7, 0x00, 0x00, 0x00 }, 79 { 0xE7, 0x94, 0x97, 0x91, 0xE7, 0x12, 0x3C, 0x48 }, { 0xF7, 0x84, 0xE7, 0x81, 0xF7, 0x00, 0x00, 0x00 }, 80 { 0xF7, 0x84, 0xF7, 0x81, 0x87, 0x00, 0x00, 0x00 }, { 0xF7, 0x84, 0xF7, 0x81, 0x87, 0x12, 0x3C, 0x48 }, 81 { 0xF7, 0x84, 0xB7, 0x91, 0xF7, 0x00, 0x00, 0x00 }, { 0xF7, 0x84, 0xB7, 0x91, 0xF7, 0x12, 0x3C, 0x48 }, 82 { 0x67, 0x94, 0x97, 0xF1, 0x97, 0x00, 0x00, 0x00 }, { 0x67, 0x94, 0xF7, 0x91, 0x97, 0x12, 0x3C, 0x48 }, 83 { 0xE7, 0x94, 0xE7, 0x91, 0xE7, 0x00, 0x00, 0x00 }, { 0xF4, 0x84, 0x87, 0x85, 0xF7, 0x00, 0x00, 0x00 }, 84 { 0xF4, 0x84, 0x87, 0x85, 0xF7, 0x12, 0x3C, 0x48 }, { 0xE4, 0x94, 0x97, 0x95, 0xE7, 0x00, 0x00, 0x00 }, 85 { 0xE4, 0x94, 0x97, 0x95, 0xE7, 0x12, 0x3C, 0x48 }, { 0xF4, 0x84, 0xE7, 0x85, 0xF7, 0x00, 0x00, 0x00 }, 86 { 0xF4, 0x84, 0xF7, 0x85, 0x87, 0x00, 0x00, 0x00 }, { 0xF4, 0x84, 0xF7, 0x85, 0x87, 0x12, 0x3C, 0x48 }, 87 { 0xF4, 0x84, 0xB7, 0x95, 0xF7, 0x00, 0x00, 0x00 }, { 0xF4, 0x84, 0xB7, 0x95, 0xF7, 0x12, 0x3C, 0x48 }, 88 { 0x64, 0x94, 0x97, 0xF5, 0x97, 0x00, 0x00, 0x00 }, { 0x64, 0x94, 0xF7, 0x95, 0x97, 0x12, 0x3C, 0x48 }, 89 { 0xE4, 0x94, 0xE7, 0x95, 0xE7, 0x00, 0x00, 0x00 }, { 0xF7, 0x81, 0x81, 0x81, 0xF1, 0x00, 0x00, 0x00 }, 90 { 0xF7, 0x81, 0x81, 0x81, 0xF1, 0x12, 0x3C, 0x48 }, { 0xE7, 0x91, 0x91, 0x91, 0xE1, 0x00, 0x00, 0x00 }, 91 { 0xE7, 0x91, 0x91, 0x91, 0xE1, 0x12, 0x3C, 0x48 }, { 0xF7, 0x81, 0xE1, 0x81, 0xF1, 0x00, 0x00, 0x00 }, 92 { 0xF7, 0x81, 0xF1, 0x81, 0x81, 0x00, 0x00, 0x00 }, { 0xF7, 0x81, 0xF1, 0x81, 0x81, 0x12, 0x3C, 0x48 }, 93 { 0xF7, 0x81, 0xB1, 0x91, 0xF1, 0x00, 0x00, 0x00 }, { 0xF7, 0x81, 0xB1, 0x91, 0xF1, 0x12, 0x3C, 0x48 }, 94 { 0x67, 0x91, 0x91, 0xF1, 0x91, 0x00, 0x00, 0x00 }, { 0x67, 0x91, 0xF1, 0x91, 0x91, 0x12, 0x3C, 0x48 }, 95 { 0xE7, 0x91, 0xE1, 0x91, 0xE1, 0x00, 0x00, 0x00 }, { 0xF7, 0x85, 0x87, 0x85, 0xF7, 0x00, 0x00, 0x00 }, 96 { 0xF7, 0x85, 0x87, 0x85, 0xF7, 0x12, 0x3C, 0x48 }, { 0xE7, 0x95, 0x97, 0x95, 0xE7, 0x00, 0x00, 0x00 }, 97 { 0xE7, 0x95, 0x97, 0x95, 0xE7, 0x12, 0x3C, 0x48 } 98}; 99const int NUM_FRAMES = sizeof(matrix_notes) / sizeof(matrix_notes[0]); 100 101// System state variables 102int scale = 0; // Current scale mode (0=OFF, 1=Continuous, etc.) 103bool output_ok = false; // Audio output enable flag 104int octave = 12; // Central octave offset 105int starting_note = 37; // Default: C4 (MIDI note 60) 106bool ON_mode = false; // Synthesis engine active flag 107String scale_name = ""; // Current scale name 108 109// Ultrasonic sensor 110SR04 sensor = SR04(12, 11); // Trig=12, Echo=11 111long distance; // Measured distance 112int echoNote = 220; // Current note frequency 113 114// Distance parameters 115const int starting_distance = 2.5; // higher note 116const int step = 2.5; // changing note step 117 118// Button configuration 119const int buttonOne = 2; 120const int buttonTwo = 4; 121const int buttonThree = 5; 122const int buttonFour = 6; 123const int buttonFive = 13; 124const int buttonSix = 7; 125 126// Button state tracking 127int buttonStates[6] = {0}; // Debounce states 128 129// Sound synthesis parameters 130uint16_t syncPhaseAcc; 131uint16_t syncPhaseInc; 132uint16_t grainPhaseAcc; 133uint16_t grainPhaseInc; 134uint16_t grainAmp; 135uint8_t grainDecay; 136uint16_t grain2PhaseAcc; 137uint16_t grain2PhaseInc; 138uint16_t grain2Amp; 139uint8_t grain2Decay; 140 141// Potentiometer mapping 142#define GRAIN_FREQ_CONTROL 0 143#define GRAIN_DECAY_CONTROL 1 144#define GRAIN2_FREQ_CONTROL 2 145#define GRAIN2_DECAY_CONTROL 3 146 147// PWM configuration 148#if defined(__AVR_ATmega1280__) 149 #define PWM_PIN 3 150 #define PWM_VALUE OCR3C 151 #define PWM_INTERRUPT TIMER3_OVF_vect 152#else 153 #define PWM_PIN 3 154 #define PWM_VALUE OCR2B 155 #define PWM_INTERRUPT TIMER2_OVF_vect 156#endif 157 158// Note buffers 159#define NUM_NOTE_FRAMES 15 160byte notes_name_matrix_to_be_played[NUM_NOTE_FRAMES][MATRIX_ROWS]; 161int notes_to_be_played[NUM_NOTE_FRAMES]; 162 163// ====================================================================== 164// INITIALIZATION 165// ====================================================================== 166void setup() { 167 // LED matrix setup 168 lc.shutdown(0, false); 169 lc.setIntensity(0, 6); 170 lc.clearDisplay(0); 171 172 // LCD setup 173 lcd.init(); 174 lcd.clear(); 175 lcd.backlight(); 176 177 // Button setup 178 pinMode(buttonOne, INPUT_PULLUP); 179 pinMode(buttonTwo, INPUT_PULLUP); 180 pinMode(buttonThree, INPUT_PULLUP); 181 pinMode(buttonFour, INPUT_PULLUP); 182 pinMode(buttonFive, INPUT_PULLUP); 183 pinMode(buttonSix, INPUT_PULLUP); 184 185 // Serial setup for debugging 186 //Serial.begin(9600); 187 188 // Initial state 189 scale_name = "OFF"; 190 update_display(); 191 update_notes(scale, starting_note, octave); 192 193 // Audio system initialization 194 pinMode(PWM_PIN, OUTPUT); 195 audioOn(); 196} 197 198// ====================================================================== 199// MAIN LOOP 200// ====================================================================== 201void loop() { 202 // 1. Process button inputs 203 handleButtons(); 204 205 // 2. Read distance sensor 206 distance = sensor.Distance(); 207 208 // 3. Determine note based on distance and scale 209 if (scale == 1) { // Continuous mode 210 // up to 15 step (step + step*15) 211 echoNote = map(distance, 0, (starting_distance + (step * 15)), 4978, 31); 212 lc.clearDisplay(0); // Clear matrix in continuous mode 213 } 214 else if (ON_mode) { 215 // Discrete note selection 216 for (int i = 0; i < NUM_NOTE_FRAMES; i++) { 217 int dist_min = starting_distance + (step * i); 218 int dist_max = starting_distance + (step * (i + 1)); 219 220 if (distance >= dist_min && distance < dist_max) { 221 echoNote = notes_to_be_played[i]; 222 223 // Display note pattern on LED matrix 224 for (int row = 0; row < MATRIX_ROWS; row++) { 225 lc.setRow(0, row, notes_name_matrix_to_be_played[i][row]); 226 } 227 break; 228 } 229 } 230 } 231 232 // 4. Update synthesis parameters if active 233 if (distance < (starting_distance + step * 15) && ON_mode) { 234 syncPhaseInc = echoNote; 235 grainPhaseInc = mapPhaseInc(analogRead(GRAIN_FREQ_CONTROL)) / 2; 236 grainDecay = analogRead(GRAIN_DECAY_CONTROL) / 8; 237 grain2PhaseInc = mapPhaseInc(analogRead(GRAIN2_FREQ_CONTROL)) / 2; 238 grain2Decay = analogRead(GRAIN2_DECAY_CONTROL) / 4; 239 output_ok = true; 240 } 241 else { 242 output_ok = false; 243 lc.clearDisplay(0); 244 } 245} 246 247// ====================================================================== 248// BUTTON HANDLING 249// ====================================================================== 250void handleButtons() { 251 bool display_updated = false; 252 253 // Button 1: Decrease starting note 254 if (digitalRead(buttonOne) == LOW && buttonStates[0] == 0) { 255 starting_note = constrain(starting_note - 1, 0, 73); 256 update_notes(scale, starting_note, octave); 257 buttonStates[0] = 1; 258 display_updated = true; 259 } 260 else if (digitalRead(buttonOne) == HIGH) { 261 buttonStates[0] = 0; 262 } 263 264 // Button 2: Increase starting note 265 if (digitalRead(buttonTwo) == LOW && buttonStates[1] == 0) { 266 starting_note = constrain(starting_note + 1, 0, 73); 267 update_notes(scale, starting_note, octave); 268 buttonStates[1] = 1; 269 display_updated = true; 270 } 271 else if (digitalRead(buttonTwo) == HIGH) { 272 buttonStates[1] = 0; 273 } 274 275 // Button 3: Decrease octave 276 if (digitalRead(buttonThree) == LOW && buttonStates[2] == 0) { 277 octave = constrain(octave - 12, 0, 48); 278 update_notes(scale, starting_note, octave); 279 buttonStates[2] = 1; 280 display_updated = true; 281 } 282 else if (digitalRead(buttonThree) == HIGH) { 283 buttonStates[2] = 0; 284 } 285 286 // Button 4: Increase octave 287 if (digitalRead(buttonFour) == LOW && buttonStates[3] == 0) { 288 octave = constrain(octave + 12, 0, 48); 289 update_notes(scale, starting_note, octave); 290 buttonStates[3] = 1; 291 display_updated = true; 292 } 293 else if (digitalRead(buttonFour) == HIGH) { 294 buttonStates[3] = 0; 295 } 296 297 // Button 5: Previous scale 298 if (digitalRead(buttonFive) == LOW && buttonStates[4] == 0) { 299 scale = constrain(scale - 1, 0, 6); 300 update_notes(scale, starting_note, octave); 301 buttonStates[4] = 1; 302 display_updated = true; 303 } 304 else if (digitalRead(buttonFive) == HIGH) { 305 buttonStates[4] = 0; 306 } 307 308 // Button 6: Next scale 309 if (digitalRead(buttonSix) == LOW && buttonStates[5] == 0) { 310 scale = constrain(scale + 1, 0, 6); 311 update_notes(scale, starting_note, octave); 312 buttonStates[5] = 1; 313 display_updated = true; 314 } 315 else if (digitalRead(buttonSix) == HIGH) { 316 buttonStates[5] = 0; 317 } 318 319 // Update display only if a button was pressed 320 if (display_updated) { 321 update_display(); 322 } 323} 324 325// ====================================================================== 326// DISPLAY UPDATE FUNCTION 327// ====================================================================== 328void update_display() { 329 // First line: Scale information 330 lcd.setCursor(0, 0); 331 lcd.print(" "); // Clear line 332 lcd.setCursor(0, 0); 333 lcd.print(scale); 334 lcd.print(" "); 335 lcd.print(scale_name); 336 337 // Second line: Conditional display 338 lcd.setCursor(0, 1); 339 lcd.print(" "); // Clear line 340 341 // Only show starting note for scales 2 and above 342 if (scale >= 2) { 343 lcd.setCursor(0, 1); 344 lcd.print("Starting note"); 345 346 // Calculate remaining space and display note name 347 int remaining_space = 16 - 13; // "Starting note" is 13 characters 348 const char* note = notes_name[starting_note + octave]; 349 int note_length = strlen(note); 350 351 if (note_length <= remaining_space) { 352 lcd.print(note); 353 } else { 354 // If note name is too long, truncate it 355 for (int i = 0; i < remaining_space; i++) { 356 lcd.print(note[i]); 357 } 358 } 359 } 360} 361 362// ====================================================================== 363// SOUND ENGINE 364// ====================================================================== 365SIGNAL(PWM_INTERRUPT) { 366 uint8_t value; 367 uint16_t output; 368 369 syncPhaseAcc += syncPhaseInc; 370 if (syncPhaseAcc < syncPhaseInc) { 371 // Time to start the next grain 372 grainPhaseAcc = 0; 373 grainAmp = 0x7fff; 374 grain2PhaseAcc = 0; 375 grain2Amp = 0x7fff; 376 PORTB ^= 1 << 5; // Toggle LED on pin 13 377 } 378 379 // Increment the phase of the grain oscillators 380 grainPhaseAcc += grainPhaseInc; 381 grain2PhaseAcc += grain2PhaseInc; 382 383 // Convert phase into a triangle wave 384 value = (grainPhaseAcc >> 7) & 0xff; 385 if (grainPhaseAcc & 0x8000) value = ~value; 386 // Multiply by current grain amplitude to get sample 387 output = value * (grainAmp >> 8); 388 389 // Repeat for second grain 390 value = (grain2PhaseAcc >> 7) & 0xff; 391 if (grain2PhaseAcc & 0x8000) value = ~value; 392 output += value * (grain2Amp >> 8); 393 394 // Make the grain amplitudes decay by a factor every sample 395 grainAmp -= (grainAmp >> 8) * grainDecay; 396 grain2Amp -= (grain2Amp >> 8) * grain2Decay; 397 398 // Scale output to the available range 399 output >>= 9; 400 if (output > 255) output = 255; 401 402 // Output to PWM if enabled 403 if (output_ok) { 404 PWM_VALUE = output; 405 } 406} 407 408void audioOn() { 409 #if defined(__AVR_ATmega1280__) 410 TCCR3A = _BV(COM3C1) | _BV(WGM30); 411 TCCR3B = _BV(CS30); 412 TIMSK3 = _BV(TOIE3); 413 #else 414 TCCR2A = _BV(COM2B1) | _BV(WGM20); 415 TCCR2B = _BV(CS20); 416 TIMSK2 = _BV(TOIE2); 417 #endif 418} 419 420// Frequency mapping functions 421uint16_t mapPhaseInc(uint16_t input) { 422 static const uint16_t antilogTable[] = { 423 64830,64132,63441,62757,62081,61413,60751,60097,59449,58809,58176,57549,56929,56316,55709,55109, 424 54515,53928,53347,52773,52204,51642,51085,50535,49991,49452,48920,48393,47871,47356,46846,46341, 425 45842,45348,44859,44376,43898,43425,42958,42495,42037,41584,41136,40693,40255,39821,39392,38968, 426 38548,38133,37722,37316,36914,36516,36123,35734,35349,34968,34591,34219,33850,33486,33125,32768 427 }; 428 return (antilogTable[input & 0x3f]) >> (input >> 6); 429} 430 431// ====================================================================== 432// SCALE MANAGEMENT 433// ====================================================================== 434void update_notes(int scale_update, int starting_note_update, int octave_update) { 435 // Update note sequences based on selected scale 436 switch (scale_update) { 437 case 0: // OFF 438 ON_mode = false; 439 scale_name = "OFF"; 440 lc.clearDisplay(0); 441 break; 442 443 case 1: // Continuous 444 ON_mode = true; 445 scale_name = "Continuous"; 446 break; 447 448 case 2: // Chromatic 449 { 450 ON_mode = true; 451 scale_name = "Chromatic"; 452 for (int i = 0; i < NUM_NOTE_FRAMES; i++) { 453 int note_index = constrain(starting_note_update + octave_update - i, 0, 87); 454 notes_to_be_played[i] = notes[note_index]; 455 for (int row = 0; row < MATRIX_ROWS; row++) { 456 notes_name_matrix_to_be_played[i][row] = 457 pgm_read_byte(&matrix_notes[note_index][row]); 458 } 459 } 460 } 461 break; 462 463 case 3: // Major 464 { 465 ON_mode = true; 466 scale_name = "Major"; 467 // Major scale intervals: W-W-H-W-W-W-H 468 int major_offsets[15] = {0, -1, -3, -5, -7, -8, -10, -12, -13, -15, -17, -19, -20, -22, -24}; 469 for (int i = 0; i < NUM_NOTE_FRAMES; i++) { 470 int note_index = constrain(starting_note_update + octave_update + major_offsets[i], 0, 87); 471 notes_to_be_played[i] = notes[note_index]; 472 for (int row = 0; row < MATRIX_ROWS; row++) { 473 notes_name_matrix_to_be_played[i][row] = 474 pgm_read_byte(&matrix_notes[note_index][row]); 475 } 476 } 477 } 478 break; 479 480 case 4: // Natural Minor 481 { 482 ON_mode = true; 483 scale_name = "Nat Minor"; 484 // Natural minor scale intervals: W-H-W-W-H-W-W 485 int minor_offsets[15] = {0, -2, -4, -5, -7, -9, -10, -12, -14, -16, -17, -19, -21, -22, -24}; 486 for (int i = 0; i < NUM_NOTE_FRAMES; i++) { 487 int note_index = constrain(starting_note_update + octave_update + minor_offsets[i], 0, 87); 488 notes_to_be_played[i] = notes[note_index]; 489 for (int row = 0; row < MATRIX_ROWS; row++) { 490 notes_name_matrix_to_be_played[i][row] = 491 pgm_read_byte(&matrix_notes[note_index][row]); 492 } 493 } 494 } 495 break; 496 497 case 5: // Harmonic Minor 498 { 499 ON_mode = true; 500 scale_name = "Harm Minor"; 501 // Harmonic minor scale intervals: W-H-W-W-H-WH-H 502 int harm_offsets[15] = {0, -1, -4, -5, -7, -9, -10, -12, -13, -16, -17, -19, -21, -22, -24}; 503 for (int i = 0; i < NUM_NOTE_FRAMES; i++) { 504 int note_index = constrain(starting_note_update + octave_update + harm_offsets[i], 0, 87); 505 notes_to_be_played[i] = notes[note_index]; 506 for (int row = 0; row < MATRIX_ROWS; row++) { 507 notes_name_matrix_to_be_played[i][row] = 508 pgm_read_byte(&matrix_notes[note_index][row]); 509 } 510 } 511 } 512 break; 513 514 case 6: // Pentatonic 515 { 516 ON_mode = true; 517 scale_name = "Pentatonic"; 518 // Pentatonic scale intervals: W-W-W-WH-W 519 int pentatonic_offsets[15] = {0, -3, -5, -8, -10, -12, -15, -17, -20, -22, -24, -27, -29, -32, -34}; 520 for (int i = 0; i < NUM_NOTE_FRAMES; i++) { 521 int note_index = constrain(starting_note_update + octave_update + pentatonic_offsets[i], 0, 87); 522 notes_to_be_played[i] = notes[note_index]; 523 for (int row = 0; row < MATRIX_ROWS; row++) { 524 notes_name_matrix_to_be_played[i][row] = 525 pgm_read_byte(&matrix_notes[note_index][row]); 526 } 527 } 528 } 529 break; 530 } 531}
Downloadable files
3d file Base Right
01 Base R.stl
3d file Base Left
02 Base L.stl
3d file Antenna Right
03 Antenna R.stl
3d file Antenna Left
04 Antenna L.stl
3d file Cover Right
05 Cover R.stl
3d file Cover Left
06 Cover L.stl
3d file Push Button
You need to print 6 items
07 Push Button X6.stl
3d file Power plug stop
08 Power plug stop.stl
3d file S Letter
09 S Letter.stl
3d file Y Letter
10 Y Letter.stl
3d file N Letter
You need to print 2 Items
11 N Letter X2.stl
3d file T Letter
12 T Letter.stl
3d file E Letter
You need to print 2 Items
13 E Letter X2.stl
3d file R Letter
14 R Letter.stl
3d file M Letter
15 M Letter.stl
3d file I Letter
16 I Letter.stl
3d file O Letter
17 O Letter.stl
3d file Little o Letter
18 o little Letter.stl
3d file Little Circle
19 Little circle.stl
Documentation
Schematics diagram
Schematics.png

Comments
Only logged in users can leave comments