I want to make a MIDI master clock that has a start/stop function quantized to the 1/4 note. Ideally, a duration I can choose, 1/8, 1/4, 1/2, 1/1, etc. But 1/4 is a good place to start. It's for DIN via TX, not USB MIDI.
How to sync button presses to beats? I want to start a sequencer on a 1/4 note. Time for MIDI is measured in 1/31250ths, right? So how to make sure my button pressing is exactly in sync and not say, a 1/32 or 1/64 note off? What if I've had a few beers and my timing is a tad off? Is there a way I can make sure start and stop messages only go out on say, 1/4 notes?
One half-baked idea I had: Let's say once every 24 clock pulses (MIDI timing clock messages are sent out 24 times per beat), EEPROM(1) is read. And in the loop, there is a button being read. When it's pressed, a "1" is written to EEPROM(1). At the next conclusion of the 24 pulse cycle, when EEPROM(1) is read, if there is a "1", then a start/stop message is sent along TX. I guess you'd have to keep track of start/stop state so next time the button is pressed, the opposite message is sent. Then rewrite EEPROM(1) as a "0", so no messages are sent until the next time button1 is pressed.
To be able to change the duration variable to be 1/8, 1/4, 1/2, 1/1, etc., would be ideal. I was thinking if the Arduino was counting MIDI clock pulses (24 per beat), you could % by 12 for 1/8 notes, or 24 for 1/4, 48 for 1/2, or 96 for 1/1. Then you could for instance, set the start/stop to go out on whole notes, leisurely hit the start/stop button within a whole note's duration, and next time the 96 pulse cycle concludes, the stop/start message goes out on the TX. That would be great for performing.
Does that make sense? Would something like that work? I'm completely inexperienced with using EEPROM. What's a better way of doing it? Is there a way to count MIDI clock pulses? Like, each time the MIDI clock interrupt is called, part of the function is to ++ a counter? Then modulo the int and every time it == 0 send out the call to read EEPROM?
Here is where I'm at, code posted below. It's based on an Arduino Uno MIDI master clock code by Eunjae Im. It includes OLED, encoder and LED stuff but I think it's pretty easy to read so I left it in.
Thanks for your time, and for reading this long post. I have some electronics experience in the analog realm but my only coding experience is via Arduino and it's limited. Let me know what else I can do to make any help I might receive easier to give. Cheers y'all!
#include <Bounce2.h>
Bounce debouncer1 = Bounce();
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
#include <TimerOne.h>
#include <Encoder.h>
#define LED_PIN1 2
Encoder myEnc(22, 23);
const int button_start1 = 9;
volatile int blinkCount1 = -1;
#define BLINK_TIME 12
#define MIDI_START 0xFA
#define MIDI_STOP 0xFC
#define MIDI_TIMING_CLOCK 0xF8
#define CLOCKS_PER_BEAT 24
#define MINIMUM_BPM 20
#define MAXIMUM_BPM 300
volatile unsigned long intervalMicroSeconds;
int bpm;
bool playing1 = false;
bool display_update = false;
void setup(void) {
debouncer1.attach(button_start1, INPUT_PULLUP);
debouncer1.interval(5);
Serial.begin(31250);
bpm = 120;
Timer1.initialize(intervalMicroSeconds);
Timer1.setPeriod(60L * 1000 * 1000 / bpm / CLOCKS_PER_BEAT);
Timer1.attachInterrupt(sendClockPulse);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(3);
display.setCursor(35,10);
display.print(bpm);
display.display();
}
void bpm_display() {
updateBpm();
display.clearDisplay();
display.setTextSize(3);
display.setCursor(0,0);
display.setTextColor(WHITE, BLACK);
display.print(" ");
display.setCursor(35,10);
display.print(bpm);
display.display();
display_update = false;
}
int oldPosition;
void loop(void) {
debouncer1.update();
if (debouncer1.fell()) {
startOrStop1();
}
byte i = 0;
long newPosition = (myEnc.read()/4);
if (newPosition != oldPosition) {
if (oldPosition < newPosition) {
i = 2;
} else if (oldPosition > newPosition) {
i = 1;
}
oldPosition = newPosition;
}
if (i == 2) {
bpm++;
if (bpm > MAXIMUM_BPM) {
bpm = MAXIMUM_BPM;
}
bpm_display();
} else if (i == 1) {
bpm--;
if (bpm < MINIMUM_BPM) {
bpm = MINIMUM_BPM;
}
bpm_display();
}
}
void startOrStop1() {
if (!playing1) {
Serial.write(MIDI_START);
} else {
digitalWrite(LED_PIN1, LOW);
Serial.write(MIDI_STOP);
}
playing1 = !playing1;
}
void sendClockPulse() {
Serial.write(MIDI_TIMING_CLOCK);
blinkCount1 = (blinkCount1 + 1) % CLOCKS_PER_BEAT;
if (playing1) {
if (blinkCount1 == 0) {
digitalWrite(LED_PIN1, HIGH);
} else {
if (blinkCount1 == BLINK_TIME) {
digitalWrite(LED_PIN1, LOW); }
}
}
} // end sendClockPulse
void updateBpm() {
long interval = 60L * 1000 * 1000 / bpm / CLOCKS_PER_BEAT;
Timer1.setPeriod(interval);
}