diff --git a/simulation/include/Arduino.h b/simulation/include/Arduino.h index 534f404..a8bced0 100644 --- a/simulation/include/Arduino.h +++ b/simulation/include/Arduino.h @@ -8,6 +8,7 @@ #include #include "Wiring.h" +#include "simusbmidi.h" #include "core_pins.h" @@ -72,28 +73,6 @@ public: }; -class SimUsbMidi -{ -public: - void sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t cable=0); - void sendNoteOn(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t cable=0); - void sendPolyPressure(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t cable=0); - void sendAfterTouchPoly(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t cable=0); - void sendControlChange(uint8_t control, uint8_t value, uint8_t channel, uint8_t cable=0); - void sendProgramChange(uint8_t program, uint8_t channel, uint8_t cable=0); - void sendAfterTouch(uint8_t pressure, uint8_t channel, uint8_t cable=0); - void sendPitchBend(int value, uint8_t channel, uint8_t cable=0); - void sendSysEx(uint16_t length, const uint8_t *data, bool hasTerm=false, uint8_t cable=0); - bool read(uint8_t channel=0); - void setHandleSystemExclusive(void (*fptr) (const uint8_t *array, unsigned int size)); - void setHandleSystemExclusive(void (*fptr) (const uint8_t *data, uint16_t length, bool complete)); - - void receiveMidiData(const uint8_t *data, const uint16_t length); //Send midi data "into simulator" -private: - //Handlers registered to receive MIDI - void (*usb_midi_handleSysExPartial)(const uint8_t *data, uint16_t length, uint8_t complete); - void (*usb_midi_handleSysExComplete)(const uint8_t *data, unsigned int size); -}; extern SimSerial Serial; extern SimSerial Serial3; //Used for MIDI serial putput with default hardware diff --git a/simulation/include/simusbmidi.h b/simulation/include/simusbmidi.h new file mode 100644 index 0000000..05c31cb --- /dev/null +++ b/simulation/include/simusbmidi.h @@ -0,0 +1,35 @@ +#ifndef __SIMUSBMIDI_H__ +#define __SIMUSBMIDI_H__ + +#include + +class SimUsbMidi +{ +public: + void sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t cable=0); + void sendNoteOn(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t cable=0); + void sendPolyPressure(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t cable=0); + void sendAfterTouchPoly(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t cable=0); + void sendControlChange(uint8_t control, uint8_t value, uint8_t channel, uint8_t cable=0); + void sendProgramChange(uint8_t program, uint8_t channel, uint8_t cable=0); + void sendAfterTouch(uint8_t pressure, uint8_t channel, uint8_t cable=0); + void sendPitchBend(int value, uint8_t channel, uint8_t cable=0); + void sendSysEx(uint16_t length, const uint8_t *data, bool hasTerm=false, uint8_t cable=0); + bool read(uint8_t channel=0); + void setHandleSystemExclusive(void (*fptr) (const uint8_t *array, unsigned int size)); + void setHandleSystemExclusive(void (*fptr) (const uint8_t *data, uint16_t length, bool complete)); + +//Things not part of Teensy USBMidi, but used to simulate sending data to it + void receiveMidiData(const uint8_t *data, const uint16_t length); //Send midi data "into simulator" + void setMidiFile(std::string filename); //MIDI data to send to device + void triggerMidi(); //"Arm" so data is sent to device next time it tries to read anything +private: + //Handlers registered to receive MIDI + void (*usb_midi_handleSysExPartial)(const uint8_t *data, uint16_t length, uint8_t complete); + void (*usb_midi_handleSysExComplete)(const uint8_t *data, unsigned int size); + std::string midiFile; + bool sendMidi; +}; + + +#endif \ No newline at end of file diff --git a/simulation/src/nuevisim.cpp b/simulation/src/nuevisim.cpp index 127163f..f7b3429 100644 --- a/simulation/src/nuevisim.cpp +++ b/simulation/src/nuevisim.cpp @@ -14,6 +14,7 @@ #include "examples/imgui_impl_sdl.h" #include "examples/imgui_impl_opengl3.h" #include "EEPROM.h" +#include "simusbmidi.h" #include @@ -413,6 +414,9 @@ static void toggleAnalogAnimation() { printf("Analog input variations: %s\n", animateAnalogs ? "ON": "OFF"); } +static void sendMidiData() { + usbMIDI.triggerMidi(); +} static void SimLoop(std::function continue_predicate, std::function loopFunc) { @@ -455,6 +459,7 @@ static void SimLoop(std::function continue_predicate, std::function midiFile(parser, "filename", "Name of file with raw MIDI data to send to NuEVI", {'m', "midi-file"}); + args::Flag midiSend(parser, "midi-send", "Trigger automatic sending of MIDI data", {'M', "midi-send"}); parser.ParseCLI(argc, argv); @@ -647,14 +654,21 @@ int main(int argc, const char** argv) } std::string eepromFileName = args::get(eepromFile); + std::string midiFileName = args::get(midiFile); //Use a default EEPROM file if none is provided. - if(eepromFileName.length()==0) - { + if(eepromFileName.length()==0) { eepromFileName = SDL_GetPrefPath("Vulk Data System", "NuEVI Simulator"); eepromFileName += "eeprom.bin"; } + if(midiFileName.length()>0) { + usbMIDI.setMidiFile(midiFileName); + if(args::get(midiSend)) { + usbMIDI.triggerMidi(); + } + } + no_delay = args::get(nodelay); return SimRun(eepromFileName, args::get(eepromWrite), args::get(factoryReset), args::get(configMode)); diff --git a/simulation/src/simeeprom.cpp b/simulation/src/simeeprom.cpp index c3fb3c5..1f0b27f 100644 --- a/simulation/src/simeeprom.cpp +++ b/simulation/src/simeeprom.cpp @@ -63,7 +63,7 @@ int16_t EEPROMClass::setStorage(const char* filename, bool write) autoUpdate = write; storage = fopen(filename, "rb"); - + //If only reading, fail if file does not exist (makes no sense otherwise) if(!storage && !autoUpdate) { diff --git a/simulation/src/simusbmidi.cpp b/simulation/src/simusbmidi.cpp index e22cea7..4560225 100644 --- a/simulation/src/simusbmidi.cpp +++ b/simulation/src/simusbmidi.cpp @@ -1,8 +1,10 @@ #include #include +#include +#include -#include "Arduino.h" +#include "simusbmidi.h" /************************************* * Stub simulation of Teensy usbMidi @@ -56,54 +58,41 @@ void SimUsbMidi::sendSysEx(uint16_t length, const uint8_t __unused *data, bool _ //Set a low chunk size on purpose just to let the receiver work for it #define MIDI_SYSEX_CHUNK_SIZE 32 -/* Test data for config mode - -//Carefully crafted config command chunk to send via midi -static const uint8_t midimessage[] = { - 0xf0, //Sysex start - 0x00, 0x3e, 0x7f, //Vendor - 'N', 'u', 'E', 'V', 'I', //header - 'c', '0', '2', //message code - 0, 102, //length - - //Payload - 0x00, 0x20, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, //00 - 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x06, 0x07, //08 - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //10 - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //18 - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //20 - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //28 - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //30 - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //38 - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //40 - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //48 - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //50 - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //58 - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, //60 - 0x2a, 0x11, 0x32, 0x5a, //crc32 - 0xf7 //sysex end marker -}; - -static bool midisent = false; - -//On first midi read, send a message bool SimUsbMidi::read(uint8_t __unused channel) { - if(!midisent) { - this->receiveMidiData(midimessage, sizeof(midimessage)); - midisent=true; + if(this->sendMidi) { + + printf("[SimUsbMidi::read] Attempting to send midi data\n"); + + std::ifstream file(this->midiFile, std::ios::binary | std::ios::ate); + std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); + + uint8_t *buffer = (uint8_t*)malloc(size); + + if (file.read((char*)buffer, size)) + { + printf("[SimUsbMidi::read] Sending %lu bytes.\n", size); + + this->receiveMidiData(buffer, size); + + } + free(buffer); + + this->sendMidi = false; + } - return false; -} -*/ - -bool SimUsbMidi::read(uint8_t __unused channel) { return false; } //Provide midi data for simulation to receive void SimUsbMidi::receiveMidiData(const uint8_t *data, const uint16_t length) { + if(length==0) return; //There is no data, what's even the point + uint8_t midi_message = data[0]; //First byte of data + + if(midi_message != 0xF0) return; //Only sysex data supported (no other handlers available) + if(this->usb_midi_handleSysExPartial) { //Chunked sysex receiver set, use that. if(length<=MIDI_SYSEX_CHUNK_SIZE) { @@ -115,7 +104,7 @@ void SimUsbMidi::receiveMidiData(const uint8_t *data, const uint16_t length) { int pos=0; while(posusb_midi_handleSysExPartial = (void (*)(const uint8_t *, uint16_t, uint8_t))fptr; } + +void SimUsbMidi::setMidiFile(std::string filename) { + this->midiFile = filename; +} + +void SimUsbMidi::triggerMidi() { + this->sendMidi = true; +}