rppicomidi / midi2usbhost

Make a Raspberry Pi Pico a USB Host to bridge modern USB MIDI to old school MIDI IN and MIDI OUT
MIT License
131 stars 22 forks source link

Problems with Midi-device Launchpad X #6

Closed utter77 closed 1 year ago

utter77 commented 2 years ago

Hi. First of all, great product. This was exactly what I was looking for. I got this to work without any issues when I connected my Launchkey 37. The midi packages was sent to my midi monitor just the same as i played them.

But when I connect my Launchpad X (mk3) I behaves weird. When I press a key, my rpipico sends the correct midi info, but when I release it, the rpipico sends the last midi message again. This behavior continues 15 times (15 note on, 15 note off). And then the rpipico bursts out messages. It also falls behind as if the rpipico keeps the last message.

TIMESTAMP IN PORT STATUS DATA1 DATA2 CHAN NOTE EVENT               What i played
 046D70B1   1  --     90    24    15    1  C  2 Note On               C on
 046D7241   1  --     90    24    15    1  C  2 Note On               C off
 046D779C   1  --     90    24    00    1  C  2 Note Off              C# on
 046D7936   1  --     90    24    00    1  C  2 Note Off              C# off
 046D7BC0   1  --     90    25    3D    1  C# 2 Note On               D on
 046D7D31   1  --     90    25    3D    1  C# 2 Note On               D off
 046D7F99   1  --     90    25    00    1  C# 2 Note Off              D# on
 046D811B   1  --     90    25    00    1  C# 2 Note Off              D# off
 046D8448   1  --     90    26    3E    1  D  2 Note On         C on
 046D85A4   1  --     90    26    3E    1  D  2 Note On               C off
 046D879A   1  --     90    26    00    1  D  2 Note Off              C# on
 046D88E7   1  --     90    26    00    1  D  2 Note Off              C# off
 046D8C43   1  --     90    27    4B    1  Eb 2 Note On               D on
 046D8D7A   1  --     90    27    4B    1  Eb 2 Note On               D off
 046D8F4E   1  --     90    27    00    1  Eb 2 Note Off             D# on
 046D909F   1  --     90    27    00    1  Eb 2 Note Off              D# off
 046D9323   1  --     90    24    16    1  C  2 Note On               C on
 046D9447   1  --     90    24    16    1  C  2 Note On               C off
 046D96E1   1  --     90    24    00    1  C  2 Note Off              C# on
 046D980C   1  --     90    24    00    1  C  2 Note Off              C# off
 046D99D4   1  --     90    25    41    1  C# 2 Note On               D on
 046D9ADB   1  --     90    25    41    1  C# 2 Note On               D off
 046D9E2C   1  --     90    25    00    1  C# 2 Note Off              D# on
 046D9F70   1  --     90    25    00    1  C# 2 Note Off              D# off
 046DA4DA   1  --     90    26    43    1  D  2 Note On               C on
 046DA5F4   1  --     90    26    43    1  D  2 Note On               C off
 046DA896   1  --     90    26    00    1  D  2 Note Off              C# on
 046DAA4E   1  --     90    26    00    1  D  2 Note Off              C# off
 046DAE19   1  --     90    27    47    1  Eb 2 Note On               D on
 046DAFBD   1  --     90    27    47    1  Eb 2 Note On               D off
 046DB31F   1  --     90    27    00    1  Eb 2 Note Off              D# on
 046DB320   1  --     90    24    3C    1  C  2 Note On               nothing
 046DB321   1  --     90    24    00    1  C  2 Note Off              --
 046DB322   1  --     90    25    44    1  C# 2 Note On               --
 046DB323   1  --     90    25    00    1  C# 2 Note Off              --
 046DB324   1  --     90    26    42    1  D  2 Note On               --
 046DB325   1  --     90    26    00    1  D  2 Note Off              --
 046DB326   1  --     90    27    3B    1  Eb 2 Note On               --
 046DB327   1  --     90    27    00    1  Eb 2 Note Off              --
 046DB328   1  --     90    24    2E    1  C  2 Note On               --
 046DB329   1  --     90    24    00    1  C  2 Note Off              --
 046DB32A   1  --     90    25    36    1  C# 2 Note On               --
 046DB32B   1  --     90    25    00    1  C# 2 Note Off              --
 046DB32C   1  --     90    26    43    1  D  2 Note On               --
 046DB32D   1  --     90    26    00    1  D  2 Note Off              --
 046DB32E   1  --     90    27    44    1  Eb 2 Note On               --
 046DB5D6   1  --     90    27    00    1  Eb 2 Note Off            D# off

How can I fix this? Is there some buffer size I can change? Is there a way to clear the buffer so it doesn't keep the last message?

/Mattias

utter77 commented 2 years ago

After sniffing the USB I can see that the working device, Launchkey 37 sends midi-packages with size 31 and the Laundpad (not working) sends them with length 34.

rppicomidi commented 2 years ago

@utter77 When you say "sniffing the USB" do you mean you have a trace from a USB Bus Analyzer? If so, would it be possible for you to attach the trace to this issue? I am interested to see what is in the data packets for the device that fails.

utter77 commented 2 years ago

This is the dump from the failing Launchpad X

Packet 7515 from C:\Program Files\USBPcap\sample2.pcap
- 8017
- 7.888522
- 1.12.1
- host
- USBAUDIO
- 35
- USB-MIDI Event Packets
0000   1b 00 e0 07 ae 8e 04 84 ff ff 00 00 00 00 09 00
0010   01 01 00 0c 00 81 03 08 00 00 00 09 90 24 00 19
0020   90 24 00

...

and from the working Launchkey

Packet 38 from C:\Program Files\USBPcap\sample1.pcap
- 11670
- 33.936315
- 1.11.1
- host
- USBAUDIO
- 31
- USB-MIDI Event Packets

0000   1b 00 a0 28 81 8b 04 84 ff ff 00 00 00 00 09 00
0010   01 01 00 0b 00 81 03 04 00 00 00 09 90 3c 00
utter77 commented 2 years ago

And here's descriptions.

Launchpad X (not working)

Frame 3871: 35 bytes on wire (280 bits), 35 bytes captured (280 bits)
    Encapsulation type: USB packets with USBPcap header (152)
    Arrival Time: Nov  7, 2022 15:00:33.670825000 Västeuropa, normaltid
    [Time shift for this packet: 0.000000000 seconds]
    Epoch Time: 1667829633.670825000 seconds
    [Time delta from previous captured frame: 0.001410000 seconds]
    [Time delta from previous displayed frame: 0.001410000 seconds]
    [Time since reference or first frame: 3.780516000 seconds]
    Frame Number: 3871
    Frame Length: 35 bytes (280 bits)
    Capture Length: 35 bytes (280 bits)
    [Frame is marked: False]
    [Frame is ignored: False]
    [Protocols in frame: usb:usbaudio]
USB URB
    [Source: 1.12.1]
    [Destination: host]
    USBPcap pseudoheader length: 27
    IRP ID: 0xffff84048c97d550
    IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
    URB Function: URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER (0x0009)
    IRP information: 0x01, Direction: PDO -> FDO
        0000 000. = Reserved: 0x00
        .... ...1 = Direction: PDO -> FDO (0x1)
    URB bus id: 1
    Device address: 12
    Endpoint: 0x81, Direction: IN
        1... .... = Direction: IN (1)
        .... 0001 = Endpoint number: 1
    URB transfer type: URB_BULK (0x03)
    Packet Data Length: 8
    [bInterfaceClass: Audio (0x01)]
USB Midi Event Packet: Note-on
    0000 .... = Cable Number: 0x0
    .... 1001 = Code Index: Note-on (0x9)
    MIDI Event: 902456
USB Midi Event Packet: Note-on
    0001 .... = Cable Number: 0x1
    .... 1001 = Code Index: Note-on (0x9)
    MIDI Event: 902456

Launchkey37 (Working)

Frame 11670: 31 bytes on wire (248 bits), 31 bytes captured (248 bits)
    Encapsulation type: USB packets with USBPcap header (152)
    Arrival Time: Nov  7, 2022 14:51:22.626331000 Västeuropa, normaltid
    [Time shift for this packet: 0.000000000 seconds]
    Epoch Time: 1667829082.626331000 seconds
    [Time delta from previous captured frame: 0.001003000 seconds]
    [Time delta from previous displayed frame: 0.001003000 seconds]
    [Time since reference or first frame: 33.936315000 seconds]
    Frame Number: 11670
    Frame Length: 31 bytes (248 bits)
    Capture Length: 31 bytes (248 bits)
    [Frame is marked: False]
    [Frame is ignored: False]
    [Protocols in frame: usb:usbaudio]
USB URB
    [Source: 1.11.1]
    [Destination: host]
    USBPcap pseudoheader length: 27
    IRP ID: 0xffff84048b8128a0
    IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
    URB Function: URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER (0x0009)
    IRP information: 0x01, Direction: PDO -> FDO
        0000 000. = Reserved: 0x00
        .... ...1 = Direction: PDO -> FDO (0x1)
    URB bus id: 1
    Device address: 11
    Endpoint: 0x81, Direction: IN
        1... .... = Direction: IN (1)
        .... 0001 = Endpoint number: 1
    URB transfer type: URB_BULK (0x03)
    Packet Data Length: 4
    [Request in: 291]
    [Time from request: 20.777568000 seconds]
    [bInterfaceClass: Audio (0x01)]
USB Midi Event Packet: Note-on
    0000 .... = Cable Number: 0x0
    .... 1001 = Code Index: Note-on (0x9)
    MIDI Event: 903c00
utter77 commented 2 years ago

So I guess the problem is that the Launchpad sends two messages on two cables. Can I filter out this?

utter77 commented 2 years ago

I think I found a temporary solution.. It seems to work if I make C:\linux\pico\pico-sdk\lib\tinyusb\src\class\midi\midi_host.c ignore the first read of 4 bytes data.

  uint32_t nread = tu_fifo_read_n(&p_midi_host->rx_ff, p_midi_host->stream_read.buffer, 4);
  //Added another read to make it work on device with two cable
  nread = tu_fifo_read_n(&p_midi_host->rx_ff, p_midi_host->stream_read.buffer, 4);

I don't know if this is the root to my problem. It would be nice if I don't need to keep track on whitch pico i connect to whitch midi device. Can this be done in your code or is this a bug in tiny_usb?

rppicomidi commented 2 years ago

@utter77 You can filter it out. Look in midi2usbhost.c starting line 146 function tuh_midi_rx_cb(). At line 154, tuh_midi_stream_read() assigns to cable_num the value of the MIDI cable.

uint32_t bytes_read = tuh_midi_stream_read(dev_addr, &cable_num, buffer, sizeof(buffer));

Add after line 154 and before the current line 155 the following code

if (cable_num != 0)    // Only route MIDI from cable 0 to the MIDI UART TX
    return;

Please let me know how that works for you. If it does, I will apply this patch to my repository. Thank you for finding this.

utter77 commented 2 years ago

That was actually the first thing I tried. If I do that, I only get every other message and after a while, it bursts messages.

It’s as if your code reads a buffer correctly but tiny-usb doesn’t clear it the right way.

utter77 commented 2 years ago

The more I think about it, the more I’m certain of that the problem is in tiny-usb. No matter what, tiny-usb midi host will only read 4 bytes of midi data per usb package. And my Launchpad sends 8 bytes.

I will try to other changes in tiny-usb once I get new picos. I popped my last one trying to add external usb port. Btw. Do you know if external port through TP2 and TP3 would work? I.e. no use of otg-cable. I saw some schematics and it doesn’t look like the pico uses the ID-pin from the micro usb port.

rppicomidi commented 2 years ago

I have not tried TP2 and TP3. I see no reason why they would not work.

I would be surprised if the issue were in the main tinyusb code. It is likely the MIDI USB Host class driver code I wrote. I will take a deeper look. Would you please post a copy of the USB Descriptor for the LaunchPad X? The host code I wrote is supposedto figure out that the LaunchPad X is sending messages on different cables should separate them out in separate callbacks by cable number. If you send the descriptor I will attempt to simulate the LaunchPad X behavior here on another Pico board configured as a MIDI device.

rppicomidi commented 2 years ago

I think I have found the issue. It should be in the application, not the host driver. My recollection of how the host driver works was wrong. Every time the driver gets a data transfer, it calls the callback. In the LaunchPad X, the data transfer contains two packets on different cables. The callback in the application is only reading one of them. I believe that is why it falls behind. I think this change to the application callback function should fix it for you. I will attempt to test it here with my simulated MIDI device sometime this week.

void tuh_midi_rx_cb(uint8_t dev_addr, uint32_t num_packets)
{
    if (midi_dev_addr == dev_addr)
    {
        if (num_packets != 0)
        {
            uint8_t cable_num;
            uint8_t buffer[48];
            // continue reading all available MIDI messages until none remain
            while(1) {
                uint32_t bytes_read = tuh_midi_stream_read(dev_addr, &cable_num, buffer, sizeof(buffer));
                if (bytes_read == 0)
                    return;
                // Filter all messages not for cable 0
                if (cable_num == 0) {
                    uint8_t npushed = midi_uart_write_tx_buffer(midi_uart_instance,buffer,bytes_read);
                    if (npushed != bytes_read) {
                        TU_LOG1("Warning: Dropped %d bytes sending to UART MIDI Out\r\n", bytes_read - npushed);
                    }
                }
            }
        }
    }
}
rppicomidi commented 2 years ago

@utter77 I pushed up that change today. I have not tested yet with my simulated Launchpad X.

rppicomidi commented 2 years ago

@utter77 I just tried TP2 & TP3 wired directly to a USB A breakout board; USB works as expected

rppicomidi commented 1 year ago

@utter77 Given no response since my last message last year, I am closing this issue.