diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/adlmidi.cpp | 21 | ||||
-rw-r--r-- | src/adlmidi_midiplay.cpp | 167 | ||||
-rw-r--r-- | src/adlmidi_private.hpp | 40 | ||||
-rw-r--r-- | src/adlmidi_sequencer.cpp | 5 | ||||
-rw-r--r-- | src/midi_sequencer_impl.hpp | 10 |
5 files changed, 239 insertions, 4 deletions
diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp index efd716c..e77278c 100644 --- a/src/adlmidi.cpp +++ b/src/adlmidi.cpp @@ -68,6 +68,17 @@ ADLMIDI_EXPORT struct ADL_MIDIPlayer *adl_init(long sample_rate) return midi_device; } +ADLMIDI_EXPORT int adl_setDeviceIdentifier(ADL_MIDIPlayer *device, unsigned id) +{ + if(!device || id > 0x0f) + return -1; + MIDIplay *play = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + if(!play) + return -1; + play->setDeviceId(id); + return 0; +} + ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numCards) { if(device == NULL) @@ -1330,3 +1341,13 @@ ADLMIDI_EXPORT void adl_rt_bankChange(struct ADL_MIDIPlayer *device, ADL_UInt8 c return; player->realTime_BankChange(channel, (uint16_t)bank); } + +ADLMIDI_EXPORT int adl_rt_systemExclusive(struct ADL_MIDIPlayer *device, const ADL_UInt8 *msg, unsigned size) +{ + if(!device) + return -1; + MIDIplay *player = reinterpret_cast<MIDIplay *>(device->adl_midiPlayer); + if(!player) + return -1; + return player->realTime_SysEx(msg, size); +} diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index f539b16..8b4f27f 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -117,6 +117,7 @@ void MIDIplay::AdlChannel::AddAge(int64_t ms) MIDIplay::MIDIplay(unsigned long sampleRate): cmf_percussion_mode(false), + m_sysExDeviceId(0), m_arpeggioCounter(0) #if defined(ADLMIDI_AUDIO_TICK_HANDLER) , m_audioTickCounter(0) @@ -706,6 +707,172 @@ void MIDIplay::realTime_BankChange(uint8_t channel, uint16_t bank) Ch[channel].bank_msb = uint8_t((bank >> 8) & 0xFF); } +void MIDIplay::setDeviceId(uint8_t id) +{ + m_sysExDeviceId = id; +} + +bool MIDIplay::realTime_SysEx(const uint8_t *msg, unsigned size) +{ + if(size < 4 || msg[0] != 0xF0 || msg[size - 1] != 0xF7) + return false; + + unsigned manufacturer = msg[1]; + unsigned dev = msg[2]; + msg += 3; + size -= 4; + + switch(manufacturer) + { + default: + break; + case Manufacturer_UniversalNonRealtime: + case Manufacturer_UniversalRealtime: + return doUniversalSysEx( + dev, manufacturer == Manufacturer_UniversalRealtime, msg, size); + case Manufacturer_Roland: + return doRolandSysEx(dev, msg, size); + case Manufacturer_Yamaha: + return doYamahaSysEx(dev, msg, size); + } + + return false; +} + +bool MIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, unsigned size) +{ + bool devicematch = dev == 0x7F || dev == m_sysExDeviceId; + if(size < 2 || !devicematch) + return false; + + unsigned address = + (((unsigned)data[0] & 0x7F) << 8) | + (((unsigned)data[1] & 0x7F)); + data += 2; + size -= 2; + + switch(((unsigned)realtime << 16) | address) + { + case (0 << 16) | 0x0901: // GM System On + /*TODO*/ + return true; + case (0 << 16) | 0x0902: // GM System Off + /*TODO*/ + return true; + case (1 << 16) | 0x0401: // MIDI Master Volume + if(size != 2) + break; + unsigned volume = + (((unsigned)data[0] & 0x7F)) | + (((unsigned)data[1] & 0x7F) << 7); + /*TODO*/ + (void)volume; + return true; + } + + return false; +} + +bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, unsigned size) +{ + bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; + if(size < 6 || !devicematch) + return false; + + unsigned model = data[0] & 0x7F; + unsigned mode = data[1] & 0x7F; + unsigned checksum = data[size - 1] & 0x7F; + data += 2; + size -= 3; + +#if !defined(ADLMIDI_SKIP_ROLAND_CHECKSUM) + { + unsigned checkvalue = 0; + for(unsigned i = 0; i < size; ++i) + checkvalue += data[i] & 0x7F; + checkvalue = (128 - (checkvalue & 127)) & 127; + if(checkvalue != checksum) + return false; + } +#endif + + unsigned address = + (((unsigned)data[0] & 0x7F) << 16) | + (((unsigned)data[1] & 0x7F) << 8) | + (((unsigned)data[2] & 0x7F)); + data += 3; + size -= 3; + + if(mode != RolandMode_Send) // don't have MIDI-Out reply ability + return false; + + switch((model << 24) | address) + { + case (RolandModel_GS << 24) | 0x00007F: // System Mode Set + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + unsigned mode = data[0] & 0x7F; + /*TODO*/ + (void)mode; + return true; + } + case (RolandModel_GS << 24) | 0x40007F: // Mode Set + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + unsigned value = data[0] & 0x7F; + /*TODO*/ + (void)value; + return true; + } + } + + return false; +} + +bool MIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, unsigned size) +{ + bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; + if(size < 1 || !devicematch) + return false; + + unsigned model = data[0] & 0x7F; + ++data; + --size; + + switch((model << 8) | (dev & 0xF0)) + { + case (YamahaModel_XG << 8) | 0x10: // parameter change + { + if(size < 3) + break; + + unsigned address = + (((unsigned)data[0] & 0x7F) << 16) | + (((unsigned)data[1] & 0x7F) << 8) | + (((unsigned)data[2] & 0x7F)); + data += 3; + size -= 3; + + switch(address) + { + case 0x00007E: // XG System On + if(size != 1) + break; + unsigned value = data[0] & 0x7F; + /*TODO*/ + (void)value; + return true; + } + + break; + } + } + + return false; +} + void MIDIplay::realTime_panic() { Panic(); diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp index 058a431..67f1ccb 100644 --- a/src/adlmidi_private.hpp +++ b/src/adlmidi_private.hpp @@ -108,6 +108,7 @@ typedef int32_t ssize_t; #include <deque> #include <algorithm> +#include <iterator> /* * Workaround for some compilers are has no those macros in their headers! @@ -723,6 +724,7 @@ public: std::vector<MIDIchannel> Ch; bool cmf_percussion_mode; + uint8_t m_sysExDeviceId; MIDIEventHooks hooks; @@ -901,6 +903,20 @@ public: void realTime_BankChange(uint8_t channel, uint16_t bank); /** + * @brief Sets the Device identifier + * @param id 7-bit Device identifier + */ + void setDeviceId(uint8_t id); + + /** + * @brief System Exclusive message + * @param msg Raw SysEx Message + * @param size Length of SysEx message + * @return true if message was passed successfully. False on any errors + */ + bool realTime_SysEx(const uint8_t *msg, unsigned size); + + /** * @brief Turn off all notes and mute the sound of releasing notes */ void realTime_panic(); @@ -934,6 +950,30 @@ public: private: enum { + Manufacturer_Roland = 0x41, + Manufacturer_Yamaha = 0x43, + Manufacturer_UniversalNonRealtime = 0x7E, + Manufacturer_UniversalRealtime = 0x7F + }; + enum + { + RolandMode_Request = 0x11, + RolandMode_Send = 0x12 + }; + enum + { + RolandModel_GS = 0x42, + RolandModel_SC55 = 0x45, + YamahaModel_XG = 0x4C + }; + + bool doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, unsigned size); + bool doRolandSysEx(unsigned dev, const uint8_t *data, unsigned size); + bool doYamahaSysEx(unsigned dev, const uint8_t *data, unsigned size); + +private: + enum + { Upd_Patch = 0x1, Upd_Pan = 0x2, Upd_Volume = 0x4, diff --git a/src/adlmidi_sequencer.cpp b/src/adlmidi_sequencer.cpp index fcc1380..ac983d0 100644 --- a/src/adlmidi_sequencer.cpp +++ b/src/adlmidi_sequencer.cpp @@ -79,10 +79,7 @@ static void rtPitchBend(void *userdata, uint8_t channel, uint8_t msb, uint8_t ls static void rtSysEx(void *userdata, const uint8_t *msg, size_t size) { MIDIplay *context = reinterpret_cast<MIDIplay *>(userdata); - ADL_UNUSED(context); - ADL_UNUSED(msg); - ADL_UNUSED(size); - /* TODO: pass SysEx HERE! */ + context->realTime_SysEx(msg, (unsigned)size); } diff --git a/src/midi_sequencer_impl.hpp b/src/midi_sequencer_impl.hpp index b3bfefa..7ecefa2 100644 --- a/src/midi_sequencer_impl.hpp +++ b/src/midi_sequencer_impl.hpp @@ -919,6 +919,10 @@ BW_MidiSequencer::MidiEvent BW_MidiSequencer::parseEvent(const uint8_t **pptr, c evt.isValid = 0; return evt; } + evt.type = MidiEvent::T_SYSEX; + evt.data.clear(); + evt.data.push_back(byte); + std::copy(ptr, ptr + length, std::back_inserter(evt.data)); ptr += (size_t)length; return evt; } @@ -1120,6 +1124,12 @@ void BW_MidiSequencer::handleEvent(size_t tk, const BW_MidiSequencer::MidiEvent { //std::string data( length?(const char*) &TrackData[tk][CurrentPosition.track[tk].ptr]:0, length ); //UI.PrintLn("SysEx %02X: %u bytes", byte, length/*, data.c_str()*/); +#if 0 + std::fputs("SysEx:", stderr); + for(size_t i = 0; i < evt.data.size(); ++i) + std::fprintf(stderr, " %02X", evt.data[i]); + std::fputc('\n', stderr); +#endif m_interface->rt_systemExclusive(m_interface->rtUserData, evt.data.data(), evt.data.size()); return; } |