Roland TB303 main engine added as example for a step sequencer using uClock

pull/7/head
midilab 6 years ago
parent 80586ad0e6
commit 698c789514
  1. 176
      README.md

@ -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();
}
```

Loading…
Cancel
Save