mirror of https://github.com/midilab/uClock
AcidStepSequencer: separation of code into main sequencer engine, user interface and hardware interface for better code reuse. Added ATOMIC() macro to access sequencer data in a atomic way(since uClock runs on timmer interrupt)
parent
b4e95eaa85
commit
db5682f08b
@ -0,0 +1,276 @@ |
|||||||
|
|
||||||
|
#define SEQUENCER_MIN_BPM 50 |
||||||
|
#define SEQUENCER_MAX_BPM 177 |
||||||
|
|
||||||
|
// Ui config
|
||||||
|
#define LOCK_POT_SENSTIVITY 3 |
||||||
|
|
||||||
|
// 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 |
||||||
|
|
||||||
|
// User Interface data
|
||||||
|
uint16_t _step_edit = 0; |
||||||
|
uint8_t _last_octave = 3; |
||||||
|
uint8_t _last_note = 0; |
||||||
|
|
||||||
|
uint8_t _bpm_blink_timer = 1; |
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// getting first value state
|
||||||
|
pressed(PREVIOUS_STEP_BUTTON_PIN); |
||||||
|
pressed(NEXT_STEP_BUTTON_PIN); |
||||||
|
pressed(REST_BUTTON_PIN); |
||||||
|
pressed(GLIDE_BUTTON_PIN); |
||||||
|
pressed(ACCENT_BUTTON_PIN); |
||||||
|
pressed(PLAY_STOP_BUTTON_PIN); |
||||||
|
|
||||||
|
// getting first values
|
||||||
|
getPotChanges(OCTAVE_POT_PIN, 0, 10); |
||||||
|
getPotChanges(NOTE_POT_PIN, 0, 11); |
||||||
|
getPotChanges(STEP_LENGTH_POT_PIN, 1, STEP_MAX_SIZE); |
||||||
|
getPotChanges(TEMPO_POT_PIN, SEQUENCER_MIN_BPM, SEQUENCER_MAX_BPM); |
||||||
|
|
||||||
|
lockPotsState(true); |
||||||
|
|
||||||
|
//acidRandomize();
|
||||||
|
} |
||||||
|
|
||||||
|
void processInterface() |
||||||
|
{ |
||||||
|
processButtons(); |
||||||
|
processLeds(); |
||||||
|
processPots();
|
||||||
|
} |
||||||
|
|
||||||
|
void tempoInterface(uint32_t * tick)
|
||||||
|
{ |
||||||
|
// BPM led indicator
|
||||||
|
if ( !(*tick % (96)) || (*tick == 0) ) { // first compass step will flash longer
|
||||||
|
_bpm_blink_timer = 8; |
||||||
|
digitalWrite(PLAY_STOP_LED_PIN , HIGH); |
||||||
|
} else if ( !(*tick % (24)) ) { // each quarter led on
|
||||||
|
digitalWrite(PLAY_STOP_LED_PIN , HIGH); |
||||||
|
} else if ( !(*tick % _bpm_blink_timer) ) { // get led off
|
||||||
|
digitalWrite(PLAY_STOP_LED_PIN , LOW); |
||||||
|
_bpm_blink_timer = 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void sendPreviewNote(uint16_t step) |
||||||
|
{ |
||||||
|
unsigned long milliTime, preMilliTime; |
||||||
|
|
||||||
|
sendMidiMessage(NOTE_ON, _sequencer[step].note, _sequencer[step].accent ? ACCENT_VELOCITY : NOTE_VELOCITY); |
||||||
|
|
||||||
|
// avoid delay() call because of uClock timmer1 usage
|
||||||
|
//delay(200);
|
||||||
|
preMilliTime = millis(); |
||||||
|
while ( true ) { |
||||||
|
milliTime = millis(); |
||||||
|
if (abs(milliTime - preMilliTime) >= 200) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sendMidiMessage(NOTE_OFF, _sequencer[step].note, 0); |
||||||
|
} |
||||||
|
|
||||||
|
void processPots() |
||||||
|
{ |
||||||
|
static int8_t octave, note, step_note; |
||||||
|
static int16_t tempo, step_length; |
||||||
|
|
||||||
|
octave = getPotChanges(OCTAVE_POT_PIN, 0, 10); |
||||||
|
if ( octave != -1 ) {
|
||||||
|
_last_octave = octave; |
||||||
|
} |
||||||
|
|
||||||
|
note = getPotChanges(NOTE_POT_PIN, 0, 11); |
||||||
|
if ( note != -1 ) {
|
||||||
|
_last_note = note; |
||||||
|
} |
||||||
|
|
||||||
|
// changes on octave or note pot?
|
||||||
|
if ( octave != -1 || note != -1 ) { |
||||||
|
ATOMIC(_sequencer[_step_edit].note = (_last_octave * 8) + _last_note); |
||||||
|
if ( _playing == false && _sequencer[_step_edit].rest == false ) { |
||||||
|
sendPreviewNote(_step_edit); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
step_length = getPotChanges(STEP_LENGTH_POT_PIN, 1, STEP_MAX_SIZE); |
||||||
|
if ( step_length != -1 ) {
|
||||||
|
ATOMIC(_step_length = step_length); |
||||||
|
if ( _step_edit >= _step_length ) { |
||||||
|
_step_edit = _step_length-1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
tempo = getPotChanges(TEMPO_POT_PIN, SEQUENCER_MIN_BPM, SEQUENCER_MAX_BPM); |
||||||
|
if ( tempo != -1 ) {
|
||||||
|
//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(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ramdom test
|
||||||
|
//if ( pressed(PREVIOUS_STEP_BUTTON_PIN) && pressed(NEXT_STEP_BUTTON_PIN) ) {
|
||||||
|
//acidRandomize();
|
||||||
|
//return;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// previous step edit
|
||||||
|
if ( pressed(PREVIOUS_STEP_BUTTON_PIN) ) { |
||||||
|
if ( _step_edit != 0 ) { |
||||||
|
// add a lock here for octave and note to not mess with edit mode when moving steps around
|
||||||
|
lockPotsState(true);
|
||||||
|
--_step_edit; |
||||||
|
} else { // TODO: just for tests.. take this guy off here and put it on second page
|
||||||
|
acidRandomize(); |
||||||
|
} |
||||||
|
if ( _playing == false && _sequencer[_step_edit].rest == false ) { |
||||||
|
sendPreviewNote(_step_edit); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// next step edit
|
||||||
|
if ( pressed(NEXT_STEP_BUTTON_PIN) ) { |
||||||
|
if ( _step_edit < _step_length-1 ) { |
||||||
|
// add a lock here for octave and note to not mess with edit mode when moving steps around
|
||||||
|
lockPotsState(true);
|
||||||
|
++_step_edit; |
||||||
|
} |
||||||
|
if ( _playing == false && _sequencer[_step_edit].rest == false ) { |
||||||
|
sendPreviewNote(_step_edit); |
||||||
|
}
|
||||||
|
} |
||||||
|
|
||||||
|
// step rest
|
||||||
|
if ( pressed(REST_BUTTON_PIN) ) { |
||||||
|
ATOMIC(_sequencer[_step_edit].rest = !_sequencer[_step_edit].rest); |
||||||
|
if ( _playing == false && _sequencer[_step_edit].rest == false ) { |
||||||
|
sendPreviewNote(_step_edit); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// step glide
|
||||||
|
if ( pressed(GLIDE_BUTTON_PIN) ) { |
||||||
|
ATOMIC(_sequencer[_step_edit].glide = !_sequencer[_step_edit].glide); |
||||||
|
} |
||||||
|
|
||||||
|
// step accent
|
||||||
|
if ( pressed(ACCENT_BUTTON_PIN) ) { |
||||||
|
ATOMIC(_sequencer[_step_edit].accent = !_sequencer[_step_edit].accent); |
||||||
|
if ( _playing == false && _sequencer[_step_edit].rest == false ) { |
||||||
|
sendPreviewNote(_step_edit); |
||||||
|
}
|
||||||
|
}
|
||||||
|
} |
||||||
|
|
||||||
|
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); |
||||||
|
}
|
||||||
|
|
||||||
|
// shut down play led if we are stoped
|
||||||
|
if ( _playing == false ) { |
||||||
|
digitalWrite(PLAY_STOP_LED_PIN , LOW); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void acidRandomize()
|
||||||
|
{ |
||||||
|
// ramdom it all
|
||||||
|
for ( uint16_t i = 0; i < STEP_MAX_SIZE; i++ ) { |
||||||
|
ATOMIC(_sequencer[i].note = random(36, 70)); // octave 2 to 4. octave 3 to 5 (40 - 83)
|
||||||
|
ATOMIC(_sequencer[i].accent = random(0, 2)); |
||||||
|
ATOMIC(_sequencer[i].glide = random(0, 2)); |
||||||
|
ATOMIC(_sequencer[i].rest = random(0, 1)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,120 @@ |
|||||||
|
|
||||||
|
#define POT_NUMBER 4 |
||||||
|
#define BUTTON_NUMBER 6 |
||||||
|
|
||||||
|
// pot data
|
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
uint8_t pin; |
||||||
|
uint16_t state; |
||||||
|
bool lock; |
||||||
|
} POT_DATA; |
||||||
|
|
||||||
|
// button data
|
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
uint8_t pin; |
||||||
|
bool state; |
||||||
|
} BUTTON_DATA; |
||||||
|
|
||||||
|
POT_DATA _pot[POT_NUMBER]; |
||||||
|
BUTTON_DATA _button[BUTTON_NUMBER]; |
||||||
|
|
||||||
|
void lockPotsState(bool lock) |
||||||
|
{ |
||||||
|
for ( uint8_t i = 0; i < POT_NUMBER; i++ ) { |
||||||
|
_pot[i].lock = lock; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool pressed(uint8_t button_pin) |
||||||
|
{ |
||||||
|
bool value; |
||||||
|
bool * last_value; |
||||||
|
|
||||||
|
switch(button_pin) { |
||||||
|
case PREVIOUS_STEP_BUTTON_PIN: |
||||||
|
last_value = &_button[0].state; |
||||||
|
break; |
||||||
|
case NEXT_STEP_BUTTON_PIN: |
||||||
|
last_value = &_button[1].state; |
||||||
|
break; |
||||||
|
case REST_BUTTON_PIN: |
||||||
|
last_value = &_button[2].state; |
||||||
|
break;
|
||||||
|
case GLIDE_BUTTON_PIN: |
||||||
|
last_value = &_button[3].state; |
||||||
|
break; |
||||||
|
case ACCENT_BUTTON_PIN: |
||||||
|
last_value = &_button[4].state; |
||||||
|
break; |
||||||
|
case PLAY_STOP_BUTTON_PIN: |
||||||
|
last_value = &_button[5].state; |
||||||
|
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, value_ranged, last_value_ranged; |
||||||
|
uint16_t * last_value; |
||||||
|
bool * lock_pot; |
||||||
|
uint8_t pot_sensitivity = 1; |
||||||
|
|
||||||
|
switch(pot_pin) { |
||||||
|
case OCTAVE_POT_PIN: |
||||||
|
last_value = &_pot[0].state; |
||||||
|
lock_pot = &_pot[0].lock; |
||||||
|
break; |
||||||
|
case NOTE_POT_PIN: |
||||||
|
last_value = &_pot[1].state; |
||||||
|
lock_pot = &_pot[1].lock; |
||||||
|
break; |
||||||
|
case STEP_LENGTH_POT_PIN: |
||||||
|
last_value = &_pot[2].state; |
||||||
|
lock_pot = &_pot[2].lock; |
||||||
|
break;
|
||||||
|
case TEMPO_POT_PIN: |
||||||
|
last_value = &_pot[3].state; |
||||||
|
lock_pot = &_pot[3].lock; |
||||||
|
break; |
||||||
|
default: |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
// get absolute value
|
||||||
|
value = analogRead(pot_pin); |
||||||
|
|
||||||
|
// range that value and our last_value
|
||||||
|
value_ranged = (value / (1024 / ((max_value - min_value) + 1))) + min_value; |
||||||
|
last_value_ranged = (*last_value / (1024 / ((max_value - min_value) + 1))) + min_value;
|
||||||
|
|
||||||
|
// a lock system to not mess with some data(pots are terrible for some kinda of user interface data controls, but lets keep it low cost!)
|
||||||
|
if ( *lock_pot == true ) { |
||||||
|
pot_sensitivity = LOCK_POT_SENSTIVITY; |
||||||
|
} |
||||||
|
|
||||||
|
if ( abs(value_ranged - last_value_ranged) >= pot_sensitivity ) { |
||||||
|
*last_value = value; |
||||||
|
if ( *lock_pot == true ) { |
||||||
|
*lock_pot = false; |
||||||
|
} |
||||||
|
return value_ranged;
|
||||||
|
} else { |
||||||
|
return -1; |
||||||
|
}
|
||||||
|
} |
Loading…
Reference in new issue