Acid step sequencer clone of Roland TB303, testing release

pull/7/head
midilab 7 years ago
parent 609242b8d7
commit 96f045c97a
  1. 328
      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:
// 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();
}

Loading…
Cancel
Save