Thread: Using a callback function with waveOutOpen in a dll to be called from Visual Basic

  1. #1
    Registered User
    Join Date
    Jul 2008
    Posts
    3

    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!

  2. #2
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    What, exactly, is the problem?
    Do you pass your arrays in VB by taking the address of the first element (instead of passing the array directly)?
    It's one common pitfall, because VB stores the length of the array before the actual array.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  3. #3
    Registered User
    Join Date
    Jul 2008
    Posts
    3
    That seemsto be okay, I pass the first element.
    (How To Pass Arrays Between Visual Basic and C, http://support.microsoft.com/kb/207931/en-us)

    The problem seems that I do not hear sound and the VB program crashes when using the StopSound function.

    MM_WOM_CLOSE never gets called...

    This is how I generate the sound buffers.

    Code:
    VOID StoreSample (PBYTE pBuffer, LONG i, DOUBLE dValueL, DOUBLE dValueR)
    {
    	  INT iValueL, iValueR ;
    
    	  iValueL = (INT) (32767 * dValueL) ;
    	  iValueR = (INT) (32767 * dValueR) ;
    
    	  pBuffer [i * 4] = (BYTE) (iValueL & 0x00ff) ;
    	  pBuffer [i * 4 + 1] =  (BYTE) ((iValueL & 0xff00) / 0x100);
    	  pBuffer [i * 4 + 2] = (BYTE) (iValueR & 0x00ff) ;
    	  pBuffer [i * 4 + 3] =  (BYTE) ((iValueR & 0xff00) / 0x100);
    }
    
    VOID FillBuffer (PBYTE pBuffer)
    {
         static DOUBLE fAngle[MAX_CH] ;   // Phasenwinkel
         static LONG          i, j ;
    	 static DOUBLE dTmpL, dTmpR ;
    	 static DOUBLE dFreqs[MAX_CH] ;
    
         // Calculate the frequency factors
         for (i=0 ; i<MAX_CH ; i++)
    	 {
    	    dFreqs[i]=PIx2 * dblFreq[i] / SAMPLE_RATE ;
    	 }
    
    	 // Generate the sound data
    	 for (i = 0 ; i < OUT_BUFFER_SIZE / 4 ; i++)
         {
    
            dTmpL =0 ;
            dTmpR =0 ;
    
            for (j = 0 ; j < MAX_CH ; j++)
            {
    
                dTmpL += dblVol[j] * sin (fAngle[j]) ;
                dTmpR += dblVol[j] * sin (fAngle[j] + dblPhase[j]) ;
                fAngle[j] +=  dFreqs[j] ;
                if (fAngle[j] > PIx2) fAngle[j] -= PIx2 ;
    
            	// Average the MAX_CH sines and apply master volume
            	dTmpL = dblVolume * dTmpL / siNChannels ;
            	dTmpR = dblVolume * dTmpR / siNChannels ;
    
            	StoreSample (pBuffer, i, dTmpL, dTmpR) ;
            }
         }
    }
    Thanks!

  4. #4
    Registered User
    Join Date
    Jul 2008
    Posts
    3
    Solved the problem:

    I had to use WOM_DONE in stead of MM_WOM_DONE, because I was using the callback function, not the windows procedure.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 4
    Last Post: 05-13-2011, 08:28 AM
  2. Compiling sample DarkGDK Program
    By Phyxashun in forum Game Programming
    Replies: 6
    Last Post: 01-27-2009, 03:07 AM
  3. Can we have vector of vector?
    By ketu1 in forum C++ Programming
    Replies: 24
    Last Post: 01-03-2008, 05:02 AM
  4. Replies: 28
    Last Post: 07-16-2006, 11:35 PM
  5. c++ linking problem for x11
    By kron in forum Linux Programming
    Replies: 1
    Last Post: 11-19-2004, 10:18 AM