Android Example Midi USB device manager

The attached project shows the use of the USB library to communicate with Midi Devices over USB. I realize that this may open a can of worms, as it seems nothing about Midi communication is straightforward.

So if there are issues and questions, perhaps we can learn together on this one.

There are two main classes, MidiUSBManager and MidiInClass, the MidiUSBManager deals with the USB connectivity side and holds the USB objects and methods required for the communication. The MidiInClass is basically just a thread that captures incoming mididata and parses it according to the message received (Sysex is ignored).

Midi thru is implemented in the MidiInClass by sending the received messages directly to the USB OutEndpoint, basically a repeater. There are also three buttons which pass Kick Drum, Snare and Hi-Hat, midi data directly to the attached device.

As my main sound device is a Yamaha MU10, I had to implement an XG mode switch to get Drum sounds to play on channel 10.

I have tried it on a couple of interfaces, both of which seem to work OK.

You will need to be aware that midi for USB has it's own standard for communications, which is basically 4 byte packets. Details of which can be found here: http://www.usb.org/developers/devclass_docs/midi10.pdf . If you need information on Midi data structures good places to look are here: http://www.sonicspot.com/guide/midifiles.html

or here: http://www.somascape.org/midi/tech/mfile.html which is a bit more complete.

If you start the application with a usb Midi device already plugged in you will need to press the Connect button. If you plug the device in while the app is running it will automatically connect. It would be straightforward to do a scan when the app starts to see if a device is available if you want to.

If this brings up any questions, which I'm sure it will, please ask them here and I will try to find the answers.

Minimum API is 12 and the device needs to have an OTG USB port. And you obviously need a USB midi cable or device.

The demo requires: Broadcast Receiver Library V2 from Thomas (xverhelstx), which although not strictly necessary, it is nice to know when a device has been disconnected.

Also required: JavaObject, RandomAccessFile, Thread and USB libraries.

It was developed on V3.5 of B4a, but I don't think it uses anything that would preclude it running on older versions.

Please post here and let me know how it works for you.
 

Attachments

  • USBMidiTest3.zip
    13.7 KB · Views: 760
  • USBMidiTest3-B4a3.2.zip
    13.5 KB · Views: 656
Last edited:

stevel05

Expert
Licensed User
Longtime User
The messages you have there will send program change 0x25 to channel 1 and channel 2, you then need to send the Note on event to both channels to get them to play.

0x09,0x90,NoteNumber,Velocity
0x09,0x91,NoteNumber,Velocity

Midi is a serial protocol, so you can only send one message at a time. It will happen so fast that the events will seem to occur at the same time.
 

3394509365

Active Member
Licensed User
Longtime User
No, I do not have to play the notes, as I need during my live concerts, I just want to be able to change the tools on my three keyboards between a song and 'other, that's all.

To me that with just a button sending the message to the keyboards but in reality are three messages addressed to the three keyboards on different channels.

In vb6 you already do:

Public Sub Tastiera1 () 'copy of XP 50
'xxxxxxxxxxxxxxxxxxxxxxxxxx XP 50 xxxxxxxxxxxxxxxxxxxxxxxxxx
'Attention & H51 MSB MSB = 0 A, B, C, GM & H50 = User 50 would see msb80 manual midi roland XP50 51 would msb81


Select Case MSBK1 (s)

Case "ALL" 'A, B, C, GM & H51
midiOutShortMsg Hmidi, HB0 + & H0 & * & * & H100 + & H51 + H10000 keyboard1can - 1 'roland XP50

Case "USER" 'user & H50
midiOutShortMsg Hmidi, HB0 + & H0 & * & * & H100 + & H50 + H10000 keyboard1can - 1 'roland XP50

end Select




'LSB' H0 = A = B H1 H2 H3 = C = GM:
Select Case BancoK1 (s)

Case "A" 'H0 = A
midiOutShortMsg Hmidi, HB0 & H20 + & H100 + & * & * & H10000 + H0 keyboard1can - 1 'roland XP50 channels ABC GM

Case "B" 'H1 = B
midiOutShortMsg Hmidi, HB0 & H20 + & H100 + & * & * & H10000 + H1 keyboard1can - 1 'roland XP50 channels ABC GM

Case "C" 'H2 = C
midiOutShortMsg Hmidi, HB0 & H20 + & H100 + & * & * & H10000 + H2 keyboard1can - 1 'roland XP50 channels ABC GM

Case "GM" 'H3 = GM
midiOutShortMsg Hmidi, HB0 & H20 + & H100 + & * & * & H10000 + H3 keyboard1can - 1 'roland XP50 channels ABC GM

end Select



'Check tool

midiOutShortMsg Hmidi, & HC0 + (H0 & * & H100) + SuonoK1 (s) * & H100 + keyboard1can - 1

'END xxxxxxxxxxxxxxxx XP 50 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

end sub

as you can see I choose the first byte MSB then LSB H50 or H51 (which are the different benches of the keyboard eg. A, B, C, D and then SuonoK1 is the instrument of first keyboard on the right hand
The messages in all three, and allow me to change the instrument on the keyboard first, then do the same with the other 2.

Maybe a little too complicated?
 

stevel05

Expert
Licensed User
Longtime User
OK, what is your midi setup? Are the keyboards chained, or do you have a midi switch as an interface?

Please use [ code ] [ /code ] tags (without the spaces when posting code, it makes it easier to read, or use the code button (5th icon from left on the message toolbar).
 

3394509365

Active Member
Licensed User
Longtime User
Grazie per la pazienza
io uso una interfaccia midi che va alla prima tastiera poi attraverso il Thru va alle altre con i canali 1 prima tastiera mano dx, seconda tastiera 2 e 3 mano dx e sx , terza tastiera 4 5 mano sx e dx.

Comunque il mio problema come ho detoo per adesso è riuscire a mandare almeno oltre al suono, anche il banco cioè MSB e LSB attraverso un singolo pulsante.
 

stevel05

Expert
Licensed User
Longtime User
Thanks for your patience
I use a midi interface that goes to the first keyboard then go to the other through the Thru channel 1 first keyboard with the right hand, depending on keyboard 2:03 right and left hand, third-hand keyboard 4 5, left and right.

However my problem as I detoo for now is to be able to send at least in addition to the sound, even the bank that is the MSB and LSB through a single button.

OK, so you want to send Bank Select and program change messages.

Bank select is a Controller event, so the format would be:

B4X:
Array As Byte(0x0B,0xBc,0X00,0xbn)
Where:
0x0B : USB header
0xBc : B = ControllerMessage, c = channel number
0x00 : Controller type = BankSelect
bn : BankNo (0 - 127)

Then send the Program Change:

B4X:
Array As Byte(0x0C,0xCc,0xpn,0x00)
Where:

0x0C :USB Header
0XCc : C = ProgramChangeMessage, c = channel number
0xpn : Program Number (0-127)
0x00 : USBpadding requirement as Program change is a 2 byte message

And send both for each synth on separate channels
 
Last edited:

3394509365

Active Member
Licensed User
Longtime User
I'm back here I would like to do the following, if edittext1 is a number up to 9 works then that would put 10 "A" in hex me error and exits the program.

B4X:
Dim val1, VAL2, VAL3, Value4
val1 = Bit.ToHexString (EditText1.Text) '
btnKick.Tag As Byte = Array (0x0C, 0xC0, val1, 0x00)

but it does not work because?

because I would like to insert values through a text box from 0 to 255 or 0 to 127
 

stevel05

Expert
Licensed User
Longtime User
Because you are passing a string, where a Byte is required. Strings that can be converted to numbers will be automatically converted, but a string Value of A cannot be converted and will give the error. If you are entering a value as a number, you can just pass that without converting it to a hex string. Just make sure it's within the required range, for a channel >= 0 and <= 15.

'0x0C' is a hex literal that is automatically converted to an int as needed.
 

3394509365

Active Member
Licensed User
Longtime User
I did so and now I can send the values that I insert the tablet into a text box from 0 to 127 and the sound changes it with me but never changes the counter. I used the same parameters I use in Visual Basic 6


B4X:
VAL3=EditText3.Text'
VAL4=EditText4.Text'

VAL33=EditText33.Text'0x00
VAL44=EditText44.Text'0x00

B4X:
    btnKick.Tag = Array As Byte(0x0B,0xB5,VAL3,VAL4)
B4X:
btnSnare.Tag = Array As Byte(0x0C,0xC5,VAL33,VAL44)


transmit on channel 5

val3 and Val4 and I tried to put values from 0 to 127 but nothing
 

stevel05

Expert
Licensed User
Longtime User
OK, you need to send two messages for each program change if you want to select a bank and a program change.

The first message must be the bank change message, followed by the program change message.

In your post, you have assigned a bank change message to btnKick, and a program change message to btnSnare.

The first three bytes of the bank change message are not changeable, they define the message. Only the last byte defines which bank to select and must be a number between 0 and 127. The last byte of the program change message must always be 0x00 as it is a message that only requires one data byte, the third byte of the program change message defines the program number again between 0 and 127.

You will need to store both messages in the tag, either as a two dimensional array, or as an 8 byte array, then split it to send.

So for example:

B4X:
btnKick.Tag = Array As Byte(0x0B,0xBc,0x00,BankNumber,0x0C,0xCc,ProgramNumber,0x00)

The hex literals in the message above are not changable, they define the messages. Only change the BankNumber and the ProgramNumber paramters, or the channel number (c) in the 0xBc and 0xCc parameters.

Then in your Button_Click sub:

B4X:
Dim Msg() As Byte = B.Tag
Dim Msg1(4),Msg2(4) As Byte

For i = 0 to 3
    Msg1(i) = Msg(i)
    Msg2(i) = Msg(i+4)
Next
MidiUSBMan.Connection.BulkTransfer(MidiUSBMan.OutEndPoint,Msg1,Msg1.Length,0)
MidiUSBMan.Connection.BulkTransfer(MidiUSBMan.OutEndPoint,Msg2,Msg2.Length,0)

If the synths banks and programs are numbered 1-16, then by sending on channel 5, it will actually set channel 6 on the synth.
 

3394509365

Active Member
Licensed User
Longtime User
Ok, finally, thanks to your help, I succeeded in the end I had to send the parameters for how I sent them in VB6.

I started with a USBMidi Monitor program that intercepts messages traveling and I discovered that they had to be 3 and not 2.

As I wrote in the code, you need 1 MSB, LSB then one, and finally one that sends sound.

Of course, the MSB and LSB change from keyboard to keyboard.

One of my wants as for example 50 or 51 MSB and LSB as from 0 to 3 and changes the banch1 A, B, C, GM etc. ....

I hope that it serves to help someone 'else.

See you soon.



B4X:
ButtonSendMessage.Tag = Array As Byte(0x0B,0xB4,0x00,0x51,0x0B,0xB4,0x20,0x02,0x0C,0xC4,0x127,0x00)' in hex Roland xp 50
'                                      fix ,B+Can,fix, MSB,fix, B+can,LSB,0-127,Fix,C+Can,Suon,Fix
'                                      fix ,B+Can,fix, MSB,fix, B+can,LSB,Bank, Fix,C+Can,Suon,Fix

Dim B As Button = Sender
Dim Msg() As Byte = B.Tag
Dim Msg1(4),Msg2(4),Msg3(4) As Byte
Dim i As Int

For i = 0 To 3
    Msg1(i) = Msg(i)
    Msg2(i) = Msg(i+4)
    Msg3(i) = Msg(i+8)
Next
MidiUSBMan.Connection.BulkTransfer(MidiUSBMan.OutEndPoint,Msg1,Msg1.Length,0)
MidiUSBMan.Connection.BulkTransfer(MidiUSBMan.OutEndPoint,Msg2,Msg2.Length,0)
MidiUSBMan.Connection.BulkTransfer(MidiUSBMan.OutEndPoint,Msg3,Msg3.Length,0)
 

stevel05

Expert
Licensed User
Longtime User
Great, you've got it working. The LSB message should not be required unless your synth supports more than 128 banks, but it doesn't cause a problem if you send it, it will just be ignored if not needed.
 

3394509365

Active Member
Licensed User
Longtime User
I still have some problem, of course, the values of the MIDI message that is the 'arrey I would like to send him through the variables introduced by the tablet,
Canb I do drive with the select case from 0 to 15 and there 'is no problem and instead VAL2 VAL3 introduced by the tablet does not like it either in decimal in exadecimale,
Value4 and VAL5 are fine.

I tried this:

B4X:
Dim VAL1, VAL2, VAL3, Value4, Val5 As Byte '


VAL2 = '0 TxtMSB.Text xB5
VAL3 = TxtLSB.Text '


'TRANSFORMATION 1
VAL2 = Bit.ToHexString (VAL2) 'exadecimale
VAL3 = Bit.ToHexString (VAL3) 'exadecimale

'TRANSFORMATION 2
VAL2 = Bit.ParseInt (VAL2, 16) 'decimal
VAL3 = Bit.ParseInt (VAL3, 16) 'decimal

ButtonSendMessage.Tag As Byte = Array (0x0B, canb, 0x00, VAL2, 0x0B, canb, VAL3, Value4, 0x0C, DELETE, Val5, 0x00)

the 'array variables without working was this:

B4X:
'ButtonSendMessage.Tag As Byte = Array (0x0B, 0xB4, 0x00, 0x51, 0x0B, 0xB4, 0x20, 0x02, 0x0C, 0xC4, 0x127, 0x00)' in hex Roland xp 50 'WORKING
 

stevel05

Expert
Licensed User
Longtime User
See post #28.

You don't need to convert the values to a hexstring and back again, you can assign valid integer values to the array, just make sure they are in the valid range for the contoller (0-127), or channel (0-15)

If Val3 is part of the LSB for the bank change message then it should only have a value of 0x10, the variable DELETE should have the value 0xC&channelNo.(0 - 15), VAL2 and VAL3 can have a value in the range 0-127 depending on the bank you want to select, Val5 can have a value in the range 0 - 127.

What are the values of canb and DELETE?
 

3394509365

Active Member
Licensed User
Longtime User
maybe I can not explain, but the values that I send them I know are these:
val2 value 0x051 or 050

val3 0x20

canb = 0xB4

Only I would like to put them in a variable, but when I insert them from the tablet does not accept input from those values that do.

the values it clear that I discovered with midi monitor are:

0x0B, 0xB4, 0x00, 0x51 1 message 'MSB 51 or 50 I would put them in a variable
0x0B, 0xB4, 0x20, 0xxx 2nd message 'LMB 20 fix I would put it in a variable
0x0C, 0xC4, 0xxxx, 0x00 3rd message 'Sound

if I put in 51 ex 81 or vice versa does not accept it.

for example in vb6 put in front & H51

thanks again
 

stevel05

Expert
Licensed User
Longtime User
The messages look ok now in that format. Are you sure the MSB should be 51, and not the LSB, that's an awful lot of banks.
 

3394509365

Active Member
Licensed User
Longtime User
is the message is so:
B4X:
'ButtonSendMessage.Tag = Array As Byte(0x0B,0xB4,0x00,0x51,0x0B,0xB4,0x20,0x02,0x0C,0xC4,0x127,0x00)'
but I would like that:
B4X:
ButtonSendMessage.Tag = Array As Byte(0x0B,CanB,0x00,VAL2,0x0B,CanB,VAL3,VAL4,0x0C,CanC,Val5,0x00)

I would like to build the 'array with fixed values and variable values introduced by the tablet, (VAL2, VAL3 etc ... "
 

stevel05

Expert
Licensed User
Longtime User
The only problem I can see is that VAL3 MUST always = 0x20 and is not variable

Build canB using:

B4X:
canB = Bit.or(0XB0,chan)

and canC using

B4X:
canc = Bit.Or(0xC0,chan)

Where chan >=0 and <=15

In your example, VAL2 = 0x51, VAL4 = 0x02 and VAL5 = 0x7F. NOT(0x127) as it has to be >-0 and <= 127.
 
Top