usbMIDI transmission error

Thanks Rolf
I've tried all your suggestions, unfortunately I'm still stuck with no complete bank dump.
I'm going crazy with such a "simple" thing.

If my calculations are right (and MIDIOX displays the blocks right) I receive 3584 bytes (14*256 blocks: 1 sysex start block with F0, 13 sysex continue).
So there are 3 blocks missing to get to 4102 bytes:
- 2*256 continue
- 1*8 bytes sysex end (with F7)


I do not understand what you mean :confused:

1.Block
F0 ....data... F7

2.Block
F0 ...data... F7

3.Block
F0 ...data... F7

and more..
 
Can you explain to me exactly which line of code you mean?

The fourth line in the screenshot of MIOS Studio in post#1 shows f0 only.

Sketch in post#23 ran on bare T4.1 usbType MIDI, Win10, IDE 1.8.19 TD 1.56, produced this:-

TruncatedSysex.jpg

Note the second block, finishes with F0 and has 115 bytes, should be 130.

If you run the sketch as it is, monitoring, received blocks scroll upwards and when a faulty block is received is easy to spot. Good idea to have Actions open with cursor on Stop Display ready to click. Makes it easier to find the dodgy block.

Just play with it, change chunkSize and delayAmt. Occurrence of truncated blocks seems pretty random, does seem to happen more frequently with larger blocks.
 
Did you change the usb.c in your Arduino Library as I suggested ?

After that it is important that your project has to be completely recompiled (restart Arduino IDE).


File Path: C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy4\usb.c

Change green text in line number 364

flush.png
 
Last edited:
This is my code to send SysEx to usbMIDI()

In the SysEx dump menu from my synthesizer I query a key and set the sendSyssExflag.
If the flag was true then send sysex dump. I always send a block of 348 bytes.
Then I increment count for the number of sysex blocks and send the next block.
After each block I call a usb_midi_flush_output().

20220523_135020.jpg20220523_135048.jpg


Code:
//*************************************************************************
// Update SysEx Status
//*************************************************************************
FLASHMEM void updateSysExStatus(void)
{
    // send SysEx ---------------------------------------------------------
    if (sendSysExFlag == true){


        static uint8_t count = 0;
        uint8_t filenumbers = 0;
        uint8_t patchno = 0;
        
        if (sysexDest == false && sysexTyp == false) {        // Patch via USB
            filenumbers = 1;                                // Filenumbers = 1
            progressBarPercent = 300;                        // Progressbar fast
            patchno = sysexSource;                            // set Patch No from menu
            timer_ProgressbarFlag = true;                    // start Progressbar
        } 
        else if (sysexDest == false && sysexTyp == true){    // Bank via USB
            filenumbers = 128;                                // Filenumbers = 128
            progressBarPercent = 110;                        // Progressbar slow
            patchno = 0;                                    // start Patch No                    
            timer_ProgressbarFlag = true;                    // start Progressbar
        }
        else if (sysexDest == true && sysexTyp == false) {    // Patch via Midi
            filenumbers = 1;                                // Filenumbers = 1
            progressBarPercent = 300;                        // Progressbar fast
            patchno = sysexSource;                            // set Patch No from menu
            timer_ProgressbarFlag = true;                    // start Progressbar
        }
        else if (sysexDest == true && sysexTyp == true){    // Bank via Midi
            filenumbers = 128;                                // Filenumbers = 128
            progressBarPercent = 7;                            // Progressbar slow
            patchno = 0;                                    // start Patch No
            timer_ProgressbarFlag = true;                    // start Progressbar
        }
        
        
        progressBarFlag = true;                                // start ProgressBar
        uint8_t sysexpatch = patchno + count;
        mySendSysEx(sysexpatch, sysexBank);
        count++;
        
        sysexDataLenght += 348;
                
        //progressBarPercent = count;
        if (count >= filenumbers) {
            sendSysExFlag = false;
            count = 0;
        }
    }
    
    
    // wait Time to SysEx Receive Data (10sec)
    if (sysexRecTimeStatus == true && time_sysexRecStatus == false) {
        timer_sysexRec = millis();
        time_sysexRecStatus = true;
        if (PageNr == 11 && myPageShiftStatus[11] == true) {
            tft.fillRoundRect(123,20+(19*4),31,10,2,ST7735_RED);
            tft.setTextColor(ST7735_WHITE);
            print_String(198,130,97); // print "REC"
        }
    }
    
    // wait Time end
    else if (sysexRecTimeStatus == true && time_sysexRecStatus == true) {
        if ((millis() - timer_sysexRec) >= 15000){
            sysexRecTimeStatus = false;
            time_sysexRecStatus = false;
            if (PageNr == 11 && myPageShiftStatus[11] == true) {
                tft.fillRoundRect(123,20+(19*4),31,10,2,ST7735_BLACK);
                tft.fillRoundRect(123,19+(19*4),31,10,2,ST7735_BLUE);
                tft.setTextColor(ST7735_WHITE);
                print_String(198,130,97); // print "REC"
            }
        }
    }
}


//*************************************************************************
// send Midi system exclusive dump
//*************************************************************************
FLASHMEM void mySendSysEx(uint8_t PatchNo, uint8_t BankNo)
{
    byte sysexData[350];                                        // SysEx buffer 349 Byte
    uint8_t data_len = NO_OF_PARAMS;
    uint16_t sysexCount = 0;
    String numString = String(PatchNo + 1);
    String bankString = char(BankNo + 65);
    String fileString = (bankString + "/" + numString);
    
    // get Sound File String
    File patchFile = SD.open(fileString.c_str());
    String data[data_len]; //Array of data read in
    recallPatchData(patchFile, data);
    patchFile.close();
    
    // convert Patch data into 7bit SysEx format
                                                                   // Ind. len   value
    sysexData[sysexCount++] = 0xF0;                                // -    1    0    - Start SysEx
    sysexData[sysexCount++] = 0x00;                                // -    1    1    - ID
    sysexData[sysexCount++] = 0x00;                                // -    1    2    - ID
    sysexData[sysexCount++] = 0x00;                                // -    1    3    - ID
    sysexData[sysexCount++] = 0x00;                                // -    1    4    - Device ID 0-64
    sysexData[sysexCount++] = PatchNo;                            // -    1    5    - Patch No
    sysexData[sysexCount++] = BankNo;                            // -    1    6    - Bank No
    float_to_string(data[0], 12, sysexCount, sysexData);        // (0)    12    7    - Patch Name
    float_to_sysex1Byte(data[1], sysexCount, sysexData);        // (1)    1    19  - oscALevel (0 - 1.00)
    float_to_sysex1Byte(data[2], sysexCount, sysexData);        // (2)    1    20  - oscBLevel (0 - 1.00)
    float_to_sysex2Bytes(data[3], sysexCount, sysexData);        // (3)    2    21  - noiseLevel ( -1.00 - +1.00)
    uint8_to_sysex1Byte(data[4], sysexCount, sysexData);        // (4)    1    23  - unison (0 - 2)
    uint8_to_sysex1Byte(data[5], sysexCount, sysexData);        // (5)    1    24  - oscFX    (0 - 6)
    float_to_sysex5Bytes(data[6], sysexCount, sysexData);        // (6)    5     25  - detune (0 - 1.00000)
                                                                // (7)             - (not available)
    float_to_string(data[8], 4, sysexCount, sysexData);            // (8)    4    30    - midiClkTimeInterval
                                                                // (9)            - (not available)
    float_to_sysex5Bytes(data[10], sysexCount, sysexData);        // (10) 5     34  - keytrackingAmount (0 - 1.0000)
    float_to_sysex5Bytes(data[11], sysexCount, sysexData);        // (11)    5    39  - glideSpeed (0 - 1.00000)
    int8_to_sysex2Bytes(data[12], sysexCount, sysexData);        // (12)    2    44  - oscPitchA (-24 - +24)
    int8_to_sysex2Bytes(data[13], sysexCount, sysexData);        // (13) 2     46  - oscPitchB (-24 - +24)
    uint8_to_sysex1Byte(data[14], sysexCount, sysexData);        // (14) 1     48  - oscWaveformA (0 - 63)
    uint8_to_sysex1Byte(data[15], sysexCount, sysexData);        // (15)    1    49    - oscWaveformB (0 - 63)
                                                                // (16)            - (not available)
    float_to_sysex1Byte(data[17], sysexCount, sysexData);        // (17) 1   50  - pwmAmtA (0 - 0.99)
    float_to_sysex1Byte(data[18], sysexCount, sysexData);        // (18) 1   51  - pwmAmtB (0 - 0.99)
                                                                // (19)            - (not available)
    float_to_sysex2Bytes(data[20], sysexCount, sysexData);        // (20) 2     52  - pwA (-1.00 - +1.00)
    float_to_sysex2Bytes(data[21], sysexCount, sysexData);        // (21) 2     54  - pwB (-1.00 - +1.00)
    float_to_string(data[22], 4, sysexCount, sysexData);        // (22) 4    56  - filterRes (0 - 15.0)
    uint14_to_sysex2Bytes(data[23], sysexCount, sysexData);        // (23) 2     60  - filterFreq (18 - 12000)
    float_to_string(data[24], 4, sysexCount, sysexData);        // (24) 4     62  - filterMix (0 - -99.0)
    float_to_sysex2Bytes(data[25], sysexCount, sysexData);        // (25) 2    66  - filterEnv (-1.00 - +1.00)
    float_to_sysex5Bytes(data[26], sysexCount, sysexData);        // (26) 5    67  - oscLfoAmt (0 - 1.00000)
    float_to_sysex5Bytes(data[27], sysexCount, sysexData);        // (27) 5     73  - oscLfoRate (0 - 40.0000)
    uint8_to_sysex1Byte(data[28], sysexCount, sysexData);        // (28) 1     78  - oscLFOWaveform (0 - 12)
    uint8_to_sysex1Byte(data[29], sysexCount, sysexData);        // (29) 1     79  - oscLfoRetrig (0 - 1)
                                                                // (30)         - (not available)
    uint8_to_sysex1Byte(data[31], sysexCount, sysexData);        // (31) 1     80  - myFilterLFORateValue (1 - 127)
    uint8_to_sysex1Byte(data[32], sysexCount, sysexData);        // (32) 1    81  - filterLfoRetrig (0 - 1)
                                                                // (33)         - (not available)
    float_to_sysex5Bytes(data[34], sysexCount, sysexData);        // (34) 5     82  - filterLfoAmt (0 - 1.00000)
    uint8_to_sysex1Byte(data[35], sysexCount, sysexData);        // (35) 1    87  - filterLFOWaveform (0 - 12)
    uint14_to_sysex2Bytes(data[36], sysexCount, sysexData);        // (36) 2    88  - filterAttack (0 - 11880)
    uint14_to_sysex2Bytes(data[37], sysexCount, sysexData);        // (37) 2     90  - filterDecay (0 - 11880)
    float_to_sysex1Byte(data[38], sysexCount, sysexData);        // (38) 1    92  - filterSustain (0 - 1.00)
    uint14_to_sysex2Bytes(data[39], sysexCount, sysexData);        // (39) 2    94  - filterRelease (0 - 11880)
    uint14_to_sysex2Bytes(data[40], sysexCount, sysexData);        // (40) 2    96  - ampAttack (0 - 11880)
    uint14_to_sysex2Bytes(data[41], sysexCount, sysexData);        // (41) 2     98  - ampDecay (0 - 11880)
    float_to_sysex1Byte(data[42], sysexCount, sysexData);        // (42) 1    100  - ampSustain (0 - 1.00)
    uint14_to_sysex2Bytes(data[43], sysexCount, sysexData);        // (43) 2     101  - ampRelease (0 - 11880)
                                                                // (44)         - (not available)
                                                                // (45)         - (not available)
                                                                // (46)         - (not available)
    uint8_to_sysex1Byte(data[47], sysexCount, sysexData);        // (47) 1    102  - velocitySens (0-4)
    uint8_to_sysex1Byte(data[48], sysexCount, sysexData);        // (48) 1    103  - chordDetune (0 - 127)
    uint8_to_sysex1Byte(data[49], sysexCount, sysexData);        // (49) 1    104  - FxPot1value (0 - 127)
    uint8_to_sysex1Byte(data[50], sysexCount, sysexData);        // (50) 1    105  - FxPot2value (0 - 127)
    uint8_to_sysex1Byte(data[51], sysexCount, sysexData);        // (51) 1    106  - FxPot3value (0 - 127)
    uint8_to_sysex1Byte(data[52], sysexCount, sysexData);        // (52) 1    107  - FxPrgNo (0 - 15)
    uint8_to_sysex1Byte(data[53], sysexCount, sysexData);        // (53) 1    108  - FxMixValue (0 - 127)
    float_to_sysex5Bytes(data[54], sysexCount, sysexData);        // (54) 5    109  - FxMixValue (10000 - 60000)
    uint8_to_sysex1Byte(data[55], sysexCount, sysexData);        // (55) 1    114  - Osc1WaveBank (0 - 15)
    uint8_to_sysex1Byte(data[56], sysexCount, sysexData);        // (56) 1    115  - Osc1WaveBank (0 - 15)
    uint8_to_sysex1Byte(data[57], sysexCount, sysexData);        // (57) 1    116  - myBoost (0 - 1)
    float_to_sysex5Bytes(data[58], sysexCount, sysexData);        // (58) 5    117  - pitchEnvA (-1.00 - +1.00)
    float_to_sysex5Bytes(data[59], sysexCount, sysexData);        // (59) 5    122  - pitchEnvB (-1.00 - +1.00)
    float_to_sysex2Bytes(data[60], sysexCount, sysexData);        // (60) 2    127  - driveLevel (Osc level) (0.00 - 1.25)
    float_to_sysex5Bytes(data[61], sysexCount, sysexData);        // (61) 5    129  - myFilVelocity (0 - 1.00)
    float_to_sysex5Bytes(data[62], sysexCount, sysexData);        // (62) 5    134  - myAmpVelocity (0 - 1.00)
    uint8_to_sysex1Byte(data[63], sysexCount, sysexData);        // (63) 1    139  - myUnisono (0-2)
                                                                // (64)             - (not available)
                                                                // (65)             - (not available)
    uint8_to_sysex1Byte(data[66], sysexCount, sysexData);        // (66) 1    140  - WShaperNo (0-14)
    float_to_sysex5Bytes(data[67], sysexCount, sysexData);        // (67) 5    141  - WShaperDrive (0.10 - 5.00)
    uint14_to_sysex2Bytes(data[68], sysexCount, sysexData);        // (68) 2    146  - LFO1phase (0 - 180.0)
    uint14_to_sysex2Bytes(data[69], sysexCount, sysexData);        // (69) 2    148  - LFO2phase (0 - 180.0)
    for (uint8_t i = 0; i < 16; i++) {
        uint8_to_sysex1Byte(data[70+i], sysexCount, sysexData);    // (70-85)  16    150 - SeqNote1Buf (0 - 127)
    }
    for (uint8_t i = 0; i < 16; i++) {
        uint8_to_sysex1Byte(data[86+i], sysexCount, sysexData);    // (86-101) 16    166 - SeqNoteBufStatus (0 - 1)
    }
    uint14_to_sysex2Bytes(data[102], sysexCount, sysexData);    // (102) 2     182  - SEQbpmValue (101 - 462)
    float_to_sysex5Bytes(data[103], sysexCount, sysexData);        // (103) 5    184  - SEQdivValue (float)
    uint8_to_sysex1Byte(data[104], sysexCount, sysexData);        // (104) 1    189  - SEQstepNumbers (0 - 15)
    float_to_sysex5Bytes(data[105], sysexCount, sysexData);        // (105) 5    190  - SEQGateTime (float)
    uint8_to_sysex1Byte(data[106], sysexCount, sysexData);        // (106) 1    195  - SEQdirection (0 - 3)
    uint8_to_sysex1Byte(data[107], sysexCount, sysexData);        // (107) 1    196  - oscDetuneSync (0 - 1)
    int8_to_sysex2Bytes(data[108], sysexCount, sysexData);        // (108) 2    197  - oscPitchA (-12 - +12)
    float_to_sysex5Bytes(data[109], sysexCount, sysexData);        // (109) 5    199  - oscMasterTune (float)
    float_to_sysex5Bytes(data[110], sysexCount, sysexData);        // (110) 5    204  - OscVCFMOD (float)
    for (uint8_t i = 0; i < 16; i++) {
        uint8_to_sysex1Byte(data[111+i], sysexCount, sysexData);    // (111-126) 16     209 - SeqVeloBuf (0 - 127)
    }
    for (uint8_t i = 0; i < 48; i++) {
        uint8_to_sysex1Byte(data[127+i], sysexCount, sysexData);    // (127-174) 48     225 - SeqNote1Buf (0 - 127)
    }
    
    for (uint8_t i = 0; i < 16; i++) {
        uint8_to_sysex1Byte(data[175+i], sysexCount, sysexData);    // (175-190) 16     273 - SeqNoteCount (0 - 3)
    }
    uint8_to_sysex1Byte(data[191], sysexCount, sysexData);        // (191) 1    289 - SEQmode (0 - 2)
    uint8_to_sysex1Byte(data[192], sysexCount, sysexData);        // (192) 1    290 - SEQMidiClkSwitch (0 - 1)
    uint8_to_sysex1Byte(data[193], sysexCount, sysexData);        // (193) 1    291 - LadderFilterpassbandgain (0 - 127)
    uint8_to_sysex1Byte(data[194], sysexCount, sysexData);        // (194) 1    292 - LadderFilterDrive (1 - 127)
    int8_to_sysex2Bytes(data[195], sysexCount, sysexData);        // (195) 2    293 - envelopeType1 (-8 - +8)
    int8_to_sysex2Bytes(data[196], sysexCount, sysexData);        // (196) 2    295 - envelopeType2 (-8 - +8)
    float_to_sysex5Bytes(data[197], sysexCount, sysexData);        // (197) 5    297 - PitchWheelAmt (float)
    float_to_sysex5Bytes(data[198], sysexCount, sysexData);        // (198) 5    302 - MODWheelAmt (float)
    uint8_to_sysex1Byte(data[199], sysexCount, sysexData);        // (199) 1    307 - myFilter (1 - 2)
    float_to_sysex5Bytes(data[200], sysexCount, sysexData);        // (200) 5    308 - pwmRateA (-10.00 - + 10.00)
    float_to_sysex5Bytes(data[201], sysexCount, sysexData);        // (201) 5    313 - pwmRateB (-10.00 - + 10.00)
    uint14_to_sysex2Bytes(data[202], sysexCount, sysexData);    // (202) 2    318 - LFO1fadeTime (0 - 12000) Fade in
    uint14_to_sysex2Bytes(data[203], sysexCount, sysexData);    // (203) 2    320 - LFO1releaseTime (0 - 12000) Fade out
    float_to_sysex5Bytes(data[204], sysexCount, sysexData);        // (204) 5    322 - filterFM (0.00000 - 1.00000)  Osc1
    float_to_sysex5Bytes(data[205], sysexCount, sysexData);        // (205) 5    327 - filterFM2 (0.00000 - 1.00000)    Osc2
    uint14_to_sysex2Bytes(data[206], sysexCount, sysexData);    // (206) 2    332 - LFO2fadeTime (0 - 12000) Fade in
    uint14_to_sysex2Bytes(data[207], sysexCount, sysexData);    // (207) 2    334 - LFO2releaseTime (0 - 12000) Fade out
    float_to_sysex5Bytes(data[208], sysexCount, sysexData);        // (208) 5    336 - Osc1ModAmt (0.00000 - 1.00000) Fx Mod
    int8_to_sysex2Bytes(data[209], sysexCount, sysexData);        // (209) 2    341 - LFO1enCurve (-8 - +8)
    int8_to_sysex2Bytes(data[210], sysexCount, sysexData);        // (210) 2    343 - LFO2enCurve (-8 - +8)
    uint8_to_sysex1Byte(data[211], sysexCount, sysexData);        // (211) 1    345 - LFO1mode (0 - 1)
    uint8_to_sysex1Byte(data[212], sysexCount, sysexData);        // (212) 1    346 - LFO2mode (0 - 1)
    sysexData[sysexCount++] = 0xF7;                                //         1    347 - End SysEx (0xF7)
    
    // Send SysEx Dump
    if (sysexDest == false) {
        usbMIDI.sendSysEx(sysexCount, sysexData, true);            // send SysEx Dump via USB (default)
        } else {
        MIDI.sendSysEx(sysexCount, sysexData, true);            // send SysEx Dump via Midi
    }
    usb_midi_flush_output();
}
 
Last edited:
I wrote a small Arduino sketch and tested it on a Teensy 4.1 board.
Transmission errors occur with unchanged usb.c file. With the change to usb.c file, there is no transmission error.
The waiting time after transmission of a data block is very short.

Press Button on Teensy Pin A3 (low activ) send 128 SysEx data blocks.
Code:
//*****************************************************************
// Send SysEx data to usbMidi
//*****************************************************************
#include <usb_midi.h>
#include <Bounce2.h>


#define chunkSize 130
#define Blocknumbers 128
#define delayAmt 1
#define buttonPin PIN_A3


const int ledPin = 13;
Bounce  myButton  = Bounce();
boolean startFlag = false;
byte data[chunkSize];


//****************************************************************
// fill buffer
//****************************************************************
void fillBuff() {
  unsigned scratch;


  // set Start and End Byte for SysEx 
  data[0] = 0xF0;
  data[129] = 0xF7;


  // fill with data
  for (uint16_t ct = 1; ct < (chunkSize-1); ct++) {
    scratch = (ct & 0b01111111);
    data[ct] = scratch;
  }
}


//***************************************************************
// transmit SysEx data
//***************************************************************
void sendSysExData () {
   usbMIDI.sendSysEx(chunkSize, data, true);
   digitalWrite(ledPin, HIGH);
}


//***************************************************************
// init setup
//***************************************************************
void setup() {
  usbMIDI.begin();
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  myButton.attach(buttonPin);
  myButton.interval(5); // 5ms for Keys debouncing
  fillBuff();
}




//**************************************************************
// loop
//**************************************************************
void loop() {


  // press Key for transmit SysEx data
  myButton.update();
  if (myButton.fallingEdge()) {
    startFlag = true;
  }


  // send 128 Blocks with 130 Byte
  if (startFlag == true){
    for (uint8_t i = 0; i < Blocknumbers; i++) {
      sendSysExData();
      delay(delayAmt);
    }
    startFlag = false;
     digitalWrite(ledPin, LOW);
  } 
}
 
Been able to reproduce this with the following sketch:-

Code:
#define chunkSize 128
#define delayAmt 100

byte data[chunkSize];

void fillBuff() {
  unsigned scratch;
  for (uint16_t ct = 0; ct <= chunkSize; ct++) {
    scratch = (ct & 0b01111111);
    data[ct] = scratch;
  }
}

void setup() {
  delay(1000);
  fillBuff();
}

void loop() {
  usbMIDI.sendSysEx(chunkSize, data, false, 0);
  delay(delayAmt);
}

Monitored with Midiox.


When the error occurs, a block may be shorter or longer than is supposed to be.


I missing 0xF0 (SysEx start) and 0xF7 (SysEx end) in your fillBuff() function :confused:
 
I do not understand what you mean :confused:

1.Block
F0 ....data... F7

2.Block
F0 ...data... F7

3.Block
F0 ...data... F7

and more..

Hi Rolf,
It's a dump of a DX7 bank with 32 patches inside. So the file actually already has only one F0, then 4102 bytes, then one F7 at the end.
In MIDIOX, I see that the usbmidi lib truncates this 4104 buffer in 266 bytes blocks.
My understanding is until F7 is not encountered, it is considered as "sysex continue" message, like this :
image_2022-05-23_192146406.png

So here you see the first block "System exclusive" detected from the F0. Then "Sysex continue" blocks.
And there are 2 "continue" blocks missing, and the last one too (the one I would like to see the F7).
 
Yes. Your SysEx data block is too large. With MIDI-OX a maximum of only 2048 bytes per SysEx block can be received.
Such a large block of data is not normal. All synthesizers I know send a block of less than 500 bytes for each patch.
My DIY synthesizer does this too. The Yamaha technicians programmed badly :cool:
 
Last edited:
Yes. Your SysEx data block is too large. With MIDI-OX a maximum of only 2048 bytes per SysEx block can be received.
Such a large block of data is not normal. All synthesizers I know send a block of less than 500 bytes for each patch.
My DIY synthesizer does this too. The Yamaha technicians programmed badly :cool:

I wanted to be sure, so I've checked the official Yamaha documentation. See page 37 here :
http://synthmanuals.com/manuals/yamaha/tx7/owners_manual/tx7e1.pdf
image_2022-05-23_211403044.png
 
I don't see any problem with long sysex messages. Like the MIDI Sample Dump Standard you can send samples.

I've found this :
I think your real problem is the 'generic USB to Midi cable' at it uses Microsoft class driver that doesn't work for long sysex.

This is particularly a problem for older synths as their sysex messages were longer than 256 bytes (which is correct under the Midi spec. MS decided they knew better).


I really think it's a USB problem, as I have also difficulties to get the whole bank with MIDIOS or Bome SendSX
 
Commented out line 364 C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy4\usb.c as you suggest and no longer have truncated sysex. Thank you for finding it Rolf.

Is not necessary to include Sysex status bytes (F0 and F7) in
Code:
byte data[chunkSize];
when using
Code:
usbMIDI.sendSysEx(chunkSize, data, false, 0);

@localhero, in your screenshot post# 32. I *think* that "Sysex Continue" is Midiox's way of telling us that its buffer is full so you need to increase it's buffer size.

In Midiox>Options>Configure Buffers, set Size 2048 and Number 127.
 
Following on, 2048 bytes Sysex block size does seem to be the limit but I suspect this limit is imposed by the MS generic usbMidi driver.

Perhaps borrow from Midiox the concept of Sysex Continue and say you want to send 4096 bytes, split it into two blocks of 2048 with F0 at the start and NO F7 at the end of the first block and NO F0 at the start but with F7 at the end for the second block if you get the drift..
 
Annotation:


The normal processor clock of the Teensy4.1 CPU is 600MHz. I reduced the clock setting for the processor in Arduino to 24Mhz. Transmission errors no longer occur.


The transfer rate on the USB interface is probably so low that the input buffer of the MIDI-OX has enough time to process the received data.


Greetings Rolf
 
24Mhz. Ouch! We gotta figure how to sidestep that. Neither Jeannie or my project will like it.
Busy with home renovations, rain is coming, have a couple of ideas.

Thinking of post 39, let's say we have a block of 512 bytes of sysex to send. How about splitting it into two blocks and we send the first with F0 at the start, then pause a little, then send the last block with the F7 at the end. Then see what MidiOx has to say.

Second idea. I have an Iconnectivity MIO-10 interface which has a usbHost port. T4x works here. Windows can see the MIO-10 using it's generic class compliant driver. Better is to use the correct driver for access to the full capabilities of the device. When it rains, will plug that experimental T4.1 sysex code into it's usbHost then see what Midiox has to say.
 
SysEx Dump works now. See short video.

It was a bit more complicated than I thought. First, conversion routines had to be developed for all 212 parameter values in the Jeannie, which convert 8/16-bit integer values and 32-bit floating-point values into a 7-bit format for the SysEx dump. The same had to be converted back again.
This was followed by functions for the transmission and reception of SysEx data. Last but not least, a simple menu for operation had to be developed.

20220523-135020.jpg


The transmission path is selected with Destination (Midi or USB)
DUMP type is either a patch or a bank.
DUMP-Bank sets the bank (A-P) for transmission or reception.
Source is either the patch no. or when receiving the space in the selected patch bank.
SYSEX-DUMP is set to send or receive SysEX data.
The "Load/Save" button starts transmission or reception.
A progress bar shows the progress of the transfer.

Video: https://youtu.be/xRoLsR9HgU0

Greetings Rolf
 
Glad you got it working.

Following up, quoting myself
How about splitting it into two blocks and we send the first with F0 at the start, then pause a little, then send the last block with the F7 at the end. Then see what MidiOx has to say.

Got this idea to work. mentioning as the strategy may be worth looking at to slow large amounts of sysex for older synths.
 
Glad you got it working.

Following up, quoting myself

Got this idea to work. mentioning as the strategy may be worth looking at to slow large amounts of sysex for older synths.

Ok guys, I have good news.
After a lot of trials, and errors in MIDIOX, I have finally managed to get it to work :D
I had previously tried to split the message but it iddn't work because it was sent too quickly.

So this is the code that works with the Teensy 4.1 kept at 600Mz and send the 4104 bytes in one F0 ... F7 message :
usbMIDI.sendSysEx(2048, bank_data, true);
delay(50);
usbMIDI.sendSysEx(2048, bank_data+2048, true);
delay(50);
usbMIDI.sendSysEx(8, bank_data+4096, true);


Houra, now I see the missing blocks, especially the "Sysex end" !!! :) :) :)
image_2022-05-25_075130012.png

It's amazing I have spent so much time to get it. I had no need to change the usb.c (I prefer to keep the original files until Paul finds and fixes that in the Teensy code).
Thanks for your help !
 
I think it's a bug in the teensy 4 USB library. Paul should know what doesn't work there.

I modified the usb.c in the midi library. It works. But it's not a good solution.

File Path: C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy4\usb.c

Change red text in line number 364

{
code...

if ((USB1_USBINTR & USB_USBINTR_SRE) && (status & USB_USBSTS_SRI)) {
//printf("sof %d\n", usb_reboot_timer);
if (usb_reboot_timer) {
if (--usb_reboot_timer == 0) {
usb_stop_sof_interrupts(NUM_INTERFACE);
_reboot_Teensyduino_();
}
}
#ifdef MIDI_INTERFACE
//usb_midi_flush_output();
#endif
#ifdef MULTITOUCH_INTERFACE
usb_touchscreen_update_callback();
#endif
#ifdef FLIGHTSIM_INTERFACE
usb_flightsim_flush_output();
#endif
}
}

Thanks for the thread. I have a very similar problem, but on Teensy 3.6 (using midi Ox). My synth's sysex messages are about 1200 bytes long, and after adjusting max limits in the library files, I can get single message transfers to happen without issues. But when I try to send say 50-100 of these in a row in bulk dump, I get small variable loss/insertion of data bytes on some, but not every attempt. I have tried splitting into blocks of about 200, with and without F0...F7 on each block, with all kinds of delays, and with and without explicit usb_midi_flush_output calls. But the problem persists.

However, I haven't been able to try your approach of removing the flush call in USB.c as described here because I couldn't find matching code in Teensy 3 core usb.c file. Do you know if your method can be applied to T3?
 
Even running absolutely nothing but a sysex dump in a simple sketch I get errors on Teensy 3.6 with midi Ox.

Here is my code, using a button press on pin 2 to start a dump. It should show 50000 bytes transferred, and sometimes it does (especially the first one or two times it's run it seems). But as often as not, there are some bytes missing, and the count is less than 50000. And changing the buffer size, or delay values etc. doesn't seem to solve it. Please let me know if you have any ideas on what else I can try. Thanks!

Code:
#include <Arduino.h>  
#include <MIDI.h> 
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI ); 

void setup() {
  pinMode(2, INPUT_PULLUP);
  MIDI.begin(MIDI_CHANNEL_OMNI); // midi serial init 
}

void loop() {

  if(!digitalRead(2))
  {
   byte patchData[50] = {0};  
   patchData[0]=0xF0;                    
   patchData[49]=0xF7;    
  
   for(int P=0;P<50;P++)    
   {                     
     for(int i=0;i<20;i++)
     {  
        usbMIDI.sendSysEx( 50, patchData, true);        
        delay(10);                  
     }   
     delay(50);
   } 
  }   
}
 
However, I haven't been able to try your approach of removing the flush call in USB.c as described here because I couldn't find matching code in Teensy 3 core usb.c file. Do you know if your method can be applied to T3?
No idea if it’ll work, but line 957 of usb_dev.c might be the one you’re looking for?
 
No idea if it’ll work, but line 957 of usb_dev.c might be the one you’re looking for?

Excellent, thanks very much. And yes, it fixed my problem on T3.6 too, so thank you Rolf also!

Paul, (if you see this) are you able to make use of these findings for T3 & T4 to improve the library, so this change doesn't need to be made manually?
 
After more thorough testing, it seems this is not a good solution because some of my other midi USB functions misbehaved (badly) after making the change. It seems we need the change specifically for sysex transfers, but not other USB midi functions.
 
OK, so usb_midi_flush_output() is in usb_midi.c ... can you modify that so it does nothing IF a sysex is in progress, but works normally at all other times? You may need to add a parameter so you can force it to do something from your application, even during sysex - I think the code in your cross-post does that. There seem to be only 5 files that reference it in teensy3, and 4 files in teensy4.

I'd have a go myself, but I'm in the throes of trying to get multi-file playback and recording from/to SD cards working...
 
Back
Top