[RESOLVED] Pls help to debug a piece of subclass code about audio.
I don't like subclass very much and always avoid it in my program. But the winmm.dll APIs that play wave files must use the callback function, which means that subclass must be used.
I found a piece of source code on the web, and after clearing some of its bugs, the source code now works. I want to encapsulate this code as a Class, and replace the subclass in this source code with the safe-subclass from TheTrick's clsTrickSound2, so I made a few changes to this source code. After the change, the program doesn't work. I don't know where the problem is.
The original code (which works fine):
Code:
Declare Function CallWindowProc Lib "user32" _
Alias "CallWindowProcA" ( _
ByVal lpPrevWndFunc As Long, _
ByVal hWnd As Long, _
ByVal msg As Long, _
ByVal wParam As Long, _
ByRef lParam As WAVEHDR) As Long
Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, ByVal wParam As Long, ByRef wavhdr As WAVEHDR) As Long
Static dataRemaining As Long
If (uMsg = MM_WOM_DONE) Then
If (fPlaying = True) Then
'{
dataRemaining = (dataOffset + audioLength - mmioSeek(hmmioIn, 0, SEEK_CUR))
If (bufferSize < dataRemaining) Then
rc = mmioRead(hmmioIn, wavhdr.lpData, bufferSize)
Else
rc = mmioRead(hmmioIn, wavhdr.lpData, dataRemaining)
fPlaying = False
End If
wavhdr.dwBufferLength = rc
rc = waveOutWrite(hWaveOut, wavhdr, Len(wavhdr))
'}
Else
For i = 1 To NUM_BUFFERS
waveOutUnprepareHeader hWaveOut, hdr(i), Len(hdr(i))
Next
waveOutClose hWaveOut
End If
End If
WindowProc = CallWindowProc(lpPrevWndProc, hw, uMsg, wParam, wavhdr)
End Function
My modified code (which doesn't work):
Code:
Declare Function CallWindowProc_B Lib "user32" _
Alias "CallWindowProcA" ( _
ByVal lpPrevWndFunc As Long, _
ByVal hWnd As Long, _
ByVal msg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Static dataRemaining As Long
Dim tWaveHdr As WAVEHDR
If (uMsg = MM_WOM_DONE) Then
If (fPlaying = True) Then
memcpy tWaveHdr, ByVal lParam, Len(tWaveHdr)
dataRemaining = (dataOffset + audioLength - mmioSeek(hmmioIn, 0, SEEK_CUR))
If (bufferSize < dataRemaining) Then
rc = mmioRead(hmmioIn, tWaveHdr.lpData, bufferSize)
Else
rc = mmioRead(hmmioIn, tWaveHdr.lpData, dataRemaining)
fPlaying = False
End If
tWaveHdr.dwBufferLength = rc
rc = waveOutWrite(hWaveOut, tWaveHdr, Len(tWaveHdr))
Else
For i = 1 To NUM_BUFFERS
waveOutUnprepareHeader hWaveOut, hdr(i), Len(hdr(i))
Next
waveOutClose hWaveOut
End If
End If
WindowProc = CallWindowProc_B(lpPrevWndProc, hw, uMsg, wParam, lParam)
End Function
Last edited by dreammanor; Dec 8th, 2017 at 11:47 AM.
Re: Pls help to debug a piece of subclass code about audio.
One thing you are not doing...
The original passed the UDT byRef. And changes are being made to it in the subclass routine. Ok?
In your modified version, you are making changes only to the copy. Once changes are made, you need to replace the version at lParam with the copy you modified, i.e.,
memcpy ByVal lParam, tWaveHdr, Len(tWaveHdr)
Insomnia is just a byproduct of, "It can't be done"
Re: Pls help to debug a piece of subclass code about audio.
Hi LaVolpe, thanks for your reply. I don't quite understand what you mean. please read the red words above, or look at the source code of the attachment. Really appreciate any help you can provide.
Edit:
Oh, I see, I'll try it now.
Last edited by dreammanor; Dec 8th, 2017 at 09:40 PM.
Re: Pls help to debug a piece of subclass code about audio.
I modified it as you said, but the program still crashes.
Code:
Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Static dataRemaining As Long
Dim tWaveHdr As WAVEHDR
If (uMsg = MM_WOM_DONE) Then
If (fPlaying = True) Then
memcpy tWaveHdr, ByVal lParam, Len(tWaveHdr)
dataRemaining = (dataOffset + audioLength - mmioSeek(hmmioIn, 0, SEEK_CUR))
If (bufferSize < dataRemaining) Then
rc = mmioRead(hmmioIn, tWaveHdr.lpData, bufferSize)
Else
rc = mmioRead(hmmioIn, tWaveHdr.lpData, dataRemaining)
fPlaying = False
End If
tWaveHdr.dwBufferLength = rc
rc = waveOutWrite(hWaveOut, tWaveHdr, Len(tWaveHdr))
memcpy ByVal lParam, tWaveHdr, Len(tWaveHdr)
Else
For i = 1 To NUM_BUFFERS
waveOutUnprepareHeader hWaveOut, hdr(i), Len(hdr(i))
Next
waveOutClose hWaveOut
End If
End If
WindowProc = CallWindowProc_B(lpPrevWndProc, hw, uMsg, wParam, lParam)
End Function
The following code comes from TheTrick's clsTrickSound2, TheTrick didn't replace the lParam with the hdr(didn't use the "memcpy ByVal lParam, hdr, Len (hdr)") . But his program can run normally. I don't know why. (Note: TheTrick uses DefWindowProc instead of CallWindowProc)
Code:
Private Function WndProc( _
ByVal hwnd As Long, _
ByVal Msg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Dim idx As Long
Dim hdr As WAVEHDR
If unavailable Then
Select Case Msg
Case MM_WIM_DATA
memcpy hdr, ByVal lParam, Len(hdr)
RaiseEvent NewData(hdr.lpData, mSmpCount * mFormat.nBlockAlign)
idx = GetBufferIndex(hdr.lpData)
If idx = -1 Then Exit Function
waveInAddBuffer hWaveIn, Buffers(idx).Header, Len(Buffers(idx).Header)
Exit Function
Case MM_WOM_DONE
If mActive Then
memcpy hdr, ByVal lParam, Len(hdr)
RaiseEvent NewData(hdr.lpData, mSmpCount * mFormat.nBlockAlign)
idx = GetBufferIndex(hdr.lpData)
If idx = -1 Then Exit Function
waveOutWrite hWaveOut, Buffers(idx).Header, Len(Buffers(idx).Header)
End If
Exit Function
End Select
End If
WndProc = DefWindowProc(hwnd, Msg, wParam, lParam)
End Function
Last edited by dreammanor; Dec 8th, 2017 at 10:04 PM.
Re: Pls help to debug a piece of subclass code about audio.
I modified your sample project just as you did above. Crashes.
So, first I want you to know that I know little to nothing about those APIs. Just tweaked a couple things, I did get it to work, but required some re-organizing and an API declaration tweak.
Re: Pls help to debug a piece of subclass code about audio.
Hi LaVolpe, thank you so much. After I modified the code as you said, the program doesn't crash now. But I feel very strange, why TheTrick's code is normal, and my code is similar to his code and crashes.
Originally Posted by LaVolpe
Above said and done. Not sure why you want to fix something that ain't broke
I need to use TheTrick's clsTrickSound2 to process my wave files, but clsTrickSound2 doesn't have the OpenWaveFile function and can't open wave files directly, it only accepts byte arrays. So I need to analyze the wave file format myself and read the audio data from the wave file into a byte array for clsSound2 to process. In order to deal with large wave files and to play audio smoothly, I also need to use multiple buffer mechanism to fill the byte arrays. These changs or operations are quite easy for an audio expert like TheTrick, but it's very difficult for me because I don't know much about the audio APIs (I just started learning about it for some time). I looked for a piece of source code from the Internet, hoping to be able to merge this source code into TheTrick's clsTrickSound2 class, so I need to rewrite this source code according to the API declarations in the clsTrickSound2.
I still don't know if I can successfully merge these two pieces of code together. Your help greatly enhances my confidence. Thanks very much.
Last edited by dreammanor; Dec 9th, 2017 at 03:53 AM.
Re: Pls help to debug a piece of subclass code about audio.
Hi LaVolpe, I'm sorry to disturb you again. Now I changed the Module1.bas to a class and replaced the subclass in it with the safe-subclass from TheTrick's cslTrickSound2. After making these changes, the program crashed when I clicked the Play button on the form.
Do I need to change all "ByRef wavhdr As WAVEHDR" in all the winmm.dll API declarations to "ByVal lParam As Long"?
Could you help me find out what is the reason? Really appreciate any help you can provide.
Re: Pls help to debug a piece of subclass code about audio.
I don't use that type of ASM subclassing (using the heap). Additionally, no idea even if the ASM is correct or not. Wherever you got that (and it may very well be valid code), suggest you put together a test project as simple as possible to see if you can get subclassing to call back to your class. Just FYI. You don't need to go ASM, thunk-type subclassing. Just use a module
Edited: Just taking a closer look at the thunk code. This line:
I'm making the assumption that the window procedure in your class must be at a specific position within the class, i.e., 1st, 2nd, 3rd, etc procedure. Additionally, is the window procedure routine suppose to be Public vs. Private? Again, I'd look at the project you got that code from & see what the relationship is between the window procedure in that project with its position in the class.
Last edited by LaVolpe; Dec 9th, 2017 at 12:01 PM.
Insomnia is just a byproduct of, "It can't be done"
I used subclass in my controls 10 years ago. I downloaded a lot of subclass code from PsCode, but I never found an absolutely safe subclass solution, and then I removed all the subclass from my program. Now, I have completely forgotten the subclass knowledge I have ever learned.
I know Krool, TheTrick, DEXWERX, vbAccelerator and you have adopted different subclass solutions, I don't know which solution is the most suitable for me.
Re: Pls help to debug a piece of subclass code about audio.
Here's what I see based on his class.
1. The WNDPROCINDEX constant has the value 20
2. The line I mentioned above (GetMem4 ByVal vTable + WNDPROCINDEX * 4 + &H1C, lpMeth) is using a known class offset of &H1C. Offset is 1st public method of a class.
3. The "Private Function WndProc(...)" routine happens to be the 1st private function after the 20th Public method. Note "method" means: sub, function, property. 20 cannot be coincidence
So based on the above, I'd say a simple test is
1. Place all your Public methods at the top of your class, no exceptions. Order doesn't matter but no private methods between your declarations section and any Public method
2. Add your window procedure routine as the 1st private method after the last Public method
3. Count how many Public methods you have starting immediately after the declarations section. Change the WNDPROCINDEX to that number. If you add any public methods afterwards, ensure it's above the 1st private method & adjust that constant. Likewise, if you remove any public methods, adjust that number. Never place a new private method above your window procedure routine.
4. Save and hope for the best?
note: I wouldn't use any public variables in the declarations section. If needed, make them Public properties; otherwise, your count of Public methods may be off.
Edited: Don't count Public Event() declarations in the declarations section. In the class, there is one and it appears it is not part of the count.
Last edited by LaVolpe; Dec 9th, 2017 at 02:29 PM.
Insomnia is just a byproduct of, "It can't be done"
Re: Pls help to debug a piece of subclass code about audio.
The reason is exactly what you said, now the program no longer crashes.
Previously, I had seen some subclasses that require WndProc to be placed in a specific location, but I didn't understand what it meant at that time. Now I finally understand.