Issue with MidiSysex messages
Hey
i've been trying to create a VB program that interfaces with my midi keyboard and i've got stuck
i'm trying to send a midi sysex message, but it doesn't work. i've confirmed that the actual message is corrected (via a 3rd party program) but i need it to do it within my program.
any help is GREATLY appreciated, cause i've been working on this for about 2 weeks!
Code:
Sub SendLongMsg()
Dim MyHdr As MIDIHDR
Dim sysex As String
sysex = Chr(&HF0) & Chr(&H41) & Chr(&H10) & Chr(&H0) & Chr(&H0) & Chr(&H27) & Chr(&H12) & Chr(&H1) & Chr(&H0) & Chr(&H0) & Chr(&H0) & Chr(&H0) & Chr(&H7E) & Chr(&HF7)
MyHdr.dwBufferLength = Len(sysex)
MyHdr.dwBytesRecorded = Len(sysex)
MyHdr.lpData = sysex
MyHdr.dwFlags = 0
MsgBox(sysex(0))
'Nur zur Info wieviel Bytes die Sysex hat
midiOutPrepareHeader(hMidiOUT, MyHdr, Len(MyHdr))
midiOutLongMsg(hMidiOUT, MyHdr, Len(MyHdr))
midiOutUnprepareHeader(hMidiOUT, MyHdr, Len(MyHdr))
Exit Sub
i believe the problem is the Chr(&H0) is sending Null rather then 00
Re: Issue with MidiSysex messages
lpData is a pointer to a string, not the string itself so you method will always fail. Try ...
vb.net Code:
Sub SendLongMsg()
Dim MyHdr As MIDIHDR
Dim sysex As String
sysex = Chr(&HF0) & Chr(&H41) & Chr(&H10) & Chr(&H0) & Chr(&H0) & Chr(&H27) & Chr(&H12) & Chr(&H1) & Chr(&H0) & Chr(&H0) & Chr(&H0) & Chr(&H0) & Chr(&H7E) & Chr(&HF7)
MyHdr.dwBufferLength = Len(sysex)
MyHdr.dwBytesRecorded = Len(sysex)
MyHdr.lpData = = Runtime.InteropServices.Marshal.StringToHGlobalAuto(sysex) ' this line should convert to IntPtr
MyHdr.dwFlags = 0
MsgBox(sysex(0))
'Nur zur Info wieviel Bytes die Sysex hat
midiOutPrepareHeader(hMidiOUT, MyHdr, Len(MyHdr))
midiOutLongMsg(hMidiOUT, MyHdr, Len(MyHdr))
midiOutUnprepareHeader(hMidiOUT, MyHdr, Len(MyHdr))
Exit Sub
Re: Issue with MidiSysex messages
Quote:
Originally Posted by
dunfiddlin
lpData is a pointer to a string, not the string itself so you method will always fail. Try ...
vb.net Code:
Sub SendLongMsg()
Dim MyHdr As MIDIHDR
Dim sysex As String
sysex = Chr(&HF0) & Chr(&H41) & Chr(&H10) & Chr(&H0) & Chr(&H0) & Chr(&H27) & Chr(&H12) & Chr(&H1) & Chr(&H0) & Chr(&H0) & Chr(&H0) & Chr(&H0) & Chr(&H7E) & Chr(&HF7)
MyHdr.dwBufferLength = Len(sysex)
MyHdr.dwBytesRecorded = Len(sysex)
MyHdr.lpData = = Runtime.InteropServices.Marshal.StringToHGlobalAuto(sysex) ' this line should convert to IntPtr
MyHdr.dwFlags = 0
MsgBox(sysex(0))
'Nur zur Info wieviel Bytes die Sysex hat
midiOutPrepareHeader(hMidiOUT, MyHdr, Len(MyHdr))
midiOutLongMsg(hMidiOUT, MyHdr, Len(MyHdr))
midiOutUnprepareHeader(hMidiOUT, MyHdr, Len(MyHdr))
Exit Sub
Thanks for your fast reply!
i gave it ago, and my keyboard did do some stuff. but it was not what i was expecting.
i still think there is an issue with converting the HEX to Binary for Midi
ive read in some places that they had issues where vb.net was sending a Null value rather then Hex(00) and also, that wasn't handling the Hex(f0) very well.
any thoughts?
Re: Issue with MidiSysex messages
I've just had a look at the function definitions for the last 3 lines and there is a similar problem in all of them in that you are using a value where there should be a pointer. VB.Net has a real problem in that it doesn't do pointers and we have to fake them. Off the top of my head I'm afraid I don't know the syntax for a pointer to a structure so I'll have to do some research.
In the meantime if you could verify exactly what the message should consist of particularly noting any encoding required that would help with regard to the problem you mention.
Re: Issue with MidiSysex messages
Quote:
Originally Posted by
dunfiddlin
I've just had a look at the function definitions for the last 3 lines and there is a similar problem in all of them in that you are using a value where there should be a pointer. VB.Net has a real problem in that it doesn't do pointers and we have to fake them. Off the top of my head I'm afraid I don't know the syntax for a pointer to a structure so I'll have to do some research.
In the meantime if you could verify exactly what the message should consist of particularly noting any encoding required that would help with regard to the problem you mention.
Thank you.
http://www.roland.com/support/articl...-G8&id=1810745
thats the midi Implementation guide for my keyboard. judging by page 14, it says nothing about special encoding or anything. it just wants the byte values of the Hex Values.
Re: Issue with MidiSysex messages
Quote:
Originally Posted by
dunfiddlin
VB.Net has a real problem in that it doesn't do pointers and we have to fake them.
It's true that VB doesn't have pointers however claiming that that is a problem is a problem. :) VB do have constructs to fully support pointers when it comes to P/Invoke.
Quote:
Originally Posted by
megabytemb
ive read in some places that they had issues where vb.net was sending a Null value rather then Hex(00)
Well Hex(00) is NULL. The problem is really that you're sending a string where the function really wants a byte array. VB.Net uses Unicode strings which means that Chr(&H0) is two bytes which both contains a null character. You should change the declaration of the MIDIHRD struct to accept an IntPtr instead of a string for the lpData member. It should look something like this:
Code:
Structure MIDIHDR
Dim lpData As IntPtr
Dim dwBufferLength As Integer
Dim dwBytesRecorded As Integer
Dim dwUser As Integer
Dim dwFlags As Integer
Dim lpNext As Integer
Dim Reserved As Integer
Dim dwOffset As Integer
Dim dwReserved1 As Integer
Dim dwReserved2 As Integer
Dim dwReserved3 As Integer
Dim dwReserved4 As Integer
End Structure
Instead of using an array for the dwReserved I opted to create 4 integers.
Now if you want to build up your string as you've done, then call this sub to send it.
Code:
Public Sub SendMidiMessageLong(hMidi As Integer, msg As String)
Dim hdr As MIDIHDR
Dim bArr() As Byte = System.Text.Encoding.ASCII.GetBytes(msg)
Dim size As Integer = bArr.Length
hdr.lpData = System.Runtime.InteropServices.Marshal.AllocHGlobal(size)
For i As Integer = 0 To bArr.Length - 1
System.Runtime.InteropServices.Marshal.WriteByte(hdr.lpData, i, bArr(i))
Next
hdr.dwBufferLength = size
hdr.dwBytesRecorded = size
midiOutPrepareHeader(hMidi, hdr, 48)
midiOutLongMsg(hMidi, hdr, 48)
midiOutUnprepareHeader(hMidi, hdr, 48)
System.Runtime.InteropServices.Marshal.FreeHGlobal(hdr.lpData)
End Sub
So basically you would call this method like this:
Code:
Dim sysex As String = Chr(&HF0) & Chr(&H41) & Chr(&H10) & ...
SendMidiMessageLong(hMidiOUT, sysex)
Re: Issue with MidiSysex messages
I like what Joacim has done there. It's like watching Picasso paint: don't know what the hell is going on, but the way it all fits together is fascinating :p
Just as quick note, though. Remember that ASCII encoding is a 7 bit encoding, so any character defined by more than 7 bits will not be encoded properly (it will be encoded to a byte with value 0x3F by default). Your sysex string contains two such characters: Chr(&HF0) and Chr(&HF7).
On my system there are only three encodings that will encode the string to give the bytes as expected, and they are:
Windows-1252
iso-8859-1
iso-8859-15
You might want to change this line from Joacim's second block of code:
Code:
Dim bArr() As Byte = System.Text.Encoding.ASCII.GetBytes(msg)
to something along the lines of:
Code:
Dim enc As System.Text.Encoding = System.Text.Encoding.GetEncoding("iso-8859-1")
Dim bArr() As Byte = enc.GetBytes(msg)
Re: Issue with MidiSysex messages
SUCCESS!!! Thank you so much!!!
for future generations, so they don't have the same trouble i did (most of the examples i found were for windows 3.1!) here is the useful segments of my final code
Code:
Structure MIDIHDR
Dim lpData As IntPtr
Dim dwBufferLength As Integer
Dim dwBytesRecorded As Integer
Dim dwUser As Integer
Dim dwFlags As Integer
Dim lpNext As Integer
Dim Reserved As Integer
Dim dwOffset As Integer
Dim dwReserved1 As Integer
Dim dwReserved2 As Integer
Dim dwReserved3 As Integer
Dim dwReserved4 As Integer
End Structure
Sub SendMidiMessageLong(msg As String)
Dim hdr As MIDIHDR
Dim enc As System.Text.Encoding = System.Text.Encoding.GetEncoding("iso-8859-1")
Dim bArr() As Byte = enc.GetBytes(msg)
Dim size As Integer = bArr.Length
hdr.lpData = System.Runtime.InteropServices.Marshal.AllocHGlobal(size)
For i As Integer = 0 To bArr.Length - 1
System.Runtime.InteropServices.Marshal.WriteByte(hdr.lpData, i, bArr(i))
Next
hdr.dwBufferLength = size
hdr.dwBytesRecorded = size
midiOutPrepareHeader(hMidiOUT, hdr, 48)
midiOutLongMsg(hMidiOUT, hdr, 48)
midiOutUnprepareHeader(hMidiOUT, hdr, 48)
System.Runtime.InteropServices.Marshal.FreeHGlobal(hdr.lpData)
End Sub
Dim sysmsg = Chr(&HF0) & Chr(&H41) & Chr(&H10) & Chr(&H0) & Chr(&H0) & Chr(&H27) & Chr(&H12) & Chr(&H1) & Chr(&H0) & Chr(&H0) & Chr(&H0) & Chr(&H0) & Chr(&H7E) & Chr(&HF7)
SendMidiMessageLong(sysmsg)
Re: Issue with MidiSysex messages
Quote:
Originally Posted by
Inferrd
Remember that ASCII encoding is a 7 bit encoding, so any character defined by more than 7 bits will not be encoded properly
Oh yeah, sorry my bad. I wasn't really paying that much attention to the actual string that was sent to it. However since we're now more interested in using a byte array rather than a string you can bypass the whole creation of the string, with all those Chr() calls and skip the encoding part, and instead simply create the byte array directly.
Code:
Public Sub SendMidiMessageLong(hMidi As Integer, bArr() As Byte)
Dim hdr As MIDIHDR
Dim size As Integer = bArr.Length
hdr.lpData = System.Runtime.InteropServices.Marshal.AllocHGlobal(size)
For i As Integer = 0 To size - 1
System.Runtime.InteropServices.Marshal.WriteByte(hdr.lpData, i, bArr(i))
Next
hdr.dwBufferLength = size
hdr.dwBytesRecorded = size
midiOutPrepareHeader(hMidi, hdr, 48)
midiOutLongMsg(hMidi, hdr, 48)
midiOutUnprepareHeader(hMidi, hdr, 48)
System.Runtime.InteropServices.Marshal.FreeHGlobal(hdr.lpData)
End Sub
Now you can call this method like this:
Code:
Dim sysmsg() As Byte = New Byte() {&HF0, &H41, &H10, &H0, &H0, &H27, &H12, &H1, &H0, &H0, &H0, &H0, &H7E, &HF7}
SendMidiMessageLong(hMidiOUT, sysmsg)