midi2stepper/midi2stepper.ino

108 lines
3.6 KiB
C++

/// \file midi2stepper.ino
/// \brief main routine for midi to stepper tool
/// \author GrumpyDeveloper https://contentnation.net/en/grumpydevelop
/// \license MIT
#include <EEPROM.h>
#include "midi2stepper.h"
extern Midi2Stepper m2s;
int numChannels; ///< number of channels
uint16_t channels[8]; ///< channel frequencies
uint8_t msg[4]; ///< input messages over serial
uint8_t nextChannel = 0; ///< next channel to test
bool power; ///< current power status
bool sendStatus = true; ///< send current output over serial
void setup() {
Serial.begin(115200);
m2s.begin();
numChannels = EEPROM.read(0); // read number of channels from eeprom
if (numChannels < 1 || numChannels > 8) { // unreasonable value
numChannels = 8;
}
memset(channels, 0, sizeof(uint16_t) * numChannels); // clear channel frequencies
power = false; // power is off
pinMode(LED_BUILTIN, OUTPUT); // set LED pin to output
}
void loop() {
while (Serial.available()) {
Serial.readBytes(msg, 3);
uint8_t nibble = msg[0] >> 4;
if (nibble == 0b1001) { // note on
if (!power) { // not powered on yet?
// enable steppers
power = true;
m2s.power(true);
}
// calculate frequency from midi note
uint16_t freq = pow(2, (static_cast<double>(msg[1]) - 69.0)/12.0) * 440.0;
// velocity byte 2 is ignored
// loop through channels, starting with nextChannel to find the next free one
uint8_t channel = nextChannel;
bool good = false;
do {
if (channels[channel] == 0) {
m2s.setChannel(channel, freq);
channels[channel] = freq;
nextChannel = (channel + 1) % (numChannels); // next channel = current channel + 1 modulo num channels
good = true;
break;
}
channel = (channel + 1) % numChannels;
} while (channel != nextChannel); // if we end at our start channel, no free channels, ignore note
if (!good && sendStatus) {
Serial.print(F("\ntoo many channels\n"));
}
}
if (nibble == 0b1000) { // note off
uint16_t freq = pow(2, (static_cast<double>(msg[1]) - 69.0)/12.0) * 440.0;
// velocity byte 2 is ignored
// loop through channels to find an active one with that frequency
for (uint8_t channel = 0; channel < numChannels; ++channel) {
if (channels[channel] == freq) {
m2s.setChannel(channel, 0);
channels[channel] = 0;
break;
}
}
}
if (nibble == 0b1011) { // control
switch (msg[1]) {
case 121: // reset
case 123: // all notes off
if (power) {
power = false;
m2s.power(false);
}
break;
}
}
// hack to set number of channels via simple usb connection:
// send <number of channel>\r\n
if (msg[0] > '0' && msg[0] < '9') {
numChannels = msg[0] - '0';
Serial.print(F("channel count set to "));
Serial.println(numChannels);
// clear channel frequencies
memset(channels, 0, sizeof(uint16_t) * numChannels);
// save new value to EEPROM
EEPROM.write(0, numChannels);
}
if (sendStatus) {
// send current status over serial connection
char buf[8]; ///< status message buffer
buf[0] = power ? '+' : '-';
buf[1] = ' ';
Serial.write(buf, 2);
for (uint8_t i = 0; i < numChannels; ++i) {
uint8_t len = snprintf(buf, sizeof(buf) - 1, "%5i ", channels[i]);
Serial.write(buf, len);
}
Serial.write('\r');
}
}
}