features added: pitch mode like edit when sequencer is stoped.

fixes glide mode using a note_stack on 96ppqn check instead of brute code  for note off within 16ppqn resolution
pull/7/head
midilab 7 years ago
parent b851d38086
commit 3be8005f56
  1. 114
      examples/AcidStepSequencer/AcidStepSequencer.ino

@ -1,3 +1,4 @@
// PITCH MODE EMULATION OF ROLAND TB303
#include "Arduino.h" #include "Arduino.h"
#include <uClock.h> #include <uClock.h>
@ -6,7 +7,7 @@
#define SEQUENCER_MIN_BPM 50 #define SEQUENCER_MIN_BPM 50
#define SEQUENCER_MAX_BPM 177 #define SEQUENCER_MAX_BPM 177
#define NOTE_VELOCITY 90 #define NOTE_VELOCITY 90
#define ACCENT_VELOCITY 110 #define ACCENT_VELOCITY 127
// MIDI modes // MIDI modes
#define MIDI_CHANNEL 0 // 0 = channel 1 #define MIDI_CHANNEL 0 // 0 = channel 1
@ -44,8 +45,16 @@ typedef struct
SEQUENCER_STEP_DATA _sequencer[STEP_MAX_SIZE]; SEQUENCER_STEP_DATA _sequencer[STEP_MAX_SIZE];
typedef struct
{
uint8_t note;
int8_t length;
} STACK_NOTE_DATA;
STACK_NOTE_DATA _note_stack[2];
bool _playing = false; bool _playing = false;
uint16_t _step, _last_step, _step_edit = 0; uint16_t _step, _step_edit = 0;
uint16_t _step_length = STEP_MAX_SIZE; uint16_t _step_length = STEP_MAX_SIZE;
// MIDI clock, start, stop, note on and note off byte definitions - based on MIDI 1.0 Standards. // MIDI clock, start, stop, note on and note off byte definitions - based on MIDI 1.0 Standards.
@ -62,6 +71,7 @@ uint8_t _button_state[6] = {1};
uint16_t _pot_state[4] = {0}; uint16_t _pot_state[4] = {0};
uint8_t _last_octave = 3; uint8_t _last_octave = 3;
uint8_t _last_note = 0; uint8_t _last_note = 0;
uint8_t _bpm_blink_timer = 1;
void sendMidiMessage(uint8_t command, uint8_t byte1, uint8_t byte2) void sendMidiMessage(uint8_t command, uint8_t byte1, uint8_t byte2)
{ {
@ -76,31 +86,35 @@ void sendMidiMessage(uint8_t command, uint8_t byte1, uint8_t byte2)
// Each call represents exactly one step here. // Each call represents exactly one step here.
void ClockOut16PPQN(uint32_t * tick) void ClockOut16PPQN(uint32_t * tick)
{ {
uint8_t velocity = NOTE_VELOCITY; uint16_t step;
bool glide_ahead;
// get actual step. // get actual step.
_step = *tick % _step_length; _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 ) {
sendMidiMessage(NOTE_OFF, _sequencer[_last_step].note, 0);
}
// send note on only if this step are not in rest mode // send note on only if this step are not in rest mode
if ( _sequencer[_step].rest == false ) { if ( _sequencer[_step].rest == false ) {
if ( _sequencer[_step].accent == true ) { sendMidiMessage(NOTE_ON, _sequencer[_step].note, _sequencer[_step].accent ? ACCENT_VELOCITY : NOTE_VELOCITY);
velocity = ACCENT_VELOCITY; // do we have a glide ahead us?
step = _step;
for ( uint16_t i = 1; i < _step_length; i++ ) {
++step;
step = step % _step_length;
if ( _sequencer[step].glide == true && _sequencer[step].rest == false ) {
_note_stack[1].note = _sequencer[_step].note;
_note_stack[1].length = 2 + (i * 6);
glide_ahead = true;
break;
} else if ( _sequencer[step].rest == false ) {
glide_ahead = false;
break;
} }
sendMidiMessage(NOTE_ON, _sequencer[_step].note, velocity);
} }
if ( glide_ahead == false ) {
// time to let glide go away? be shure to send glided note off after the actual step send his note on _note_stack[0].note = _sequencer[_step].note;
// same note? do not send note off _note_stack[0].length = 4;
if ( _sequencer[_last_step].glide == true && _sequencer[_step].note != _sequencer[_last_step].note ) { }
sendMidiMessage(NOTE_OFF, _sequencer[_last_step].note, 0);
} }
_last_step = _step;
} }
// The callback function wich will be called by uClock each Pulse of 96PPQN clock resolution. // The callback function wich will be called by uClock each Pulse of 96PPQN clock resolution.
@ -108,12 +122,46 @@ void ClockOut96PPQN(uint32_t * tick)
{ {
// Send MIDI_CLOCK to external hardware // Send MIDI_CLOCK to external hardware
Serial.write(MIDI_CLOCK); Serial.write(MIDI_CLOCK);
// handle note on stack
if ( _note_stack[1].length != -1 ) {
--_note_stack[1].length;
if ( _note_stack[1].length == 0 ) {
sendMidiMessage(NOTE_OFF, _note_stack[1].note, 0);
_note_stack[1].length = -1;
}
}
if ( _note_stack[0].length != -1 ) {
--_note_stack[0].length;
if ( _note_stack[0].length == 0 ) {
sendMidiMessage(NOTE_OFF, _note_stack[0].note, 0);
_note_stack[0].length = -1;
}
}
// 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 ) {
// sendMidiMessage(NOTE_OFF, _sequencer[_last_step].note, 0);
//}
// 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;
}
} }
// The callback function wich will be called when clock starts by using Clock.start() method. // The callback function wich will be called when clock starts by using Clock.start() method.
void onClockStart() void onClockStart()
{ {
Serial.write(MIDI_START); Serial.write(MIDI_START);
digitalWrite(PLAY_STOP_LED_PIN , LOW);
_playing = true; _playing = true;
} }
@ -121,8 +169,9 @@ void onClockStart()
void onClockStop() void onClockStop()
{ {
Serial.write(MIDI_STOP); Serial.write(MIDI_STOP);
sendMidiMessage(NOTE_OFF, _sequencer[_last_step].note, 0); //sendMidiMessage(NOTE_OFF, _last_note_on, 0);
sendMidiMessage(NOTE_OFF, _sequencer[_step].note, 0); sendMidiMessage(NOTE_OFF, _note_stack[1].note, 0);
sendMidiMessage(NOTE_OFF, _note_stack[0].note, 0);
_playing = false; _playing = false;
} }
@ -193,6 +242,13 @@ void setup()
configureInterface(); configureInterface();
} }
void sendPreviewNote(uint16_t step)
{
sendMidiMessage(NOTE_ON, _sequencer[step].note, _sequencer[step].accent ? ACCENT_VELOCITY : NOTE_VELOCITY);
delay(200);
sendMidiMessage(NOTE_OFF, _sequencer[step].note, 0);
}
bool pressed(uint8_t button_pin) bool pressed(uint8_t button_pin)
{ {
uint8_t value; uint8_t value;
@ -286,6 +342,9 @@ void processPots()
// changes on octave or note pot? // changes on octave or note pot?
if ( octave != -1 || note != -1 ) { if ( octave != -1 || note != -1 ) {
_sequencer[_step_edit].note = (_last_octave * 8) + _last_note; _sequencer[_step_edit].note = (_last_octave * 8) + _last_note;
if ( _playing == false ) {
sendPreviewNote(_step_edit);
}
} }
step_length = getPotChanges(STEP_LENGTH_POT_PIN, 1, STEP_MAX_SIZE); step_length = getPotChanges(STEP_LENGTH_POT_PIN, 1, STEP_MAX_SIZE);
@ -317,13 +376,19 @@ void processButtons()
if ( _step_edit != 0 ) { if ( _step_edit != 0 ) {
--_step_edit; --_step_edit;
} }
if ( _playing == false ) {
sendPreviewNote(_step_edit);
}
} }
// next step edit // next step edit
if ( pressed(NEXT_STEP_BUTTON_PIN) ) { if ( pressed(NEXT_STEP_BUTTON_PIN) ) {
if ( _step_edit < STEP_MAX_SIZE-1 ) { if ( _step_edit < _step_length-1 ) {
++_step_edit; ++_step_edit;
} }
if ( _playing == false ) {
sendPreviewNote(_step_edit);
}
} }
// step rest // step rest
@ -379,13 +444,10 @@ void processLeds()
digitalWrite(ACCENT_LED_PIN , LOW); digitalWrite(ACCENT_LED_PIN , LOW);
} }
// Play/Stop // shut down play led if we are stoped
if ( _playing == true ) { if ( _playing == false ) {
digitalWrite(PLAY_STOP_LED_PIN , HIGH);
} else {
digitalWrite(PLAY_STOP_LED_PIN , LOW); digitalWrite(PLAY_STOP_LED_PIN , LOW);
} }
} }
// User interaction goes here // User interaction goes here

Loading…
Cancel
Save