Add functionality to use files as MIDI data to send to simulated device
This commit is contained in:
parent
3b405adebb
commit
4dbca53871
5 changed files with 91 additions and 66 deletions
|
@ -8,6 +8,7 @@
|
|||
#include <inttypes.h>
|
||||
|
||||
#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
|
||||
|
|
35
simulation/include/simusbmidi.h
Normal file
35
simulation/include/simusbmidi.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#ifndef __SIMUSBMIDI_H__
|
||||
#define __SIMUSBMIDI_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
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
|
|
@ -14,6 +14,7 @@
|
|||
#include "examples/imgui_impl_sdl.h"
|
||||
#include "examples/imgui_impl_opengl3.h"
|
||||
#include "EEPROM.h"
|
||||
#include "simusbmidi.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
|
@ -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<bool()> continue_predicate, std::function<void()> loopFunc)
|
||||
{
|
||||
|
@ -455,6 +459,7 @@ static void SimLoop(std::function<bool()> continue_predicate, std::function<void
|
|||
case SDLK_UP: digitalInputs[uPin] = 1; break;
|
||||
case SDLK_DOWN: digitalInputs[dPin] = 1; break;
|
||||
case SDLK_w: toggleAnalogAnimation(); break;
|
||||
case SDLK_m: sendMidiData(); break;
|
||||
|
||||
case SDLK_1: touchSensor.mockFilteredData(K1Pin, ctouchThrVal +100); break;
|
||||
case SDLK_2: touchSensor.mockFilteredData(K2Pin, ctouchThrVal +100); break;
|
||||
|
@ -632,6 +637,8 @@ int main(int argc, const char** argv)
|
|||
args::Flag factoryReset(parser, "factory-reset", "Trigger factory reset", {'r', "factory-reset"});
|
||||
args::Flag configMode(parser, "config-mode", "Trigger config-management mode", {'c', "config-mode"});
|
||||
args::Flag nodelay(parser, "nodelay", "Skip all delays when running", {'n', "nodelay"});
|
||||
args::ValueFlag<std::string> 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));
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
#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(pos<length) {
|
||||
int remaining = length-pos;
|
||||
int bytesToSend = min(remaining, MIDI_SYSEX_CHUNK_SIZE);
|
||||
int bytesToSend = std::min(remaining, MIDI_SYSEX_CHUNK_SIZE);
|
||||
bool complete = (bytesToSend == remaining);
|
||||
|
||||
memcpy(buf, data+pos, bytesToSend);
|
||||
|
@ -143,3 +132,11 @@ void SimUsbMidi::setHandleSystemExclusive(void (*fptr) (const uint8_t *array, un
|
|||
void SimUsbMidi::setHandleSystemExclusive(void (*fptr) (const uint8_t *array, uint16_t size, bool last)) {
|
||||
this->usb_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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue