From 96f045c97a3401632037c91f38d2a6b4da128828 Mon Sep 17 00:00:00 2001 From: midilab Date: Thu, 8 Mar 2018 19:21:59 -0300 Subject: [PATCH] Acid step sequencer clone of Roland TB303, testing release --- .../AcidStepSequencer/AcidStepSequencer.ino | 330 ++++++++++++++++-- 1 file changed, 296 insertions(+), 34 deletions(-) diff --git a/examples/AcidStepSequencer/AcidStepSequencer.ino b/examples/AcidStepSequencer/AcidStepSequencer.ino index 2d43907..03c54d4 100644 --- a/examples/AcidStepSequencer/AcidStepSequencer.ino +++ b/examples/AcidStepSequencer/AcidStepSequencer.ino @@ -4,29 +4,49 @@ // Sequencer config #define STEP_MAX_SIZE 16 #define SEQUENCER_MIN_BPM 50 -#define SEQUENCER_MAX_BPM 178 +#define SEQUENCER_MAX_BPM 177 #define NOTE_VELOCITY 90 #define ACCENT_VELOCITY 110 -// Choose only 1 mode and comment the other. -// OLD_SCHOOL_ACID_ACCENTED -// a button/led extra hardware to control steps velocity using accent only -// NEW_SCHOOL_VELOCITY_CONTROLLED -// a 10k potentiomer extra hardware to freely control steps velocity -#define OLD_SCHOOL_ACID_ACCENTED -#define NEW_SCHOOL_FREE_VELOCITY +// MIDI modes +#define MIDI_CHANNEL 0 // 0 = channel 1 +#define MIDI_MODE +//#define SERIAL_MODE +// hardware setup to fit different kinda of setups and arduino models +#define OCTAVE_POT_PIN A3 +#define NOTE_POT_PIN A2 +#define STEP_LENGTH_POT_PIN A1 +#define TEMPO_POT_PIN A0 + +#define PREVIOUS_STEP_BUTTON_PIN 2 +#define NEXT_STEP_BUTTON_PIN 3 +#define REST_BUTTON_PIN 4 +#define GLIDE_BUTTON_PIN 5 +#define ACCENT_BUTTON_PIN 6 +#define PLAY_STOP_BUTTON_PIN 7 + +#define PREVIOUS_STEP_LED_PIN 8 +#define NEXT_STEP_LED_PIN 9 +#define REST_LED_PIN 10 +#define GLIDE_LED_PIN 11 +#define ACCENT_LED_PIN 12 +#define PLAY_STOP_LED_PIN 13 + +// Sequencer data typedef struct { uint8_t note; - uint8_t velocity; bool accent; bool glide; bool rest; } SEQUENCER_STEP_DATA; SEQUENCER_STEP_DATA _sequencer[STEP_MAX_SIZE]; -uint16_t _last_step = 0; + +bool _playing = false; +uint16_t _step, _last_step, _step_edit = 0; +uint16_t _step_length = STEP_MAX_SIZE; // MIDI clock, start, stop, note on and note off byte definitions - based on MIDI 1.0 Standards. #define MIDI_CLOCK 0xF8 @@ -35,22 +55,34 @@ uint16_t _last_step = 0; #define NOTE_ON 0x90 #define NOTE_OFF 0x80 +// User Interface data +// 6 buttons to keep last value track +uint8_t _button_state[6] = {1}; +// 4 10k potentiometers to keep lasta value track +uint16_t _pot_state[4] = {0}; +uint8_t _last_octave = 3; +uint8_t _last_note = 0; + +// for interrupted control access of shared memory data +uint8_t _tmpSREG; + void sendMidiMessage(uint8_t command, uint8_t byte1, uint8_t byte2) { // send midi message + command = command | (uint8_t)MIDI_CHANNEL; Serial.write(command); Serial.write(byte1); Serial.write(byte2); } // The callback function wich will be called by uClock each Pulse of 16PPQN clock resolution. -// At this resolution each call represents exactly one step. +// Each call represents exactly one step here. void ClockOut16PPQN(uint32_t * tick) { - uint16_t step; + uint8_t velocity = NOTE_VELOCITY; // get actual step. - step = *tick % STEP_MAX_SIZE; + _step = *tick % _step_length; // send note off for the last step note on if we had send it on last ClockOut16PPQN() call and if this step are not in glide mode also. if ( _sequencer[_last_step].rest == false && _sequencer[_last_step].glide == false ) { @@ -58,23 +90,26 @@ void ClockOut16PPQN(uint32_t * tick) } // send note on only if this step are not in rest mode - if ( _sequencer[step].rest == false ) { - sendMidiMessage(NOTE_ON, _sequencer[step].note, _sequencer[step].velocity); + if ( _sequencer[_step].rest == false ) { + if ( _sequencer[_step].accent == true ) { + velocity = ACCENT_VELOCITY; + } + sendMidiMessage(NOTE_ON, _sequencer[_step].note, velocity); } - // time to let glide go away? be shure to send glided note off before the _last_step send his note off + // time to let glide go away? be shure to send glided note off after the actual step send his note on // same note? do not send note off - if ( _sequencer[_last_step].glide == true && _sequencer[step].note != _sequencer[_last_step].note ) { + if ( _sequencer[_last_step].glide == true && _sequencer[_step].note != _sequencer[_last_step].note ) { sendMidiMessage(NOTE_OFF, _sequencer[_last_step].note, 0); } - _last_step = step; + _last_step = _step; } // The callback function wich will be called by uClock each Pulse of 96PPQN clock resolution. void ClockOut96PPQN(uint32_t * tick) { - // Send MIDI_CLOCK to external gears + // Send MIDI_CLOCK to external hardware Serial.write(MIDI_CLOCK); } @@ -82,18 +117,56 @@ void ClockOut96PPQN(uint32_t * tick) void onClockStart() { Serial.write(MIDI_START); + _playing = true; } // The callback function wich will be called when clock stops by using Clock.stop() method. void onClockStop() { Serial.write(MIDI_STOP); + sendMidiMessage(NOTE_OFF, _sequencer[_last_step].note, 0); + sendMidiMessage(NOTE_OFF, _sequencer[_step].note, 0); + _playing = false; +} + +void configureInterface() +{ + // Buttons config + // use internal pullup for buttons + pinMode(PREVIOUS_STEP_BUTTON_PIN, INPUT_PULLUP); + pinMode(NEXT_STEP_BUTTON_PIN, INPUT_PULLUP); + pinMode(REST_BUTTON_PIN, INPUT_PULLUP); + pinMode(GLIDE_BUTTON_PIN, INPUT_PULLUP); + pinMode(ACCENT_BUTTON_PIN, INPUT_PULLUP); + pinMode(PLAY_STOP_BUTTON_PIN, INPUT_PULLUP); + + // Leds config + pinMode(PREVIOUS_STEP_LED_PIN, OUTPUT); + pinMode(NEXT_STEP_LED_PIN, OUTPUT); + pinMode(REST_LED_PIN, OUTPUT); + pinMode(GLIDE_LED_PIN, OUTPUT); + pinMode(ACCENT_LED_PIN, OUTPUT); + pinMode(PLAY_STOP_LED_PIN, OUTPUT); + + digitalWrite(PREVIOUS_STEP_LED_PIN, LOW); + digitalWrite(NEXT_STEP_LED_PIN, LOW); + digitalWrite(REST_LED_PIN, LOW); + digitalWrite(GLIDE_LED_PIN, LOW); + digitalWrite(ACCENT_LED_PIN, LOW); + digitalWrite(PLAY_STOP_LED_PIN, LOW); } void setup() { - // Initialize serial communication at 31250 bits per second, the default MIDI serial speed communication: - Serial.begin(31250); + // Initialize serial communication +#ifdef MIDI_MODE + // the default MIDI serial speed communication at 31250 bits per second + Serial.begin(31250); +#endif +#ifdef SERIAL_MODE + // for usage with a PC with a serial to MIDI bridge + Serial.begin(115200); +#endif // Inits the clock uClock.init(); @@ -114,27 +187,216 @@ void setup() // initing sequencer data for ( uint16_t i = 0; i < STEP_MAX_SIZE; i++ ) { _sequencer[i].note = 36; - _sequencer[i].velocity = NOTE_VELOCITY; _sequencer[i].accent = false; _sequencer[i].glide = false; _sequencer[i].rest = false; } - // Starts the clock, tick-tac-tick-tac... - uClock.start(); + // pins, buttons, leds and pots config + configureInterface(); +} + +bool pressed(uint8_t button_pin) +{ + uint8_t value; + uint8_t * last_value; + + switch(button_pin) { + case PREVIOUS_STEP_BUTTON_PIN: + last_value = &_button_state[0]; + break; + case NEXT_STEP_BUTTON_PIN: + last_value = &_button_state[1]; + break; + case REST_BUTTON_PIN: + last_value = &_button_state[2]; + break; + case GLIDE_BUTTON_PIN: + last_value = &_button_state[3]; + break; + case ACCENT_BUTTON_PIN: + last_value = &_button_state[4]; + break; + case PLAY_STOP_BUTTON_PIN: + last_value = &_button_state[5]; + break; + default: + return false; + } + + value = digitalRead(button_pin); + // check, using pullup pressed button goes LOW + if ( value != *last_value && value == LOW ) { + *last_value = value; + return true; + } else { + *last_value = value; + return false; + } + +} + +int16_t getPotChanges(uint8_t pot_pin, uint16_t min_value, uint16_t max_value) +{ + uint16_t value; + uint16_t * last_value; + + switch(pot_pin) { + case OCTAVE_POT_PIN: + last_value = &_pot_state[0]; + break; + case NOTE_POT_PIN: + last_value = &_pot_state[1]; + break; + case STEP_LENGTH_POT_PIN: + last_value = &_pot_state[2]; + break; + case TEMPO_POT_PIN: + last_value = &_pot_state[3]; + break; + default: + return -1; + } + + // range our value + value = (analogRead(pot_pin) / (1024 / ((max_value - min_value) + 1))) + min_value; + + // check, using pullup pressed button goes LOW + if ( abs(value - *last_value) >= 1 ) { + *last_value = value; + return value; + } else { + *last_value = value; + return -1; + } +} + +void processPots() +{ + int8_t octave, note, step_note; + uint16_t tempo, step_length; + + // process octave + if ( (octave = getPotChanges(OCTAVE_POT_PIN, 0, 10)) >= 0 ) { + _last_octave = octave; + } + + if ( (note = getPotChanges(NOTE_POT_PIN, 0, 11)) >= 0 ) { + _last_note = note; + } + + // changes on octave or note pot? + if ( octave != -1 || note != -1 ) { + _tmpSREG = SREG; cli(); + _sequencer[_step_edit].note = (_last_octave * 8) + _last_note; + SREG = _tmpSREG; + } +/* + if ( (step_length = getPotChanges(STEP_LENGTH_POT_PIN, 1, STEP_MAX_SIZE)) >= 0 ) { + _tmpSREG = SREG; cli(); + _step_length = step_length; + SREG = _tmpSREG; + } + + if ( (tempo = getPotChanges(TEMPO_POT_PIN, SEQUENCER_MIN_BPM, SEQUENCER_MAX_BPM)) >= 0 ) { + uClock.setTempo(tempo); + } +*/ +} + +void processButtons() +{ + // play/stop + if ( pressed(PLAY_STOP_BUTTON_PIN) ) { + if ( _playing == false ) { + // Starts the clock, tick-tac-tick-tac... + uClock.start(); + } else { + // stop the clock + uClock.stop(); + } + } + + // previous step edit + if ( pressed(PREVIOUS_STEP_BUTTON_PIN) ) { + if ( _step_edit != 0 ) { + --_step_edit; + } + } + + // next step edit + if ( pressed(NEXT_STEP_BUTTON_PIN) ) { + if ( _step_edit < STEP_MAX_SIZE-1 ) { + ++_step_edit; + } + } + + // step rest + if ( pressed(REST_BUTTON_PIN) ) { + _sequencer[_step_edit].rest = !_sequencer[_step_edit].rest; + } + + // step glide + if ( pressed(GLIDE_BUTTON_PIN) ) { + _sequencer[_step_edit].glide = !_sequencer[_step_edit].glide; + } + + // step accent + if ( pressed(ACCENT_BUTTON_PIN) ) { + _sequencer[_step_edit].accent = !_sequencer[_step_edit].accent; + } +} + +void processLeds() +{ + // Editing First Step? + if ( _step_edit == 0 ) { + digitalWrite(PREVIOUS_STEP_LED_PIN , HIGH); + } else { + digitalWrite(PREVIOUS_STEP_LED_PIN , LOW); + } + + // Editing Last Step? + if ( _step_edit == _step_length-1 ) { + digitalWrite(NEXT_STEP_LED_PIN , HIGH); + } else { + digitalWrite(NEXT_STEP_LED_PIN , LOW); + } + + // Rest + if ( _sequencer[_step_edit].rest == true ) { + digitalWrite(REST_LED_PIN , HIGH); + } else { + digitalWrite(REST_LED_PIN , LOW); + } + + // Glide + if ( _sequencer[_step_edit].glide == true ) { + digitalWrite(GLIDE_LED_PIN , HIGH); + } else { + digitalWrite(GLIDE_LED_PIN , LOW); + } + + // Accent + if ( _sequencer[_step_edit].accent == true ) { + digitalWrite(ACCENT_LED_PIN , HIGH); + } else { + digitalWrite(ACCENT_LED_PIN , LOW); + } + + // Play/Stop + if ( _playing == true ) { + digitalWrite(PLAY_STOP_LED_PIN , HIGH); + } else { + digitalWrite(PLAY_STOP_LED_PIN , LOW); + } + } // User interaction goes here void loop() { - // octave pot - // note pot - // tempo pot - // pattern step init pot - // pattern step size pot - // previous step button - // next step button - // rest button/led - // glide button/led - // accent button/led + processPots(); + processButtons(); + processLeds(); }