|
|
|
@ -75,11 +75,181 @@ void loop() { |
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
### A Simple Step Sequencer |
|
|
|
|
A simple step sequencer for MIDI devices: |
|
|
|
|
### Acid Step Sequencer |
|
|
|
|
A clone of Roland TB303 step sequencer main engine, the schematics for user interface are avaliable on example folder. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```c++ |
|
|
|
|
Soon... |
|
|
|
|
// Roland TB303 Step Sequencer engine clone. |
|
|
|
|
// No interface here, just the engine as example. |
|
|
|
|
#include "Arduino.h" |
|
|
|
|
#include <uClock.h> |
|
|
|
|
|
|
|
|
|
// Sequencer config |
|
|
|
|
#define STEP_MAX_SIZE 16 |
|
|
|
|
#define NOTE_LENGTH 4 // min: 1 max: 5 DO NOT EDIT BEYOND!!! |
|
|
|
|
#define NOTE_VELOCITY 90 |
|
|
|
|
#define ACCENT_VELOCITY 127 |
|
|
|
|
|
|
|
|
|
// MIDI modes |
|
|
|
|
#define MIDI_CHANNEL 0 // 0 = channel 1 |
|
|
|
|
|
|
|
|
|
// Sequencer data |
|
|
|
|
typedef struct |
|
|
|
|
{ |
|
|
|
|
uint8_t note; |
|
|
|
|
bool accent; |
|
|
|
|
bool glide; |
|
|
|
|
bool rest; |
|
|
|
|
} SEQUENCER_STEP_DATA; |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
uint16_t _step = 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 |
|
|
|
|
#define MIDI_START 0xFA |
|
|
|
|
#define MIDI_STOP 0xFC |
|
|
|
|
#define NOTE_ON 0x90 |
|
|
|
|
#define NOTE_OFF 0x80 |
|
|
|
|
|
|
|
|
|
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. |
|
|
|
|
// Each call represents exactly one step here. |
|
|
|
|
void ClockOut16PPQN(uint32_t * tick) |
|
|
|
|
{ |
|
|
|
|
uint16_t step; |
|
|
|
|
bool glide_ahead; |
|
|
|
|
|
|
|
|
|
// get actual step. |
|
|
|
|
_step = *tick % _step_length; |
|
|
|
|
|
|
|
|
|
// 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].accent ? ACCENT_VELOCITY : NOTE_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 = NOTE_LENGTH + (i * 6); |
|
|
|
|
glide_ahead = true; |
|
|
|
|
break; |
|
|
|
|
} else if ( _sequencer[step].rest == false ) { |
|
|
|
|
glide_ahead = false; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if ( glide_ahead == false ) { |
|
|
|
|
_note_stack[0].note = _sequencer[_step].note; |
|
|
|
|
_note_stack[0].length = NOTE_LENGTH; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 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 hardware |
|
|
|
|
Serial.write(MIDI_CLOCK); |
|
|
|
|
|
|
|
|
|
// handle note on stack |
|
|
|
|
// [1] is notes to be glided, its in hold on mode until we reach the glided step |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// [0] is the actual step note stack |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// The callback function wich will be called when clock starts by using Clock.start() method. |
|
|
|
|
void onClockStart() |
|
|
|
|
{ |
|
|
|
|
Serial.write(MIDI_START); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// The callback function wich will be called when clock stops by using Clock.stop() method. |
|
|
|
|
void onClockStop() |
|
|
|
|
{ |
|
|
|
|
Serial.write(MIDI_STOP); |
|
|
|
|
sendMidiMessage(NOTE_OFF, _note_stack[1].note, 0); |
|
|
|
|
sendMidiMessage(NOTE_OFF, _note_stack[0].note, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void setup() |
|
|
|
|
{ |
|
|
|
|
// Initialize serial communication |
|
|
|
|
// the default MIDI serial speed communication at 31250 bits per second |
|
|
|
|
Serial.begin(31250); |
|
|
|
|
|
|
|
|
|
// Inits the clock |
|
|
|
|
uClock.init(); |
|
|
|
|
|
|
|
|
|
// Set the callback function for the clock output to send MIDI Sync message. |
|
|
|
|
uClock.setClock96PPQNOutput(ClockOut96PPQN); |
|
|
|
|
|
|
|
|
|
// Set the callback function for the step sequencer on 16ppqn |
|
|
|
|
uClock.setClock16PPQNOutput(ClockOut16PPQN); |
|
|
|
|
|
|
|
|
|
// Set the callback function for MIDI Start and Stop messages. |
|
|
|
|
uClock.setOnClockStartOutput(onClockStart); |
|
|
|
|
uClock.setOnClockStopOutput(onClockStop); |
|
|
|
|
|
|
|
|
|
// Set the clock BPM to 126 BPM |
|
|
|
|
uClock.setTempo(126); |
|
|
|
|
|
|
|
|
|
// initing sequencer data |
|
|
|
|
for ( uint16_t i = 0; i < STEP_MAX_SIZE; i++ ) { |
|
|
|
|
_sequencer[i].note = 36; |
|
|
|
|
_sequencer[i].accent = false; |
|
|
|
|
_sequencer[i].glide = false; |
|
|
|
|
_sequencer[i].rest = false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// starts the sequencer |
|
|
|
|
uClock.start(); |
|
|
|
|
|
|
|
|
|
// pins, buttons, leds and pots config |
|
|
|
|
//configureYourUserInterface(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// User interaction goes here |
|
|
|
|
void loop() |
|
|
|
|
{ |
|
|
|
|
//processYourButtons(); |
|
|
|
|
//processYourLeds(); |
|
|
|
|
//processYourPots(); |
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|