MIDI. How to get midiInUnprepareHeader work correctly ?

Just starting out? Need help? Post your questions and find answers here.
Joris
Addict
Addict
Posts: 890
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

MIDI. How to get midiInUnprepareHeader work correctly ?

Post by Joris »

Hi,

This code is used to recieve sysex-dumps from MIDI-instruments.

Point is that the code in this topic http://www.purebasic.fr/english/viewtop ... 13&t=47391 works fine but only ONCE
Of coarse that is not the intention. I must be able to select/deselect different inputs or at least reselect the same one (if there is only one) after a close input command, and do again a sysex-dumps recieve.
Welll that is not working.

I tried lot's of things and have no clue. To me the problem is in the API-function midiInUnprepareHeader (or midiInAddBuffer).
The source now may look a bit dirty as left all test source inside (to make it less works for others to research the fault, hopefully).

The last setup here is now almost 100% identical to my old G32 (GFA Basic) source code which works very fine, even today on my old XP32 system.

Can anyone check and find out where it goes wrong or would it be a bad PB implementation of midiInUnprepareHeader or midiInAddBuffer ?
I hope not. I really need this, so thanks to all who tries or better find the sollution.

(P.s. I'm a self made 'programmer', maybe you can see that, sorry then...)

Code: Select all

; Incoming MIDI Sysex Handler
Global NewList Hsmf_SX.s()
Global MHI.MIDIHDR
Global hMiP0.i = 0
Global s.s
Global *SysexBuffer = AllocateMemory(1024)
MHI\lpData = *SysexBuffer
MHI\dwBufferLength = 1024

Procedure Midi_In_Err(err_idx.i) 
   Protected ls.s=Space(#MAXERRORLENGTH * 2)
   
   Select err_idx
      Case #MMSYSERR_NOERROR : ProcedureReturn #True  ;MMSYSERR_NOERROR
      Default : midiInGetErrorText_(err_idx, ls, Len(ls))
         MessageRequester("MIDI INPUT ERROR",PeekS(@ls),#MB_ICONEXCLAMATION)
         ProcedureReturn #False
   EndSelect
   
EndProcedure
Procedure.i MIDI_GetInputDevices(hnd.i)
    Protected mic.MIDIINCAPS, Num.i, i.i
   
   Num = midiInGetNumDevs_()
   
   For i = 0 To Num - 1
      If #MMSYSERR_NOERROR=midiInGetDevCaps_(i, @mic, SizeOf(MIDIINCAPS)) 
         AddGadgetItem(hnd, -1, PeekS(@mic\szPname))
      EndIf
   Next i
   
   ProcedureReturn Num
   
EndProcedure
Procedure MIDICallBack0(hMi.i, wMsg.i, dummy.i , Data1.i, Data2.i)

  Protected ls.s
  Select wMsg
    ;Case #MIM_DATA : Debug Data1
    Case #MIM_OPEN  : Debug "MIDI Input Opened"                                                                                                                                                            
    Case #MIM_CLOSE : Debug "MIDI Input Closed"
    Case #MIM_LONGDATA
      
      For i = 0 To MHI\dwBytesRecorded - 1                 ; Read Buffer Sent from LPData
        ls = ls + RSet(Hex(PeekB(MHI\lpData + i),#PB_Byte),2,"0")+ " "
        If (i % 17)=0
          Debug ls
          ls=""
        EndIf
      Next
      Debug ls
      ;**************************************
      Debug "*******"
      Debug MHI\dwBytesRecorded
      midiInAddBuffer_(hMiP0, @MHI, SizeOf(MIDIHDR))    ; Recycle Sysex Buffer
  EndSelect
  
EndProcedure
Procedure oStartsysex(idx.i) ;original setup like used in the link above
   
   midiInOpen_(@hMiP0,idx, @MIDICallBack0(), 0, #CALLBACK_FUNCTION)
   Debug "midiInOpened"
   Debug hMiP0
  ;If hMiP0
    midiInPrepareHeader_(hMiP0, @MHI, SizeOf(MIDIHDR)) 
    midiInAddBuffer_(hMiP0, @MHI, SizeOf(MIDIHDR))
    midiInStart_(hMiP0)
  ;EndIf
EndProcedure
Procedure Startsysex(idx.i)
  ;this setup is almost identical to my old G32 (GFA Basic) source code which works very fine
  midiInOpen_(@hMiP0,idx, @MIDICallBack0(), 0, #CALLBACK_FUNCTION)
  Debug "midiInOpened"  ;should be debugged in the callback too
  Debug hMiP0
  If hMiP0
    If midiInStart_(hMiP0)=#MMSYSERR_NOERROR
      ;     MHI\lpData = *SysexBuffer
      ;     MHI\dwBufferLength = 1024
      midiInPrepareHeader_(hMiP0, @MHI, SizeOf(MIDIHDR)) 
      midiInAddBuffer_(hMiP0, @MHI, SizeOf(MIDIHDR))
      Debug "midiInStart done"
      Debug MemorySize(MHI\lpData)
;     Else
;      Debug "FreeMemory"
;       FreeMemory(MHI\lpData)
;       MHI\lpData = 0
;       MHI\dwBufferLength = 0
    EndIf
  EndIf
  
EndProcedure
Procedure oStopsysex()
  midiInStop_(hMiP0)
  ;midiInUnprepareHeader_(hMiP0, @MHI, SizeOf(MIDIHDR)) 
  If midiInUnprepareHeader_(hMiP0,@MHI,SizeOf(MIDIHDR))=#MMSYSERR_NOERROR
    Debug "midiInUnprepareHeader MMSYSERR_NOERROR"
  EndIf
  midiInClose_(hMiP0)
  hMiP0=0
EndProcedure
Procedure ooStopsysex(hnd.i)
  midiInStop_(hnd)
  Debug "midiInStop"
  Debug hMiP0
  ;midiInUnprepareHeader_(hnd, @MHI, SizeOf(MIDIHDR)) 
  err=midiInUnprepareHeader_(hnd,@MHI,SizeOf(MIDIHDR))
  If err=#MMSYSERR_NOERROR
    Debug "midiInUnprepareHeader MMSYSERR_NOERROR"
  Else
    Midi_In_Err(err)
  EndIf
  midiInClose_(hnd)
EndProcedure
Procedure Stopsysex(hnd.i)
  ;midiInUnprepareHeader_(hnd, @MHI, SizeOf(MIDIHDR)) 
  err=midiInUnprepareHeader_(hnd,@MHI,SizeOf(MIDIHDR))
  If err=#MMSYSERR_NOERROR
    Debug "midiInUnprepareHeader MMSYSERR_NOERROR"
  Else
    Midi_In_Err(err)
  EndIf
   midiInStop_(hnd)
  Debug "midiInStop"
  Debug hMiP0
  midiInReset_(hnd)
  Debug "midiInReset"
  midiInClose_(hnd)   ;should be debugged in the callback too
  
EndProcedure

OpenWindow(0, 10, 10, 300, 200, "MIDI Test")
lvw_M_In = ListViewGadget(#PB_Any, 5, 30, 200, 150)
btn_close = ButtonGadget(#PB_Any, 220, 100, 60, 20, "No Out")
MIDI_GetInputDevices(lvw_M_In)

Repeat
  Event = WaitWindowEvent()
  Select Event
    Case #PB_Event_Gadget
      Select EventGadget()
        Case lvw_M_In :
          Select EventType(): 
            Case #PB_EventType_LeftClick : 
              If hMiP0      
                Stopsysex(hMiP0)
                hMiP0=0
              EndIf
              Startsysex(GetGadgetState(lvw_M_In))
          EndSelect
          Case btn_close : Stopsysex(hMiP0) : hMiP0=0
      EndSelect
  EndSelect
  
Until Event = #PB_Event_CloseWindow
If hMiP0
  Stopsysex(hMiP0)
EndIf
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
infratec
Always Here
Always Here
Posts: 7582
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: MIDI. How to get midiInUnprepareHeader work correctly ?

Post by infratec »

Hi,

just played a bit with your code:

I think you build an endless loop with your

Code: Select all

midiInAddBuffer_(hMiP0, @MHI, SizeOf(MIDIHDR))    ; Recycle Sysex Buffer
inside the callback.

So you are not able to terminate it corectly.
If you comment it out, it works.
Since I never used AddBuffer, I can not tell you more.
But maybe that's a hint for you.

This works without:

Code: Select all

Procedure Startsysex(idx.i)
  
  ;this setup is almost identical to my old G32 (GFA Basic) source code which works very fine
  If midiInOpen_(@hMiP0, idx, @MIDICallBack0(), 0, #CALLBACK_FUNCTION) = #MMSYSERR_NOERROR
    Debug "midiInOpened"  ;should be debugged in the callback too
    If hMiP0
      midiInPrepareHeader_(hMiP0, @MHI, SizeOf(MIDIHDR))
      midiInAddBuffer_(hMiP0, @MHI, SizeOf(MIDIHDR))
      If midiInStart_(hMiP0) = #MMSYSERR_NOERROR
        Debug "midiInStart done"
      EndIf
    EndIf
  Else
    Debug "midiInOpen failed"
  EndIf
 
EndProcedure




Procedure.i Stopsysex(hnd.i)
  
  Protected Result.i
  
  
  If midiInStop_(hnd) = #MMSYSERR_NOERROR
    Debug "midiInStop"
    
    midiInReset_(hnd)
    
    err = midiInUnprepareHeader_(hnd, @MHI, SizeOf(MIDIHDR))
    If err = #MMSYSERR_NOERROR
      Debug "midiInUnprepareHeader"
      err = midiInClose_(hnd)
      If err = #MMSYSERR_NOERROR
        Debug "midiInClose"
        Result = #True
      Else
        Midi_In_Err(err)
      EndIf
    Else
      Midi_In_Err(err)
    EndIf
    
  EndIf
  
  ProcedureReturn Result
  
EndProcedure
Bernd
Joris
Addict
Addict
Posts: 890
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Re: MIDI. How to get midiInUnprepareHeader work correctly ?

Post by Joris »

infratec that hint was usefull.

Nevertheless midiInAddBuffer_(hMiP0, @MHI, SizeOf(MIDIHDR)) in the callback is really necessary, otherwhise only one buffer will be recieved while there can be many.
Only problem is to know if many are just one, but that's impossible. So I now have it solved by just detecting the size of recorded data and that seems to work fine.

Code: Select all

If MHI\dwBytesRecorded
  midiInAddBuffer_(hMiP0, @MHI, SizeOf(MIDIHDR)) 
Endif
I hoped to get use the MHI\dwFlags but they look meaningless, as once set in the callback, they never change (#MHDR_DONE ??? or #MIM_MOREDATA never called ???).
Yet, it seems to be solved with only the MHI\dwBytesRecorded (and I forgot that I used that too in my old G32 code).

Thanks for helping.
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
infratec
Always Here
Always Here
Posts: 7582
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: MIDI. How to get midiInUnprepareHeader work correctly ?

Post by infratec »

I looked at the microsoft page
https://msdn.microsoft.com/en-us/librar ... 85%29.aspx

But I didn't found something to remove the added buffers.
Is this not neccessary?

Bernd
Joris
Addict
Addict
Posts: 890
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Re: MIDI. How to get midiInUnprepareHeader work correctly ?

Post by Joris »

infratec wrote:I looked at the microsoft page
https://msdn.microsoft.com/en-us/librar ... 85%29.aspx

But I didn't found something to remove the added buffers.
Is this not neccessary?

Bernd
You can use, reuse one and the same buffer for which the size and adress have been placed in the MIDIHDR and must be kept there until you examine a midiInUnprepareHeader. So, do one midiInprepareHeader at the opening a midi input and one midiInUnprepareHeader when closing the input
I store each recieved buffer in a list element, works fine, never found problems with that ... :wink:
MSDN is very basic with the info on all this. Better is to look at some in depth websites : http://www.blitter.com/~russtopia/MIDI/ ... owmidi.htm
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
infratec
Always Here
Always Here
Posts: 7582
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: MIDI. How to get midiInUnprepareHeader work correctly ?

Post by infratec »

Khorus
User
User
Posts: 25
Joined: Sat Nov 13, 2010 11:22 pm

Re: MIDI. How to get midiInUnprepareHeader work correctly ?

Post by Khorus »

Hello guys!

I have a friend who's working at Ubisoft/Hercules/Guillemot. He used to write drivers for the many audio interfaces they manufactured back in the day. He sent me his code which handles all of the MIDI messages. It is in C (or C++, I can't recall) but if I remember correctly, you have to prepare many Buffers and "clean" them once you exit your app. If you'd like, I'll post it here when I'm home. Cheers all!

Marc Girard aka Khorus
sq4
User
User
Posts: 98
Joined: Wed Feb 26, 2014 3:16 pm
Contact:

Re: MIDI. How to get midiInUnprepareHeader work correctly ?

Post by sq4 »

Khorus wrote:Hello guys!

I have a friend who's working at Ubisoft/Hercules/Guillemot. He used to write drivers for the many audio interfaces they manufactured back in the day. He sent me his code which handles all of the MIDI messages. It is in C (or C++, I can't recall) but if I remember correctly, you have to prepare many Buffers and "clean" them once you exit your app. If you'd like, I'll post it here when I'm home. Cheers all!

Marc Girard aka Khorus
Please do, because I always prepare- and unprepare per midi message. I use the MidiStream api to send MTC to external devices, and the timing seems ok.
So I am really curious how multiple headers could improve the timing.
Thanks in advance!
Post Reply