珍現象、解決(?)す
ち~んw珍現象、解決す
ご心配をおかけしました。
前回
のち~んw珍現象がなぜか解決いたしましたので、ご報告申し上げまする。ニンニン。
あまりにしょうもない顛末なので、怒らないように。
これまでの対処法
カウントダウンさせるcountDown
メソッドの概略は、当初次のようなものだった。
リスト1
Public Sub countDown( _ Optional ByVal milliSecPerSec As Long = 1000) カウントダウン表示をする処理 End Sub
当り前だ。
で、次に、wavを再生するplaySound
メソッドを作成し、カウントダウン終了時にだけ鳴らせばよいわけだから、次のようにcountDown
メソッドの末尾に追加した。
リスト2
Public Sub countDown( _ Optional ByVal milliSecPerSec As Long = 1000) カウントダウン表示をする処理 Call playSound End Sub
カウントダウン表示が完了してからplaySound
メソッドを呼んでいるのだから、これで完璧のはずだった。しかし、実際は、〈表示が「あと0分01秒」となっているのに派手に音が鳴る〉という実にマヌケなことになっていたのだ。
なんでやねん。
私は焦った。
そこで、次のようにしてみた。
リスト3
Public Sub countDown( _ Optional ByVal milliSecPerSec As Long = 1000) カウントダウン表示をする処理 DoEvents Call playSound End Sub
見ての通り、カウントダウン処理の後、playSound
メソッドを呼ぶ前にDoEvents
をかましてみた。
それでもだめだった。
私はもっと焦った。
さらに次のようにしてみた。
リスト4
Public Sub countDown( _ Optional ByVal milliSecPerSec As Long = 1000) カウントダウン表示をする処理 DoEvents WindowsAPI関数でWaitする処理 Call playSound End Sub
カウントダウン表示処理終了後にWaitを入れてみたのである。
しかしこれでもダメだった。
私は途方に暮れた。
しかし、希望を捨てなかった。
ついにplaySound
メソッドをPublic
にして、呼び出し側を次のようにしてみた。
リスト5
Private Sub entry() KitchenTimerクラスのインスタンスを用意する処理 Call kitTimer.countDown(引数) Call kitTimer.playCount End Sub
countDown
メソッドとplaySound
メソッドを二段階に分けて呼び出すのだ。
まさに盤石の布陣。諸葛孔明の八卦の陣とて、この布陣には敵うまいて、ふふふ……。
結果は、やっぱりダメw
ついに万策尽き果てたのだった……。
なぜか治る
処理の節目節目にDebug.Print
を入れ、改めてplaySound
メソッドの前後に長めのWaitを入れてゴニョゴニョしていると、突然治った。
※音が鳴らないと何のことかわからないので、playSound
メソッドの開始時にA2セルに「演奏中」、終了時に「演奏おわり」と書き込む処理を加えています。
ほら。
で、一度この状態になると、
playSound
メソッドの冒頭にDoEvents
を入れるcountDown
メソッド内でplaySound
メソッド呼び出し前に1ミリ秒のWaitを入れる
など、とにかくどこかでDoEvents
かWaitを入れるようにすれば、ちゃんとカウントダウン終了後に音を鳴らしてくれるようになった。
おわりに
うーむ……、何だったんだろう……。
とりあえず、現時点でのKitchenTimer
クラスのコードを載っけておく。
クラスモジュール KitchenTimer
Option Explicit 'Win32API Declaration' 'For Calc Time' Private Declare Function GetTickCount Lib "kernel32" () As Long Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) 'For Play Sound' Private Declare Function mciSendString _ Lib "winmm.dll" _ Alias "mciSendStringA" ( _ ByVal lpstrCommand As String, _ ByVal lpstrReturnString As String, _ ByVal uReturnLength As Long, _ ByVal hwndCallback As Long) As Long 'Constants' Private Enum TimerStatus tsRemaining tsPaused tsFinished End Enum 'Module Level Variables' Private fsObj As New FileSystemObject 'Module Level Variables; Fields' Private minutes As Long Private seconds As Long Private soundSrcPath_ As String Private totalSeconds As Long Private status_ As TimerStatus Private minDisp As Range Private secDisp As Range Private statDisp As Range Private isInitialized As Boolean Private isPaused As Boolean Private Sub Class_Initialize() totalSeconds = 0 isPaused = True isInitialized = False End Sub 'Properties' Private Property Get Status() As String Dim ret As String Select Case status_ Case tsRemaining: ret = "あと" Case tsPaused: ret = "一時停止" Case tsFinished: ret = "終了!" End Select Status = ret End Property Public Property Get HasDone() As Boolean HasDone = True If status_ = tsFinished Then Exit Property HasDone = False End Property 'Methods' Public Sub init(ByVal minDisplay As Range, _ ByVal secDisplay As Range, _ ByVal statDisplay As Range, _ Optional ByVal soundSrcPath As String) Set minDisp = minDisplay Set secDisp = secDisplay Set statDisp = statDisplay If soundSrcPath <> "" Then If Not fsObj.FileExists(soundSrcPath) Or _ StrConv(Right(soundSrcPath, 4), vbLowerCase) <> ".wav" Then _ soundSrcPath = "" End If soundSrcPath_ = soundSrcPath isInitialized = True End Sub Public Sub countDown( _ Optional ByVal milliSecPerSec As Long = 1000) If Not isInitialized Then Exit Sub isPaused = False minutes = minDisp.Value If minutes < 0 Then minutes = 0 seconds = secDisp.Value If seconds < 0 Then seconds = 0 If seconds > 59 Then seconds = 0 totalSeconds = minutes * 60 + seconds If minutes + seconds = 0 Then Exit Sub Dim interval As Long interval = milliSecPerSec - 1 status_ = tsRemaining statDisp.Value = Status Do While totalSeconds > 0 If isPaused Then _ status_ = tsPaused: statDisp.Value = Status: _ Exit Do Call waitFor(interval) totalSeconds = totalSeconds - 1 minutes = totalSeconds \ 60 minDisp.Value = minutes seconds = totalSeconds Mod 60 secDisp.Value = seconds Loop If totalSeconds > 0 Then status_ = tsPaused If totalSeconds = 0 Then status_ = tsFinished statDisp.Value = Status Call playSound End If End Sub Public Sub pause() isPaused = Not isPaused End Sub Public Sub reset() isPaused = True totalSeconds = 0 minutes = 0 minDisp.Value = minutes seconds = 0 secDisp.Value = seconds status_ = tsFinished: statDisp.Value = Status End Sub 'Internal Methods' Private Function callGetTickCount() As Long Dim ret As Variant ret = GetTickCount() ret = CDec(ret) If ret < 0 Then ret = ret + 2 ^ 32 callGetTickCount = ret End Function Private Sub callSleep(ByVal milliSeconds As Long) Call Sleep(milliSeconds) End Sub Private Sub waitFor(ByVal milliSeconds As Long) Dim startTime As Long startTime = callGetTickCount() Dim endTime As Long Do Call Sleep(1) DoEvents endTime = callGetTickCount() Loop Until endTime - startTime > milliSeconds End Sub Private Sub playSound() If soundSrcPath_ = "" Then Exit Sub DoEvents Dim rc As Long soundSrcPath_ = """" & soundSrcPath_ & """" rc = mciSendString("Open " & soundSrcPath_, "", 0, 0) rc = mciSendString("Play " & soundSrcPath_ & " wait", "", 0, 0) rc = mciSendString("Close " & soundSrcPath_, "", 0, 0) status_ = tsPaused End Sub