|
code
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Karl, I need to understand your Timer!past, but couldn't fathom its depths (Notify, Countdown, Stopwatch, phew!) All I want is a replacement for Sleep, as in Sleep 32 as accurate as poss, where the duration might be as low as 1. How can I best achieve that with the CCRP Timer? Use the Countdown thingy? The Notify thingy? I've already spent hundreds of hours (well, okay, slight exaggeration) trying to work through the examples. At present I'm using clsWaitableTimer.cls from A. N. Other and I simply do objTimer.Wait DeltaTime MM "MM" <kylix***@yahoo.co.uk> schrieb im Newsbeitrag You can increase the resolution of the Sleep-commandnews:7g27355tppmghm92mo0raqt1smksrtms6m@4ax.com... > All I want is a replacement for Sleep, as in > > Sleep 32 to about 1-2msec (instead of the former 15msec). with the usage of: Private Declare Function timeBeginPeriod& Lib "winmm" (ByVal uPeriod&) Private Declare Function timeEndPeriod& Lib "winmm" (ByVal uPeriod&) On process-startup: timeBeginPeriod 1 On process-exit: timeEndPeriod 1 > as accurate as poss, As said - Sleep will then work with a reachable minimum-intervalof about 2msec. Also if you call: Sleep 1 .... on most systems the Sleep-Call returns after 2msec only - interestingly on my oldest machine here, a PIII-500MHz - the Sleep-Call returns indeed after 1msec, but on all other systems I've tested, Sleep 1 will give you 2msec "timeout" in case the timeBeginPeriod 1 call was done at startup. So, if that minimum-timeout of 2msec is enough, and we talk about accuracy now, then you could simply specify your longer sleep-calls (of about 10-30msec) just 1-2msec lower than you really want, and simply loop the remaining msec in a loop, which uses QueryPerformance-Frequency to wait until the exact given delta is reached. 10msec delta, specified as Sleep 9 - will give you then ca. 10% CPU-load, in case Sleep 9 returns relative exactly after 9msec in most cases (the 10% load then caused, because you have to perform your loop for the remaining 1msec duration until your delta is reached exactly). > where the duration might be as low as 1. As said, the minimum timeout of that "tuned Sleep-Call"is mostly 2msec - you cannot reach below that - if you need to go below these 2msec, then just do your endless- loop, which is based on QueryPerformance-Counter "purely". But not sure, what for you will need such small wait-timeouts for Midi - the 2msec Minimum-Wait-Timeout of the normal (tuned) Sleep-Call should be enough IMO - even the well- working ASIO-drivers of professional Soundcards for example only reach about 1-2msec latency - which in the Sound- and Recording-scene (Cubase et al.) is considered "enough" - the Pros only begin to "revolt", if the latency is above 4-5. So I'd just call the plain (tuned) Sleep-Call - try to see, if that already is enough for you (without that extra-efforts regarding shorter given intervals + "rest-looping" with QueryPerformance-timers). Olaf
Show quote
Hide quote
On Sat, 13 Jun 2009 14:23:34 +0200, "Schmidt" <s**@online.de> wrote: I have tried exactly what you describe with timeBeginPeriod 1 and> >"MM" <kylix***@yahoo.co.uk> schrieb im Newsbeitrag >news:7g27355tppmghm92mo0raqt1smksrtms6m@4ax.com... > >> All I want is a replacement for Sleep, as in >> >> Sleep 32 >You can increase the resolution of the Sleep-command >to about 1-2msec (instead of the former 15msec). > >with the usage of: >Private Declare Function timeBeginPeriod& Lib "winmm" (ByVal uPeriod&) >Private Declare Function timeEndPeriod& Lib "winmm" (ByVal uPeriod&) > >On process-startup: >timeBeginPeriod 1 > >On process-exit: >timeEndPeriod 1 > > >> as accurate as poss, >As said - Sleep will then work with a reachable minimum-interval >of about 2msec. >Also if you call: >Sleep 1 >... on most systems the Sleep-Call returns after 2msec only - >interestingly on my oldest machine here, a PIII-500MHz - the >Sleep-Call returns indeed after 1msec, but on all other systems >I've tested, Sleep 1 will give you 2msec "timeout" in case the >timeBeginPeriod 1 call was done at startup. > >So, if that minimum-timeout of 2msec is enough, and >we talk about accuracy now, then you could simply specify >your longer sleep-calls (of about 10-30msec) just 1-2msec >lower than you really want, and simply loop the remaining >msec in a loop, which uses QueryPerformance-Frequency >to wait until the exact given delta is reached. > >10msec delta, specified as Sleep 9 - will give you then >ca. 10% CPU-load, in case Sleep 9 returns relative exactly >after 9msec in most cases (the 10% load then caused, >because you have to perform your loop for the remaining >1msec duration until your delta is reached exactly). > > >> where the duration might be as low as 1. >As said, the minimum timeout of that "tuned Sleep-Call" >is mostly 2msec - you cannot reach below that - if you >need to go below these 2msec, then just do your endless- >loop, which is based on QueryPerformance-Counter "purely". > >But not sure, what for you will need such small wait-timeouts >for Midi - the 2msec Minimum-Wait-Timeout of the normal >(tuned) Sleep-Call should be enough IMO - even the well- >working ASIO-drivers of professional Soundcards for example >only reach about 1-2msec latency - which in the Sound- and >Recording-scene (Cubase et al.) is considered "enough" - >the Pros only begin to "revolt", if the latency is above 4-5. > >So I'd just call the plain (tuned) Sleep-Call - try to see, >if that already is enough for you (without that extra-efforts >regarding shorter given intervals + "rest-looping" with >QueryPerformance-timers). > > >Olaf timeEndPeriod 1, but it makes no difference. As soon as I start to move the form, or scroll a scrollbar, the output stops. I've tried it on 98SE and on XP. This is the salient part of the output loop (I've removed any minor details so as not to generate reams of code here): '****** START OF ROUTINE ***** (where Dim's are done etc) timeBeginPeriod 1 Do With MIDIEventQueue(PlayBackPtr) If .DeltaTime Then DeltaTime = .DeltaTime * DeltaTimeMultiplier ' objTimer.Wait DeltaTime ' <---- what i've used until now Sleep DeltaTime ' <---- and now with your suggestion End If Select Case .Status ' Ignore following meta events Case METASEQN, METATEXT, METACOPYR, _ METATNAME, METAINAME, METALYRIC, _ METAMARKER, METACUEPT, METACHANPFX, _ METAMIDIPORT, METAEOT, METASMPTEOFF, METASEQEVENT Case &H51 ' a Tempo change, so we must adjust the multiplier If SuppressEmbeddedTempoChanges = False Then b(0) = 0 b(1) = .Data1 ' Microseconds b(2) = .Data2 ' per b(3) = .Data3 ' quarternote w = MakeLong(b()) tempo = MICROSECONDS_PER_MINUTE \ w DeltaTimeMultiplier = ((MICROSECONDS_PER_MINUTE / tempo) / 1000) / g_TicksPerBeat End If Case &H58, &H59 ' Ignore Time signature, key signature Case &HB0 To &HBF ' Control Change midiMsg = (.Data1 * &H100) + (.Data2 * &H10000) + ..Status Call midiOutShortMsg(g_MIDI_Out, midiMsg) Case &HC0 To &HCF ' Program Change midiMsg = (.Data1 * &H100) + .Status Call midiOutShortMsg(g_MIDI_Out, midiMsg) Case Else ' MIDI note on/off data Data2 = .Data2 midiMsg = (.Data1 * &H100) + (Data2 * &H10000) + ..Status Call midiOutShortMsg(g_MIDI_Out, midiMsg) End Select End With DoEvents PlayBackPtr = PlayBackPtr + 1 Loop While (PlayBackPtr <= PlayBackTo) timeEndPeriod 1 '****** END OF ROUTINE ******* However, even though the main problem, that of freeze-up when moving the form, is still present, this exercise does show that Sleep is adequate when the resolution is increased, as there is no perceptible difference to the sound output (no lags etc). But the freeze-up remains obstinate! MM PS: I also changed the call to timeBeginPeriod slightly to ensure that
it is being implemented: If timeBeginPeriod(1) = TIMERR_NOERROR Then Debug.Print "timeBeginPeriod ok" MM "MM" <kylix***@yahoo.co.uk> schrieb im Newsbeitrag The timeBeginPeriod/..endperiod-calls have nothing to donews:73b7355ovggpocjoc06mmr6jadgh7dh4hk@4ax.com... > I have tried exactly what you describe with timeBeginPeriod 1 > and timeEndPeriod 1, but it makes no difference. As soon as > I start to move the form, or scroll a scrollbar, the output stops. with your sound-blocking issues in case your GUI is "touched". They are only there, to (normally) increase the accuracy ot the multimedia-timer - but they also have that nice side- effect on the sleep-call, which then is able to work below 15msec granularity - just what you need IMO for your (wait-)purposes. Your blocking-issues (at least on Win98) were not solvable as I read in your other thread - the callback-approach, posted by Alfie [UK] is a nice idea - but it seems to work only on XP currently, if I read that correctly. Hmm, maybe time now, to really think about a threaded approach <g>. But be aware, that if you want to do it properly, you will have to move everything that's Midi-related into a Class then (probably not that much of a problem) - but the larger effort would be, that this Class (which later on runs on its own thread) needs to communicate with your GUI in an asynchronous way (at least as long as your loops are running, which play a current continuous stream). And that will cause side-effekts (coding-effort) on how you need to work then with your GUI (rendering-) code. So, are there more actions than playing a current stream (wherever that stream comes from) which currently need an "unblocking" - or is that the only one. And if it is - in what format do you currently hold your Data for a given Stream - if in an (userdefined) Array - how large is it in the worst-case (does that structure already contain the maximum of all 16-possible channels?). Or do you organize each channel in a separate Array. And what is with interaction whilst a stream is playing? Start-Stop is clear - but do you want to allow live-playing (or even additional recording) whilst a current stream is played within the thread? I assume yes, if your App acts something like a sequencer - but if Not, that would ease the threading-design a bit. Olaf
Show quote
Hide quote
On Sat, 13 Jun 2009 17:13:03 +0200, "Schmidt" <s**@online.de> wrote: Thanks, Olaf. Here's an overview:> >"MM" <kylix***@yahoo.co.uk> schrieb im Newsbeitrag >news:73b7355ovggpocjoc06mmr6jadgh7dh4hk@4ax.com... > >> I have tried exactly what you describe with timeBeginPeriod 1 >> and timeEndPeriod 1, but it makes no difference. As soon as >> I start to move the form, or scroll a scrollbar, the output stops. > >The timeBeginPeriod/..endperiod-calls have nothing to do >with your sound-blocking issues in case your GUI is >"touched". > >They are only there, to (normally) increase the accuracy >ot the multimedia-timer - but they also have that nice side- >effect on the sleep-call, which then is able to work below 15msec >granularity - just what you need IMO for your (wait-)purposes. > >Your blocking-issues (at least on Win98) were not solvable >as I read in your other thread - the callback-approach, posted >by Alfie [UK] is a nice idea - but it seems to work only on >XP currently, if I read that correctly. > >Hmm, maybe time now, to really think about a threaded >approach <g>. > >But be aware, that if you want to do it properly, you will >have to move everything that's Midi-related into a Class >then (probably not that much of a problem) - but the >larger effort would be, that this Class (which later on runs >on its own thread) needs to communicate with your GUI >in an asynchronous way (at least as long as your loops >are running, which play a current continuous stream). >And that will cause side-effekts (coding-effort) on how >you need to work then with your GUI (rendering-) code. > >So, are there more actions than playing a current stream >(wherever that stream comes from) which currently >need an "unblocking" - or is that the only one. > >And if it is - in what format do you currently hold your >Data for a given Stream - if in an (userdefined) Array - >how large is it in the worst-case (does that structure >already contain the maximum of all 16-possible channels?). > >Or do you organize each channel in a separate Array. > >And what is with interaction whilst a stream is playing? >Start-Stop is clear - but do you want to allow live-playing >(or even additional recording) whilst a current stream is >played within the thread? I assume yes, if your App acts >something like a sequencer - but if Not, that would ease >the threading-design a bit. > >Olaf So, the MIDI data is held in a UDT of the following Type: Type MIDIEventType DeltaTime As Long status As Byte data1 As Byte data2 As Byte data3 As Byte bytepointer As Long End Type Public MIDEventQueue() As MIDIEventType The UDT is ReDimmed according to need as events are read from the MIDI file and pumped into it. Thus the array could hold 1 event or 30,000. There IS a limit, since it is only using memory, but I can hold enough data for a Beethoven Sonata, for instance, which is more than enough for the purpose of the app (i.e. to practise riffs on the piano!) The Playback routine is started. Basically, this is a loop from pointer=0 to UBound(MIDEventQueue) During the Playback the program also "plays" the key on a virtual keyboard (see picture at http://www.littletyke.myzen.co.uk/ktn/index.html) So, each midiOutShortMsg is preceded by PlayOrReleaseKey. It all works VERY nicely! The virtual keys are exactly in sync with the MIDI output. Just what I wanted it to do. But the slight fly in the ointment is the blocking that occurs when moving the main form, or the keyboard, or scrolling either the grid or scrolling the keyboard. It is not a MAJOR problem, since the whole purpose of this app is to help orientation around a piano or organ keyboard for those who have never learned to sight-read successfully. Actually, the *original* purpose was to provide a driver for an array of LEDs (physical hardware LEDs) along a piano keyboard (see http://www.littletyke.myzen.co.uk/vb6tobasicstamp2/html/the_application.html) and that is the next stage on my ToDo list. That is where PlayOrReleaseKey (see above) will be augmented by: SwitchLEDOnOff, whereupon a suitable message is sent out of the serial port to the BASIC Stamp and the BS causes the appropriate LED to be lighted (or switched off). Well it ~was~ intended as "only" a driver originally, but has turned into a full-blown app in its own right over the past 18 months. MM "MM" <kylix***@yahoo.co.uk> schrieb im Newsbeitrag Yep.news:mus735tf17boel4buapbdqkpum6dfan5aq@4ax.com... > It all works VERY nicely! The virtual keys are exactly in > sync with the MIDI output. Just what I wanted it to do. > But the slight fly in the ointment is the blocking that occurs > when moving the main form, or the keyboard, or scrolling > either the grid or scrolling the keyboard. From looking at your code again, this is caused by your DoEvents-Call which is (necessarily) placed within your PlayStream-Loop. It is acting as a black-hole for win-messages, in case you cause many of them (scrolling, moving a form) - and that "black-hole" returns back to your loop only, when all "catched" message-blobs are "done". You have to get rid of that - but the question is "how" of course (thereby allowing Control- and Form-Event- processing further). There are different solutions then. The "cleanest" (and best working) singlethreaded approach would be, to work with MidiStreamOpen/-Pause/-Restart/-Stop etc., since that is, what the MCI-Control does. You can adjust also the Tempo and the Volume of a just playing stream(chunk) - and you could also check cyclically with a normal timer, where the current Stream-Position is - and you could also register for notify-messages about the just played (last) Midi-Events against a Window-Procedure (using subclassing). But that would be a bit of work (preparing the stream-header- structures - fill in the next chunk of Midi-Events, etc)... and since your own current (single Message-based) stream-approach works well enough, why should you change that (ok, to get rid of the fly of course ;-) The second-best singlethreaded approach, to get rid of the black-hole (DoEvents) is (and here we get OnTopic again in this thread), to work with one of the MultiMedia-Timer- encapsulations. You would have to set their resolution not too high (e.g. 1msec resolution would probably a bit "stressy", but 2msec should work good enough for your purposes IMO). These encapsulations usually work with a PostMessage- Call under the hood, to a hidden (internal) Window, and that in turn is raising a normal VB-Event into your App. In that HighResTimer-Event (which occurs with a resultion of 2msec then) you will have to ensure a mechanism, which exits the Event immediately, should the Time-Delta to your next Midi-Note larger than the next tick-interval (2msec) - but if the remaining time-delta is lower, then just fire up your next note. You should nonetheless sum-up your current StreamTime from another reliable Time- measurement-Function, and not by simply suming up from the incoming 2msec-ticks. This would ensure, that your StreamTime/-Speed would be correct - only your played Notes would be fired out with a somewhat reduced accuracy of +- 1msec (in case of a 2msec interval) - but as already mentioned - this is normally not heard - and in fact many sequencers implement something like that "on purpose", with even a bit larger "accuracy-intervals" than only 2msec, to make a played Midi-Stream sound somewhat more naturally, and not with an more "robot-like" exact timing. But - although this approach would get rid of the blackhole, you have now incoming (Window-)messages which work in concurrency - e.g. a heavy Form-Movement - or heavy Scrolling would cause a nice amount of messages in "parallel" to the (per PostMessage) incoming MultiMediaTimer-Ticks. Maybe you can lower these side-effects, if you decouple your Scroll-Events for example with another (normal) timer, to ensure something like a "privileged mode" for your tick- events. Maybe that worth a try, since it would require the least amount of changes to your current (working) scheme. And then there's the threading - you would have to decide, which way you want to go - your ThreadClass then hosted in an ActiveX-Exe - or in an external Dll (supported by an additional Helper-Lib). Both approaches have pros and cons. The ActiveX-Exe-approach a bit easier to implement presumably (but not much) - and your Project could be hosted and managed (and debuged) within a single VB- project further. Maybe give the MMTimer-approach (-controls) a try - and see, how the "win-message-concurrency" works out, compared with the entirely non-satisfying DoEvents- "workaround" you currently have. Olaf On Sun, 14 Jun 2009 14:10:09 +0200, "Schmidt" <s**@online.de> wrote: An MMTimer is probably the best idea, as it runs in it's own thread,>Maybe give the MMTimer-approach (-controls) a try - and >see, how the "win-message-concurrency" works out, >compared with the entirely non-satisfying DoEvents- >"workaround" you currently have. and as far as I know isn't blocked by other messages like a system timer. As Steve McMahon mentions http://www.vbaccelerator.com/home/vb/code/Libraries/HiResTimer/article.asp it is better to encapsulate the MMTimer in it's own AX DLL to avoid issues when working within the IDE. -- Alfie [UK] <http://www.delphia.co.uk/> If god had meant us to travel economy class, he would have made us narrower. "Alfie [UK]" <alfie@mail.invalid> schrieb im Newsbeitrag Yep - but the decoupling from this thread-callback in suchnews:mvgc35lshp3rhle9hn7bsjitf17ffpgm4m@4ax.com... > On Sun, 14 Jun 2009 14:10:09 +0200, "Schmidt" <s**@online.de> wrote: > > >Maybe give the MMTimer-approach (-controls) a try - > >and see, how the "win-message-concurrency" works out, > >compared with the entirely non-satisfying DoEvents- > >"workaround" you currently have. > > An MMTimer is probably the best idea, as it runs in it's own > thread, implementations is usually done per PostMessage-Call, which is also one of the MSDN-recommended calls, which should be placed within such an MMTimer-callback-function. > and as far as I know isn't blocked by other messages like Up to the *send* of the PostMessage-Call in the MMTimer-> a system timer. Callback nothing is blocked or "delayed", yes. But the *incoming* PostMessage-Call(s) (within the hidden Window in such a MM-Timer-Implementation, in turn able to raise Events to VB) are received in the Apps normal MainThread then. And within that Process(MainThread) of the Std-VBApp these incoming (Post-)Messages are in concurrency to other incoming messages within the same App (in the Apps MainForm for example). > As Steve McMahon mentions Yep - that's recommended in either case - but this Dll> http://www.vbaccelerator.com/home/vb/code/Libraries/HiResTimer/article.asp > it is better to encapsulate the MMTimer in it's own AX DLL > to avoid issues when working within the IDE. (or OCX, whatever) also provides only a PostMessage- based MMTimer-Event within the VB-ConsumerApp. But as said - this should already work (at least a bit) better than the current Loop-approach having a DoEvents- Call within). Olaf
Show quote
Hide quote
On Mon, 15 Jun 2009 14:01:19 +0100, "Alfie [UK]" <alfie@mail.invalid> I am at last making progress! Using a multimedia timer I have justwrote: >On Sun, 14 Jun 2009 14:10:09 +0200, "Schmidt" <s**@online.de> wrote: > >>Maybe give the MMTimer-approach (-controls) a try - and >>see, how the "win-message-concurrency" works out, >>compared with the entirely non-satisfying DoEvents- >>"workaround" you currently have. > >An MMTimer is probably the best idea, as it runs in it's own thread, >and as far as I know isn't blocked by other messages like a system >timer. > >As Steve McMahon mentions >http://www.vbaccelerator.com/home/vb/code/Libraries/HiResTimer/article.asp >it is better to encapsulate the MMTimer in it's own AX DLL to avoid >issues when working within the IDE. successfully "played" a sequence of MIDI events that kept on playing (on Windows 98SE nota bene!) even while I moved the form. I even added a flexgrid to the form and in Form_Load filled it with 1000 rows of dummy data so that I could stress the CPU by scrolling up and down while the MIDI output is in progress. Again, NO interruption! This is still early days, because I have to work out timing based on a tick generator (the multimedia timer) rather than Sleep(ing) for a precise period. Basically, as far as I can make out from Paul Messick's Maximum MIDI (written in C/C++), the tick generator generates ticks and at each one you increment a tick counter and compare it with the delta time of the next MIDI event pending in the queue. If the tick counter meets or exceeds the DT, then midiOutShortMsg for that event and reset the counter. I'll give that a go next. MM "MM" <kylix***@yahoo.co.uk> schrieb im Newsbeitrag That's what I basically meant in my post with the commentsnews:h5lf355vliea08lsebhhfr4ut483v2jcei@4ax.com... > This is still early days, because I have to work out timing based on a > tick generator (the multimedia timer) rather than Sleep(ing) for a > precise period. Basically, as far as I can make out from Paul > Messick's Maximum MIDI (written in C/C++), the tick generator > generates ticks and at each one you increment a tick counter and > compare it with the delta time of the next MIDI event pending in the > queue. If the tick counter meets or exceeds the DT, then > midiOutShortMsg for that event and reset the counter. I'll give > that a go next. about the Delta-Timing. But since suming-up only the ticks can be a bit unprecise (unpredictable), because *our* VB-HighResTimer works already decoupled (per Postmessage), you cannot rely on the same accuracy as demonstrated e.g. in a C-Source, in case these implementations work within the MMTimer- Callback directly. What I meant with my comment here: "You should nonetheless sum-up your current StreamTime from another reliable Time-measurement-Function, and not by simply suming up from the incoming 2msec-ticks..." ....is needed at the receiving end (our VB-Mainthread that receives the redirected PostMessages - and implemented in the following small Demo, which does not use a HighRes- Timer-encapsulation from a secondary Dll, but works within your App directly - to make it more obvious, what usually happens in such a VB-HighResTimer-component. Switch to a dedicated Dll-implementation for the HighRes- Ticks, if you need a bit more Debug-stability - but the Demo should also behave stable (also in Debug-Mode) as long as you don't place a BreakPoint directly within the CallBack-Procedure, which only contains the TypeLib- defined PostMessage-Call. Another advantage regarding IDE-behaviour in case you will use a secondary Dll is, that in case you pressed the Stop-Button to end your running App, then the (in case the timer was enabled) timer will fire further - where a Class that is defined within an external AX-Dll-Binary will go above Class_Terminate in case you press the IDEs Stop-Button - and in case of a HighResTimer-Dll that usually means, that the Timer is properly cancelled (killed). Ok, so for the following example you will need such a small PostMessageCall-Typelib-definition, bound into a normal StdExe-Test-project - e.g. the small typelib, which is included in the vbAccelerator-Example will do... and as always - such Typelibs don't have to be shipped with your App once it is compiled. Ok - here the Code now (no additional Controls needed, just click the plain Form): '***Into a Form Option Explicit Private Type SimpleNoteDef 'somewhat simplified Value As Long Duration As Long End Type Private WithEvents TickReceiver As PictureBox 'receives the HighRes-Ticks Private T_StartNote As Long 'to store the Time, the first Note was played Private T_NextNote As Long 'Time the next Note should be played Private NextNoteIdx As Long 'the next Note-Idx that should be played Private Notes() As SimpleNoteDef 'define our simple "Notes-Stream-Array" Private Sub Form_Load() timeBeginPeriod 1 'switch to highres-environment Set TickReceiver = Controls.Add("VB.PictureBox", "TR") 'a hidden PB InitSimpleDemoStream End Sub Private Sub Form_Click() If Not AutoRedraw Then AutoRedraw = True Cls 'since the demo-output is directed (printed) to the Form PlayStream End Sub Private Sub Form_Unload(Cancel As Integer) StopTimer 'just ensure, that everything is on hold if we leave timeEndPeriod 1 'reset highres-environment End Sub Private Sub InitSimpleDemoStream() Dim i As Long ReDim Notes(15) 'only 16 Notes here For i = 0 To UBound(Notes) Notes(i).Value = i Notes(i).Duration = 100 'msec Duration (for all Notes, to simplify) Next i End Sub Private Sub PlayStream(Optional ByVal FromNoteIdx As Long) Dim i As Long StopTimer 'just to make sure T_StartNote = timeGetTime PlayNote FromNoteIdx 'play the start-note already here T_NextNote = T_StartNote 'initialize to the current start-time For i = 0 To FromNoteIdx T_NextNote = T_NextNote + Notes(i).Duration 'increase as needed Next i NextNoteIdx = FromNoteIdx + 1 'define the next NoteIdx StartTimer TickReceiver.hWnd, 2 'and enable the HighResTimer End Sub 'just use a Msg with a somewhat higher prio than wm_keydown or wm_paint Private Sub TickReceiver_Resize() CheckForNextNote timeGetTime End Sub Private Sub CheckForNextNote(ByVal T_Current As Long) If T_Current < T_NextNote Then Exit Sub PlayNote NextNoteIdx 'play the now current Note T_NextNote = T_NextNote + Notes(NextNoteIdx).Duration NextNoteIdx = NextNoteIdx + 1 If NextNoteIdx > UBound(Notes) Then StopTimer 'check for stream-end End Sub Private Sub PlayNote(ByVal CurNoteIdx As Long) Print Notes(CurNoteIdx).Value, _ "played at Abs-StreamTime:"; timeGetTime - T_StartNote End Sub '***and here the small MMTimer-redirection per PostMessage '***put that into a module Option Explicit Public Declare Function timeBeginPeriod& Lib "winmm" (ByVal uPeriod&) Public Declare Function timeEndPeriod& Lib "winmm" (ByVal uPeriod&) Public Declare Function timeGetTime& Lib "winmm" () Private Declare Function timeSetEvent& Lib "winmm" (ByVal uDelay&, _ ByVal uResolution&, ByVal lpFunction&, ByVal dwUser&, ByVal uFlags&) Private Declare Function timeKillEvent& Lib "winmm" (ByVal uID&) Private m_TID As Long Public Function StartTimer(ByVal hWnd&, ByVal Interval As Long) As Boolean StopTimer 'don't start more than one timer in our small demo m_TID = timeSetEvent(Interval, 1, AddressOf TimerProc, hWnd, 1) StartTimer = m_TID End Function Public Sub StopTimer() If m_TID = 0 Then Exit Sub Else timeKillEvent m_TID: m_TID = 0 End Sub 'always use a typelib-defined PostMessage-call in here Private Sub TimerProc(ByVal TID As Long, ByVal iMsg As Long, _ ByVal dwUser As Long, ByVal dw1 As Long, ByVal dw2 As Long) PostMessage dwUser, &H5, 0, 0 'just send a simple wm_size End Sub Olaf On Wed, 17 Jun 2009 18:56:17 +0200, "Schmidt" <s**@online.de> wrote: Do you mean I should add a Reference (in Project/References) to e.g.>Ok, so for the following example you will need such a >small PostMessageCall-Typelib-definition, bound into a >normal StdExe-Test-project - e.g. the small typelib, which >is included in the vbAccelerator-Example will do... and >as always - such Typelibs don't have to be shipped with >your App once it is compiled. ...\HiResTimer6\TLB\vbalHRTA.tlb ? That is what I've done anyway and your application works. Now I need to study the code to see how it works. By the way, can you explain the need for the Typelib? Why wasn't it needed in VB5? Many thanks for all your efforts - vielen Dank! MM "MM" <kylix***@yahoo.co.uk> schrieb im Newsbeitrag Yep - that's the small typelib I had in mind.news:6mtk3555f8joel4if5bvesf9v24ce6lbpd@4ax.com... > On Wed, 17 Jun 2009 18:56:17 +0200, "Schmidt" <s**@online.de> wrote: > > >Ok, so for the following example you will need such a > >small PostMessageCall-Typelib-definition, bound into a > >normal StdExe-Test-project - e.g. the small typelib, which > >is included in the vbAccelerator-Example will do... and > >as always - such Typelibs don't have to be shipped with > >your App once it is compiled. > > Do you mean I should add a Reference (in Project/References) > to e.g. ..\HiResTimer6\TLB\vbalHRTA.tlb ? > That is what I've done anyway and your application works. Just make sure, that you replace the code with the just> Now I need to study the code to see how it works. posted newer version, that works with the OneShot- approach. > By the way, can you explain the need for the Typelib? The need for the Typelib is caused by VBs threading-> Why wasn't it needed in VB5? behaviour - VB allows threads, but these threads, to be able to make use of COM in the desired way have to ensure a special environment *within* such a thread (Thread-Local-Storage initialization beforehand). With VB5 these TLS-requirements were low... - up to SP2 (IIRC) you were even able to use lightweight- threading per CreateThread directly within a *.bas- module-procedure which was given into the CreateThread- call per AddressOf. Then since SP2 VB5 was enforcing higher TLS-requirements, and the direct usage of CreateThread - and also the Callbacks from other lightweight-threads (as the MMTimer-Callback) became more difficult. Nonetheless the TLS-inits (requirements) are even higher within VB6, compared with VB5 as it is now. In short - you may not use *any* VB.COM-Object within a lightweight-thread (or a lightweight-thread-callback) without initializing TLS correctly beforehand (on that thread). But you can call TypeLib-declared functions, which bypass VBs Err-Object (also a COM-Instance) in such scenarios without the TLS-init-requirements then. Normal VB-Declarations for such external Calls (as PostMessage for example) would not do, since under the hood the VB-Error- Object would be touched whilst performing Calls to such normally declared API-Defs (e.g. per Err.LastDllError). So you are on the safe side (with VB5 and also with VB6) if you only perform a Typelib-defined Call within a procedure, which is run from another (lightweight-) thread. And in our case that is the tlb-defined PostMessage, which decouples already, because the receiving end of the posted message (our hWnd) is in our mainthread again. That hwnd is provided by a dynamically created (invisible) PictureBox - and to avoid subclassing, to be able to receive the sent (Post)-Message - I've simply sent a WM_SIZE to that hidden PictureBox.hWnd, resulting in a PictureBox_ Resize-Event then - you may call that a hack - but this approach avoids subclassing and is IDE-safe to use. After all it is only a Windows-Message, which reuses an already implemented EventHandler in a Window-Class that is already contained in the VB-Runtime (our PBox). Olaf
Show quote
Hide quote
On Mon, 15 Jun 2009 14:01:19 +0100, "Alfie [UK]" <alfie@mail.invalid> I note that Steve's HiRes Timer example TestHiResTmr6.exe hangs 98SEwrote: >On Sun, 14 Jun 2009 14:10:09 +0200, "Schmidt" <s**@online.de> wrote: > >>Maybe give the MMTimer-approach (-controls) a try - and >>see, how the "win-message-concurrency" works out, >>compared with the entirely non-satisfying DoEvents- >>"workaround" you currently have. > >An MMTimer is probably the best idea, as it runs in it's own thread, >and as far as I know isn't blocked by other messages like a system >timer. > >As Steve McMahon mentions >http://www.vbaccelerator.com/home/vb/code/Libraries/HiResTimer/article.asp >it is better to encapsulate the MMTimer in it's own AX DLL to avoid >issues when working within the IDE. totally when I move the form around. Initially, the test app keeps on truckin', then I move the form a fraction more, it hangs and I have to use Ctrl-Alt-Delete to remove the hung application and get Windows back. This DOESN'T happen with CCRP Timer. That works perfectly, even in the IDE. I can move the form around no problem. MM "MM" <kylix***@yahoo.co.uk> schrieb im Newsbeitrag I can see that here on my Win98-VM too.news:opji351lbpjb3a29i92hp8sv79epkumcsp@4ax.com... > On Mon, 15 Jun 2009 14:01:19 +0100, "Alfie [UK]" <alfie@mail.invalid> > wrote: > > >On Sun, 14 Jun 2009 14:10:09 +0200, "Schmidt" <s**@online.de> wrote: > > > >>Maybe give the MMTimer-approach (-controls) a try - and > >>see, how the "win-message-concurrency" works out, > >>compared with the entirely non-satisfying DoEvents- > >>"workaround" you currently have. > > I note that Steve's HiRes Timer example TestHiResTmr6.exe > hangs 98SE totally when I move the form around. But I can reproduce the freezes also with the CCRP- timer-Classes, if I switch the resolution of them to the same high-frequencies, Steve is using in his Demo - and of course also my "simple-approach" is able, to freeze Win98, since it is using the same PostMessage-Calls for decoupling. For the CCRP-Timer-Class - you will have to switch the default-resolution of 20msec to 1msec beforehand, to work withing the same stress-conditions. ccrpTimer.Stats.Frequency = 1 followed by ccrpTimer.Interval = 1 can ensure that. To reproduce that with my former example, you can shorten the current Timer-Interval of 2msec to 1msec too - and then simply define a somewhat longer Notes-Array-Ubound - then just click the Form- Caption on Win98 and hold the MouseKey down - after a short while the incoming PostMessage-Calls will "flood" the MessageQueue - and then you have your freeze in all three cases. XP has apparently a MessageQueue-Overflow-protection built in - and Win98 does lack this feature as it seems. How to work around: Change the already somewhat StateMachine-like Scheme to a "full one", which retriggers itself with a new OneShot- Request against the MMTimer - as long as certain conditions are (yet) true, which require "to stream further". As I see it - the OneShot-Mode is also available in the MMTimer-Components you already have. Again my example, which already works in that mode - and is now also doing fine on Win98 with these changes. The OneShot-Mode also solves another problem - even the following "source-only"-solution is working stable now, in case you simply press the IDEs Stop-Button - because in that mode it is impossible, that a "forgotten" MMTimer-Instance fires further - you don't even need an explicite Kill on the OneShot- MMTimer-Handle, since it cleans up itself already internally in each OneShot. Ok, here the adapted code again - now with some more comments. '***the *.bas-module-code is reduced now as you see...but as '***in the other example, this project needs the small TypeLib too '***which only contains the PostMessage-Call declaration. Option Explicit Public Declare Function timeBeginPeriod& Lib "winmm" (ByVal uPeriod&) Public Declare Function timeEndPeriod& Lib "winmm" (ByVal uPeriod&) Public Declare Function timeGetTime& Lib "winmm" () Private Declare Function timeSetEvent& Lib "winmm" (ByVal uDelay&, _ ByVal uResolution&, ByVal lpFunction&, ByVal dwUser&, ByVal uFlags&) Public Function MMT_OneShot(ByVal hWnd&, ByVal Interval As Long) As Boolean 'last Param at 0 ensures OneShot, no explicite Kill needed in that mode MMT_OneShot = timeSetEvent(Interval, 1, AddressOf TimerProc, hWnd, 0) End Function 'always use a typelib-defined PostMessage-call in here Private Sub TimerProc(ByVal TID As Long, ByVal iMsg As Long, _ ByVal dwUser As Long, ByVal dw1 As Long, ByVal dw2 As Long) PostMessage dwUser, &H5, 0, 0 'just send a simple wm_size End Sub '*** And finally that into a Form again - then click the Form... Option Explicit Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Private Type SimpleNoteDef 'somewhat simplified Value As Long Duration As Long End Type Private WithEvents TickReceiver As PictureBox 'receives the HighRes-Ticks Private T_StartNote As Long 'to store the Time, the first Note was played Private T_NextNote As Long 'Time the next Note should be played Private NextNoteIdx As Long 'the next Note-Idx that should be played Private Notes() As SimpleNoteDef 'define our simple "Notes-Stream-Array" Private Sub Form_Load() timeBeginPeriod 1 'switch to highres-environment Set TickReceiver = Controls.Add("VB.PictureBox", "TR") 'a hidden PB InitSimpleDemoStream End Sub Private Sub Form_Unload(Cancel As Integer) timeEndPeriod 1 'reset highres-environment End Sub 'that Click-Event is "misused", to start the streaming Private Sub Form_Click() If Not AutoRedraw Then AutoRedraw = True Cls 'since the demo-print-output is directed to the Form PlayStream 0 'here we start at Note-Index 0 End Sub Private Sub StopStream() 'and that procedure is able, to stop streaming If T_NextNote = 0 Then Exit Sub 'nothing to stop T_NextNote = 0 'singalize "no-streaming" Sleep 2 'just wait for an eventual last, yet incoming PostMessage DoEvents 'this is allowed here, since we want to stop anyways End Sub Private Sub PlayStream(Optional ByVal FromNoteIdx As Long) Dim i As Long StopStream T_StartNote = timeGetTime T_NextNote = T_StartNote 'initialize to the current time... For i = 0 To FromNoteIdx T_NextNote = T_NextNote + Notes(i).Duration '...increase as needed Next i NextNoteIdx = FromNoteIdx + 1 'define the next NoteIdx PlayNote FromNoteIdx 'play the start-note already here CheckForNextNote T_StartNote 'and enter our OneShot-Loop End Sub Private Sub InitSimpleDemoStream() 'just the init for our DemoStream-Array Dim i As Long ReDim Notes(19) 'only 20 Notes here For i = 0 To UBound(Notes) Notes(i).Value = i Notes(i).Duration = 100 'msec Duration (for all Notes, to simplify) Next i End Sub '********* and the state-machine like retriggering, based '********* on the OneShot-MMTimer-Events, which are delegated '********* to the PictureBox-Resize-Event per PostMessage-Call 'just use a Msg with a somewhat higher prio than wm_keydown or wm_paint Private Sub TickReceiver_Resize() 'no retriggering, in case T_NextNote = 0 (this is "no-streaming" state) If T_NextNote = 0 Then Exit Sub CheckForNextNote timeGetTime End Sub Private Sub CheckForNextNote(ByVal T_Current As Long) If T_Current < T_NextNote Then 'we're not yet there, so... MMT_OneShot TickReceiver.hWnd, 1 '...just trigger the next round Exit Sub End If PlayNote NextNoteIdx 'first play the now current Note If NextNoteIdx >= UBound(Notes) Then 'we reached the end of the stream T_NextNote = 0 'T_NextNote at Zero is our "no-streaming" indicator Exit Sub End If 'prepare for the next round T_NextNote = T_NextNote + Notes(NextNoteIdx).Duration NextNoteIdx = NextNoteIdx + 1 'not yet at the end of the stream, so trigger the next MMTimer-OneShot MMT_OneShot TickReceiver.hWnd, 1 End Sub Private Sub PlayNote(ByVal CurNoteIdx As Long) If CurNoteIdx > UBound(Notes) Then Exit Sub 'just to make sure ... Print Notes(CurNoteIdx).Value, _ "played at Abs-StreamTime:"; timeGetTime - T_StartNote End Sub Olaf MM wrote:
> All I want is a replacement for Sleep, as in I'd setup a notification interface, myself, but what really matters is how you > > Sleep 32 > > as accurate as poss, where the duration might be as low as 1. > > How can I best achieve that with the CCRP Timer? intend to respond to the notification. Hard to tell with a quick browse of the thread -- did you get your objective(s) worked out? |
|||||||||||||||||||||||