From ee85989cd9cf9eee9da2738090b93a8b7ec9bd2d Mon Sep 17 00:00:00 2001 From: lathoub Date: Thu, 5 Aug 2021 23:10:17 +0200 Subject: [PATCH] updated simulator simulator using actual BLE parser, using a BLE simulator Note: you need a manually add a path to ../src and arduino_midi_library/src --- test/msvc/Arduino.h | 57 +++++ test/msvc/BLEMIDI_Sim.h | 220 +++++++++++++++++ test/msvc/ConsoleApplication2.cpp | 331 -------------------------- test/msvc/ConsoleApplication2.vcxproj | 7 +- test/msvc/midiBLE.cpp | 40 ++++ 5 files changed, 323 insertions(+), 332 deletions(-) create mode 100644 test/msvc/Arduino.h create mode 100644 test/msvc/BLEMIDI_Sim.h delete mode 100644 test/msvc/ConsoleApplication2.cpp create mode 100644 test/msvc/midiBLE.cpp diff --git a/test/msvc/Arduino.h b/test/msvc/Arduino.h new file mode 100644 index 0000000..36d888e --- /dev/null +++ b/test/msvc/Arduino.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +#define HEX 0 +#define DEC 1 + +#include +typedef uint8_t byte; + +void begin(); +void loop(); + +int main() +{ + begin(); + + while (true) + { + loop(); + } +} + +// avoid strncpy security warning +#pragma warning(disable:4996) + +#define __attribute__(A) /* do nothing */ + +#include + +float analogRead(int pin) +{ + return 0.0f; +} + +void randomSeed(float) +{ + srand(static_cast(time(0))); +} + +unsigned long millis() +{ + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + return (unsigned long)now; +} + +int random(int min, int max) +{ + return RAND_MAX % std::rand() % (max - min) + min; +} + +template const T& min(const T& a, const T& b) { + return !(b < a) ? a : b; // or: return !comp(b,a)?a:b; for version (2) +} + +#define F(x) x diff --git a/test/msvc/BLEMIDI_Sim.h b/test/msvc/BLEMIDI_Sim.h new file mode 100644 index 0000000..cee0949 --- /dev/null +++ b/test/msvc/BLEMIDI_Sim.h @@ -0,0 +1,220 @@ +#pragma once + +BEGIN_BLEMIDI_NAMESPACE + +template +class Fifo { +public: + const size_t size; //speculative feature, in case it's needed + + Fifo() : size(rawSize) + { + flush(); + } + + T dequeue() + { + numberOfElements--; + nextOut %= size; + return raw[nextOut++]; + }; + + bool enqueue(T element) + { + if (count() >= rawSize) + return false; + + numberOfElements++; + nextIn %= size; + raw[nextIn] = element; + nextIn++; //advance to next index + + return true; + }; + + T peek() const + { + return raw[nextOut % size]; + } + + void flush() + { + nextIn = nextOut = numberOfElements = 0; + } + + // how many elements are currently in the FIFO? + size_t count() { return numberOfElements; } + +private: + size_t numberOfElements; + size_t nextIn; + size_t nextOut; + T raw[rawSize]; +}; + +class BLEMIDI_Sim +{ +private: + static BLEMIDI_Transport* _bleMidiTransport; + + Fifo mRxBuffer; + +public: + BLEMIDI_Sim() + { + } + + bool begin(const char*, BLEMIDI_Transport*); + + void end() + { + } + + void test() + { + + } + + void write(uint8_t* buffer, size_t size) + { + } + + bool available(byte* pvBuffer) + { + if (mRxBuffer.count() > 0) { + *pvBuffer = mRxBuffer.dequeue(); + + return true; + } + + return false; + } + + void add(byte value) + { + mRxBuffer.enqueue(value); + } + +}; + +BLEMIDI_Transport* BLEMIDI_Sim::_bleMidiTransport = nullptr; + +bool BLEMIDI_Sim::begin(const char* deviceName, BLEMIDI_Transport* bleMidiTransport) +{ + _bleMidiTransport = bleMidiTransport; + + byte sysExAndRealTime[] = { 0xB0, 0xF4, // header + timestamp + 0xF0, // start SysEx + 0x01, 0x02, 0x03, 0x04, // SysEx data + // RealTime message in the middle of a SysEx + 0xF3, // timestampLow + 0xFA, // Realtime msg: Start + // rest of sysex data + 0x05, 0x06, 0x07, 0x08, + 0xF4, // timestampLow + 0xF7 // end of SysEx + }; + _bleMidiTransport->receive(sysExAndRealTime, sizeof(sysExAndRealTime)); + + byte sysExPart[] = { 0xB0, 0xF4, // header + timestamp + 0xF0, // start SysEx + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // sysex data + 0xF4, // timestampLow + 0xF7 }; // end of SysEx + + _bleMidiTransport->receive(sysExPart, sizeof(sysExPart)); + + byte sysExPart1[] = { 0xB0, 0xF4, // header + timestamp + 0xF0, // start SysEx + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; // sysex data + _bleMidiTransport->receive(sysExPart1, sizeof(sysExPart1)); + + byte sysExPart2[] = { 0xB0, // 1 byte header + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // sysex data (cont) + 0xF4, // timestampLow + 0xF7 }; // end of SysEx + _bleMidiTransport->receive(sysExPart2, sizeof(sysExPart2)); + + byte blePacketWithOneMIDIMessage[] = { 0xA8, 0xC0, + 0x90, 0x3E, 0x3E }; + _bleMidiTransport->receive(blePacketWithOneMIDIMessage, sizeof(blePacketWithOneMIDIMessage)); + + byte blePacketWithTwoMIDIMessage[] = { 0xA8, 0xC0, + 0x90, 0x3E, 0x3E, + 0xC1, + 0x91, 0x3E, 0x3E }; + _bleMidiTransport->receive(blePacketWithTwoMIDIMessage, sizeof(blePacketWithTwoMIDIMessage)); + + byte blePacketWithThreeMIDIMessage[] = { 0xA8, 0xC0, + 0x90, 0x3E, 0x3E, + 0xC1, + 0xF0, + 0x01, 0x02, + 0xC2, + 0xF7, + 0xC3, + 0x91, 0x3E, 0x3E }; + _bleMidiTransport->receive(blePacketWithThreeMIDIMessage, sizeof(blePacketWithThreeMIDIMessage)); + + byte twoMIDIMessageWithRunningStatus[] = { 0xA9, 0xAD, + 0xD1, 0x74, //Full Midi 2 bytes(afterTouch) + 0x73, //running + 0xAE, //timeStamp + 0x72, //running after timeStamp + 0xAF, //timeStamp + 0x71, //running after timeStamp + 0x70, + 0x69, + 0x68, + 0xB2, // + 0x92, 0x36, 0x70, //Full Midi 3 bytes (noteOn) + 0xB3, // + 0x93, 0x37, 0x71, + 0x38, 0x72, + 0x39, 0x73, + 0xB4, // + 0x40, 0x74 + }; + _bleMidiTransport->receive(twoMIDIMessageWithRunningStatus, sizeof(twoMIDIMessageWithRunningStatus)); + + byte twoMIDIMessageWithRunningStatusPlusSys[] = { 0xA9, 0xAD, + 0xD1, 0x74, //Full Midi 2 bytes(afterTouch) + 0x73, //running + 0xAE, //timeStamp + 0x72, //running after timeStamp + 0xAF, //timeStamp + 0x71, //running after timeStamp + 0x70, + 0x69, + 0x68, + 0xB2, // + 0xFA, // <- Sys START + 0xB2, + 0x92, 0x36, 0x70, //Full Midi 3 bytes (noteOn) + 0xB3, // + 0x93, 0x37, 0x71, + 0x38, 0x72, + 0xB3, // + 0xFC, // <- Sys STOP + 0xB3, + 0x39, 0x73, + 0xB4, // + 0x40, 0x74 + }; + _bleMidiTransport->receive(twoMIDIMessageWithRunningStatusPlusSys, sizeof(twoMIDIMessageWithRunningStatusPlusSys)); + + return true; +} + +/*! \brief Create an instance for nRF52 named +*/ +#define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \ +BLEMIDI_NAMESPACE::BLEMIDI_Transport BLE##Name(DeviceName); \ +MIDI_NAMESPACE::MidiInterface, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport &)BLE##Name); + +/*! \brief Create a default instance for nRF52 (Nano 33 BLE) named BLE-MIDI +*/ +#define BLEMIDI_CREATE_DEFAULT_INSTANCE() \ +BLEMIDI_CREATE_INSTANCE("BLE-MIDI", MIDI) + +END_BLEMIDI_NAMESPACE diff --git a/test/msvc/ConsoleApplication2.cpp b/test/msvc/ConsoleApplication2.cpp deleted file mode 100644 index ffa28de..0000000 --- a/test/msvc/ConsoleApplication2.cpp +++ /dev/null @@ -1,331 +0,0 @@ -// ConsoleApplication2.cpp : This file contains the 'main' function. Program execution begins and ends there. -// - -#include - -typedef unsigned int word; -typedef uint8_t boolean; -typedef unsigned char byte; - -#define CHECK_BIT(var,pos) (!!((var) & (1<<(pos)))) - -//#define RUNNINGSTATUS_ENABLE - -void transmitMIDIonDIN(byte status, byte data1, byte data2) -{ - std::cout << "0x" << std::hex << (int)status; - - if (data1 > 0) - std::cout << " 0x" << std::hex << (int)data1; - if (data2 > 0) - std::cout << " 0x" << std::hex << (int)data2; - - std::cout << std::endl; -} - -void receive(byte* buffer, size_t length) -{ - // Pointers used to search through payload. - int lPtr = 0; - int rPtr = 0; - - // lastStatus used to capture runningStatus - byte lastStatus; - // previousStatus used to continue a runningStatus interrupted by a timeStamp or a System Message. - byte previousStatus = 0x00; - - - byte headerByte = buffer[lPtr++]; -// auto signatureIs1 = CHECK_BIT(headerByte, 7 - 1); -// auto reservedIs0 = !CHECK_BIT(headerByte, 6 - 1); - auto timestampHigh = 0x3f & headerByte; - - byte timestampByte = buffer[lPtr++]; - uint16_t timestamp = 0; - - bool sysExContinuation = false; - bool runningStatusContinuation = false; - - - if (timestampByte >= 80) { - auto timestampLow = 0x7f & timestampByte; - timestamp = timestampLow + (timestampHigh << 7); - } - else { - sysExContinuation = true; - lPtr--; // the second byte is part of the SysEx - } - - //While statement contains incrementing pointers and breaks when buffer size exceeded. - while (true) - { - lastStatus = buffer[lPtr]; - - if(previousStatus==0x00){ - if ((lastStatus < 0x80) && !sysExContinuation) - return; // Status message not present and it is not a runningStatus continuation, bail - }else if(lastStatus < 0x80) - { - lastStatus = previousStatus; - runningStatusContinuation = true; - } - - - // Point to next non-data byte - rPtr = lPtr; - while ((buffer[rPtr + 1] < 0x80) && (rPtr < (length - 1))) - rPtr++; - - if(!runningStatusContinuation){ - // If not System Common or System Real-Time, send it as running status - - auto midiType = lastStatus & 0xF0; - if (sysExContinuation) - midiType = 0xF0; - - switch (midiType) - { - - // 3 bytes - case 0x80: - case 0x90: - case 0xA0: - case 0xB0: - case 0xE0: -#ifndef RUNNINGSTATUS_ENABLE - for (auto i = lPtr; i < rPtr; i = i + 2) - { - - transmitMIDIonDIN(lastStatus, buffer[i + 1], buffer[i + 2]); - } -#else - transmitMIDIonDIN(lastStatus, 0, 0); - for (auto i = lPtr; i < rPtr; i = i + 2) - { - - transmitMIDIonDIN(buffer[i + 1], buffer[i + 2], 0); - } -#endif - break; - - // 2 bytes - case 0xC0: - case 0xD0: -#ifndef RUNNINGSTATUS_ENABLE - for (auto i = lPtr; i < rPtr; i = i + 1) - { - transmitMIDIonDIN(lastStatus, buffer[i + 1], 0); - } -#else - transmitMIDIonDIN(lastStatus, 0, 0); - for (auto i = lPtr; i < rPtr; i = i + 1) - { - - transmitMIDIonDIN(buffer[i + 1], 0 , 0); - } -#endif - break; - // 1 byte or n bytes - case 0xF0: - transmitMIDIonDIN(buffer[lPtr], 0, 0); - for (auto i = lPtr; i < rPtr; i++) - transmitMIDIonDIN(buffer[i + 1], 0, 0); - - break; - - default: - break; - } - } - else - { - -#ifndef RUNNINGSTATUS_ENABLE - auto midiType = lastStatus & 0xF0; - if (sysExContinuation) - midiType = 0xF0; - - switch (midiType) - { - case 0x80: - case 0x90: - case 0xA0: - case 0xB0: - case 0xE0: - //3 bytes full Midi -> 2 bytes runningStatus - for (auto i = lPtr; i <= rPtr; i = i + 2) - { - transmitMIDIonDIN(lastStatus, buffer[i], buffer[i + 1]); - } - break; - case 0xC0: - case 0xD0: - //2 bytes full Midi -> 1 byte runningStatus - for (auto i = lPtr; i <= rPtr; i = i + 1) - { - transmitMIDIonDIN(lastStatus, buffer[i], 0); - } - break; - - default: - break; - } -#else - transmitMIDIonDIN(lastStatus, 0, 0); - - for (auto i = lPtr; i <= rPtr; i++) - { - transmitMIDIonDIN(buffer[i], 0, 0); - } -#endif - runningStatusContinuation = false; - } - - if (++rPtr >= length) - return; // end of packet - - if(lastStatus < 0xf0) //exclude System Message. They must not be RunningStatus - { - previousStatus = lastStatus; - } - - timestampByte = buffer[rPtr++]; - if (timestampByte >= 80) // is bit 7 set? - { - auto timestampLow = 0x7f & timestampByte; - timestamp = timestampLow + (timestampHigh << 7); - - std::cout << "timestamp low is 0x" << std::hex << (int)timestampByte << std::endl; - } - - // Point to next status - lPtr = rPtr; - if (lPtr >= length) - return; //end of packet - } -} - - -int main() -{ - std::cout << std::endl << "SysEx with RealTime msg in the middle --------" << std::endl; - - byte sysExAndRealTime[] = { 0xB0, 0xF4, // header + timestamp - 0xF0, // start SysEx - 0x01, 0x02, 0x03, 0x04, // SysEx data - - // RealTime message in the middle of a SysEx - 0xF3, // timestampLow - 0xFA, // Realtime msg: Start - - 0x05, 0x06, 0x07, 0x08, // rest of sysex data - 0xF4, // timestampLow - 0xF7 }; // end of SysEx - - receive(sysExAndRealTime, sizeof(sysExAndRealTime)); - - std::cout << std::endl << "SysEx ---------" << std::endl; - - byte sysExPart[] = { 0xB0, 0xF4, // header + timestamp - 0xF0, // start SysEx - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // sysex data - 0xF4, // timestampLow - 0xF7 }; // end of SysEx - - receive(sysExPart, sizeof(sysExPart)); - - std::cout << std::endl << "SysEx part 1" << std::endl; - - byte sysExPart1[] = { 0xB0, 0xF4, // header + timestamp - 0xF0, // start SysEx - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; // sysex data - receive(sysExPart1, sizeof(sysExPart1)); - - std::cout << std::endl << "SysEx part 2" << std::endl; - - byte sysExPart2[] = { 0xB0, // 1 byte header - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // sysex data (cont) - 0xF4, // timestampLow - 0xF7 }; // end of SysEx - receive(sysExPart2, sizeof(sysExPart2)); - - std::cout << "ble Packet with 1 MIDI messages" << std::endl; - - byte blePacketWithOneMIDIMessage[] = { 0xA8, 0xC0, - 0x90, 0x3E, 0x3E }; - receive(blePacketWithOneMIDIMessage, sizeof(blePacketWithOneMIDIMessage)); - - std::cout << std::endl << "ble Packet with 2 MIDI messages" << std::endl; - - byte blePacketWithTwoMIDIMessage[] = { 0xA8, 0xC0, - 0x90, 0x3E, 0x3E, - 0xC1, - 0x91, 0x3E, 0x3E }; - receive(blePacketWithTwoMIDIMessage, sizeof(blePacketWithTwoMIDIMessage)); - - std::cout << std::endl << "ble Packet with 3 MIDI messages" << std::endl; - - byte blePacketWithThreeMIDIMessage[] = { 0xA8, 0xC0, - 0x90, 0x3E, 0x3E, - 0xC1, - 0xF0, - 0x01, 0x02, - 0xC2, - 0xF7, - 0xC3, - 0x91, 0x3E, 0x3E }; - receive(blePacketWithThreeMIDIMessage, sizeof(blePacketWithThreeMIDIMessage)); - - - std::cout << std::endl << "2 MIDI messages with multiple running status" << std::endl; - - byte twoMIDIMessageWithRunningStatus[] = { 0xA9, 0xAD, - 0xD1, 0x74, //Full Midi 2 bytes(afterTouch) - 0x73, //running - 0xAE, //timeStamp - 0x72, //running after timeStamp - 0xAF, //timeStamp - 0x71, //running after timeStamp - 0x70, - 0x69, - 0x68, - 0xB2, // - 0x92, 0x36, 0x70, //Full Midi 3 bytes (noteOn) - 0xB3, // - 0x93, 0x37, 0x71, - 0x38, 0x72, - 0x39, 0x73, - 0xB4, // - 0x40, 0x74 - }; - receive(twoMIDIMessageWithRunningStatus, sizeof(twoMIDIMessageWithRunningStatus)); - - - std::cout << std::endl << "2 MIDI messages with multiple running status and a System message in middle" << std::endl; - - byte twoMIDIMessageWithRunningStatusPlusSys[] = { 0xA9, 0xAD, - 0xD1, 0x74, //Full Midi 2 bytes(afterTouch) - 0x73, //running - 0xAE, //timeStamp - 0x72, //running after timeStamp - 0xAF, //timeStamp - 0x71, //running after timeStamp - 0x70, - 0x69, - 0x68, - 0xB2, // - 0xFA, // <- Sys START - 0xB2, - 0x92, 0x36, 0x70, //Full Midi 3 bytes (noteOn) - 0xB3, // - 0x93, 0x37, 0x71, - 0x38, 0x72, - 0xB3, // - 0xFC, // <- Sys STOP - 0xB3, - 0x39, 0x73, - 0xB4, // - 0x40, 0x74 - }; - receive(twoMIDIMessageWithRunningStatusPlusSys, sizeof(twoMIDIMessageWithRunningStatusPlusSys)); -} diff --git a/test/msvc/ConsoleApplication2.vcxproj b/test/msvc/ConsoleApplication2.vcxproj index e33e11d..776dd61 100644 --- a/test/msvc/ConsoleApplication2.vcxproj +++ b/test/msvc/ConsoleApplication2.vcxproj @@ -116,6 +116,7 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true + C:\Users\bart\Documents\Arduino\libraries\arduino_midi_library-master\src;C:\Users\bart\Documents\Arduino\libraries\Arduino-BLE-MIDI-master\src;%(AdditionalIncludeDirectories) Console @@ -139,7 +140,11 @@ - + + + + + diff --git a/test/msvc/midiBLE.cpp b/test/msvc/midiBLE.cpp new file mode 100644 index 0000000..13ad093 --- /dev/null +++ b/test/msvc/midiBLE.cpp @@ -0,0 +1,40 @@ +#include "Arduino.h" +#include +#include "BLEMIDI_Sim.h" + +BLEMIDI_CREATE_DEFAULT_INSTANCE() + +void begin() +{ + MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) { + std::cout << std::hex << "NoteOn from Channel:" << (int)channel << " Note:" << (int)note << " Velocity:" << (int)velocity << std::endl; + }); + MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) { + std::cout << std::hex << "NoteOff from Channel:" << (int)channel << " Note:" << (int)note << " Velocity:" << (int)velocity << std::endl; + }); + MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) { + std::cout << std::hex << "NoteOff from Channel:" << (int)channel << " Note:" << (int)note << " Velocity:" << (int)velocity << std::endl; + }); + MIDI.setHandleControlChange([](byte channel, byte v1, byte v2) { + std::cout << std::hex << "ControlChange from Channel:" << (int)channel << " v1:" << (int)v1 << " v2:" << (int)v2 << std::endl; + }); + MIDI.setHandleProgramChange([](byte channel, byte v1) { + std::cout << std::hex << "ProgramChange from Channel:" << (int)channel << " v1:" << (int)v1 << std::endl; + }); + MIDI.setHandlePitchBend([](byte channel, int v1) { + std::cout << std::hex << "PitchBend from Channel:" << (int)channel << " v1:" << (int)v1 << std::endl; + }); + MIDI.setHandleSystemExclusive([](byte* data, unsigned length) { + std::cout << std::hex << "SysEx:"; + for (uint16_t i = 0; i < length; i++) + std::cout << std::hex << "0x" << (int)data[i] << " "; + std::cout << std::endl; + }); + + MIDI.begin(MIDI_CHANNEL_OMNI); +} + +void loop() +{ + MIDI.read(); +} \ No newline at end of file