From 13b92752fc8e17f70d4ada4fd2a353b442adac25 Mon Sep 17 00:00:00 2001
From: probonopd <probonopd@users.noreply.github.com>
Date: Sat, 3 May 2025 12:04:41 +0200
Subject: [PATCH] Try to send bank dump (does not work yet)

00:15:54.281 >1 - 192.168.0.61 midikeyboard - - - MIDI-SYSEX: channel: 0, len: 5, TG: 0
00:15:54.281 >1 - 192.168.0.61 midikeyboard - - - SysEx bank dump request: device 0
00:15:54.281 >1 - 192.168.0.61 midikeyboard - - - SendSystemExclusiveBank
00:15:54.282 >1 - 192.168.0.61 minidexed - - - getSysExBankDump: called for TG=0
00:15:54.282 >1 - 192.168.0.61 midikeyboard - - - SendSystemExclusiveBank: after getSysExBankDump
00:15:54.282 >1 - 192.168.0.61 midikeyboard - - - SendSystemExclusiveBank: before chunking
00:15:54.282 >1 - 192.168.0.61 midikeyboard - - - SendSystemExclusiveBank: after chunker creation
00:15:54.282 >1 - 192.168.0.61 midikeyboard - - - SendSystemExclusiveBank: device found, starting chunk send loop
00:15:54.283 >1 - 192.168.0.61 midikeyboard - - - SendSystemExclusiveBank: sending chunk 0, size=512
00:15:54.283 >1 - 192.168.0.61 midikeyboard - - - SendSystemExclusiveBank: sending chunk 1, size=512
00:15:54.283 >1 - 192.168.0.61 midikeyboard - - - SendSystemExclusiveBank: sending chunk 2, size=512
00:15:54.284 >1 - 192.168.0.61 midikeyboard - - - SendSystemExclusiveBank: sending chunk 3, size=512
00:15:54.284 >1 - 192.168.0.61 midikeyboard - - - SendSystemExclusiveBank: sending chunk 4, size=512
00:15:54.284 >1 - 192.168.0.61 midikeyboard - - - SendSystemExclusiveBank: sending chunk 5, size=512
00:15:54.284 >1 - 192.168.0.61 midikeyboard - - - SendSystemExclusiveBank: sending chunk 6, size=512
00:15:54.284 >1 - 192.168.0.61 midikeyboard - - - SendSystemExclusiveBank: sending chunk 7, size=512
00:15:54.284 >1 - 192.168.0.61 midikeyboard - - - SendSystemExclusiveBank: all chunks sent, total=8
00:15:54.284 >1 - 192.168.0.61 midikeyboard - - - Send SYSEX bank dump 0 to "umidi1" in 512-byte chunks
00:15:54.284 >1 - 192.168.0.61 midikeyboard - - - SendSystemExclusiveBank: exit
00:15:54.285 >1 - 192.168.0.61 usbmidigadgetendpoint.cpp(134) - - - stack[1] is 0x113E94
00:15:54.285 >1 - 192.168.0.61 usbmidigadgetendpoint.cpp(134) - - - stack[11] is 0xF7CC4
00:15:54.285 >1 - 192.168.0.61 usbmidigadgetendpoint.cpp(134) - - - assertion failed: !(nLength % CUSBMIDIDevice::EventPacketSize)
---
 src/Makefile          |   3 +-
 src/mididevice.cpp    | 309 +++++++++++++++++++++++-------------------
 src/midikeyboard.cpp  |   1 +
 src/minidexed.cpp     |  32 ++++-
 src/udpmididevice.cpp |   6 +-
 5 files changed, 197 insertions(+), 154 deletions(-)

diff --git a/src/Makefile b/src/Makefile
index 73dbddc..86105c0 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -11,7 +11,8 @@ OBJS = main.o kernel.o minidexed.o config.o userinterface.o uimenu.o \
        sysexfileloader.o performanceconfig.o perftimer.o \
        effect_compressor.o effect_platervbstereo.o uibuttons.o midipin.o \
        arm_float_to_q23.o \
-       net/ftpdaemon.o net/ftpworker.o net/applemidi.o net/udpmidi.o net/mdnspublisher.o udpmididevice.o
+       net/ftpdaemon.o net/ftpworker.o net/applemidi.o net/udpmidi.o net/mdnspublisher.o udpmididevice.o \
+       midichunker.o
 
 OPTIMIZE = -O3
 
diff --git a/src/mididevice.cpp b/src/mididevice.cpp
index 633ea69..4039afd 100644
--- a/src/mididevice.cpp
+++ b/src/mididevice.cpp
@@ -29,6 +29,7 @@
 #include <assert.h>
 #include "midi.h"
 #include "userinterface.h"
+#include "midichunker.h"
 
 LOGMODULE("midikeyboard");
 
@@ -607,146 +608,146 @@ bool CMIDIDevice::HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval)
 
 void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG)
 {
-
-  // Check if it is a dump request; these have the format F0 43 2n ff F7
-  // with n = the MIDI channel and ff = 00 for voice or 09 for bank
+    
+    // Check if it is a dump request; these have the format F0 43 2n ff F7
+// with n = the MIDI channel and ff = 00 for voice or 09 for bank
   // It was confirmed that on the TX816, the device number is interpreted as the MIDI channel; 
-  if (nLength == 5 && pMessage[3] == 0x00)
-  {
-	LOGDBG("SysEx voice dump request: device %d", nTG);
-	SendSystemExclusiveVoice(nTG, m_DeviceName, nCable, nTG);
-	return;
-  }
-  else if (nLength == 5 && pMessage[3] == 0x09)
-  {
-	LOGDBG("SysEx bank dump request: device %d", nTG);
-	SendSystemExclusiveBank(nTG, m_DeviceName, nCable, nTG);
-	return;
-  }
-
-  int16_t sysex_return;
-
-  sysex_return = m_pSynthesizer->checkSystemExclusive(pMessage, nLength, nTG);
-  LOGDBG("SYSEX handler return value: %d", sysex_return);
-
-  switch (sysex_return)
-  {
-    case -1:
-      LOGERR("SysEx end status byte not detected.");
-      break;
-    case -2:
-      LOGERR("SysEx vendor not Yamaha.");
-      break;
-    case -3:
-      LOGERR("Unknown SysEx parameter change.");
-      break;
-    case -4:
-      LOGERR("Unknown SysEx voice or function.");
-      break;
-    case -5:
-      LOGERR("Not a SysEx voice bulk upload.");
-      break;
-    case -6:
-      LOGERR("Wrong length for SysEx voice bulk upload (not 155).");
-      break;
-    case -7:
-      LOGERR("Checksum error for one voice.");
-      break;
-    case -8:
-      LOGERR("Not a SysEx bank bulk upload.");
-      break;
-    case -9:
-      LOGERR("Wrong length for SysEx bank bulk upload (not 4096).");
-    case -10:
-      LOGERR("Checksum error for bank.");
-      break;
-    case -11:
-      LOGERR("Unknown SysEx message.");
-      break;
-    case 64:
-      LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
-      m_pSynthesizer->setMonoMode(pMessage[5],nTG);
-      break;
-    case 65:
-      LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
-      m_pSynthesizer->setPitchbendRange(pMessage[5],nTG);
-      break;
-    case 66:
-      LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
-      m_pSynthesizer->setPitchbendStep(pMessage[5],nTG);
-      break;
-    case 67:
-      LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
-      m_pSynthesizer->setPortamentoMode(pMessage[5],nTG);
-      break;
-    case 68:
-      LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
-      m_pSynthesizer->setPortamentoGlissando(pMessage[5],nTG);
-      break;
-    case 69:
-      LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
-      m_pSynthesizer->setPortamentoTime(pMessage[5],nTG);
-      break;
-    case 70:
-      LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
-      m_pSynthesizer->setModWheelRange(pMessage[5],nTG);
-      break;
-    case 71:
-      LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
-      m_pSynthesizer->setModWheelTarget(pMessage[5],nTG);
-      break;
-    case 72:
-      LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
-      m_pSynthesizer->setFootControllerRange(pMessage[5],nTG);
-      break;
-    case 73:
-      LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
-      m_pSynthesizer->setFootControllerTarget(pMessage[5],nTG);
-      break;
-    case 74:
-      LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
-      m_pSynthesizer->setBreathControllerRange(pMessage[5],nTG);
-      break;
-    case 75:
-      LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
-      m_pSynthesizer->setBreathControllerTarget(pMessage[5],nTG);
-      break;
-    case 76:
-      LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
-      m_pSynthesizer->setAftertouchRange(pMessage[5],nTG);
-      break;
-    case 77:
-      LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
-      m_pSynthesizer->setAftertouchTarget(pMessage[5],nTG);
-      break;
-    case 100:
-      // load sysex-data into voice memory
-      LOGDBG("One Voice bulk upload");
-      m_pSynthesizer->loadVoiceParameters(pMessage,nTG);
-      break;
-    case 200:
-      LOGDBG("Bank bulk upload.");
-      //TODO: add code for storing a bank bulk upload
-      LOGNOTE("Currently code  for storing a bulk bank upload is missing!");
-      break;
-    case 455:
-      // Parameter 155 + 300 added by Synth_Dexed = 455
-      LOGDBG("Operators enabled: %d%d%d%d%d%d", (pMessage[5] & 0x20) ? 1 : 0, (pMessage[5] & 0x10) ? 1 : 0, (pMessage[5] & 0x08) ? 1 : 0, (pMessage[5] & 0x04) ? 1 : 0, (pMessage[5] & 0x02) ? 1 : 0, (pMessage[5] & 0x01) ? 1 : 0);
-      m_pSynthesizer->setOPMask(pMessage[5], nTG);
-      break;
-    default:
-      if(sysex_return >= 300 && sysex_return < 500)
-      {
-        LOGDBG("SysEx voice parameter change: Parameter %d value: %d",pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5]);
-        m_pSynthesizer->setVoiceDataElement(pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5],nTG);
-        switch(pMessage[4] + ((pMessage[3] & 0x03) * 128))
-        {
-          case 134:
-            m_pSynthesizer->notesOff(0,nTG);
+    if (nLength == 5 && pMessage[3] == 0x00)
+    {
+        LOGDBG("SysEx voice dump request: device %d", nTG);
+        SendSystemExclusiveVoice(nTG, m_DeviceName, nCable, nTG);
+                return;
+    }
+    else if (nLength == 5 && pMessage[3] == 0x09)
+    {
+        LOGDBG("SysEx bank dump request: device %d", nTG);
+        SendSystemExclusiveBank(nTG, m_DeviceName, nCable, nTG);
+                return;
+    }
+
+    int16_t sysex_return;
+
+    sysex_return = m_pSynthesizer->checkSystemExclusive(pMessage, nLength, nTG);
+    LOGDBG("SYSEX handler return value: %d", sysex_return);
+
+    switch (sysex_return)
+    {
+        case -1:
+            LOGERR("SysEx end status byte not detected.");
             break;
-        }
-      }
-  }
+        case -2:
+            LOGERR("SysEx vendor not Yamaha.");
+            break;
+        case -3:
+            LOGERR("Unknown SysEx parameter change.");
+            break;
+        case -4:
+            LOGERR("Unknown SysEx voice or function.");
+            break;
+        case -5:
+            LOGERR("Not a SysEx voice bulk upload.");
+            break;
+        case -6:
+            LOGERR("Wrong length for SysEx voice bulk upload (not 155).");
+            break;
+        case -7:
+            LOGERR("Checksum error for one voice.");
+            break;
+        case -8:
+            LOGERR("Not a SysEx bank bulk upload.");
+            break;
+        case -9:
+            LOGERR("Wrong length for SysEx bank bulk upload (not 4096).");
+        case -10:
+            LOGERR("Checksum error for bank.");
+            break;
+        case -11:
+            LOGERR("Unknown SysEx message.");
+            break;
+        case 64:
+            LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+            m_pSynthesizer->setMonoMode(pMessage[5],nTG);
+            break;
+        case 65:
+            LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+            m_pSynthesizer->setPitchbendRange(pMessage[5],nTG);
+            break;
+        case 66:
+            LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+            m_pSynthesizer->setPitchbendStep(pMessage[5],nTG);
+            break;
+        case 67:
+            LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+            m_pSynthesizer->setPortamentoMode(pMessage[5],nTG);
+            break;
+        case 68:
+            LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+            m_pSynthesizer->setPortamentoGlissando(pMessage[5],nTG);
+            break;
+        case 69:
+            LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+            m_pSynthesizer->setPortamentoTime(pMessage[5],nTG);
+            break;
+        case 70:
+            LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+            m_pSynthesizer->setModWheelRange(pMessage[5],nTG);
+            break;
+        case 71:
+            LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+            m_pSynthesizer->setModWheelTarget(pMessage[5],nTG);
+            break;
+        case 72:
+            LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+            m_pSynthesizer->setFootControllerRange(pMessage[5],nTG);
+            break;
+        case 73:
+            LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+            m_pSynthesizer->setFootControllerTarget(pMessage[5],nTG);
+            break;
+        case 74:
+            LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+            m_pSynthesizer->setBreathControllerRange(pMessage[5],nTG);
+            break;
+        case 75:
+            LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+            m_pSynthesizer->setBreathControllerTarget(pMessage[5],nTG);
+            break;
+        case 76:
+            LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+            m_pSynthesizer->setAftertouchRange(pMessage[5],nTG);
+            break;
+        case 77:
+            LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+            m_pSynthesizer->setAftertouchTarget(pMessage[5],nTG);
+            break;
+        case 100:
+            // load sysex-data into voice memory
+            LOGDBG("One Voice bulk upload");
+            m_pSynthesizer->loadVoiceParameters(pMessage,nTG);
+            break;
+        case 200:
+            LOGDBG("Bank bulk upload.");
+            //TODO: add code for storing a bank bulk upload
+            LOGNOTE("Currently code  for storing a bulk bank upload is missing!");
+            break;
+        case 455:
+            // Parameter 155 + 300 added by Synth_Dexed = 455
+            LOGDBG("Operators enabled: %d%d%d%d%d%d", (pMessage[5] & 0x20) ? 1 : 0, (pMessage[5] & 0x10) ? 1 : 0, (pMessage[5] & 0x08) ? 1 : 0, (pMessage[5] & 0x04) ? 1 : 0, (pMessage[5] & 0x02) ? 1 : 0, (pMessage[5] & 0x01) ? 1 : 0);
+            m_pSynthesizer->setOPMask(pMessage[5], nTG);
+            break;
+        default:
+            if(sysex_return >= 300 && sysex_return < 500)
+            {
+                LOGDBG("SysEx voice parameter change: Parameter %d value: %d",pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5]);
+                m_pSynthesizer->setVoiceDataElement(pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5],nTG);
+                switch(pMessage[4] + ((pMessage[3] & 0x03) * 128))
+                {
+                    case 134:
+                        m_pSynthesizer->notesOff(0,nTG);
+                        break;
+                }
+            }
+    }
 }
 
 void CMIDIDevice::SendSystemExclusiveVoice(uint8_t nVoice, const std::string& deviceName, unsigned nCable, uint8_t nTG)
@@ -765,14 +766,36 @@ void CMIDIDevice::SendSystemExclusiveVoice(uint8_t nVoice, const std::string& de
 
 void CMIDIDevice::SendSystemExclusiveBank(uint8_t nVoice, const std::string& deviceName, unsigned nCable, uint8_t nTG)
 {
-	// Example: F0 43 20 09 F7
-    static uint8_t voicedump[4104]; // Use static buffer, correct size for DX7 bank dump
+    LOGNOTE("SendSystemExclusiveBank");
+    static uint8_t voicedump[4096]; // Correct size for DX7 bank dump
     m_pSynthesizer->getSysExBankDump(voicedump, nTG);
+    LOGNOTE("SendSystemExclusiveBank: after getSysExBankDump");
+    LOGNOTE("SendSystemExclusiveBank: before chunking");
+    MIDISysExChunker chunker(voicedump, 4096, 512);
+    LOGNOTE("SendSystemExclusiveBank: after chunker creation");
     TDeviceMap::const_iterator Iterator = s_DeviceMap.find(deviceName);
     if (Iterator != s_DeviceMap.end()) {
-        Iterator->second->Send(voicedump, 4104, nCable);
-        LOGDBG("Send SYSEX bank dump %u to \"%s\"", nVoice, deviceName.c_str());
+        LOGNOTE("SendSystemExclusiveBank: device found, starting chunk send loop");
+        int chunkCount = 0;
+        while (chunker.hasNext()) {
+            std::vector<uint8_t> chunk = chunker.next();
+            // Pad chunk to a multiple of 4 bytes for USB MIDI
+            size_t pad = chunk.size() % 4;
+            if (pad != 0) {
+                chunk.resize(chunk.size() + (4 - pad), 0x00);
+            }
+            if (chunk.size() % 4 != 0) {
+                LOGERR("Chunk size %u is not a multiple of 4 before Send!", (unsigned)chunk.size());
+                assert(chunk.size() % 4 == 0);
+            }
+            LOGNOTE("SendSystemExclusiveBank: sending chunk %d, size=%u", chunkCount, (unsigned)chunk.size());
+            Iterator->second->Send(chunk.data(), chunk.size(), nCable);
+            chunkCount++;
+        }
+        LOGNOTE("SendSystemExclusiveBank: all chunks sent, total=%d", chunkCount);
+        LOGDBG("Send SYSEX bank dump %u to \"%s\" in 512-byte chunks", nVoice, deviceName.c_str());
     } else {
-        LOGWARN("No device found in s_DeviceMap for name: %s", deviceName.c_str());
+        LOGWARN("SendSystemExclusiveBank: No device found in s_DeviceMap for name: %s", deviceName.c_str());
     }
+    LOGNOTE("SendSystemExclusiveBank: exit");
 }
diff --git a/src/midikeyboard.cpp b/src/midikeyboard.cpp
index 6d1f410..b581210 100644
--- a/src/midikeyboard.cpp
+++ b/src/midikeyboard.cpp
@@ -65,6 +65,7 @@ void CMIDIKeyboard::Process (boolean bPlugAndPlayUpdated)
         // Only send one chunk per Process() call to avoid blocking or watchdog reset
         if (offset < m_QueuedSysEx.size()) {
             size_t chunk = std::min(kUSBMIDIMaxChunk, m_QueuedSysEx.size() - offset);
+            LOGNOTE("SendEventPackets: about to send chunk at offset %u, length=%u", offset, chunk);
             m_pMIDIDevice->SendEventPackets(m_QueuedSysEx.data() + offset, chunk);
             offset += chunk;
             // Save progress for next Process() call
diff --git a/src/minidexed.cpp b/src/minidexed.cpp
index 9843174..0f28f37 100644
--- a/src/minidexed.cpp
+++ b/src/minidexed.cpp
@@ -1870,13 +1870,8 @@ void CMiniDexed::getSysExVoiceDump(uint8_t* dest, uint8_t nTG)
 
 void CMiniDexed::getSysExBankDump(uint8_t* dest, uint8_t nTG)
 {
-    // DX7 Bulk Dump: 32 voices
-    // Header: F0 43 00 09 20 00
-    // Data:   4096 bytes (32 voices x 128 bytes packed)
-    // Checksum: 1 byte (2's complement of sum of 4096 data bytes, masked to 7 bits)
-    // Footer: F7
-    // Total: 4104 bytes
-
+    LOGNOTE("getSysExBankDump: called for TG=%u", nTG);
+	return;
     constexpr size_t kVoices = 32;
     constexpr size_t kPackedVoiceSize = 128;
     constexpr size_t kBulkDataSize = kVoices * kPackedVoiceSize; // 4096
@@ -1884,6 +1879,7 @@ void CMiniDexed::getSysExBankDump(uint8_t* dest, uint8_t nTG)
     constexpr size_t kTotalSize = kHeaderSize + kBulkDataSize + 2; // +checksum +F7 = 4104
 
     // Header (Yamaha DX7 standard)
+    LOGNOTE("getSysExBankDump: writing header");
     dest[0] = 0xF0; // SysEx start
     dest[1] = 0x43; // Yamaha ID
     dest[2] = 0x00; // Sub-status (0), device/channel (0)
@@ -1891,6 +1887,8 @@ void CMiniDexed::getSysExBankDump(uint8_t* dest, uint8_t nTG)
     dest[4] = 0x20; // Byte count MSB (4096 = 0x1000, MSB=0x20)
     dest[5] = 0x00; // Byte count LSB
 
+    LOGNOTE("getSysExBankDump: header: %02X %02X %02X %02X %02X %02X", dest[0], dest[1], dest[2], dest[3], dest[4], dest[5]);
+
     // Fill packed voice data
     uint8_t* pData = dest + kHeaderSize;
     uint8_t checksum = 0;
@@ -1902,13 +1900,33 @@ void CMiniDexed::getSysExBankDump(uint8_t* dest, uint8_t nTG)
             checksum += packedVoice[b];
         }
     }
+    LOGNOTE("getSysExBankDump: packed data filled, checksum before complement: %02X", checksum);
 
     // Checksum: 2's complement, masked to 7 bits
     checksum = (~checksum + 1) & 0x7F;
     dest[kHeaderSize + kBulkDataSize] = checksum;
+    LOGNOTE("getSysExBankDump: checksum after complement: %02X", checksum);
 
     // Footer
     dest[kHeaderSize + kBulkDataSize + 1] = 0xF7;
+    LOGNOTE("getSysExBankDump: footer: %02X", dest[kHeaderSize + kBulkDataSize + 1]);
+
+    // Log summary of dump
+    LOGNOTE("getSysExBankDump: total size: %d bytes", kTotalSize);
+    std::string dumpStart, dumpEnd;
+    char buf[8];
+    for (size_t i = 0; i < 16; ++i) {
+        snprintf(buf, sizeof(buf), "%02X", dest[i]);
+        dumpStart += buf;
+        if (i < 15) dumpStart += " ";
+    }
+    for (size_t i = kTotalSize - 16; i < kTotalSize; ++i) {
+        snprintf(buf, sizeof(buf), "%02X", dest[i]);
+        dumpEnd += buf;
+        if (i < kTotalSize - 1) dumpEnd += " ";
+    }
+    LOGNOTE("getSysExBankDump: first 16 bytes: %s", dumpStart.c_str());
+    LOGNOTE("getSysExBankDump: last 16 bytes: %s", dumpEnd.c_str());
 }
 
 void CMiniDexed::setOPMask(uint8_t uchOPMask, uint8_t nTG)
diff --git a/src/udpmididevice.cpp b/src/udpmididevice.cpp
index 4c46649..8a33e98 100644
--- a/src/udpmididevice.cpp
+++ b/src/udpmididevice.cpp
@@ -103,14 +103,14 @@ void CUDPMIDIDevice::Send(const u8 *pMessage, size_t nLength, unsigned nCable)
     bool sentRTP = false;
     if (m_pAppleMIDIParticipant && m_pAppleMIDIParticipant->SendMIDIToHost(pMessage, nLength)) {
         sentRTP = true;
-        LOGNOTE("Sent %zu bytes to RTP-MIDI host", nLength);
+        LOGNOTE("Sent %d bytes to RTP-MIDI host", nLength);
     }
     if (!sentRTP && m_pUDPSendSocket) {
         int res = m_pUDPSendSocket->SendTo(pMessage, nLength, 0, m_UDPDestAddress, m_UDPDestPort);
         if (res < 0) {
-            LOGERR("Failed to send %zu bytes to UDP MIDI host", nLength);
+            LOGERR("Failed to send %d bytes to UDP MIDI host", nLength);
         } else {
-            LOGNOTE("Sent %zu bytes to UDP MIDI host (broadcast)", nLength);
+            LOGNOTE("Sent %d bytes to UDP MIDI host (broadcast)", nLength);
         }
     }
 }
\ No newline at end of file