Using a callback function with waveOutOpen in a dll to be called from Visual Basic
I want to create a dll that I can use in Visual Basic. The dll should generate custom sounds live of which I provide the frequencies, volumes and phases. I am trying to do this with the waveOutOpen call. The sound is 44100 Hz, stereo.
I created these functions to interface with VB:
DLLIMPORT short __stdcall SetPhase (short Channel, double Phase);
DLLIMPORT short __stdcall SetMasterVolume (double Volume);
DLLIMPORT short __stdcall SetVolume (short Channel,double Volume);
DLLIMPORT short __stdcall SetFrequency (short Channel,double Frequency);
DLLIMPORT short __stdcall StartSound (double MasterVolume, short NChannels, double Frequencies[], double Volumes[], double Phases[]);
DLLIMPORT short __stdcall StopSound ();
and the callback function:
void CALLBACK MyWaveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) ;
The StartSound function should take care of initialising the audio device and starting the callback function.
StopSound should terminate the callback calling.
The other functions just change the global variables for the various channels. (A channel is a sound of one frequency.)
Here is part of my code:
Code:
DLLIMPORT short __stdcall StartSound (double MasterVolume, short NChannels, double Frequencies[], double Volumes[], double Phases[])
{
// Variable used to stop the sound, to notify the callback function
bShutOff=FALSE;
SetMasterVolume (MasterVolume);
// Check and set the number of requested channels
if (NChannels<1 || NChannels>=MAX_CH) return -2;
siNChannels = NChannels;
// Initialise the global variables
short i;
for (i=0; i<siNChannels; i++)
{
SetFrequency (i, Frequencies[i]);
SetVolume (i, Volumes[i]);
SetPhase (i, Phases[i]);
}
if (hWaveOut!=NULL) waveOutReset (hWaveOut) ; // reset device when already open
// Speicher anfordern für 2 Header und 2 Puffer
pWaveHdr1 = malloc (sizeof (WAVEHDR)) ;
pWaveHdr2 = malloc (sizeof (WAVEHDR)) ;
pBuffer1 = malloc (OUT_BUFFER_SIZE) ;
pBuffer2 = malloc (OUT_BUFFER_SIZE) ;
if (!pWaveHdr1 || !pWaveHdr2 || !pBuffer1 || !pBuffer2)
{
if (!pWaveHdr1) free (pWaveHdr1) ;
if (!pWaveHdr2) free (pWaveHdr2) ;
if (!pBuffer1) free (pBuffer1) ;
if (!pBuffer2) free (pBuffer2) ;
MessageBeep (MB_ICONEXCLAMATION) ;
MessageBox (NULL, TEXT ("Not enough memory!"),
TEXT ("SecretSound.dll"), MB_ICONEXCLAMATION | MB_OK) ;
return TRUE ;
}
// Audio-Gerät für Ausgabe vorbereiten
waveformat.wFormatTag = WAVE_FORMAT_PCM ;
waveformat.nChannels = 2 ;
waveformat.nSamplesPerSec = SAMPLE_RATE ;
waveformat.wBitsPerSample = 16 ;
waveformat.nBlockAlign = waveformat.nChannels * waveformat.wBitsPerSample / 8 ;
waveformat.nAvgBytesPerSec = waveformat.nBlockAlign * waveformat.nSamplesPerSec ;
waveformat.cbSize = 0 ;
if (waveOutOpen (&hWaveOut, WAVE_MAPPER, &waveformat,
(DWORD) MyWaveOutProc, 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
{ // Soundkarte besetzt oder nicht vorhanden
free (pWaveHdr1) ;
free (pWaveHdr2) ;
free (pBuffer1) ;
free (pBuffer2) ;
hWaveOut = NULL ;
MessageBeep (MB_ICONEXCLAMATION) ;
MessageBox (NULL,
TEXT ("Error occured while opening audio device!"),
TEXT ("SecretSound.dll"), MB_ICONEXCLAMATION | MB_OK) ;
return TRUE ;
}
// Header initialisieren und vorbereiten
pWaveHdr1->lpData = pBuffer1 ;
pWaveHdr1->dwBufferLength = OUT_BUFFER_SIZE;
pWaveHdr1->dwBytesRecorded = 0 ;
pWaveHdr1->dwUser = 0 ;
pWaveHdr1->dwFlags = 0 ;
pWaveHdr1->dwLoops = 0 ;
pWaveHdr1->lpNext = NULL ;
pWaveHdr1->reserved = 0 ;
ret=waveOutPrepareHeader (hWaveOut, pWaveHdr1, sizeof (WAVEHDR)) ;
ShowError (ret, TEXT("waveOutPrepareHeader (hWaveOut, pWaveHdr1, sizeof (WAVEHDR))"));
pWaveHdr2->lpData = pBuffer2 ;
pWaveHdr2->dwBufferLength = OUT_BUFFER_SIZE;
pWaveHdr2->dwBytesRecorded = 0 ;
pWaveHdr2->dwUser = 0 ;
pWaveHdr2->dwFlags = 0 ;
pWaveHdr2->dwLoops = 0 ;
pWaveHdr2->lpNext = NULL ;
pWaveHdr2->reserved = 0 ;
ret=waveOutPrepareHeader (hWaveOut, pWaveHdr2, sizeof (WAVEHDR)) ;
ShowError (ret, TEXT("waveOutPrepareHeader (hWaveOut, pWaveHdr2, sizeof (WAVEHDR))"));
// Zwei Pufferinhalte an Audio-Gerät übergeben
FillBuffer (pBuffer1) ;
ret=waveOutWrite (hWaveOut, pWaveHdr1, sizeof (WAVEHDR)) ;
ShowError (ret, TEXT("waveOutWrite (hWaveOut, pWaveHdr1, sizeof (WAVEHDR))"));
FillBuffer (pBuffer2) ;
ret=waveOutWrite (hWaveOut, pWaveHdr2, sizeof (WAVEHDR)) ;
ShowError (ret, TEXT("waveOutWrite (hWaveOut, pWaveHdr2, sizeof (WAVEHDR))"));
return 0;
}
DLLIMPORT short __stdcall StopSound ()
{
if (hWaveOut!=NULL)
{
DebugPrint (1,"Stop",TEXT("StopSound"));
ret=waveOutClose (hWaveOut) ;
ShowError (ret,TEXT("waveOutClose (hWaveOut)"));
DebugPrint (2,"Stop",TEXT("StopSound"));
if (ret==0)
{
ret=waveOutReset (hWaveOut) ;
ShowError (ret, TEXT("waveOutReset (hWaveOut)"));
}
DebugPrint (3,"Stop",TEXT("StopSound"));
hWaveOut=NULL;
}
return 0;
}
void CALLBACK MyWaveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
switch (uMsg)
{
case MM_WOM_OPEN: // diese Nachricht folgt auf den Aufruf waveOutOpen
return;
case MM_WOM_DONE: // Puffer abgespielt
// Puffer erneut füllen und an Audio-Gerät übergeben
FillBuffer (((PWAVEHDR) dwParam1)->lpData) ;
ret=waveOutWrite (hWaveOut, (PWAVEHDR) dwParam1, sizeof (WAVEHDR)) ;
ShowError (ret, TEXT(""));
return;
case MM_WOM_CLOSE: // Reaktion auf waveOutReset
ret=waveOutUnprepareHeader (hWaveOut, pWaveHdr1, sizeof (WAVEHDR)) ;
ShowError (ret, TEXT("waveOutUnprepareHeader (hWaveOut, pWaveHdr1, sizeof (WAVEHDR))"));
if (ret==0)
{
free (pWaveHdr1) ;
free (pBuffer1) ;
}
ret=waveOutUnprepareHeader (hWaveOut, pWaveHdr2, sizeof (WAVEHDR)) ;
ShowError (ret, TEXT("waveOutUnprepareHeader (hWaveOut, pWaveHdr2, sizeof (WAVEHDR))"));
if (ret==0)
{
free (pWaveHdr2) ;
free (pBuffer2) ;
}
MessageBox (NULL, TEXT ("MM_WOM_CLOSE"),
TEXT ("SecretSound.dll"), MB_ICONEXCLAMATION | MB_OK) ;
return;
}
}
I have been staring at it for days, but I can not get any clou as to what I am doing wrong (or right ;-)
Some advice would be highly appreciated!