Comments (37)
For
this package (08 F7 80 34 2B F7 81 34 2B F8 82 34 2B
), receive() had to add to the buffer theThe first byte (
0x08
) is not valid, bit 7 needs to be 1 (I guess a typo) (the message from the logs had the bit set to 1)
It is true. First byte is wrong in my example. It is possible that when I copied this I accidentally changed 0x80 to 0x08. First bit always must be 1 in high timeStamp byte
from arduino-ble-midi.
You are right, it is not neccessary.
0xF0s have 1 byte lenght, except SysEx.
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Lines 341 to 342 in c20062e
Is this code needed? Looks like it can be removed. The switch below does not test for
SystemExclusive
from arduino-ble-midi.
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Lines 229 to 231 in c20062e
You recommend not using
RUNNING_ENABLE
- yet the underlying parser does support runningStatus for incoming messages and runs fine with theRUNNING_ENABLE
enabled or disabled (running msvc examples renders the same result). I kinda like the efficiency that runningStatus brings.Based on the current examples in BLEMIDI_Sim.h, i recommend making
RUNNING_ENABLE
permanent. WDYT @RobertoHE ?
I only have tested the parser with default settings of serial parser of main midi class. I haven't have time for check if any other options of setting class would interference with runningStatus mode.
If any setting option not interferences with it, runningStatus enable is prefered because it uses less resorces.
Other option is change #defines by if(Settings::option). This will add some computer cycles to parser but it would be transparent to end user or end application.
Would you check it in paralle?
from arduino-ble-midi.
I have seen the same bug too.
from arduino-ble-midi.
DAW's tend to send a lot of MIDI messages, could you add a Serial.println(byte);
at L108 in BLEMIDI_Transport.h (and enable logging in setup) and send the dump
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Lines 103 to 110 in aa0f6bc
from arduino-ble-midi.
I have seen the same bug too.
do you have the exact same setup @RobertoHE ?
from arduino-ble-midi.
Hi @lathoub
I apologize for the lenght of this message.
I have seen the bug in 2 differents ways: the first one with ServerNimBLE, and the second one with my Client proposal (pull request).
In the server Mode (BleNimble), I used the following setup:
Server (ESP32 ServerNimBLE):
-board = esp32dev
-framework = VSCode/PlatformIO (arduino)
-Code: Basic example with noteOn, noteOff and AffterTouch callback handlers. Loop() only contains Midi.read().
Client:
-DAWCubase
-Win10
-BLE (using loopMidi in the Middle)
-MidiController: Roland AX-EDGE by USB (DAW in the middle) or Casio Pravia PX-560
I generated "Midi Chords" (some notes simultaneously) and some afterTouch automatizations in channel 5 using DAW while playing MidiController generating notes, pitch-bends and aftertouchs messages in channels 1, 2 and 3. The server was in OMNI_CHANNEL mode (receiving 1 ,2 ,3 and 5 channels).
I saw sometimes that same behavour that @aphleps had explained before. I received some notes over 127 pitch or over 127 velocity. I didn't save any logs, sorry. I tried to increment the maxBufferSize, but that did not solve anything (The buffer was never seen overflowed in traces).
In the second setup I used:
Client (ESP - clientNimble):
-board = esp32dev
-framework = VSCode/PlatformIO (arduino)
-Code: Custom code that converts received Midi notes in light patrons using FastLed. Midi.read() is called in a FreeRTOS task, like client example.
Server (AX-EDGE)
-MidiController: AX-EDGE over BLE
This setup is "a little weird" because this client is not in this repo yet, but I hope that you might accept it in the future if it works properly (I am still working on it, but I have seen some lines that I want to change).
The "client version" uses the same midiTransport template and the same Midi decoder than Server Version.
I generated notes, pitch-bends and aftertouchs messages in channels 1, 2 and 3 when I played MidiController (server in this case with BLE notify characteristic) and I could see the NoteOn events using the callback handler. I received in some ocassions weird notesOn events like @aphleps and, at other times, NoteOn callbacks stopped working during severals seconds (between 10s and 90s) but BLE notifications were still received (debug traces were activated and they worked propertly, it still dettected any message generated by MidiController).
I have added some traces in notifyCB(), receive() and read() (and others) functions in Client , Transport and Midi layers and I have seen that BLE notify works fine. NimBLE and notifyCB() still receive incoming data correctly and the data uplinks correctly to the receive() transport layer.
Receive() function adds data to buffer, but I don't know whether it adds correctly F7 (SysEx) type messages.
I think that the problem may appear when SysEx message comes between other midi messages in the same pakage. I used the callback setHandleMessage() and when this error appears MidiMessage callback object is message.type = F0 (system Exclusive) and message.lenght = 128 (max buffer size in my case). After that, a weird noteOn or other type event is reveived. I think that way of extracting data from the package ( while(true){}, lPtr and rPtr) may origine the issue when F7 appear in the middle of the package.
from arduino-ble-midi.
Thanks @RobertoHE
I think that the problem may appear when SysEx message comes between other midi messages in the same pakage. I used the callback setHandleMessage() and when this error appears MidiMessage callback object is message.type = F0 (system Exclusive) and message.lenght = 128 (max buffer size in my case). After that, a weird noteOn or other type event is reveived. I think that way of extracting data from the package ( while(true){}, lPtr and rPtr) may origine the issue when F7 appear in the middle of the package.
Good catch, this might be the cause of the issue. Let me check over the weekend with the specs. (A message dump would be ideal for me to debug)
from arduino-ble-midi.
Hi @lathoub
I have to apologize again for the lenght of the message.
I send you a log of one of the errors. In this case, the error is which cause that parser get crashes and it stops to trigger callbacks (second error of the previous message).
I used the second setup (Client Mode) that it was explained in the previous comment.
In this case, I only have sent Midi Messages playing the AX-EDGE (only noteOn, noteOff and afterTouch event types in channels 1, 2, and 3 simultaneously, or triplicated).
In this case, the crash occurred when I was sending a lot of afterTouch events (syntetizer may sends more than 50 events in a second).
I think that BLEMIDI_Transport.receive() has some problems when it receives a second message that arrives while it is processing other one (for example: DAW sends a note and SysEx or other system data in a sort time or, in this case, a lot of affterTouch messages in sort time). Client mode uses a callback that it is trigged automaticly when a notification occurred, and it may cause that two parser may run simultaneously and they may add data at the same time in the RX buffer. I don't sure completly if the bug occurres because the two parser try to add() simultaneously their data in the buffer and they mix the data accidentally, but it may be a possibility.
After the crash bug, some events continued to arrive and parser sended again a event randomly some times until it started to work fine again. I think that the machine of states of parse() suffers when a bad-created message is added to RX buffer and it tries to process it. Until some "bad midis" pass throught the parser and one of them cleans the machine of states, the parser can't trigger any callback. The extra bad-created messages don't trigger error handler, and it isn't normal and it may be a sympthom.
I have tested a little be other MIDI parser in C++ (https://github.com/TheKikGen/midiXparser) that may do the same that received() in less time. I haven't tested it enoughtly, I will continue to investigate it. But unfortunately, it may present the same inconvenient when it tries to insert data in RX buffer ( add() method).
Alternatively, a flag or semaphore may be added in add() and read() to prevent similtaneous read/write or write/write errors.
I show you the traces that I added for "debugging".
When BLE notification is received (notifiCB()) from AX-EDGE, it prints "-New MSG-" and the incoming data in HEX format.
I set MIDI callbacks as follow:
MIDI.setHandleError([](int8_t fptr)
{
Serial.println("\n\nError in parser:" + String(fptr) + "\n\r\n");
});
MIDI.setHandleMessage([](const midi::Message<128U> &msg) //It is triggered by MIDI.read() when
{ //MIDI.parser() process correctly a event
Serial.println("\n### Parser ###");
Serial.print("Type: "); Serial.print((msg.type),HEX);
Serial.println();
Serial.println("Lenght: " + String(msg.length));
Serial.println("Channel: " + String(msg.channel));
});
LOGS:
(Bug appear when ###Parser### stops)
[...]
-New MSG-
A8 C0 90 3E 3E C1 91 3E 3E C1 92 3E 3E C2 90 40 3E
-New MSG-
A8 C3 91 40 3E C3 92 40 3E
### Parser ###
Type: 90
Lenght: 1
Channel: 1
### Parser ###
Type: 90
Lenght: 1
Channel: 2
### Parser ###
Type: 90
Lenght: 1
Channel: 3
### Parser ###
Type: 90
Lenght: 1
Channel: 1
### Parser ###
Type: 90
Lenght: 1
Channel: 2
### Parser ###
Type: 90
Lenght: 1
Channel: 3
-New MSG-
A9 99 D0 3 99 D1 3
-New MSG-
A9 9A D2 3 9C D0 8 9C D1 8
### Parser ###
Type: D0
Lenght: 1
Channel: 1
### Parser ###
Type: D0
Lenght: 1
-New MSG-
Channel: 2
A9 9D D2 8 9F D0 15 9F D1 15
### Parser ###
Type: D0
Lenght: 1
-New MSG-
A9 A0 D2 15 A2 D0 22 A2 D1 22
Channel: 3
-New MSG-
A9 A3 D2 22 A5 D0 33
### Parser ###
Type: D0
-New MSG-
A9 A5 D1 33 A6 D2 33 A8 D0 42
-New MSG-
A9 A8 D1 42 A9 D2 42 AB D0 57
Lenght: 1
Channel: 1
### Parser ###
Type: D0
Lenght: 1
Channel: 2
### Parser ###
Type: D0
Lenght: 1
Channel: 3
-New MSG-
A9
### Parser ###
Type: D0
Lenght: 1
AB D1 57 AC D2 57 AE D0 74
Channel: 1
-New MSG-
A9 AE D1 74 AF D2 74 B1 D0 7F B1 D1 7F B2 D2 7F
### Parser ###
Type: D0
Lenght: 1
Channel: 2
### Parser ###
Type: D0
Lenght: 1
Channel: 3
### Parser ###
Type: D0
Lenght: 1
Channel: 1
### Parser ###
Type: D0
Lenght: 1
Channel: 2
### Parser ###
Type: D0
Lenght: 1
Channel: 3
### Parser ###
Type: D0
Lenght: 1
Channel: 1
### Parser ###
Type: D0
Lenght: 1
Channel: 2
### Parser ###
Type: D0
Lenght: 1
Channel: 3
### Parser ###
Type: D0
Lenght: 1
Channel: 1
### Parser ###
Type: D0
Lenght: 1
Channel: 2
### Parser ###
Type: D0
Lenght: 1
Channel: 3
### Parser ###
Type: D0
Lenght: 1
Channel: 1
### Parser ###
Type: D0
Lenght: 1
Channel: 2
### Parser ###
Type: D0
Lenght: 1
Channel: 3
### Parser ###
Type: D0
Lenght: 1
Channel: 1
### Parser ###
Type: D0
Lenght: 1
Channel: 2
### Parser ###
Type: D0
Lenght: 1
Channel: 3
### Parser ###
Type: D0
Lenght: 1
Channel: 1
### Parser ###
Type: D0
Lenght: 1
Channel: 2
### Parser ###
Type: D0
Lenght: 1
Channel: 3
-New MSG-
AE AA 80 40 3E AA 81 40 3E AB 82 40 3E
-New MSG-
AE AC 80 3E 3E AC 81 3E 3E AD 82 3E 3E
### Parser ###
Type: 80
Lenght: 1
Channel: 1
### Parser ###
Type: 80
Lenght: 1
Channel: 2
### Parser ###
Type: 80
Lenght: 1
Channel: 3
### Parser ###
Type: 80
Lenght: 1
Channel: 1
### Parser ###
Type: 80
Lenght: 1
Channel: 2
### Parser ###
Type: 80
Lenght: 1
Channel: 3
-New MSG-
AF DF D0 78 DF D1 78 E0 D2 78
-New MSG-
AF E2 D0 67 E2 D1 67 E3 D2 67
### Parser ###
Type: D0
Lenght: 1
Channel: 1
-New MSG-
AF
### Parser ###
Type: D0
Lenght: 1
Channel: 2
E5 D0 59 E5 D1 59 E6 D2 59
### Parser ###
Type: D0
Lenght: 1
-New MSG-
AFChannel: 3
E8 D0 50 E8 D1 50 E9 D2 50
### Parser ###
Type: D0
-New MSG-
AF EB D0 4C EB D1 4C EC D2 4C
Lenght: 1
-New MSG-
AF EE D0 43 EE D1 43 EF D2 43
Channel: 1
-New MSG-
AF F1 D0 40 F1 D1 40 F2 D2 40
### Parser ###
Type: D0
Lenght: 1
Channel: 2
### Parser ###
Type: D0
Lenght: 1
-New MSG-
Channel: 3
AF F4 D0 35 F4 D1 35 F5 D2 35
### Parser ###
Type: D0
Lenght: 1
-New MSG-
AF F7 D0 26 F7 D1 26 F8 D2 26 FA D0 F FA D1 F FB D2 F
-New MSG-
B0 81 D0 3 82 D1 3 82 D2 3
Channel: 1
-New MSG-
B0 83 D0 0 83 D1 0 84 D2 0
### Parser ###
Type: D0
Lenght: 1
Channel: 2
### Parser ###
Type: D0
Lenght: 1
Channel: 3
### Parser ###
Type: D0
Lenght: 1
Channel: 1
### Parser ###
Type: D0
Lenght: 1
-New MSG-
B0Channel: 2
C8 90 3E 28 C9 91 3E 28 C9 92 3E 28
### Parser ###
Type: D0
Lenght: 1
Channel: 3
### Parser ###
Type: D0
Lenght: 1
Channel: 1
### Parser ###
Type: D0
Lenght: 1
Channel: 2
### Parser ###
Type: D0
Lenght: 1
Channel: 3
### Parser ###
Type: D0
Lenght: 1
Channel: 1
### Parser ###
Type: D0
Lenght: 1
Channel: 2
### Parser ###
Type: D0
Lenght: 1
Channel: 3
### Parser ###
Type: D0
Lenght: 1
Channel: 1
-New MSG-
### Parser ###
Type: D0
Lenght: 1
B0Channel: 2
CB 90 40 25 CB 91 40 25 CD 92 40 25
### Parser ###
Type: D0
Lenght: 1
Channel: 3
### Parser ###
Type: D0
Lenght: 1
Channel: 1
### Parser ###
Type: D0
Lenght: 1
Channel: 2
### Parser ###
Type: D0
Lenght: 1
Channel: 3
### Parser ###
Type: D0
Lenght: 1
Channel: 1
-New MSG-
B2 BA D0 3 BA D1 3 BB D2 3
-New MSG-
B2 BD D0 8 BE D1 8 BE D2 8
-New MSG-
B2 C0 D0 19 C0 D1 19 C1 D2 19
-New MSG-
B2 C3 D0 2B C3 D1 2B C4 D2 2B
-New MSG-
B2 C6 D0 3A C6 D1 3A C7 D2 3A
-New MSG-
B2 C9 D0 47 C9 D1 47 CA D2 47
-New MSG-
B2 CC D0 54 CC D1 54 CD D2 54
-New MSG-
B2 CF D0 6E CF D1 6E D0 D2 6E
-New MSG-
B2 D2 D0 7F D2 D1 7F D3 D2 7F
-New MSG-
B4 E4 D0 77 E4 D1 77
-New MSG-
B4 E5 D2 77 E7 D0 31 E7 D1 31
-New MSG-
B4 E8 D2 31 EA D0 C EA D1 C
-New MSG-
B4 EB D2 C ED D0 0 ED D1 0
-New MSG-
B4 EE D2 0
-New MSG-
B5 AE 80 40 39 AF 81 40 39 AF 82 40 39
-New MSG-
B5 BB 80 3E 27
-New MSG-
B5 BC 81 3E 27 BC 82 3E 27
-New MSG-
B6 84 90 3E 23 84 91 3E 23 85 92 3E 23
-New MSG-
B6 8E 90 40 2C 8E 91 40 2C 8F 92 40 2C
### Parser ###
Type: F0
Lenght: 128
Channel: 0
-New MSG-
B6 BE D0 3 BE D1 3 BF D2 3
-New MSG-
B6 C1 D0 14 C1 D1 14 C2 D2 14
-New MSG-
B6 C4 D0 3C
-New MSG-
B6 C6 D1 3C C6 D2 3C C7 D0 77 C7 D1 77
-New MSG-
B6 C8 D2 77 CA D0 7F CA D1 7F CB D2 7F
-New MSG-
B7 B7 D0 74 B7 D1 74 B8 D2 74
-New MSG-
B7 BA D0 3E BA D1 3E BB D2 3E
-New MSG-
B7 BD D0 19 BE D1 19 BE D2 19
-New MSG-
B7 C0 D0 5 C0 D1 5 C1 D2 5
-New MSG-
B7 C3 D0 0 C3 D1 0 C4 D2 0
-New MSG-
B7 FE 80 40 57
-New MSG-
B7 FE 81 40 57 FF 82 40 57
-New MSG-
B8 83 80 3E 61 84 81 3E 61
-New MSG-
B8 84 82 3E 61
-New MSG-
B8 C2 80 43 45 C3 81 43 45
-New MSG-
B8 C4 82 43 45 C4 80 47 36 C5 81 47 36 C5 82 47 36
-New MSG-
B8 C6 80 45 4F C6 81 45 4F C7 82 45 4F
from arduino-ble-midi.
Thanks @RobertoHE , appreciate the long message :-)
I think that BLEMIDI_Transport.receive() has some problems when it receives a second message that arrives while it is processing other one (for example: DAW sends a note and SysEx or other system data in a sort time or, in this case, a lot of affterTouch messages in sort time). Client mode uses a callback that it is trigged automaticly when a notification occurred, and it may cause that two parser may run simultaneously and they may add data at the same time in the RX buffer. I don't sure completly if the bug occurres because the two parser try to add() simultaneously their data in the buffer and they mix the data accidentally, but it may be a possibility.
Does this would mean that ESP32's xQueueReceive
(thread 2) and xQueueSend
(thread 1) are not fenced and need additional semaphores? (and therefor should work on non-threaded hardware, like a Arduino NANO 33 BLE?)
Or, is it because SysEx messages are intertwined in the normal messages (and would occur on all hardware)?
Where in the above stream are things going wrong?
from arduino-ble-midi.
Hi again @lathoub .
The last bug is NOT generated by a SysEx. The data streams only had noteON
, noteOFF
and afterTouch
events. I think that the bug is related to 2 or more multiples writer tasks for the same buffer.
I have analyzed receive()
function and I have some questions, because I am newbie in multi-task RTOS.
In receive()
, parsed (or accepted) midi package is added to queue byte by byte. For example, a noteOn
midi is 3 bytes length and receive()
must call 3 times to add()
function. In other words, it needs to call 3 differents times to xQueueSend()
function.
Add()
calls to xQueueSend()
and copies only 1 byte to Queue
. When xQueueSend()
is running, the queue is locked and protected, and that's fine. But when xQueueSend()
ends, it creates some little gaps between a add()
function and the next one in which Queue
is unprotected, doesn't it?
Example of 2 writer threads:
[ ] -> xQueueSend (). queue is locked and protected.
-- -> time between xQueueSend. queue is unprotected.
Thread NotifyCB A: Start |->> Parser -> Note ON ok -> [A0 add] -- [A1 add] -- [A2 add] ->| ends
^ |
| v
Thread NotifyCB B: Start |->> Parser -> Note ON ok -> [B0 add] -- [B1 add] -- [B2 add] ->| ends
Expected Buffer : A0 A1 A2 B0 B1 B2
Received Buffer: A0 A1 B0 A2 B1 B2
I am a beginner in FreeRTOS, so I am not completely sure whether Queue
works this way or not.
I think that it may be necessary to call taskENTER_CRITICAL()
or vTaskSuspendAll()
(or something similar) to protect Queue
.
Midi Messages have a variable length and Queue
forces to divide messages in bytes to adapt to this variable length. Splitting messages up may mix some packages and it may cause a bug in Midi.read()
, which only can process "well-generated" messages.
One inconvenient of Queue
is that you only can add fixed size objects to queue, and this doesn't fit well with midi packages. Stream Buffers
or Messages Buffers
adapt better to variable size packages but they have the inconvenient that they don't have any implicit protection to lock the buffer for 2 o more writer tasks, and they need taskENTER_CRITICAL()
or vTaskSuspendAll()
again.
Using taskENTER_CRITICAL()
may be dangerous with BLE because it disables temporally the interruptions, and it may cause a bug in BLE stack (I am guessing here, I am not sure).
I will try to test this hypothesis using my setup (client version).
Regards
from arduino-ble-midi.
Thanks @RobertoHE
Add() calls to xQueueSend() and copies only 1 byte to Queue. When xQueueSend() is running, the queue is locked and protected, and that's fine. But when xQueueSend() ends, it creates some little gaps between a add() function and the next one in which Queue is unprotected, doesn't it?
True, very good catch!
I create a new Branch with a Semaphore
around the receive
call (doing the 1 byte at a time adding using xQueueSend
) and xQueueReceive
Admitting, I'm not an ESP32 threading expert, just relying on some old knowledge of thread syncing; please check
from arduino-ble-midi.
Hi @lathoub
I tried to adapt your code to my client class and it still crashed in the same way.
I captured another log. In this case, parser detected a middle timeStamp
byte as SysEx start
wrongly.
I used the same setup as the last test: I only added 3 types of events (noteOn
, noteOff
and afterTouch
). Midi controller coudn't generate a SysEx
in this setup.
In this case, receive()
parser miss-interpreted a timestamp that had a valued of F7
or F0
and it interpreted this value as a SysEx start
or end
. In this case, I think that parser readed a middle-timestamp
wrongly and timestamp separator had a value over 0x80 (128)
.
Example MSG: 08 F7 80 34 2B F7 81 34 2B F8 82 34 2B
Decoder:
08 F7 -> TimeStamp -> Must be removed
80 -> Note Off Ch1
34 -> Note
2B -> Vel
F7 -> TimeStamp -> MUST BE IGNORED -> It is not a SysEx start
81 -> Note Off Ch2
34 -> Note
2B -> Vel
F8 -> TimeStamp -> MUST BE IGNORED
82 -> Note Off Ch3
34 -> Note
2B -> Vel
The receiver()
parser might have interpreted wrongly the timeStamp separator F0
as SysEx start
, and it continued processing like SysEx message
until it received 128 byte (F7 SysEx
not present and buffer was full).
You can see how parser missed when it returned a type: F0
(SysEx
) with a lenght of 128 (max size of SysEx
). And the follow parsed message did the same.
LOGS:
(remember that some messages may be represented divided by another interruption)
-New MSG-
96 F2 D2 3 F4 D0 10 F4 D1 10
-New MSG-
96 F5 D2 10 F7
### Parser ###
Type: D0
Lenght: 0
D0 38 F7 D1 38
Channel: 2
-New MSG-
96 F8 D2 38 FA D0 69 FA D1 69
-New MSG-
96 FB D2 69 FD D0 7F FD
### Parser ###
Type: D0
D1 7F
Lenght: 0
Channel: 3
-New MSG-
96 FE D2 7F
### Parser ###
Type: D0
Lenght: 0
Channel: 1
### Parser ###
Type: D0
Lenght: 0
Channel: 2
### Parser ###
Type: D0
Lenght: 0
Channel: 3
### Parser ###
Type: D0
Lenght: 0
Channel: 1
### Parser ###
Type: D0
Lenght: 0
Channel: 2
### Parser ###
Type: D0
Lenght: 0
Channel: 3
### Parser ###
Type: D0
Lenght: 0
Channel: 1
### Parser ###
Type: D0
Lenght: 0
Channel: 2
### Parser ###
Type: D0
Lenght: 0
Channel: 3
-New MSG-
9E A8 80 28 0
-New MSG-
9E A9 81 28 0 AA 82 28 0
-New MSG-
9F B4 80 29 1 B4 81 29 1 B5 82 29 1
-New MSG-
9F BF D0 64 BF D1 64 C0 D2 64
-New MSG-
9F C2 D0 14 C3 D1 14 C3 D2 14
-New MSG-
9F C5 80 2B A C5 81 2B A C6 82 2B A C6 D0 0 C7 D1 0
-New MSG-
9F C7 D2 0
-New MSG-
A0 91 80 35 30 92 81 35 30 92 82 35 30
-New MSG-
A0 97 80 32 2D 98 81 32 2D
-New MSG-
A0 98 82 32 2D
-New MSG-
A0 9D 80 34 3A 9D 81 34 3A 9E 82 34 3A
-New MSG-
A0 CF 80 3C 0 D0 81 3C 0 D0 82 3C 0 D1 80 37 E
-New MSG-
A0 D1 81 37 E D2 82 37 E
-New MSG-
A0 E9 80 3B 2 EA 81 3B 2 EA 82 3B 2
-New MSG-
A1 82 80 39 1 82 81 39 1 83 82 39 1
-New MSG-
A7 FD 90 47 41 FE 91 47 41 FE 92 47 41 FF 90 45 39
-New MSG-
A7 FF 91 45 39
### Parser ###
Type: F0
Lenght: 128
Channel: 0
-New MSG-
A8 80 92 45 39
-New MSG-
A9 C8 80 47 36 C9 81 47 36 CA 82 47 36
-New MSG-
A9 CC 80 45 2E CC 81 45 2E CD 82 45 2E
-New MSG-
AE E7 90 45 D E7 91 45 D E8 92 45 D
-New MSG-
AF 8C 90 47 19
-New MSG-
AF 8C 91 47 19 8D 92 47 19
-New MSG-
B0 F4 80 47 16 F4 81 47 16 F5 82 47 16
-New MSG-
B1 88 80 45 23 89 81 45 23 89 82 45 23
-New MSG-
BA EA 90 43 1E EA 91 43 1E EB 92 43 1E
-New MSG-
BC D0 80 43 1E D0 81 43 1E D1 82 43 1E
-New MSG-
83 B9 90 41 13
-New MSG-
83 BA 91 41 13 BA 92 41 13
-New MSG-
85 B0 80 41 1A B0 81 41 1A B1 82 41 1A
-New MSG-
8A B4 90 40 18 B4 91 40 18
-New MSG-
8A B5 92 40 18
-New MSG-
8A B8 90 3E 22 B9 91 3E 22 B9 92 3E 22
-New MSG-
8C 97 80 3E 1E 97 81 3E 1E 98 82 3E 1E
### Parser ###
Type: F0
Lenght: 128
Channel: 0
-New MSG-
8C A2 80 40 1C A4 81 40 1C
-New MSG-
8C A4 82 40 1C
-New MSG-
90 AF 90 3E A
-New MSG-
90 AF 91 3E A B0 92 3E A
-New MSG-
92 DD 80 3E 16
-New MSG-
92 DD 81 3E 16 DE 82 3E 16
-New MSG-
9B 93 90 3E 12 94 91 3E 12 94 92 3E 12
-New MSG-
9B AD 90 40 19
-New MSG-
9B AD 91 40 19 AE 92 40 19
-New MSG-
A6 CB 80 40 1F
-New MSG-
A6 CC 81 40 1F CC 82 40 1F
-New MSG-
A6 E1 80 3E 25 E2 81 3E 25 E3 82 3E 25
-New MSG-
AC 80 90 3E 11 81 91 3E 11
-New MSG-
AC 81 92 3E 11
-New MSG-
AE CE 80 3E 23 CF 81 3E 23 CF 82 3E 23
-New MSG-
B1 A1 90 3E F A1 91 3E F A2 92 3E F
-New MSG-
B1 AF 90 40 F AF 91 40 F B0 92 40 F
-New MSG-
B4 9E D0 A
-New MSG-
B4 9E D1 A 9F D2 A A1 D0 30
-New MSG-
B4 A1 D1 30 A2 D2 30
-New MSG-
B4 A5 D0 5E A5 D1 5E A6 D2 5E A7 D0 7B
-New MSG-
B4 A7 D1 7B A8 D2 7B AA D0 7F
-New MSG-
B4 AA D1 7F AB D2 7F
### Parser ###
Type: F0
Lenght: 128
Channel: 0
-New MSG-
B5 C7 D0 73 C7 D1 73 C8 D2 73
-New MSG-
B5 CA D0 33 CA D1 33 CB D2 33
-New MSG-
B5 CD D0 8 CD D1 8 CE D2 8
-New MSG-
B5 D0 D0 0 D0 D1 0 D1 D2 0
-New MSG-
B6 E6 D0 3 E6 D1 3 E7 D2 3
-New MSG-
B6 E9 D0 1A E9 D1 1A EA D2 1A
-New MSG-
B6 EC D0 40 EC D1 40 ED D2 40
-New MSG-
B6 EF D0 60 EF D1 60 F0 D2 60
-New MSG-
B6 F2 D0 7F F2 D1 7F F3 D2 7F
-New MSG-
B7 DC D0 5C DC D1 5C DD D2 5C
-New MSG-
B7 DF D0 25 DF D1 25 E0 D2 25
-New MSG-
B7 E2 D0 6 E2 D1 6 E3 D2 6
-New MSG-
B7 E5 D0 0 E5 D1 0 E6 D2 0
-New MSG-
B8 EF 80 40 22 EF 81 40 22 F0 82 40 22
-New MSG-
B8 FC 80 3E 22 FD 81 3E 22
-New MSG-
B8 FD 82 3E 22
-New MSG-
83 9E 90 3E 25
-New MSG-
83 9E 91 3E 25 9F 92 3E 25
-New MSG-
86 AC 80 3E 11 AC 81 3E 11 AD 82 3E 11
from arduino-ble-midi.
Hi @lathoub
I think that I have found why receive()
parser some times bugs.
I think that it is relate with the line 238
and when a package contain 2 or more events and the middle-timeStamp is a F7
.
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Lines 236 to 239 in aa0f6bc
Let me to put an example:
Example MSG: 08 F7 80 34 2B F7 81 34 2B F8 82 34 2B
(the same example message that last comment)
Decoder:
08 F7
-> TimeStamp -> Must be removed
80
-> Note Off Ch1
34
-> Note
2B
-> Vel
F7
-> TimeStamp -> MUST BE IGNORED -> It is not a SysEx end
81
-> Note Off Ch2
34
-> Note
2B
-> Vel
F8
-> TimeStamp -> MUST BE IGNORED
82
-> Note Off Ch3
34
-> Note
2B
-> Vel
In the receive()
algorithm, 2 first bytes that cointains timeStamp are removed.
leftPointer
is set in the 3rd position of buffer 0x80 (buffer[2]
).
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Lines 223 to 224 in aa0f6bc
while()
(line 236) puts the rightPointer
in 5th position, 0x2B
Unfortunately, the middle-timeStamp is a 0xF7
(it may be possible if a package constens 2 o more MIDI events) and if()
from line 238 moves the rightPointer
one position beyond, to the 6th position.
In that point, the message must be 80, 34, 26 (noteOff event
) but line 238
added one byte more, and the message is transformed to 80, 34, 26, F7.
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Lines 236 to 239 in aa0f6bc
The size-filter interprete the event has 4 byte lenght (else line 251).
Switch()
interpretes that message is a multiple 0x80 event type (noteOff multiple instead a single noteOff)
for()
loop adds in the first iteration 3 bytes (80, 34, 26
) and in the second one adds other 3 bytes (80, F7*, 81*
)
*You can see how a noteOff
can be a pitch or a velocity over 127 wrongly. Or how NoteOn or other handler may be triggered when they shouldn't.
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Lines 240 to 284 in aa0f6bc
After that, leftPointer
is moved to 8th position (0x34). You can see that it is the second byte of the second event in the package, it skiped 0x81 (noteOff ch2 status
).
while (true)
loop starts again at the begining and the value of leftPointer
is evaluated.
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Lines 227 to 232 in aa0f6bc
Now,
leftPointer
in 8th position (0x34) has a value lesser than 0x80, and it triggers the return
prematurely. The 3rd event of the package is skiped completely.
For this package (08 F7 80 34 2B F7 81 34 2B F8 82 34 2B
), receive()
had to add to the buffer the follow bytes:
(80 34 2B / 81 34 2B / 82 34 2B
).
Instead, It added:
(80 34 2B / 80 F7 81
)
When MIDI.read()
will try to interprete this messenge... it will do some strange.
https://github.com/TheKikGen/midiXparser -> This midi parser works byte byte and I haven't see the same bug for the same input if you removed the first 2 bytes. It may help you how to the parser must discriminate correctly a SysEx and don't add a timeStamp F7 to buffer.
By the way, the semaphore protection may still necessary.
from arduino-ble-midi.
Great digging @RobertoHE
I pulled parts of the code in MSVC and was able to get something fairly stable, just be removing line 238
So remove:
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Line 238 in aa0f6bc
Somehow there was never full support for SysEx across multiple packets, i'll look into that right now.
from arduino-ble-midi.
By the way, the semaphore protection may still necessary.
Reading up on xQueueSend
and xQueueReceive
(and how they are harvested), i'm hopefull that we can do without (Branch deleted)
from arduino-ble-midi.
For
this package (08 F7 80 34 2B F7 81 34 2B F8 82 34 2B
), receive() had to add to the buffer the
The first byte (0x08
) is not valid, bit 7 needs to be 1 (I guess a typo) (the message from the logs had the bit set to 1)
from arduino-ble-midi.
Great digging @RobertoHE
I pulled parts of the code in MSVC and was able to get something fairly stable, just be removing line 238So remove:
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Line 238 in aa0f6bc
Somehow there was never full support for SysEx across multiple packets, i'll look into that right now.
I have commented this line and I couldn't replicate the bug. This is good.
I don't know how this can affect to a SysEx message splitted in any package.
If you need some help I would investigate or try code something.
Please, review my pull request of Client #30. Don't accept it now, it needs 2 corrections (uxQueueSpacesAvailable() is useless) but I will appreciate your feedback so much.
from arduino-ble-midi.
If you need some help I would investigate or try code something.
Yes, will let you know :-)
Please, review my pull request of Client #30. Don't accept it now, it needs 2 corrections
Wilco
from arduino-ble-midi.
I fixed the initial bug and initial support for (MultiPart) SysEx
https://github.com/lathoub/Arduino-BLE-MIDI/tree/MultiPartSysEx
Can you test?
I used these test sets:
byte sysEx1Packet[] = { 0xB0, 0xF4, 0xF0, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xF4, 0xF7 };
receive(sysEx1Packet, sizeof(sysEx1Packet));
std::cout << std::endl << "SysEx part 1" << std::endl;
byte sysExPart1[] = { 0xB0, 0xF4, 0xF0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
receive(sysExPart1, sizeof(sysExPart1));
std::cout << std::endl << "SysEx part 2" << std::endl;
byte sysExPart2[] = { 0xB0, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xF4, 0xF7 };
receive(sysExPart2, sizeof(sysExPart2));
std::cout << "ble Packet with 1 MIDI messages" << std::endl;
byte blePacketWithOneMIDIMessage[] = { 0xA8, 0xC0, 0x90, 0x3E, 0x3E };
receive(blePacketWithOneMIDIMessage, sizeof(blePacketWithOneMIDIMessage));
std::cout << std::endl << "ble Packet with 2 MIDI messages" << std::endl;
byte blePacketWithTwoMIDIMessage[] = { 0xA8, 0xC0, 0x90, 0x3E, 0x3E, 0xC1, 0x91, 0x3E, 0x3E };
receive(blePacketWithTwoMIDIMessage, sizeof(blePacketWithTwoMIDIMessage));
std::cout << std::endl << "2 MIDI messages with running status" << std::endl;
byte twoMIDIMessageWithRunningStatus[] = { 0xA9, 0xAE, 0xD1, 0x74, 0xAF, 0xD2, 0x74, 0xB1 };
receive(twoMIDIMessageWithRunningStatus, sizeof(twoMIDIMessageWithRunningStatus));
std::cout << std::endl << "2 MIDI messages with running status" << std::endl;
byte multipleMIDIMessagesMixedType[] = { 0x00 };
receive(multipleMIDIMessagesMixedType, sizeof(multipleMIDIMessagesMixedType));
from arduino-ble-midi.
Hi @lathoub
Can you test?
I will try to test it this evening. I may add some 0xF0 to 0xFF events to complete the test, if I have time.
I fixed the initial bug and initial support for (MultiPart) SysEx
https://github.com/lathoub/Arduino-BLE-MIDI/tree/MultiPartSysEx
I have some doubts:
lPtr
starts to evaluate the buffer (timeStampHigh
) at the second byte (buffer[1]
) instead at the beginning (buffer[0]
)
line 233 must be after 237 line and line 239 must be after line 241.
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Lines 224 to 242 in 802f7a3
rPtr
increases twice. Line 345 is unnecessary. In this part of the code, it evaluates the timeStampLow
as event divider, which has only 1 byte lenght, not 2. If it increases twice, I will evaluate the next midiStatus
instead timeStampLow divider
.
Lines 353-356 may be redundant only if rPrt
increase once. It may be considered like redundant protection, it is not bad.
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Lines 340 to 356 in 802f7a3
Lines 321 to 332 are duplicated inside and afer switch()
, the code does the same twice. rPtr
avances twice, the first one inside of switch()
and the second one after switch()
. It may evaluate the next midiStatus
instead timeStamp
, if it increase twice.
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Lines 316 to 334 in 802f7a3
What do you opine about these corrections?
from arduino-ble-midi.
@RobertoHE very well spotted Sir! (I was trying to prematurely anticipating the removing of the timestamp statements)
Correction on its way
from arduino-ble-midi.
Hi again
Are you sure that these lines are necessary?
Lines 321 to 332 are duplicated inside and afer
switch()
, the code does the same twice.rPtr
avances twice, the first one inside ofswitch()
and the second one afterswitch()
. It may evaluate the nextmidiStatus
insteadtimeStamp
, if it increase twice.
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Lines 316 to 334 in 802f7a3
I don't know if after a splitted SysEx message can appear other event in the same package. In that case, it moves rPrt
twice wrongly beyond midiStatus
of the next event.
I let me put an example (I don't know if it possible):
Package 1: 80 F7 80 34 2B F7 81 34 2B F8 **F0** 00 11
Package 2: 91 A2 22 33 44 55 **F7** A3 90 34 90 A4 80 34 00
PCK1:
80 F7 -> TimeStamp High and Low
80 -> NoteOff Ch 1
34 -> pitch
2B -> Velocity
F7 -> TimeStamp Low (divider)
81 -> NoteOff ch2
34 -> pitch
2B -> Velocity
F8 -> TimeStamp Low (divider)
F0 -> SysEx Start
11 22 -> SysEx Data
PCK2:
91 A2 -> TimeStamp High and Low
33 44 55 -> SysEx Data
F7 -> SysEx End
A3 -> TimeStamp Low (divider)
90 -> noteOn Ch 0
34 -> pitch
90 -> velocity
A4 ->TimeStamp Low (divider)
80 -> noteOff Ch 0
34 -> pitch
90 -> velocity
PCK1 set the flag sysExContinuation
because it conains F0 but not F7.
PCK2 is processed correctly until F7 as SysEx. switch()
go to case MIDI_NAMESPACE::MidiType::SystemExclusive:
and for()
add the rest of message to buffer. Until now, everything is correct. rPtr points to F7 (buffer[6])
Inside case MIDI_NAMESPACE::MidiType::SystemExclusive:
, rPtr
is increased (buffer[7]
= A3 TimeStampLow) by lines 319-321 and parser checks that package is completely scanned. In the example, that is false
, and parse continue.
In lines 323-327, it captures the timeStampLow
(buffer[7]
= A3) and after it increases rPtr
to (buffer[8]
-> midiStatus
90 noteOn
).
In line 329, rPtr
is moved again to buffer[9]
(why?) (pitch
of noteOn
34). In this point, it have skipped the midiStatus
. After that, switch()
gets exit and parser continue at line 336.
Lines 337 to 346 are the same that lines 319 to 327 and parser does twice the same for a SysEx.
At the line 337, rPtr
is increased again and now it points to buffer[10]
(vel
of noteOn
). It is not the end of the message, and the parser continue to line 341.
At the line 341, parser capture buffer[10]
(vel
of noteOn
) like timeStampLow and move the point to buffer[11]
(timeStampLow
).
After that. while()
loop ends and the parser starts again with lPtr
in buffer[11]
(timeStampLow
A4) and it miss-interpretes it like a midiStatus. .... I don't continue explaining, but you can suppose what happen after that....
In resume, it miss-interpreted everything after F7 SysEx end (NoteOn and NoteOff events).
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Lines 314 to 352 in 687676d
If you repeat the sequence with the 2 packages and you remove the lines 319-329, you will can see that message will be processed correctly.
Would you check it, please?
from arduino-ble-midi.
No sure if PCK1 and PCK2 are valid according to the spec - i need to further check.
Here is what the spec says for (multipacket) SysEx:
In case of a spanning SysEx over PCK1 and PCK2, PCK2 should start with a TimestampHigh and no low (so only 1 byte). In the PCK2 example, it contains 2 bytes.
from arduino-ble-midi.
I fixed an additional bug (test for SysExEnd) and added MSVC parser test code
from arduino-ble-midi.
Further cleanup and testing (in MSVC, not in the Arduino) and cleared all of the test data - getting closer :-)
(Including fixing/cleaning some of the remarks made above)
from arduino-ble-midi.
Please, check #32.
from arduino-ble-midi.
Changes accepted - thank you @RobertoHE
from arduino-ble-midi.
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Lines 229 to 231 in c20062e
You recommend not using RUNNING_ENABLE
- yet the underlying parser does support runningStatus for incoming messages and runs fine with the RUNNING_ENABLE
enabled or disabled (running msvc examples renders the same result). I kinda like the efficiency that runningStatus brings.
Based on the current examples in BLEMIDI_Sim.h, i recommend making RUNNING_ENABLE
permanent. WDYT @RobertoHE ?
Arduino-BLE-MIDI/test/msvc/BLEMIDI_Sim.h
Lines 106 to 110 in c20062e
from arduino-ble-midi.
Arduino-BLE-MIDI/src/BLEMIDI_Transport.h
Lines 341 to 342 in c20062e
Is this code needed? Looks like it can be removed. The switch below does not test for SystemExclusive
from arduino-ble-midi.
Parser is going OK on an ESP32 Devkit v1 using the default ESP32 BLE stack - I tried all kinds on MIDI messages and all come through - good stuff. I'm good to commit to master!
FYI: I also tried the NimBLE stack and also ran OK, but I noticed that when Disconnecting, the connect button on the Apple MIDI setup does not come back and the device disappears.
from arduino-ble-midi.
FYI: I also tried the NimBLE stack and it also ran OK, but I noticed that when Disconnecting, the connect button on the Apple MIDI setup does not come back and the device disappears.
I have never used Apple MIDI. I don't know anything about it. What is the problem? What is the setup?
from arduino-ble-midi.
I have never used Apple MIDI. I don't know anything about it. What is the problem? What is the setup?
oho - you see, i can't do 2 things at the same time: work on AppleMIDI and BLE-MIDI :-).
What i was trying to say: the ESP32 connects fine to the Apple BLE stack, but after disconnect, the ESP32 does not re-appear and i can no longer connect. Does it behave correctly with you?
from arduino-ble-midi.
Hi again @lathoub
I have seen that in this repo there is a common open issue: the server doesn't advertise (not only for apple midi).
When I was coding the client class, I used as a template the nimble server of this repo and the client example from nimble repo. I noticed that nimble example allowed multi-client connection and it advertised continuously inside loop. Your nimble server only advertise in setup and it doesn't do anything after a disconnection (the server doesn't advertise itself after a disconnection). I am not sure that nimble has a method which advertices continously.
Client mode doesn't use advertiments, it scans the advertised devices and it connects to them, but you can use that mechanism as an example.
Client mode has an "auto-reconnect mechanism" inside read (available) function that is managed with onConnect and onDisconnect callbacks and a global variable. For a server, a sofisticate mechanism for advertisement is not needed, but it may need to advertise periodically when it accepts connections from clients (after disconnection, for example). You must decide whether the server accepts one or more clients, because it determines the strategy of performing advertisements. If the server only supports one client (which I think is preferable), you can use a similar mechanism, such as client class, changing scan functions for advertising.
I can't code anything until September.
from arduino-ble-midi.
What i was trying to say: the ESP32 connects fine to the Apple BLE stack, but after disconnect, the ESP32 does not re-appear and i can no longer connect. Does it behave correctly with you?
Hi @lathoub . Check this function:
https://h2zero.github.io/esp-nimble-cpp/class_nim_b_l_e_server.html#a6bfd923ecd0ea06d5564343ab7209122
from arduino-ble-midi.
Hi @lathoub . Check this function:
https://h2zero.github.io/esp-nimble-cpp/class_nim_b_l_e_server.html#a6bfd923ecd0ea06d5564343ab7209122
Fixed!
from arduino-ble-midi.
All code moved to the master branch. Thank @RobertoHE
(I activated the RUNNING_ENABLE flag, as the underlying parser uses running status)
from arduino-ble-midi.
Related Issues (20)
- Password when pairing. HOT 3
- esp32 BLE-Midi and BluetoothSerial in the same project HOT 2
- decuple from midi transport? HOT 1
- BLE MIDI Client, get servers list HOT 5
- BLE MIDI Client, ESP32-S3, keeps sending serial when not connected HOT 5
- Midi steps audible when sending a stream of CC messages for volume control HOT 8
- BLE-MIDI client: Changes in NimBLE version 1.4.1 HOT 3
- Problem: BLEMIDI_CREATE_INSTANCE with custom names HOT 4
- Compatibility with Arduino GIGA (Arduino-mbed)
- Wrong channel number on polyphony?
- Renaming it Self and loosing connection if either Serial.begin() or Serial1.begin() is used
- Renaming it self and loosing connection if Serial1 or Serial is used.
- Active Sensing HOT 2
- BLE Client Problem with Adafruit TinyUSB HOT 2
- Error compiling on Nano 33 BLE HOT 1
- Trying to get bluetooth midi for Arduino Nano 33 BLE sense rev2 HOT 2
- Issue with Ableton and midi "feedback" HOT 6
- Can't compile anymore on arduino 2.3 - conversion from 'String' to non-scalar type 'std::string' HOT 3
- Bluetooth pairing for Arduino nano 33 BLE // auto connect with macOS HOT 5
- Data is “swallowed”...?
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from arduino-ble-midi.