diff --git a/README.md b/README.md index 2300fdb..323a912 100755 --- a/README.md +++ b/README.md @@ -22,6 +22,60 @@ Here a few examples on the usage of Clock library for MIDI devices, keep in mind If you dont want to build a MIDI interface and you are going to use your arduino only with your PC, you can use a Serial-to-Midi bridge and connects your arduino via USB cable to your conputer to use it as a MIDI tool [like this one](http://projectgus.github.io/hairless-midiserial/). +### A Simple MIDI Sync Box sketch example +Here is a example on how to create a simple MIDI Sync Box + +```c++ +#include + +// MIDI clock, start and stop byte definitions - based on MIDI 1.0 Standards. +#define MIDI_CLOCK 0xF8 +#define MIDI_START 0xFA +#define MIDI_STOP 0xFC + +// The callback function wich will be called by Clock each Pulse of 96PPQN clock resolution. +void ClockOut96PPQN(uint32_t * tick) { + // Send MIDI_CLOCK to external gears + Serial.write(MIDI_CLOCK); +} + +// 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); +} + +void setup() { + + // Initialize serial communication at 31250 bits per second, the default MIDI serial speed communication: + 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 MIDI Start and Stop messages. + uClock.setOnClockStartOutput(onClockStart); + uClock.setOnClockStopOutput(onClockStop); + // Set the clock BPM to 126 BPM + uClock.setTempo(126); + + // Starts the clock, tick-tac-tick-tac... + uClock.start(); + +} + +// Do it whatever to interface with Clock.stop(), Clock.start(), Clock.setTempo() and integrate your environment... +void loop() { + +} +``` + + ### Acid Step Sequencer A clone of Roland TB303 step sequencer main engine, here is a example with no user interface for interaction. If you're looking for a user interactable TB303 sequencer engine clone with user interface please take a look here https://github.com/midilab/uClock/tree/development/examples/AcidStepSequencer. @@ -34,11 +88,14 @@ A clone of Roland TB303 step sequencer main engine, here is a example with no us // Sequencer config #define STEP_MAX_SIZE 16 +#define SEQUENCER_MIN_BPM 50 +#define SEQUENCER_MAX_BPM 177 #define NOTE_LENGTH 4 // min: 1 max: 5 DO NOT EDIT BEYOND!!! #define NOTE_VELOCITY 90 #define ACCENT_VELOCITY 127 +#define NOTE_STACK_SIZE 3 // 1 for no glide note, other 2 for overlap glide notes -// MIDI modes +// MIDI config #define MIDI_CHANNEL 0 // 0 = channel 1 // Sequencer data @@ -58,10 +115,10 @@ typedef struct int8_t length; } STACK_NOTE_DATA; -STACK_NOTE_DATA _note_stack[2]; +STACK_NOTE_DATA _note_stack[NOTE_STACK_SIZE]; bool _playing = false; -uint16_t _step = 0; +uint16_t _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. @@ -84,33 +141,37 @@ void sendMidiMessage(uint8_t command, uint8_t byte1, uint8_t byte2) // Each call represents exactly one step here. void ClockOut16PPQN(uint32_t * tick) { - uint16_t step; - bool glide_ahead; + uint16_t step, length; // get actual step. _step = *tick % _step_length; // send note on only if this step are not in rest mode if ( _sequencer[_step].rest == false ) { + // send note on sendMidiMessage(NOTE_ON, _sequencer[_step].note, _sequencer[_step].accent ? ACCENT_VELOCITY : NOTE_VELOCITY); - // do we have a glide ahead us? + + // check for glide event ahead of _step 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; + length = NOTE_LENGTH + (i * 6); break; } else if ( _sequencer[step].rest == false ) { - glide_ahead = false; + length = NOTE_LENGTH; break; } } - if ( glide_ahead == false ) { - _note_stack[0].note = _sequencer[_step].note; - _note_stack[0].length = NOTE_LENGTH; + + // find a free note stack to fit in + for ( uint8_t i = 0; i < NOTE_STACK_SIZE; i++ ) { + if ( _note_stack[i].length == -1 ) { + _note_stack[i].note = _sequencer[_step].note; + _note_stack[i].length = length; + return; + } } } } @@ -122,21 +183,14 @@ void ClockOut96PPQN(uint32_t * tick) 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; - } + for ( uint8_t i = 0; i < NOTE_STACK_SIZE; i++ ) { + if ( _note_stack[i].length != -1 ) { + --_note_stack[i].length; + if ( _note_stack[i].length == 0 ) { + sendMidiMessage(NOTE_OFF, _note_stack[i].note, 0); + _note_stack[i].length = -1; + } + } } } @@ -144,14 +198,18 @@ 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, _note_stack[1].note, 0); - sendMidiMessage(NOTE_OFF, _note_stack[0].note, 0); + for ( uint8_t i = 0; i < NOTE_STACK_SIZE; i++ ) { + sendMidiMessage(NOTE_OFF, _note_stack[i].note, 0); + _note_stack[i].length = -1; + } + _playing = false; } void setup() @@ -183,72 +241,26 @@ void setup() _sequencer[i].glide = false; _sequencer[i].rest = false; } - - // starts the sequencer - uClock.start(); + + // initing note stack data + for ( uint8_t i = 0; i < NOTE_STACK_SIZE; i++ ) { + _note_stack[i].note = 0; + _note_stack[i].length = -1; + } // pins, buttons, leds and pots config //configureYourUserInterface(); + + // start sequencer + uClock.start(); } // User interaction goes here void loop() { + // Controls your 303 engine interacting with user here... //processYourButtons(); //processYourLeds(); //processYourPots(); -} -``` - -### A Simple MIDI Sync Box sketch example -Here is a example on how to create a simple MIDI Sync Box - -```c++ -#include - -// MIDI clock, start and stop byte definitions - based on MIDI 1.0 Standards. -#define MIDI_CLOCK 0xF8 -#define MIDI_START 0xFA -#define MIDI_STOP 0xFC - -// The callback function wich will be called by Clock each Pulse of 96PPQN clock resolution. -void ClockOut96PPQN(uint32_t * tick) { - // Send MIDI_CLOCK to external gears - Serial.write(MIDI_CLOCK); -} - -// 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); -} - -void setup() { - - // Initialize serial communication at 31250 bits per second, the default MIDI serial speed communication: - 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 MIDI Start and Stop messages. - uClock.setOnClockStartOutput(onClockStart); - uClock.setOnClockStopOutput(onClockStop); - // Set the clock BPM to 126 BPM - uClock.setTempo(126); - - // Starts the clock, tick-tac-tick-tac... - uClock.start(); - -} - -// Do it whatever to interface with Clock.stop(), Clock.start(), Clock.setTempo() and integrate your environment... -void loop() { - } ``` \ No newline at end of file