Sub AStream_NewData (Buffer() As Byte)
If Midi.attachedOutputs.Length > 0 Then SendMidiMessage(Buffer)
End Sub
Sub SendMidiMessage(Buffer() As Byte) ' <<< RECEIVE AN ARRAY OF BYTES
Dim debug As Boolean = ckDebug.Checked
If debug Then
Dim sb As StringBuilder
sb.Initialize
End If
Dim bf(Buffer.Length) As Int
For i = 0 To bf.Length -1
bf(i) = Buffer(i)
If debug Then
If bf(i) >= 0 Then
sb.Append(bf(i))
Else
sb.Append(bf(i)+256)
End If
If i < bf.Length -1 Then sb.Append(", ")
End If
Next
If debug Then LogMessage("Send Midi Message: " & bf.Length & " Bytes [" & sb.ToString & "]")
Midi.SendMessage(bf) ' <<< SEND AN ARRAY OF INTEGERS
End Sub
Sub SendMidiMessage(Buffer() As Byte) ' <<< RECEIVE AN ARRAY OF BYTES
Dim bf(Buffer.Length) As Int
For i = 0 To bf.Length - 1
bf(i) = Bit.And(Buffer(i), 0xFF)
Next
If ckDebug.Checked Then
Dim sb As StringBuilder
sb.Initialize
For i = 0 to bf.Length - 1
If i <> 0 Then
sb.Append(", ")
end if
sb.Append(bf(i))
Next
LogMessage("Send Midi Message: " & bf.Length & " Bytes [" & sb.ToString & "]")
End If
Midi.SendMessage(bf) ' <<< SEND AN ARRAY OF INTEGERS
End Sub
Sub SendMidiMessage(Buffer() As Byte) ' <<< RECEIVE AN ARRAY OF BYTES
Dim bf(Buffer.Length) As Int
For i = 0 To bf.Length - 1
bf(i) = Bit.And(Buffer(i), 0xFF)
Next
If ckDebug.Checked Then
LogMessage("Send Midi Message: " & bf.Length & " Bytes [" & IntsToString(bf, ", ") & "]")
End If
Midi.SendMessage(bf) ' <<< SEND AN ARRAY OF INTEGERS
End Sub
Sub IntsToString(Ints() As Int, Separator As String) As String
Dim sb As StringBuilder
sb.Initialize
If Ints.Length <> 0 Then
sb.Append(Ints(0))
For i = 1 to Ints.Length - 1
sb.Append(Separator)
sb.Append(Ints(i))
Next
End If
Return sb.ToString
End Sub
The problem here is that I want to avoid declare an Int array and assign it in a loop one by one, for sure this slow down.
Absolutely no, Midi is 31250 bps, but this is a Serial-Midi Bridge, by default I receive at 115200 but should work up 921600 bps, with ESP8266 and ESP32, Raspberry Pico and others, Arduino Uno and similar at 8Mhz, 16Mhz probably can max 115200. But ESP works up 240Mhz and based on usb-serial converter it mount can reach more higher speeds. Here latency in milliseconds is very important I will still searching a way to minimize..... Debug is another story, it only serves to debug, latency do no matter much.Isn't MIDI 4800 bps? I think you'll find that allocating a temporary array and converting element-by-element is thousands of times faster than that = not a bottleneck.
Absolutely no, Midi is 31250 bps, but this is a Serial-Midi Bridge, by default I receive at 115200 but should work up 921600 bps, with ESP8266 and ESP32, Raspberry Pico and others, Arduino Uno and similar at 8Mhz, 16Mhz probably can max 115200. But ESP works up 240Mhz. Here latency in milliseconds is very important I will still searching a way to minimize..... Debug is another story, it only serves to debug, latency do no matter much.
but is Bit.And slow ?
/* Notifies all types of listeners of a new MIDI message from one of the MIDI input devices.
*
* Message: The new inbound MidiMessage.
* TimeStamp: The timestamp.
*/
void NotifyListeners(MidiMessage Message, long TimeStamp) {
byte[] data = Message.getMessage();
for(MidiListener listener : listeners) {
/* -- RawMidiListener -- */
if(listener instanceof RawMidiListener) ((RawMidiListener)listener).rawMidiMessage(data);
/* -- SimpleMidiListener -- */
if(listener instanceof SimpleMidiListener) {
if((int)((byte)data[0] & 0xF0) == ShortMessage.NOTE_ON) {
((SimpleMidiListener)listener).noteOn((int)(data[0] & 0x0F),(int)(data[1] & 0xFF),(int)(data[2] & 0xFF));
} else if((int)((byte)data[0] & 0xF0) == ShortMessage.NOTE_OFF) {
((SimpleMidiListener)listener).noteOff((int)(data[0] & 0x0F),(int)(data[1] & 0xFF),(int)(data[2] & 0xFF));
} else if((int)((byte)data[0] & 0xF0) == ShortMessage.CONTROL_CHANGE) {
((SimpleMidiListener)listener).controlChange((int)(data[0] & 0x0F),(int)(data[1] & 0xFF),(int)(data[2] & 0xFF));
}
}
/* -- StandardMidiListener -- */
if(listener instanceof StandardMidiListener) ((StandardMidiListener)listener).midiMessage(Message, TimeStamp);
/* -- ObjectMidiListener -- */
if(listener instanceof ObjectMidiListener) {
if((int)((byte)data[0] & 0xF0) == ShortMessage.NOTE_ON) {
((ObjectMidiListener)listener).noteOn(new Note((int)(data[0] & 0x0F),(int)(data[1] & 0xFF),(int)(data[2] & 0xFF)));
} else if((int)((byte)data[0] & 0xF0) == ShortMessage.NOTE_OFF) {
((ObjectMidiListener)listener).noteOff(new Note((int)(data[0] & 0x0F),(int)(data[1] & 0xFF),(int)(data[2] & 0xFF)));
} else if((int)((byte)data[0] & 0xF0) == ShortMessage.CONTROL_CHANGE) {
((ObjectMidiListener)listener).controlChange(new ControlChange((int)(data[0] & 0x0F),(int)(data[1] & 0xFF),(int)(data[2] & 0xFF)));
}
}
}
from Processing MidiBus library to B4J completely on java. It and in general javax.media.midi use integer, then cast to bytes when send to real hardware midi devices.
public void sendMessage(byte[] data) {
if ((int)((byte)data[0] & 0xFF) == MetaMessage.META) {
Unless I'm overlooking something, that code does not convert the byte array to an int array. What it does is take an individual byte from the array and convert it to int. Those are two totally different operations. What you are looking for is justIf you know java this is a relevant part, as tou see it threat internally data as integer
Value as int = bit.and(data(0), 0xff)
It even serves because on B4J side all midi data will be threated as integersIt looks like the Processing MidiBus library is already prepared to handle Java's #&&$ing signed Bytes:
Java:public void sendMessage(byte[] data) { if ((int)((byte)data[0] & 0xFF) == MetaMessage.META) {
but if you've gone the clarity route of using Ints to represent Bytes as unsigned numbers, then hey, I don't think I've ever argued in favour of less clarity. ?
So in your opinion there is not a way to cast a byte array to an int array without declare a new int array ?
Yes oliver, I just confused, you are right, this function use interface listeners provided by javax midi class to receive midi input, it receive bytes then cast to int that is exactly the inverse of what I wroteUnless I'm overlooking something, that code does not convert the byte array to an int array. What it does is take an individual byte from the array and convert it to int. Those are two totally different operations. What you are looking for is just
But even that is unnecessary if you just code your NOTE_OFF, NOTE_ON and CONTROL_CHANGE as a byte value (I doubt they are larger than byte values, else the Java code would not work)B4X:Value as int = bit.and(data(0), 0xff)
/**
* Notifies any of the supported methods implemented inside the Parent of a new MIDI message from one of the MIDI input devices.
*
* Message: The new inbound MidiMessage.
* TimeStamp: The timestamp
*/
void NotifyParent(MidiMessage Message, long TimeStamp) {
if (parent == null) return;
byte[] data = Message.getMessage();
switch ( (int)((byte)data[0] & 0xF0) ) {
case ShortMessage.NOTE_ON: //////// NOTE ON ////////
LastNoteOnVelocity = Constrain((int)(data[2] & 0xFF), 0, 127);
if ( parent.subExists(eventName + "_noteon") ){
// BA.Log("esiste la sub noteon");
try {
parent.raiseEventFromDifferentThread(this, null, 0, eventName + "_noteon", false, new Object[] { (int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF) });
//parent.raiseEvent(this, eventName + "_noteon", new Object[] { (int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF) }); //ORIGINALE********************
} catch (Exception e) {
//BA.Log("MidiBus Warning: Disabling NoteOn(Channel As Int, Note As Int, Velocity As Int) because an unkown exception was thrown and caught");
e.printStackTrace();
}
}
if ( parent.subExists(eventName + "_noteon_timestamp_busname") ){
try{
parent.raiseEventFromDifferentThread(this, null, 0, eventName + "_noteon_timestamp_busname", false, new Object[] { (int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF), TimeStamp/1000, bus_name });
//parent.raiseEvent(this, eventName + "_noteon_timestamp_busname", new Object[] { (int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF), TimeStamp/1000, bus_name }); //ORIGINALE********************
} catch (Exception e){
e.printStackTrace();
}
}
if ( parent.subExists(eventName + "_noteon_note") ){
try {
parent.raiseEventFromDifferentThread(this, null, 0, eventName + "_noteon_note", false, new Object[] {new Note((int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF), TimeStamp/1000, bus_name)});
//parent.raiseEvent(this, eventName + "_noteon_note", new Note((int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF), TimeStamp/1000, bus_name) );
} catch (Exception e){
e.printStackTrace();
}
}
break;
case ShortMessage.NOTE_OFF: //////// NOTE OFF ////////
if ( parent.subExists(eventName + "_noteoff") ){
try {
parent.raiseEventFromDifferentThread(this, null, 0, eventName + "_noteoff", false, new Object[] { (int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF) });
//parent.raiseEvent(this, eventName + "_noteoff", new Object[] { (int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF) }); //ORIGINALE********************
} catch(Exception e) {
e.printStackTrace();
}
}
if ( parent.subExists(eventName + "_noteoff_timestamp_busname") ){
try {
parent.raiseEventFromDifferentThread(this, null, 0, eventName + "_noteoff_timestamp_busname", false, new Object[] { (int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF), TimeStamp/1000, bus_name });
//parent.raiseEvent(this, eventName + "_noteoff_timestamp_busname", new Object[] { (int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF), TimeStamp/1000, bus_name }); //ORIGINALE********************
} catch(Exception e) {
//BA.Log("MidiBus Warning: Disabling NoteOff_TimeStamp_BusName(Channel As Int, Note As Int, Velocity As Int, TimeStamp As Long, BusName As String) because an unkown exception was thrown and caught");
e.printStackTrace();
}
}
if ( parent.subExists(eventName + "_noteoff_note") ){
try {
parent.raiseEventFromDifferentThread(this, null, 0, eventName + "_noteoff_note", false, new Object[] {new Note((int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF), TimeStamp/1000, bus_name)});
//parent.raiseEvent(this, eventName + "_noteoff_note", new Note((int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF), TimeStamp/1000, bus_name) );
} catch(Exception e) {
e.printStackTrace();
}
}
break;
case ShortMessage.CONTROL_CHANGE: //////// CONTROL CHANGE ////////
if ( parent.subExists(eventName + "_controlchange") ){
try {
parent.raiseEventFromDifferentThread(this, null, 0, eventName + "_controlchange", false, new Object[] { (int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF)});
//parent.raiseEvent(this, eventName + "_controlchange", new Object[] { (int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF)}); //ORIGINALE********************
} catch(Exception e) {
e.printStackTrace();
}
}
if ( parent.subExists(eventName + "_controlchange_timestamp_busname") ){
try {
parent.raiseEventFromDifferentThread(this, null, 0, eventName + "_controlchange_timestamp_busname", false, new Object[] { (int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF), TimeStamp/1000, bus_name });
//parent.raiseEvent(this, eventName + "_controlchange_timestamp_busname", new Object[] { (int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF), TimeStamp/1000, bus_name }); //ORIGINALE********************
} catch(Exception e) {
e.printStackTrace();
}
}
if ( parent.subExists(eventName + "_controlchange_control") ){
try {
parent.raiseEventFromDifferentThread(this, null, 0, eventName + "_controlchange_control", false, new Object[] {new ControlChange((int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF), TimeStamp/1000, bus_name)});
//parent.raiseEvent(this, eventName + "_controlchange_control", new ControlChange((int)(data[0] & 0x0F), (int)(data[1] & 0xFF), (int)(data[2] & 0xFF), TimeStamp/1000, bus_name));
} catch(Exception e) {
e.printStackTrace();
}
}
break;
default:
break;
}
//////// RAW MIDI ////////
if ( parent.subExists(eventName + "_rawdata") ) { // Always notify raw data input if callback sub exist
try {
int[] dataint = new int[data.length];
for(int i = 0; i < data.length; i++) dataint[i] = (int)(data[i] & 0xFF);
parent.raiseEventFromDifferentThread(this, null, 0, eventName + "_rawdata", false, new Object[] { dataint });
// parent.raiseEvent(this, eventName + "_rawdata", new Object[] { data });
//parent.raiseEvent(this, eventName + "_rawdata", new Object[] { dataint }); //ORIGINALE********************
} catch(Exception e) {
e.printStackTrace();
}
}
if ( parent.subExists(eventName + "_rawdata_timestamp_busname") ) { // Always notify raw data input if callback sub exist
try {
int[] dataint = new int[data.length];
for(int i = 0; i < data.length; i++)dataint[i] = (int)(data[i] & 0xFF);
parent.raiseEventFromDifferentThread(this, null, 0, eventName + "_rawdata_timestamp_busname", false, new Object[] { dataint, TimeStamp/1000, bus_name });
//parent.raiseEvent(this, eventName + "_rawdata_timestamp_busname", new Object[] { dataint, TimeStamp/1000, bus_name }); //ORIGINALE********************
} catch(Exception e) {
e.printStackTrace();
}
}
//////// SHORT MESSAGE //////// We return a ShortMessage class instead of MidiMessage because B4J do not know java MidiMessage and we need to wrap it inside a custom class
if ( parent.subExists(eventName + "_shortmessage") ) { // Always notify short messages if callback sub exist
try {
ShortMessage SM = new ShortMessage(data);
parent.raiseEventFromDifferentThread(this, null, 0, eventName + "_shortmessage", false, new Object[] { SM });
} catch(Exception e) {
e.printStackTrace();
}
}
if ( parent.subExists(eventName + "_shortmessage_timestamp_busname") ) { // Always notify short messages if callback sub exist
try {
ShortMessage SM = new ShortMessage(data);
parent.raiseEventFromDifferentThread(this, null, 0, eventName + "_shortmessage_timestamp_busname", false, new Object[] { SM, TimeStamp/1000, bus_name });
} catch(Exception e) {
e.printStackTrace();
}
}
}
@Events(values = {"NoteOn(Channel As Int, Note As Int, Velocity As Int)",
"NoteOn_TimeStamp_BusName(Channel As Int, Note As Int, Velocity As Int, TimeStamp As Long, BusName As String)",
"NoteOn_Note(Note As Note)",
"NoteOff(Channel As Int, Note As Int, Velocity As Int)",
"NoteOff_TimeStamp_BusName(Channel As Int, Note As Int, Velocity As Int, TimeStamp As Long, BusName As String)",
"NoteOff_Note(Note As Note)",
"ControlChange(Channel As Int, Number As Int, Value As Int)",
"ControlChange_TimeStamp_BusName(Channel As Int, Number As Int, Value As Int, TimeStamp As Long, BusName As String)",
"ControlChange_Control(Change As ControlChange)",
"ShortMessage(Msg As ShortMessage)",
"ShortMessage_TimeStamp_BusName(Msg As ShortMessage, TimeStamp As Long, BusName As String)",
"RawData(Data() As Int)",
"RawData_TimeStamp_BusName(Data() As Int, TimeStamp As Long, BusName As String)"})
Unless I'm overlooking something, that code does not convert the byte array to an int array. What it does is take an individual byte from the array and convert it to int. Those are two totally different operations.
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?