aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJP Cimalando <jpcima@users.noreply.github.com>2018-06-20 21:45:54 +0200
committerJP Cimalando <jpcima@users.noreply.github.com>2018-06-20 23:23:09 +0200
commit252e65097e9b8a815485fc03e4358698803f1a73 (patch)
tree1ece6563e49c2d82bdb453f0f0c5ce258db63874 /src
parent94dba775b234a57ad45dabf77200d305c94d7bf0 (diff)
downloadlibADLMIDI-252e65097e9b8a815485fc03e4358698803f1a73.tar.gz
libADLMIDI-252e65097e9b8a815485fc03e4358698803f1a73.tar.bz2
libADLMIDI-252e65097e9b8a815485fc03e4358698803f1a73.zip
basic framework of sysex handling and sequencer support
Diffstat (limited to 'src')
-rw-r--r--src/adlmidi.cpp21
-rw-r--r--src/adlmidi_midiplay.cpp178
-rw-r--r--src/adlmidi_private.hpp29
3 files changed, 228 insertions, 0 deletions
diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp
index 9210b5b..e3d2326 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)
@@ -1267,3 +1278,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 2f186a6..e0cacfa 100644
--- a/src/adlmidi_midiplay.cpp
+++ b/src/adlmidi_midiplay.cpp
@@ -680,6 +680,7 @@ bool MIDIplay::buildTrackData()
MIDIplay::MIDIplay(unsigned long sampleRate):
cmf_percussion_mode(false),
+ m_sysExDeviceId(0),
m_arpeggioCounter(0)
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
, m_audioTickCounter(0)
@@ -1459,6 +1460,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();
@@ -1845,6 +2012,10 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t **pptr, uint8_t *end, int &stat
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;
}
@@ -2067,6 +2238,13 @@ void MIDIplay::HandleEvent(size_t tk, const MIDIplay::MidiEvent &evt, int &statu
{
//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
+ fputs("SysEx:", stderr);
+ for(size_t i = 0; i < evt.data.size(); ++i)
+ fprintf(stderr, " %02X", evt.data[i]);
+ fputc('\n', stderr);
+#endif
+ realTime_SysEx(evt.data.data(), (unsigned)evt.data.size());
return;
}
diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp
index fda629d..4b3ab10 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!
@@ -984,6 +985,7 @@ public:
std::vector<MIDIchannel> Ch;
bool cmf_percussion_mode;
+ uint8_t m_sysExDeviceId;
MIDIEventHooks hooks;
@@ -1198,6 +1200,9 @@ public:
void realTime_BankChangeMSB(uint8_t channel, uint8_t msb);
void realTime_BankChange(uint8_t channel, uint16_t bank);
+ void setDeviceId(uint8_t id);
+ bool realTime_SysEx(const uint8_t *msg, unsigned size);
+
void realTime_panic();
#if defined(ADLMIDI_AUDIO_TICK_HANDLER)
@@ -1208,6 +1213,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,