#include "Arduino.h" #include "application.h" #include "hw.h" #include "SPImcpDAC.h" #include "ihandlers.h" #include "timer.h" #include "EEPROM.h" const AppMode AppModeValues[] = {MUTE,NORMAL}; const int16_t CalibrationTolerance = 15; const int16_t PitchFreqOffset = 700; const int16_t VolumeFreqOffset = 700; const int8_t HYST_VAL = 40; static int32_t pitchCalibrationBase = 0; static int32_t pitchCalibrationBaseFreq = 0; static int32_t pitchCalibrationConstant = 0; static int32_t pitchSensitivityConstant = 70000; static int16_t pitchDAC = 0; static int16_t volumeDAC = 0; static float qMeasurement = 0; static int32_t volCalibrationBase = 0; static uint8_t new_midi_note =0; static uint8_t old_midi_note =0; static uint8_t new_midi_loop_cc_val =0; static uint8_t old_midi_loop_cc_val =0; static uint8_t midi_velocity = 0; static uint8_t loop_hand_pos = 0; static uint16_t new_midi_rod_cc_val =0; static uint16_t old_midi_rod_cc_val =0; static uint16_t new_midi_bend =0; static uint16_t old_midi_bend = 0; static uint8_t midi_bend_low; static uint8_t midi_bend_high; static uint32_t long_log_note = 0; static uint32_t midi_key_follow = 2048; ; // Configuration parameters static uint8_t registerValue = 2; // wavetable selector is defined and initialized in ihandlers.cpp static uint8_t midi_channel = 0; static uint8_t old_midi_channel = 0; static uint8_t midi_bend_range = 2; static uint8_t midi_volume_trigger = 0; static uint8_t flag_legato_on = 1; static uint8_t flag_pitch_bend_on = 1; static uint8_t loop_midi_cc = 7; static uint8_t rod_midi_cc = 255; static uint8_t rod_midi_cc_lo = 255; static uint32_t rod_cc_scale = 128; // tweakable paramameters #define VELOCITY_SENS 9 // How easy it is to reach highest velocity (127). Something betwen 5 and 12. #define PLAYER_ACCURACY 819 // between 0 (very accurate players) and 2048 (not accurate at all) static uint16_t data_pot_value = 0; static uint16_t old_data_pot_value = 0; static uint16_t param_pot_value = 0; static uint16_t old_param_pot_value = 0; Application::Application() : _state(PLAYING), _mode(NORMAL) { }; void Application::setup() { HW_LED1_ON;HW_LED2_OFF; pinMode(Application::BUTTON_PIN, INPUT_PULLUP); pinMode(Application::LED_PIN_1, OUTPUT); pinMode(Application::LED_PIN_2, OUTPUT); digitalWrite(Application::LED_PIN_1, HIGH); // turn the LED off by making the voltage LOW SPImcpDACinit(); EEPROM.get(0,pitchDAC); EEPROM.get(2,volumeDAC); SPImcpDAC2Asend(pitchDAC); SPImcpDAC2Bsend(volumeDAC); initialiseTimer(); initialiseInterrupts(); EEPROM.get(4,pitchCalibrationBase); EEPROM.get(8,volCalibrationBase); init_parameters(); midi_setup(); } void Application::initialiseTimer() { ihInitialiseTimer(); } void Application::initialiseInterrupts() { ihInitialiseInterrupts(); } void Application::InitialisePitchMeasurement() { ihInitialisePitchMeasurement(); } void Application::InitialiseVolumeMeasurement() { ihInitialiseVolumeMeasurement(); } unsigned long Application::GetQMeasurement() { int qn=0; TCCR1B = (1<>2); pitch_l=pitch_v; //HW_LED2_ON; // set wave frequency for each mode switch (_mode) { case MUTE : /* NOTHING! */; break; case NORMAL : setWavetableSampleAdvance(((pitchCalibrationBase-pitch_v)+2048-(pitchPotValue<<2))>>registerValue); break; }; // HW_LED2_OFF; pitchValueAvailable = false; } if (volumeValueAvailable) { vol = max(vol, 5000); vol_v=vol; // Averaging volume values vol_v=vol_l+((vol_v-vol_l)>>2); vol_l=vol_v; switch (_mode) { case MUTE: vol_v = 0; break; case NORMAL: vol_v = MAX_VOLUME-(volCalibrationBase-vol_v)/2+(volumePotValue<<2)-1024; break; }; // Limit and set volume value vol_v = min(vol_v, 4095); // vol_v = vol_v - (1 + MAX_VOLUME - (volumePotValue << 2)); vol_v = vol_v ; vol_v = max(vol_v, 0); loop_hand_pos = vol_v >> 4; // Give vScaledVolume a pseudo-exponential characteristic: vScaledVolume = loop_hand_pos * (loop_hand_pos + 2); volumeValueAvailable = false; } if (midi_timer > 100) // run midi app every 100 ticks equivalent to approximatevely 3 ms to avoid synth's overload { midi_application (); midi_timer = 0; } goto mloop; // End of main loop } void Application::calibrate() { resetPitchFlag(); resetTimer(); savePitchCounter(); while (!pitchValueAvailable && timerUnexpiredMillis(10)) ; // NOP pitchCalibrationBase = pitch; pitchCalibrationBaseFreq = FREQ_FACTOR/pitchCalibrationBase; pitchCalibrationConstant = FREQ_FACTOR/pitchSensitivityConstant/2+200; resetVolFlag(); resetTimer(); saveVolCounter(); while (!volumeValueAvailable && timerUnexpiredMillis(10)) ; // NOP volCalibrationBase = vol; EEPROM.put(4,pitchCalibrationBase); EEPROM.put(8,volCalibrationBase); } void Application::calibrate_pitch() { static int16_t pitchXn0 = 0; static int16_t pitchXn1 = 0; static int16_t pitchXn2 = 0; static float q0 = 0; static long pitchfn0 = 0; static long pitchfn1 = 0; static long pitchfn = 0; // limit the number of calibration iteration to 12 // the algorythm used is normaly faster than dichotomy which normaly finds a 12Bit number in 12 iterations max static uint16_t l_iteration_pitch = 0; InitialisePitchMeasurement(); interrupts(); SPImcpDACinit(); qMeasurement = GetQMeasurement(); // Measure Arudino clock frequency q0 = (16000000/qMeasurement*500000); //Calculated set frequency based on Arudino clock frequency pitchXn0 = 0; pitchXn1 = 4095; pitchfn = q0-PitchFreqOffset; // Add offset calue to set frequency SPImcpDAC2Bsend(1600); SPImcpDAC2Asend(pitchXn0); delay(100); pitchfn0 = GetPitchMeasurement(); SPImcpDAC2Asend(pitchXn1); delay(100); pitchfn1 = GetPitchMeasurement(); l_iteration_pitch = 0; while ((abs(pitchfn0 - pitchfn1) > CalibrationTolerance) && (l_iteration_pitch < 12)) { SPImcpDAC2Asend(pitchXn0); delay(100); pitchfn0 = GetPitchMeasurement()-pitchfn; SPImcpDAC2Asend(pitchXn1); delay(100); pitchfn1 = GetPitchMeasurement()-pitchfn; pitchXn2=pitchXn1-((pitchXn1-pitchXn0)*pitchfn1)/(pitchfn1-pitchfn0); // new DAC value pitchXn0 = pitchXn1; pitchXn1 = pitchXn2; HW_LED2_TOGGLE; l_iteration_pitch ++; } delay(100); EEPROM.put(0,pitchXn0); } void Application::calibrate_volume() { static int16_t volumeXn0 = 0; static int16_t volumeXn1 = 0; static int16_t volumeXn2 = 0; static float q0 = 0; static long volumefn0 = 0; static long volumefn1 = 0; static long volumefn = 0; // limit the number of calibration iteration to 12 // the algorythm used is normaly faster than dichotomy which normaly finds a 12Bit number in 12 iterations max static uint16_t l_iteration_volume = 0; InitialiseVolumeMeasurement(); interrupts(); SPImcpDACinit(); volumeXn0 = 0; volumeXn1 = 4095; q0 = (16000000/qMeasurement*460765); volumefn = q0-VolumeFreqOffset; SPImcpDAC2Bsend(volumeXn0); delay_NOP(44316);//44316=100ms volumefn0 = GetVolumeMeasurement(); SPImcpDAC2Bsend(volumeXn1); delay_NOP(44316);//44316=100ms volumefn1 = GetVolumeMeasurement(); l_iteration_volume = 0; while ((abs(volumefn0 - volumefn1) > CalibrationTolerance) && (l_iteration_volume < 12)) { SPImcpDAC2Bsend(volumeXn0); delay_NOP(44316);//44316=100ms volumefn0 = GetVolumeMeasurement()-volumefn; SPImcpDAC2Bsend(volumeXn1); delay_NOP(44316);//44316=100ms volumefn1 = GetVolumeMeasurement()-volumefn; volumeXn2=volumeXn1-((volumeXn1-volumeXn0)*volumefn1)/(volumefn1-volumefn0); // calculate new DAC value volumeXn0 = volumeXn1; volumeXn1 = volumeXn2; HW_LED2_TOGGLE; l_iteration_volume ++; } EEPROM.put(2,volumeXn0); } // calculate log2 of an unsigned from 1 to 65535 into a 4.12 fixed point unsigned // To avoid use of log (double) function uint16_t Application::log2U16 (uint16_t lin_input) { uint32_t long_lin; // To turn input into a 16.16 fixed point //unsigned long bit_pos; uint32_t log_output; // 4.12 fixed point log calculation int32_t long_x1; int32_t long_x2; int32_t long_x3; const int32_t POLY_A0 = 37; const int32_t POLY_A1 = 46390; const int32_t POLY_A2 = -18778; const int32_t POLY_A3 = 5155; if (lin_input != 0) { long_lin = (uint32_t) (lin_input) << 16; log_output = 0; // Calculate integer part of log2 and reduce long_lin into 16.16 between 1 and 2 if (long_lin >= 16777216) // 2^(8 + 16) { log_output += 8 << 12; long_lin = long_lin >> 8; } if (long_lin >= 1048576) // 2^(4 + 16) { log_output += 4 << 12; long_lin = long_lin >> 4; } if (long_lin >= 262144) // 2^(2 + 16) { log_output += 2 << 12; long_lin = long_lin >> 2; } if (long_lin >= 131072) // 2^(1 + 16) { log_output += 1 << 12; long_lin = long_lin >> 1; } // long_lin is between 1 and 2 now (16.16 fixed point) // Calculate 3rd degree polynomial approximation log(x)=Polynomial(x-1) in signed long s15.16 and reduce to unsigned 4.12 at the very end. long_lin = long_lin >> 1; // reduce to 17.15 bit to support signed operations here after long_x1 = long_lin-(32768); //(x-1) we have the decimal part in s15 now long_x2 = (long_x1 * long_x1) >> 15 ; // (x-1)^2 long_x3 = (long_x2 * long_x1) >> 15 ; // (x-1)^3 log_output += ( (POLY_A0) + ((POLY_A1 * long_x1) >> 15) + ((POLY_A2 * long_x2) >> 15) + ((POLY_A3 * long_x3) >> 15) ) >> 3; } else { log_output=0; } return log_output; } void Application::hzToAddVal(float hz) { setWavetableSampleAdvance((uint16_t)(hz * HZ_ADDVAL_FACTOR)); } void Application::playNote(float hz, uint16_t milliseconds = 500, uint8_t volume = 255) { vScaledVolume = volume * (volume + 2); hzToAddVal(hz); millitimer(milliseconds); vScaledVolume = 0; } void Application::playStartupSound() { playNote(MIDDLE_C, 150, 25); playNote(MIDDLE_C * 2, 150, 25); playNote(MIDDLE_C * 4, 150, 25); } void Application::playCalibratingCountdownSound() { playNote(MIDDLE_C * 2, 150, 25); playNote(MIDDLE_C * 2, 150, 25); } void Application::playModeSettingSound() { for (int i = 0; i <= _mode; i++) { playNote(MIDDLE_C * 2, 200, 25); millitimer(100); } } void Application::delay_NOP(unsigned long time) { volatile unsigned long i = 0; for (i = 0; i < time; i++) { __asm__ __volatile__ ("nop"); } } void Application::midi_setup() { // Set MIDI baud rate: Serial.begin(115200); // Baudrate for midi to serial. Use a serial to midi router https://github.com/projectgus/hairless-midiserial //Serial.begin(31250); // Baudrate for real midi. Use din connection https://www.arduino.cc/en/Tutorial/Midi or HIDUINO https://github.com/ddiakopoulos/hiduino _midistate = MIDI_SILENT; } void Application::midi_msg_send(uint8_t channel, uint8_t midi_cmd1, uint8_t midi_cmd2, uint8_t midi_value) { uint8_t mixed_cmd1_channel; mixed_cmd1_channel = (midi_cmd1 & 0xF0)| (channel & 0x0F); Serial.write(mixed_cmd1_channel); Serial.write(midi_cmd2); Serial.write(midi_value); } // midi_application sends note and volume and uses pitch bend to simulate continuous picth. // Calibrate pitch bend and other parameters accordingly to the receiver synth (see midi_calibrate). // New notes won't be generated as long as pitch bend will do the job. // The bigger is synth's pitch bend range the beter is the effect. void Application::midi_application () { int16_t delta_loop_cc_val = 0; int16_t calculated_velocity = 0; // Calculate loop antena cc value for midi new_midi_loop_cc_val = loop_hand_pos >> 1; new_midi_loop_cc_val = min (new_midi_loop_cc_val, 127); delta_loop_cc_val = (int16_t)new_midi_loop_cc_val - (int16_t)old_midi_loop_cc_val; // Calculate log freq if ((vPointerIncrement < 18) || (vPointerIncrement > 65518)) { // Lowest note long_log_note = 0; } else if ((vPointerIncrement > 26315) && (vPointerIncrement < 39221)) { // Highest note long_log_note = 127; } else if (vPointerIncrement < 32768) { // Positive frequencies // Find note in the playing range // 16795 = log2U16 (C0 [8.1758] * HZ_ADDVAL_FACTOR [2.09785]) long_log_note = 12 * ((uint32_t) log2U16(vPointerIncrement) - 16795); // Precise note played in the logaritmic scale } else { // Negative frequencies // Find note in the playing range // 16795 = log2U16 (C0 [8.1758] * HZ_ADDVAL_FACTOR [2.09785]) long_log_note = 12 * ((uint32_t) log2U16(65535-vPointerIncrement+1) - 16795); // Precise note played in the logaritmic scale } // Calculate rod antena cc value for midi new_midi_rod_cc_val = (uint16_t) min((long_log_note * rod_cc_scale) >> 12, 16383); // 14 bit value // State machine for MIDI switch (_midistate) { case MIDI_SILENT: // Always refresh midi loop antena cc. if (new_midi_loop_cc_val != old_midi_loop_cc_val) { if (loop_midi_cc < 128) { midi_msg_send(midi_channel, 0xB0, loop_midi_cc, new_midi_loop_cc_val); } old_midi_loop_cc_val = new_midi_loop_cc_val; } else { // do nothing } // Always refresh midi rod antena cc if applicable. if (new_midi_rod_cc_val != old_midi_rod_cc_val) { if (rod_midi_cc < 128) { midi_msg_send(midi_channel, 0xB0, rod_midi_cc, (uint8_t)(new_midi_rod_cc_val >> 7)); if (rod_midi_cc_lo < 128) { midi_msg_send(midi_channel, 0xB0, rod_midi_cc_lo, (uint8_t)(new_midi_rod_cc_val & 0x007F)); } } old_midi_rod_cc_val = new_midi_rod_cc_val; } else { // do nothing } // If player's hand moves away from volume antenna if (new_midi_loop_cc_val > midi_volume_trigger) { // Set key follow to the minimum in order to use closest note played as the center note midi_key_follow = 2048; // Calculate note and associated pitch bend calculate_note_bend (); // Send pitch bend to reach precise played note (send 8192 (no pitch bend) in case of midi_bend_range == 1) midi_msg_send(midi_channel, 0xE0, midi_bend_low, midi_bend_high); old_midi_bend = new_midi_bend; // Calculate velocity if (midi_timer != 0) { calculated_velocity = ((127 - midi_volume_trigger) >> 1 ) + (VELOCITY_SENS * midi_volume_trigger * delta_loop_cc_val / midi_timer); midi_velocity = min (abs (calculated_velocity), 127); } else { // should not happen midi_velocity = 64; } // Play the note midi_msg_send(midi_channel, 0x90, new_midi_note, midi_velocity); old_midi_note = new_midi_note; _midistate = MIDI_PLAYING; } else { // Do nothing } break; case MIDI_PLAYING: // Always refresh midi loop antena cc. if (new_midi_loop_cc_val != old_midi_loop_cc_val) { if (loop_midi_cc < 128) { midi_msg_send(midi_channel, 0xB0, loop_midi_cc, new_midi_loop_cc_val); } old_midi_loop_cc_val = new_midi_loop_cc_val; } else { // do nothing } // Always refresh midi rod antena cc if applicable. if (new_midi_rod_cc_val != old_midi_rod_cc_val) { if (rod_midi_cc < 128) { midi_msg_send(midi_channel, 0xB0, rod_midi_cc, (uint8_t)(new_midi_rod_cc_val >> 7)); if (rod_midi_cc_lo < 128) { midi_msg_send(midi_channel, 0xB0, rod_midi_cc_lo, (uint8_t)(new_midi_rod_cc_val & 0x007F)); } } old_midi_rod_cc_val = new_midi_rod_cc_val; } else { // do nothing } // If player's hand is far from volume antenna if (new_midi_loop_cc_val > midi_volume_trigger) { if ( flag_legato_on == 1) { // Set key follow so as next played note will be at limit of pitch bend range midi_key_follow = ((uint32_t) midi_bend_range * 4096) - PLAYER_ACCURACY; } else { // Set key follow to max so as no key follows midi_key_follow = 520192; // 127*2^12 } // Calculate note and associated pitch bend calculate_note_bend (); // Refresh midi pitch bend value if (new_midi_bend != old_midi_bend) { midi_msg_send(midi_channel, 0xE0, midi_bend_low, midi_bend_high); old_midi_bend = new_midi_bend; } else { // do nothing } // Refresh midi note if (new_midi_note != old_midi_note) { // Play new note before muting old one to play legato on monophonic synth // (pitch pend management tends to break expected effect here) midi_msg_send(midi_channel, 0x90, new_midi_note, midi_velocity); midi_msg_send(midi_channel, 0x90, old_midi_note, 0); old_midi_note = new_midi_note; } else { // do nothing } } else // Means that player's hand moves to the volume antenna { // Send note off midi_msg_send(midi_channel, 0x90, old_midi_note, 0); _midistate = MIDI_SILENT; } break; case MIDI_STOP: // Send all note off midi_msg_send(midi_channel, 0xB0, 0x7B, 0x00); _midistate = MIDI_MUTE; break; case MIDI_MUTE: //do nothing break; } } void Application::calculate_note_bend () { int32_t long_log_bend; int32_t long_norm_log_bend; long_log_bend = ((int32_t)long_log_note) - (((int32_t) old_midi_note) * 4096); // How far from last played midi chromatic note we are // If too much far from last midi chromatic note played (midi_key_follow depends on pitch bend range) if ((abs (long_log_bend) >= midi_key_follow) && (midi_key_follow != 520192)) { new_midi_note = (uint8_t) ((long_log_note + 2048) >> 12); // Select the new midi chromatic note - round to integer part by adding 1/2 before shifting long_log_bend = ((int32_t) long_log_note) - (((int32_t) new_midi_note) * 4096); // calculate bend to reach precise note played } else { new_midi_note = old_midi_note; // No change } // If pitch bend activated if (flag_pitch_bend_on == 1) { // use it to reach precise note played long_norm_log_bend = (long_log_bend / midi_bend_range); if (long_norm_log_bend > 4096) { long_norm_log_bend = 4096; } else if (long_norm_log_bend < -4096) { long_norm_log_bend = -4096; } new_midi_bend = 8192 + ((long_norm_log_bend * 8191) >> 12); // Calculate midi pitch bend } else { // Don't use pitch bend new_midi_bend = 8192; } // Prepare the 2 bites of picth bend midi message midi_bend_low = (int8_t) (new_midi_bend & 0x007F); midi_bend_high = (int8_t) ((new_midi_bend & 0x3F80)>> 7); } void Application::init_parameters () { // init data pot value to avoid 1st position to be taken into account param_pot_value = analogRead(REGISTER_SELECT_POT); old_param_pot_value = param_pot_value; data_pot_value = analogRead(WAVE_SELECT_POT); old_data_pot_value = data_pot_value; } void Application::set_parameters () { uint16_t data_steps; param_pot_value = analogRead(REGISTER_SELECT_POT); data_pot_value = analogRead(WAVE_SELECT_POT); // If parameter pot moved if (abs((int32_t)param_pot_value - (int32_t)old_param_pot_value) >= 8) { // Blink the LED relatively to pot position resetTimer(); if (((param_pot_value >> 7) % 2) == 0) { HW_LED1_OFF; HW_LED2_OFF; } else { HW_LED1_ON; HW_LED2_ON; } // Memorize data pot value to monitor changes old_param_pot_value = param_pot_value; } // Else If data pot moved else if (abs((int32_t)data_pot_value - (int32_t)old_data_pot_value) >= 8) { // Modify selected parameter switch (param_pot_value >> 7) { case 0: // Transpose switch (data_pot_value >> 8) { case 0: registerValue=3; // -1 Octave data_steps = 1; break; case 1: case 2: registerValue=2; // Center data_steps = 2; break; default: registerValue=1; // +1 Octave data_steps = 3; break; } break; case 1: // Waveform data_steps = data_pot_value >> 7; vWavetableSelector = data_steps; break; case 2: // Channel data_steps = data_pot_value >> 6; midi_channel = (uint8_t)(data_steps & 0x000F); if (old_midi_channel != midi_channel) { // Send all note off to avoid stuck notes midi_msg_send(old_midi_channel, 0xB0, 0x7B, 0x00); old_midi_channel = midi_channel; } break; case 3: // Rod antenna mode data_steps = data_pot_value >> 8; switch (data_steps) { case 0: flag_legato_on = 0; flag_pitch_bend_on = 0; break; case 1: flag_legato_on = 0; flag_pitch_bend_on = 1; break; case 2: flag_legato_on = 1; flag_pitch_bend_on = 0; break; default: flag_legato_on = 1; flag_pitch_bend_on = 1; break; } break; case 4: // Pitch-Bend range data_steps = data_pot_value >> 7; switch (data_steps) { case 0: midi_bend_range = 1; break; case 1: midi_bend_range = 2; break; case 2: midi_bend_range = 4; break; case 3: midi_bend_range = 5; break; case 4: midi_bend_range = 7; break; case 5: midi_bend_range = 12; break; case 6: midi_bend_range = 24; break; default: midi_bend_range = 48; break; } break; case 5: // Volume trigger data_steps = data_pot_value >> 8; midi_volume_trigger = (uint8_t)((data_pot_value >> 3) & 0x007F); break; case 6: //Rod antenna cc data_steps = data_pot_value >> 7; switch (data_steps) { case 0: rod_midi_cc = 255; // Nothing rod_midi_cc_lo = 255; // Nothing rod_cc_scale = 128; break; case 1: rod_midi_cc = 8; // Balance rod_midi_cc_lo = 255; // No least significant bits rod_cc_scale = 128; break; case 2: rod_midi_cc = 10; // Pan rod_midi_cc_lo = 255; // No least significant bits rod_cc_scale = 128; break; case 3: rod_midi_cc = 16; // General Purpose 1 (14 Bits) rod_midi_cc_lo = 48; // General Purpose 1 least significant bits rod_cc_scale = 128; break; case 4: rod_midi_cc = 17; // General Purpose 2 (14 Bits) rod_midi_cc_lo = 49; // General Purpose 2 least significant bits rod_cc_scale = 128; break; case 5: rod_midi_cc = 18; // General Purpose 3 (7 Bits) rod_midi_cc_lo = 255; // No least significant bits rod_cc_scale = 128; break; case 6: rod_midi_cc = 19; // General Purpose 4 (7 Bits) rod_midi_cc_lo = 255; // No least significant bits rod_cc_scale = 128; break; default: rod_midi_cc = 74; // Cutoff (exists of both loop and rod) rod_midi_cc_lo = 255; // No least significant bits rod_cc_scale = 128; break; } break; default: // Loop antenna cc data_steps = data_pot_value >> 7; switch (data_steps) { case 0: loop_midi_cc = 1; // Modulation break; case 1: loop_midi_cc = 7; // Volume break; case 2: loop_midi_cc = 11; // Expression break; case 3: loop_midi_cc = 71; // Resonnance break; case 4: loop_midi_cc = 74; // Cutoff (exists of both loop and rod) break; case 5: loop_midi_cc = 91; // Reverb break; case 6: loop_midi_cc = 93; // Chorus break; default: loop_midi_cc = 95; // Phaser break; } break; } // Blink the LED relatively to pot position resetTimer(); if ((data_steps % 2) == 0) { HW_LED1_OFF; HW_LED2_OFF; } else { HW_LED1_ON; HW_LED2_ON; } // Memorize data pot value to monitor changes old_data_pot_value = data_pot_value; } else { if (timerExpired(65000)) //restore LED status { if (_mode == NORMAL) { HW_LED1_ON; HW_LED2_OFF; } else { HW_LED1_OFF; HW_LED2_ON; } } } }