Extract Program change number formula

  • Follow


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)

Similiar Articles:













7/22/2012 2:18:54 PM


Reply: