|
code
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
I need a Class 101 on threading in VB6around the screen and received a lengthy reply from Olaf (Schmidt), for which I was very grateful, but much of it went over my head. Since I had lots of other work to do anyway on the body of the app in question, I forgot about the "execution freezing" potential of a typical VB6 app, but now I am interested again! Let's take a simple example. Place an MCI control (MMControl1) on a form (you don't have to actually do this, since I am sure most VB programmers can work through the scenario on their head) and get it to play any MIDI file. Add the following to the Form_Click event: MMControl1.Notify = False MMControl1.Shareable = False MMControl1.DeviceType = "Sequencer" MMControl1.FileName = "C:\MIDI Files\Chopin\chp_waltz op18.mid" MMControl1.Wait = True MMControl1.Command = "Open" MMControl1.Command = "Play" Now move the form about, load other apps, do whatever comes to mind to place a burden on the CPU. Whatever you try, the MIDI output continues uninterrupted. Fantastic! Now try setting up a loop of midiOutShortMsg messages being pumped out of the midi out port. The music plays fine..... UNTIL you start moving the form around as above. Then the output stops dead. Any other CPU activity slows the output and makes it sound erratic. What I want is MIDI output that is never interrupted, as with the MCI control example, but where I have complete control over the individual midiOutShortMsg messages. That is, I can start or end at any particular message and I can pre-empt any message and do some additional task just before it is sent, like switching on an LED or making a virtual on-screen piano key visibly depress. So, my basic question is: How does the MCI control do its thing without interruption? My second question is how can I achieve the same uninterrupted output WITHOUT the MCI control? Please use simple language and try posting an example or two! I learn best by example. Many thanks. MM On Fri, 12 Jun 2009 08:37:33 +0100, MM <kylix***@yahoo.co.uk> wrote:
>Please use simple language and try posting an example or two! I learn What are your midiOutOpen parameters? And how are you creating your>best by example. > 'stream' of midiOutShortMsgs? You can use the midiOutOpen CallBack parameters and a Timer to 'simulate' Asynchronous behaviour that won't be blocked by most events, such as other code running, the form moving, etc, although you will still be at the mercy of system and midi device loads, and you will have to code for re-entrancy. -- Alfie [UK] <http://www.delphia.co.uk/> There are two theories to arguing with women. Neither one works. On Fri, 12 Jun 2009 10:51:58 +0100, "Alfie [UK]" <alfie@mail.invalid> ret = midiOutOpen(g_MIDI_Out, dev_id, 0&, 0&, 0&)wrote: >On Fri, 12 Jun 2009 08:37:33 +0100, MM <kylix***@yahoo.co.uk> wrote: > >>Please use simple language and try posting an example or two! I learn >>best by example. >> >What are your midiOutOpen parameters? > And how are you creating your I've removed the 'housekeeping' details from the following and show>'stream' of midiOutShortMsgs? only the core part: Private Function CreateNewEvent(DeltaTime As Long, _ status As Byte, _ Optional data1 As Byte = 0, _ Optional data2 As Byte = 0, _ Optional data3 As Byte = 0) As Boolean ' MIDIEventPtr is a Public variable ' The MIDIEventQueue is a public UDT array MIDIEventPtr = MIDIEventPtr + 1 With MIDIEventQueue(MIDIEventPtr) .DeltaTime = DeltaTime .status = status .data1 = data1 .data2 = data2 .data3 = data3 End With End Function >You can use the midiOutOpen CallBack parameters and a Timer to Have you any example, or could point to one on the web?>'simulate' Asynchronous behaviour that won't be blocked by most >events, > such as other code running, the form moving, etc, although you Thanks for your feedback.>will still be at the mercy of system and midi device loads, and you >will have to code for re-entrancy. MM On Fri, 12 Jun 2009 11:53:01 +0100, MM <kylix***@yahoo.co.uk> wrote:
>Have you any example, or could point to one on the web? Have a look at http://www.vbforums.com/showthread.php?t=566767You'll see the midiOutOpen call uses a CallBack to the form to avoid blocking. There are other CallBack options, http://allapi.mentalis.org/apilist/midiOutOpen.shtml If you start with that code, add a Timer (Interval=500) and this Sub; Private Sub Timer1_Timer() Static t As Integer Command1_MouseDown t, 2, 0, 0, 0 t = t + 1 If t > 6 Then t = 0 End Sub ....you should see that you can both move the form around, and click the buttons/combo without blocking the sequence from playing. Timer Controls are not 100% accurate (due to the way that they work), you may need to guard against re-entrancy for small Intervals or lots of code in the Timer Sub, and you should enable/disable the Timer explicitly which is not shown here. If you're going to run tight time loops like this you should also be throwing in a DoEvents occasionally to keep it responsive. HTH On Fri, 12 Jun 2009 14:21:42 +0100, "Alfie [UK]" <alfie@mail.invalid> I added the callback to my code, but it made no difference. Outputwrote: >On Fri, 12 Jun 2009 11:53:01 +0100, MM <kylix***@yahoo.co.uk> wrote: > >>Have you any example, or could point to one on the web? > >Have a look at http://www.vbforums.com/showthread.php?t=566767 > >You'll see the midiOutOpen call uses a CallBack to the form to avoid >blocking. still freezes when I move the form or scroll the grid. But a question: Why would having a Callback cause blocking to be avoided? Show quoteHide quote > There are other CallBack options, The Timer made no difference either.>http://allapi.mentalis.org/apilist/midiOutOpen.shtml >If you start with that code, add a Timer (Interval=500) and this Sub; > >Private Sub Timer1_Timer() > Static t As Integer > > Command1_MouseDown t, 2, 0, 0, 0 > > t = t + 1 > If t > 6 Then t = 0 > >End Sub >...you should see that you can both move the form around, and click I'm already using DoEvents in the midiOutShortMsg loop.>the buttons/combo without blocking the sequence from playing. > >Timer Controls are not 100% accurate (due to the way that they work), >you may need to guard against re-entrancy for small Intervals or lots >of code in the Timer Sub, and you should enable/disable the Timer >explicitly which is not shown here. If you're going to run tight time >loops like this you should also be throwing in a DoEvents occasionally >to keep it responsive. I'll try the other link you provided. Thanks. MM Instead of moving the form, try adding a label and in Timer1_Timer, show the
current time using Time() function. This way if you see the time frozen, you can quickly tell if things are working out as expected. On Fri, 12 Jun 2009 16:40:19 +0100, MM <kylix***@yahoo.co.uk> wrote:
>I added the callback to my code, but it made no difference. Output I haven't done much directly with midi, as I usually use mciSendString>still freezes when I move the form or scroll the grid. > >But a question: Why would having a Callback cause blocking to be >avoided? to handle multimedia with pre-built sound files, but I believe that creating the device with midiOutOpen (by default) blocks until the device has processed the messages, so setting a path open with a callback avoids that being the cause of blocking because it returns as soon as the message is sent (the CallBack then notifies your app that the message has been handled). Show quoteHide quote > I meant add the Timer to the first sample link I provided, my bad, I>> There are other CallBack options, >>http://allapi.mentalis.org/apilist/midiOutOpen.shtml > >>If you start with that code, add a Timer (Interval=500) and this Sub; >> >>Private Sub Timer1_Timer() >> Static t As Integer >> >> Command1_MouseDown t, 2, 0, 0, 0 >> >> t = t + 1 >> If t > 6 Then t = 0 >> >>End Sub > >The Timer made no difference either. added the AllAPI link after writing the first bit. > As Nobody mentions something else may be blocking your code. The Timer>I'm already using DoEvents in the midiOutShortMsg loop. is not infallible and can be blocked itself in certain situations, and can operate differently within the IDE and a compiled app. I've just got back to my desk so let me see if I can re-work a simpler example. -- Alfie [UK] <http://www.delphia.co.uk/> This could be seen as a bug or a feature... how Microsoft do you feel today ? OK, I've simplied an example out of the code in the first link I
previously provided. This just plays the C chord every half second using a Timer firing off 3 midiOutShortMsg messages. Start a new project, add a combo box and a timer to the form (I left the combo box as for some reason my default midi device doesn't play within the IDE, so I needed to select one that does). Paste the following code into the form's code page (I've pre-wrapped the lines at 76 chars but watch for line wrap); <-- CODE --> Option Explicit Private Const MAXPNAMELEN = 32 Private Type MIDIOUTCAPS wMid As Integer wPid As Integer vDriverVersion As Long szPname As String * MAXPNAMELEN wTechnology As Integer wVoices As Integer wNotes As Integer wChannelMask As Integer dwSupport As Long End Type Private Declare Function midiOutShortMsg Lib "winmm.dll" ( _ ByVal hMidiOut As Long, ByVal dwMsg As Long) As Long Private Declare Function midiOutClose Lib "winmm.dll" ( _ ByVal hMidiOut As Long) As Long Private Declare Function midiOutOpen Lib "winmm.dll" ( _ lphMidiOut As Long, ByVal uDeviceID As Long, ByVal dwCallback As Long, _ ByVal dwInstance As Long, ByVal dwFlags As Long) As Long Private Declare Function midiOutGetNumDevs Lib "winmm" () As Integer Private Declare Function midiOutGetDevCaps Lib "winmm.dll" Alias _ "midiOutGetDevCapsA" (ByVal uDeviceID As Long, lpCaps As MIDIOUTCAPS, _ ByVal uSize As Long) As Long Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _ pDst As Any, pSrc As Any, ByVal ByteLen As Long) Private Type MidiMsg status As Byte Note As Byte Velocity As Byte Data3 As Byte End Type Private Const CALLBACK_WINDOW = &H10000 Private Const KeyOn = &H90 Private hMidi As Long Private Sub Form_Load() Dim i As Long Dim caps As MIDIOUTCAPS Timer1.Enabled = False Timer1.Interval = 500 Me.ScaleMode = vbPixels Combo1.Left = 0 Combo1.Top = 0 Combo1.Width = Me.ScaleWidth For i = 0 To midiOutGetNumDevs() - 1 midiOutGetDevCaps i, caps, Len(caps) Combo1.AddItem caps.szPname, i Next Combo1.ListIndex = 0 Timer1.Enabled = True End Sub Private Sub Combo1_Click() midiOutClose hMidi midiOutOpen hMidi, Combo1.ListIndex, Me.hWnd, 0, CALLBACK_WINDOW End Sub Private Sub Timer1_Timer() Static bWorking As Boolean Dim msg As MidiMsg Dim ml As Long If Not bWorking Then bWorking = True msg.status = KeyOn msg.Velocity = 64 msg.Data3 = 0 msg.Note = 60 CopyMemory ml, msg, 4 midiOutShortMsg hMidi, ml msg.Note = 64 CopyMemory ml, msg, 4 midiOutShortMsg hMidi, ml msg.Note = 67 CopyMemory ml, msg, 4 midiOutShortMsg hMidi, ml bWorking = False End If End Sub Private Sub Form_Unload(Cancel As Integer) midiOutClose hMidi Timer1.Enabled = False End Sub <-- CODE --> The CallBack to the form ensures that the device isn't blocking and the Timer firing attempts to ensure that other code isn't blocking. If this was production code youmay want to use a multimedia timer rather than the VB control (for greater resolution), and need more error-trapping, but this should demonstrate non-blocking asynchronous operation. Depending upon how you are producing your 'notes' stream, the CallBack_Function flag may be more appropriate for you. The CallBack function would then read your message data and pass it into the device, you may not actually need a timer method at all. -- Alfie [UK] <http://www.delphia.co.uk/> Heller's Law: The first myth of management is that it exists. On Fri, 12 Jun 2009 19:14:42 +0100, "Alfie [UK]" <alfie@mail.invalid> [snip]wrote: >OK, I've simplied an example out of the code in the first link I >previously provided. This just plays the C chord every half second >using a Timer firing off 3 midiOutShortMsg messages. > >Start a new project, add a combo box and a timer to the form (I left >the combo box as for some reason my default midi device doesn't play >within the IDE, so I needed to select one that does). > >Paste the following code into the form's code page (I've pre-wrapped >the lines at 76 chars but watch for line wrap); No joy, I'm afraid. As soon as I move the form (hold mouse down in caption bar and drag form) the output stops dead. It resumes as soon as I release the mouse button. MM On Fri, 12 Jun 2009 22:07:27 +0100, MM <kylix***@yahoo.co.uk> wrote:
Show quoteHide quote >On Fri, 12 Jun 2009 19:14:42 +0100, "Alfie [UK]" <alfie@mail.invalid> However, I have now had the opportunity to test it on XP and your>wrote: > >>OK, I've simplied an example out of the code in the first link I >>previously provided. This just plays the C chord every half second >>using a Timer firing off 3 midiOutShortMsg messages. >> >>Start a new project, add a combo box and a timer to the form (I left >>the combo box as for some reason my default midi device doesn't play >>within the IDE, so I needed to select one that does). >> >>Paste the following code into the form's code page (I've pre-wrapped >>the lines at 76 chars but watch for line wrap); > >[snip] > >No joy, I'm afraid. As soon as I move the form (hold mouse down in >caption bar and drag form) the output stops dead. It resumes as soon >as I release the mouse button. example works! Must be a failing of Windows 98SE, I reckon. MM On Sat, 13 Jun 2009 10:35:57 +0100, MM <kylix***@yahoo.co.uk> wrote:
>However, I have now had the opportunity to test it on XP and your I've done a bit of digging and Win98 doesn't support kernel-mixing, so>example works! > >Must be a failing of Windows 98SE, I reckon. > only one app can be 'in control' of the kernel level/hardware at any one time (even though winmm.dll and the audio drivers are in-process threaded and asynchronous, and we open the device with a callback to avoid blocking). I don't think that this is the issue, but something to bear in mind on Win98. The Timer control is a system timer which will both limit it's resolution and WM_TIMER messages are 'low priority' so can be 'bumped' by other messages such as GUI events, or when system load is high. You could try a multimedia timer, which runs in it's own thread. You'll have to be a bit more careful with it and it can make debugging more difficult (as it still fires in the IDE when you use breakpoints, stop on error, etc so will crash the IDE). You are also limited in what calls can be made from within a multimedia TimerProc because it is running in a seperate thread, midiOutShortMsg is permitted (it is a multimedia timer after all). http://www.vbaccelerator.com/home/vb/code/Libraries/HiResTimer/article.asp and http://www.xtremevbtalk.com/showthread.php?t=96917 have some detail and examples of using the MM Timer. -- Alfie [UK] <http://www.delphia.co.uk/> They say suffering brings wisdom. I say prepare to have enlightenment beaten into your bitch-ass. |
|||||||||||||||||||||||