Hi.
Dumb question prob. I am writing an app to send multiple program
changes as well as be controlled by midi input. I need help with
getting the Program change number from the dw1 parameter. I don't need
the midi channel number, just the program changer integer. There is a
formula to extract it, I just don't know it. It's in VB6 btw.
Thanks,
Eric
|
|
0
|
|
|
|
Reply
|
eric124 (1)
|
4/29/2004 2:17:48 AM |
|
Eric wrote:
> Dumb question prob. I am writing an app to send multiple program
> changes as well as be controlled by midi input. I need help with
> getting the Program change number from the dw1 parameter. I don't need
> the midi channel number, just the program changer integer. There is a
> formula to extract it, I just don't know it. It's in VB6 btw.
you get a dw1 that contains 3 bytes packed into a LONG value. If you
use a callback function then you get also other messages that do not
contain these short MIDI commands, you must
ignore (or interpretate them differently) the other ones and concentrate
on MIM_DATA messages.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_mim_data.asp
DWORD dw1;
status = dw1 & 0xFF; // this is C0-CF for program changes
param1 = (dw1 >> 8) & 0xFF; // this is the program change number
param2 = (dw1 >> 16) & 0xFF; // not used by program change commands
use shift-right bit operations and bit-and operations to get the 3 bytes
out of the 4-bytes-dw1 value. Alternatively you can also use "MOD 256"
and "DIV 256" to get each byte, which is a bit slower but works too.
It is the same format as used by midiOutShortMsg
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midioutshortmsg.asp
G�nter
|
|
0
|
|
|
|
Reply
|
Guenter
|
4/30/2004 7:23:14 AM
|
|
>
> use shift-right bit operations and bit-and operations to get the 3 bytes
> out of the 4-bytes-dw1 value. Alternatively you can also use "MOD 256"
> and "DIV 256" to get each byte, which is a bit slower but works too.
>
I used the mod 256 -1 method. that works ok
I use 1 program change from a foot pedal to send 15 different saved
program changes to several devices. You can also do it via a mouse as
well.
The only problem is that is highly unstable. It works fine once or
twice, then it'll bomb.
Should I issue a midiinstop before I issue a midioutshortmsg?
There just isn't much documentation on midi input.
Thanks for your help.
|
|
0
|
|
|
|
Reply
|
eric
|
4/30/2004 8:19:37 PM
|
|
Eric wrote:
> I used the mod 256 -1 method. that works ok
what is that? Doesn't seem to me that it works generally, or?
e.g. value 256 mod 256 - 1 = 0 - 1 = -1 (???)
to extract the 2nd lowest byte from a value
correct is (value / 256) mod 256 (e.g. 256/256 mod 256 = 1)
and better is ((value shiftright 8) bitand 255)
result should be a byte value (8 bits), usually off values are 0-63 and
on values are 64-127.
> I use 1 program change from a foot pedal to send 15 different saved
> program changes to several devices. You can also do it via a mouse as
> well.
> The only problem is that is highly unstable. It works fine once or
> twice, then it'll bomb.
>
> Should I issue a midiinstop before I issue a midioutshortmsg?
Not necessary since Windows is not real multitasking, next event can
occur only after you leave the callback function.
Recording and playing works at same time in many applications (even in
my ones :-).
After opening all devices you need to close them all.
Very important:
the section in the midi callback function is time critical!!!!!
So you should not do anything that takes much time and it is not
allowed to call most Windows operations (except postmessage,
midioutshortmsg)
Better: use PostMessage to send own application command back to
your application (e.g. ID_FOOTPEDAL_RECEIVED)
and when you receive this command message then you have enough time
to open all midi devices, send midi messages, close them ...
and all you want to do ...
list of allowed functions in MidiInProc:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midiinproc.asp
> There just isn't much documentation on midi input.
books like "Maximum midi" from Paul Messick explain it well
G�nter
|
|
0
|
|
|
|
Reply
|
Guenter
|
5/3/2004 9:16:18 AM
|
|
>
> Not necessary since Windows is not real multitasking, next event can
> occur only after you leave the callback function.
> Recording and playing works at same time in many applications (even in
> my ones :-).
>
> After opening all devices you need to close them all.
>
> Very important:
>
> the section in the midi callback function is time critical!!!!!
> So you should not do anything that takes much time and it is not
> allowed to call most Windows operations (except postmessage,
> midioutshortmsg)
>
> Better: use PostMessage to send own application command back to
> your application (e.g. ID_FOOTPEDAL_RECEIVED)
> and when you receive this command message then you have enough time
> to open all midi devices, send midi messages, close them ...
> and all you want to do ...
>
> list of allowed functions in MidiInProc:
> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midiinproc.asp
>
Thanks..
The MOD 256 -1 was a temp Yamaha fix (base program change 0)
Thanks for the better method. I'm green at VB & midi.
here's the code
Public Sub MidiIN_Port(ByVal OpenClose As String)
Dim midiError As Long
Dim deviceinid As Integer
deviceinid = MidiDeviceIn.List1.ListIndex
If OpenClose = "open" Then
midiError = midiInOpen(hmidiin, deviceinid, AddressOf
MidiIN_Proc, 0, CALLBACK_FUNCTION)
If midiError <> MMSYSERR_NOERROR Then
ShowMMErr "midiIN_Open", midiError
Else
midiError = midiInStart(hmidiin)
If midiError <> MMSYSERR_NOERROR Then ShowMMErr
"midiIN_Open-Start", midiError
End If
Else
If hmidiin <> 0 Then
midiError = midiInStop(hmidiin)
If midiError <> MMSYSERR_NOERROR Then ShowMMErr
"midiIN_Open-Stop", midiError
midiError = midiInClose(hmidiin)
If midiError <> MMSYSERR_NOERROR Then ShowMMErr
"midiIN_Close", midiError
hmidiin = 0
End If
End If
End Sub
Public Sub MidiIN_Proc(ByVal hmIN As Long, ByVal wMsg As Long, ByVal
dwInstance As Long, ByVal dwParam1 As Long, ByVal dwParam2 As Long)
Dim txt As String
Dim Status As Long, OnOff As Long
Dim NoteNr As Long
Dim Velocity As Long
Dim ProgramNumber As Long
Dim J As Integer
Dim T As Timer
Dim delay As Integer
delay = Main.Text3.Text
Dim deviceid As Integer
deviceid = MidiDevice.List1.ListIndex
On Error Resume Next
Select Case wMsg
Case MM_MIM_OPEN: txt = "open"
Case MM_MIM_CLOSE: txt = "close"
Case MM_MIM_DATA:
Status = (dwParam1 Mod 256)
If Status < &HF0 Then
Select Case (Status \ 16) ' filter 4-bit channel "n"
Case &H8, &H9
NoteNr = ((dwParam1 \ 256) Mod 256)
Velocity = ((dwParam1 \ (256 ^ 2)) Mod 256)
OnOff = IIf(Velocity = 0 Or Status = &H80, 0, 1)
txt = txt & "Status : Note " & IIf(OnOff = 0, "Off",
"On")&vbCrLf
txt = txt & "Note : " & isNote(NoteNr) & vbCrLf
txt = txt & "Velocity :" & Str(Velocity)
Case &HB
txt = "Status : Controller Change"
Case &HC
txt = "Status : Program Change" & Chr(32) &
(dwParam1 \ 256) Mod 256 + 1
ProgramNumber = (dwParam1 \ 256) Mod 256
End Select
End If
Case MM_MIM_LONGDATA: txt = "longdata" & " " & Hex(dwParam1) & "
" & Hex(dwParam2)
Case MM_MIM_ERROR: txt = "error" & " " & Hex(dwParam1) & " " &
Hex(dwParam2)
Case MM_MIM_LONGERROR: txt = "longerror"
Case Else: txt = "???"
End Select
If txt <> "" Then Main.midilabel.Caption = "Midi IN " & vbCrLf &
txt
If (Status \ 16) = &HC Then
MemoryRecall (ProgramNumber)
Call MIDI_Open(deviceid)
Main.ProgressBar1.Visible = True
For J = 0 To 14
If Main.MidiName(J) <> "" Then
Call MIDI_Send((191 + Main.Text1(J).Text),
Main.Text(J).Text,0)
T = Timer
Do: DoEvents: Loop Until Timer > T + delay
Main.ProgressBar1.Value = J
End If
Next J
Main.ProgressBar1.Value = 0
Call MIDI_Close
Main.ProgressBar1.Visible = False
End If
End Sub
Basically, I just want to receive a program change to recall a memory
setting and then transmit that data.
|
|
0
|
|
|
|
Reply
|
eric
|
5/5/2004 2:08:12 AM
|
|
Eric wrote:
> Public Sub MidiIN_Proc(ByVal hmIN As Long, ByVal wMsg As Long, ByVal
> dwInstance As Long, ByVal dwParam1 As Long, ByVal dwParam2 As Long)
> On Error Resume Next
> Select Case wMsg
> Case MM_MIM_DATA:
> txt = "Status : Program Change" & Chr(32) &
> (dwParam1 \ 256) Mod 256 + 1
> ProgramNumber = (dwParam1 \ 256) Mod 256
>
> If txt <> "" Then Main.midilabel.Caption = "Midi IN " & vbCrLf & txt ** NOT ALLOWED IN CRITICAL SECTION **
>
> Main.ProgressBar1.Value = 0 ** NOT ALLOWED IN CRITICAL SECTION **
> Call MIDI_Close
> Main.ProgressBar1.Visible = False ** NOT ALLOWED IN CRITICAL SECTION **
>
> End Sub
It is no wonder that this sub might crash from time to time.
As explained already, this sub MidiIn_Proc is time critical section
because it is called in a system interrupt. You must not call any
Windows functions in this, e.g. assignment of properties to a control is
not allowed here since this internally performs Windows operations that
are not allowed in interrupt. Most VB operations are not allowed in this
sub, may only copy the data and call few of the midi functions. Even the
string operations are not allowed because they probably allocate new memory.
You may only append incoming data to a static internal array buffer (but
don't allocate or free any memory in this sub!) and read the buffer and
display the content somewhere else (use a data snake to insert data at
top and remove data from bottom), but do not display the data inside
this sub, even file operation are not allowed. E.g. use a loop, a timer,
idle where you get some time to read the buffer asynchron, since you
get the time code too you can store the time in buffer too if necessary.
Simplest way: remember the incoming dwValue in an array
and remember positions where to insert and remove next data
define array of fixed length of event info globally and use it as a
cycle buffer.
in this sub only copy the dwparam values into the next unused array element
global
Dim array(1000) as integer
e.g. in sub midiinproc
' here it is time critical
if arrayisnotfull() then
array[nextfree] = dwParam1
Inc nextfree
end if
and outside the routine somewhere in your form you can simply wait that
till data is added to your array (wait in a loop or in a timer), it is
not time critical
open midi
start input
while arrayisnotempty()
dw = array[firstused]
Inc firstused
' here it is not time critical you can use all VB functions
status = dw mod 256
if isprogram(dw) then
display or write to file or whatever
end
end while
at program exit stop input, close midi
A buffer full might occur if midi input is too quick and then this would
loose data, but with today computers you are probably 100 times faster
than input arrives, so buffer full is only a theoretical problem.
To prevent this there exist other solutions that you don't need to
implement (e.g. allocation is allowed outside of the midiinproc, so
client might provide additional buffers if a buffer seems to overrun in
near future).
G�nter
|
|
0
|
|
|
|
Reply
|
Guenter
|
5/5/2004 10:43:45 AM
|
|
>
> It is no wonder that this sub might crash from time to time.
>
> As explained already, this sub MidiIn_Proc is time critical section
> because it is called in a system interrupt. You must not call any
> Windows functions in this, e.g. assignment of properties to a control is
> not allowed here since this internally performs Windows operations that
> are not allowed in interrupt. Most VB operations are not allowed in this
> sub, may only copy the data and call few of the midi functions. Even the
> string operations are not allowed because they probably allocate new memory.
>
> You may only append incoming data to a static internal array buffer (but
> don't allocate or free any memory in this sub!) and read the buffer and
> display the content somewhere else (use a data snake to insert data at
> top and remove data from bottom), but do not display the data inside
> this sub, even file operation are not allowed. E.g. use a loop, a timer,
> idle where you get some time to read the buffer asynchron, since you
> get the time code too you can store the time in buffer too if necessary.
>
> Simplest way: remember the incoming dwValue in an array
> and remember positions where to insert and remove next data
>
> define array of fixed length of event info globally and use it as a
> cycle buffer.
>
> in this sub only copy the dwparam values into the next unused array element
>
> global
> Dim array(1000) as integer
>
> e.g. in sub midiinproc
>
> ' here it is time critical
> if arrayisnotfull() then
> array[nextfree] = dwParam1
> Inc nextfree
> end if
>
>
> and outside the routine somewhere in your form you can simply wait that
> till data is added to your array (wait in a loop or in a timer), it is
> not time critical
>
> open midi
> start input
> while arrayisnotempty()
> dw = array[firstused]
> Inc firstused
>
> ' here it is not time critical you can use all VB functions
> status = dw mod 256
> if isprogram(dw) then
> display or write to file or whatever
> end
>
> end while
>
> at program exit stop input, close midi
>
>
> A buffer full might occur if midi input is too quick and then this would
> loose data, but with today computers you are probably 100 times faster
> than input arrives, so buffer full is only a theoretical problem.
> To prevent this there exist other solutions that you don't need to
> implement (e.g. allocation is allowed outside of the midiinproc, so
> client might provide additional buffers if a buffer seems to overrun in
> near future).
>
> G�nter
Thanks. That clears it up tremendously. I appreciate your help
immensely. The midi input will be either notes or program changes. The
only thing left for me to do is code a midi thru for notes only.
Thanks again. Eric
|
|
0
|
|
|
|
Reply
|
eric
|
5/6/2004 4:52:39 PM
|
|
|
6 Replies
296 Views
(page loaded in 0.083 seconds)
|