Wave Driver介绍-3(Wave API waveOutSetVolume)

1.1 定义及简要说明

函数waveOutSetVolume可以用来调整系统的音量,其定义如下:

MMRESULT WINAPI waveOutSetVolume(HWAVEOUT hwo, DWORD dwVolume);

         其中,第一个参数HWAVEOUT hwo可以传入两种值,第一种是wave-out设备ID号,此时函数调整的是设备音量,第二种是Stream句柄,调整的是所指向的Stream的音量。

         今天为这个地方晕乎乎了半天,最后还是写了点测试程序才搞懂,有关这个函数的具体使用方法及操作细节请看下面的验证过程。

注:

         下面的代码实际上是一个应用程序中部分代码,有关该应用程序的源码可以从我的资源中下载,主界面如下:

Wave Driver介绍-3(Wave API waveOutSetVolume)_第1张图片

1.2 给函数传入wave-out设备ID的验证

         下面的代码实现了对设备音量进行调整的功能,对应上图中的LoudSoft按键的功能。其中函数void CWaveAPI_TestDlg::OnBnClickedButton1()实现了将设备音量(00xffff ffff)增加0x1000的功能,也即Loud按钮的功能,而按钮Soft的功能由函数void CWaveAPI_TestDlg::OnBnClickedButton2()实现,每次点击该按钮设备音量将减少0x1000

详细如下:

static DWORD g_dwWaveOutGain = 0;

 

void CWaveAPI_TestDlg::OnBnClickedButton1()

{

     // TODO: Add your control notification handler code here

     DWORD dwDeviceID = 0;

     DWORD deDeviceGain = 0;

 

     for (dwDeviceID = 0; dwDeviceID < waveOutGetNumDevs(); dwDeviceID++)  {

 

         NKDbgPrintfW(L"Device #%d/r/n", dwDeviceID);

 

         waveOutSetVolume((HWAVEOUT)dwDeviceID, g_dwWaveOutGain);

         waveOutGetVolume((HWAVEOUT)dwDeviceID, &deDeviceGain);

 

         NKDbgPrintfW(L"/r/n         dst 0x%8x, read 0x%8x/r/n", g_dwWaveOutGain, deDeviceGain);

 

         if (g_dwWaveOutGain <= (0xffffffff - 0x1000))

              g_dwWaveOutGain+=0x1000;

         else

              g_dwWaveOutGain = 0xffffffff;

     }

}

 

void CWaveAPI_TestDlg::OnBnClickedButton2()

{

     // TODO: Add your control notification handler code here    

     DWORD dwDeviceID = 0;

     DWORD deDeviceGain = 0;

 

     for (dwDeviceID = 0; dwDeviceID < waveOutGetNumDevs(); dwDeviceID++)  {

 

         NKDbgPrintfW(L"Device #%d/r/n", dwDeviceID);

 

         waveOutSetVolume((HWAVEOUT)dwDeviceID, g_dwWaveOutGain);

         waveOutGetVolume((HWAVEOUT)dwDeviceID, &deDeviceGain);

 

         NKDbgPrintfW(L"/r/n         dst 0x%8x, read 0x%8x/r/n", g_dwWaveOutGain, deDeviceGain);

 

         if (g_dwWaveOutGain >= 0x1000)

              g_dwWaveOutGain-=0x1000;

         else

              g_dwWaveOutGain = 0;

     }

}

1.3 给函数传入Stream句柄的验证

         下面的代码中包括下面三个函数:

int StringFormatToWaveFormatEx( WAVEFORMATEX *wfx, const TCHAR* szWaveFormat );

ULONG SineWave( void* pBuffer, ULONG ulNumBytes, WAVEFORMATEX *pwfx, double dFrequency );

void PlayBackSound(bool bLoud);

         其中第一个函数StringFormatToWaveFormatEx实现了格式转换的功能。我们一般都习惯上使用形如“WAVE_FORMAT_1M08”的标记来表示wave in/out的格式,该函数完成在形如“WAVE_FORMAT_1M08”的标志和结构体WAVEFORMATEX之间进行等价转换的功能。

/* defines for dwFormat field of WAVEINCAPS and WAVEOUTCAPS */

#define WAVE_INVALIDFORMAT     0x00000000       /* invalid format */

#define WAVE_FORMAT_1M08       0x00000001       /* 11.025 kHz, Mono,   8-bit  */

#define WAVE_FORMAT_1S08       0x00000002       /* 11.025 kHz, Stereo, 8-bit  */

#define WAVE_FORMAT_1M16       0x00000004       /* 11.025 kHz, Mono,   16-bit */

#define WAVE_FORMAT_1S16       0x00000008       /* 11.025 kHz, Stereo, 16-bit */

#define WAVE_FORMAT_2M08       0x00000010       /* 22.05  kHz, Mono,   8-bit  */

#define WAVE_FORMAT_2S08       0x00000020       /* 22.05  kHz, Stereo, 8-bit  */

#define WAVE_FORMAT_2M16       0x00000040       /* 22.05  kHz, Mono,   16-bit */

#define WAVE_FORMAT_2S16       0x00000080       /* 22.05  kHz, Stereo, 16-bit */

#define WAVE_FORMAT_4M08       0x00000100       /* 44.1   kHz, Mono,   8-bit  */

#define WAVE_FORMAT_4S08       0x00000200       /* 44.1   kHz, Stereo, 8-bit  */

#define WAVE_FORMAT_4M16       0x00000400       /* 44.1   kHz, Mono,   16-bit */

#define WAVE_FORMAT_4S16       0x00000800       /* 44.1   kHz, Stereo, 16-bit */

typedef struct

{

    WORD        wFormatTag;         // format type

    WORD        nChannels;          // number of channels (i.e. mono, stereo...)

    DWORD       nSamplesPerSec;     // sample rate

    DWORD       nAvgBytesPerSec;    // for buffer estimation

    WORD        nBlockAlign;        // block size of data

    WORD        wBitsPerSample;     // number of bits per sample of mono data

    WORD        cbSize;             // the count in bytes of the size of

                                    // extra information (after cbSize)

} WAVEFORMATEX, *PWAVEFORMATEX, *LPWAVEFORMATEX;

         接下来说第二个函数SineWave,它完成根据调用者传入的参数构建正选波数据到pBuffer的功能,可以省却我们解析wav文件的烦恼。

         第三个函数PlayBackSound是今天的主角,它完成了创建Stream,并使用Stream播放音频的同时调整音量的功能。

         详细的代码如下:

// 转换数据

int StringFormatToWaveFormatEx( WAVEFORMATEX *wfx, const TCHAR* szWaveFormat )

{

     int iRet;

     DWORD tr=true;

     TCHAR channels,bits1,bits2;

 

     ZeroMemory(wfx,sizeof(*wfx));

 

 

     iRet = _stscanf ( szWaveFormat, TEXT("WAVE_FORMAT_%i%c%c%c"), &wfx->nSamplesPerSec, &channels, &bits1, &bits2 );

     if( iRet != 4 )

     {

         NKDbgPrintfW( ( TEXT( "ERROR:  %s not recognized/n" ), (LPWSTR)szWaveFormat ) );

         NKDbgPrintfW(TEXT("Possible Cause:  Supplied format not in the form of WAVE_FORMAT%%i%%c%%c%%c/n"));

         tr = false;

         goto Error;

     }

 

     wfx->wFormatTag = WAVE_FORMAT_PCM;

     switch( channels )

     {

     case 'M':

     case 'm':

         wfx->nChannels=1;

         break;

 

     default:

         wfx->nChannels=2;

     }

 

     switch( wfx->nSamplesPerSec )

     {

     case 1:

     case 2:

     case 4:

         wfx->nSamplesPerSec=11025*wfx->nSamplesPerSec;

     }

 

     wfx->wBitsPerSample=(bits1-TEXT('0'))*10+(bits2-TEXT('0'));

     wfx->nBlockAlign=wfx->nChannels*wfx->wBitsPerSample/8;

     wfx->nAvgBytesPerSec=wfx->nSamplesPerSec*wfx->nBlockAlign;

 

Error:

     return tr;

}

// 产生正选波数据

ULONG SineWave( void* pBuffer, ULONG ulNumBytes, WAVEFORMATEX *pwfx, double dFrequency )

{

     double dPhase = 0.0;

     double dAmplitude = 1.0;

     char *pClear, *pClearEnd;

     const double TWOPI = 2* 3.1415926535897931;

 

     pClearEnd = (char*)pBuffer + ulNumBytes;

     pClear = pClearEnd - ulNumBytes % 4;

     while( pClear<pClearEnd )

     {

         *pClear = 0;

         pClear++;

     }

 

     int nsamples = ulNumBytes / pwfx->nBlockAlign;

     int i, m_t0 = 0;

     int m_dSampleRate = pwfx->nSamplesPerSec;

     double dMultiplier, dOffset, dSample;

 

     if( pwfx->wBitsPerSample == 8 )

     {

         unsigned char * pSamples = (unsigned char *) pBuffer;

         dMultiplier = 127.0;

         dOffset = 128.0;

         if( pwfx->nChannels == 1 )

         {

              for (i = 0; i < nsamples; i ++ )

              {

                   double t = ((double) (i+m_t0)) / m_dSampleRate;

                   dSample = dAmplitude * sin( t*dFrequency* TWOPI + dPhase );

                   pSamples[i] = (unsigned char) (dSample * dMultiplier + dOffset);

              }

         }

         else

         {

              for (i = 0; i < nsamples; i ++)

              {

                   double t = ((double) (i+m_t0)) / m_dSampleRate;

                   dSample = dAmplitude * sin (t*dFrequency* TWOPI + dPhase);

                   pSamples[2*i] = (unsigned char) (dSample * dMultiplier + dOffset);

                   pSamples[2*i+1] = pSamples[2*i]; // replicate across both channels

              }

         }

     }

     else

     {

         short * pSamples = (short *) pBuffer;

         const double dMultiplier2 = 32767.0;

         const double dOffset2 = 0.0;

         if( pwfx->nChannels == 1 )

         {

              for (i = 0; i < nsamples; i += 1)

              {

                   double t = ((double) (i+m_t0)) / m_dSampleRate;

                   dSample = dAmplitude * sin (t*dFrequency* TWOPI + dPhase);

                   pSamples[i] = (short) (dSample * dMultiplier2 + dOffset2);

              }

         }

         else

         {

              for (i = 0; i < nsamples; i ++)

              {

                   double t = ((double) (i+m_t0)) / m_dSampleRate;

                   dSample = dAmplitude * sin( t*dFrequency* TWOPI + dPhase );

                   pSamples[2*i] = (short) (dSample * dMultiplier2 + dOffset2);

                   pSamples[2*i+1] = pSamples[2*i]; // replicate across both channels

              }

         }

     }

     m_t0 += i;

     return ulNumBytes;

}

/*

功能:

播放正选波声音文件

参数:

bLoud:调整播放声音增益

true   增大

false  减小

返回值:

*/

void PlayBackSound(bool bLoud)

{

     // TODO: Add your control notification handler code here

     HWAVEOUT       hwo = NULL;

     WAVEFORMATEX  wfx;

     MMRESULT hr;

     static HANDLE hEvent;

     WAVEHDR wh;

     char *data = NULL;

     DWORD dwSeconds = 1;

     double dFrequency = 440.0;

     DWORD dwExpectedPlaytime, dwTime = 0;

     DWORD dwSleepInterval            = 50;    //50 milliseconds

 

     // ***********************************************************************

     // 1. 初始化相关资源

     // ***********************************************************************

     // 初始化WAVEFORMATEX变量wfx

     if (!StringFormatToWaveFormatEx(&wfx,TEXT("WAVE_FORMAT_2M16")))  {

         NKDbgPrintfW(L"Convert Audio Format Failed!/r/n");

         return ;

     }

 

     // 输出格式信息

     NKDbgPrintfW(L"************************************/r/n");

     NKDbgPrintfW(L"wfx.wBitsPerSample %d/r/n", wfx.wBitsPerSample);

     NKDbgPrintfW(L"wfx.nChannels %d/r/n", wfx.nChannels);

     NKDbgPrintfW(L"wfx.nSamplesPerSec %d/r/n", wfx.nSamplesPerSec);

     NKDbgPrintfW(L"************************************/r/n");

 

     hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );

 

     // 打开wave out设备

     hr = waveOutOpen(&hwo, 0, &wfx, NULL,(DWORD)hEvent, CALLBACK_NULL);

     if(hr != MMSYSERR_NOERROR)

     {

         NKDbgPrintfW(L"waveOutOpen failed! Error number 0x%x/r/n", hr);

         return ;

     }

 

     // 分配buffer

     data = new char[dwSeconds * wfx.nAvgBytesPerSec];

     if (!data)    {

         NKDbgPrintfW(L"Out of memeory/r/n");

         return ;

     }

 

     ZeroMemory(data, dwSeconds * wfx.nAvgBytesPerSec);

 

     // 初始化whWAVEHDR

     ZeroMemory(&wh, sizeof(WAVEHDR));

     wh.lpData = data;

     wh.dwBufferLength = dwSeconds * wfx.nAvgBytesPerSec;

     wh.dwLoops = 1;

     wh.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP;

     dwExpectedPlaytime = wh.dwBufferLength * 1000 / wfx.nAvgBytesPerSec;

 

     // 创造声波

     SineWave(wh.lpData, wh.dwBufferLength, &wfx,dFrequency );

 

     // ***********************************************************************

     // 2. 开始播放声音操作

     // ***********************************************************************

     // 调整Stream音量 

     DWORD deDeviceGain = 0;

 

     waveOutSetVolume((HWAVEOUT)hwo, g_dwWaveOutGain);

     waveOutGetVolume((HWAVEOUT)hwo, &deDeviceGain);

 

     NKDbgPrintfW(L"Volume of device 0 successfully set to 0x%8x, previous 0x%8x/r/n", g_dwWaveOutGain, deDeviceGain);

 

     if (bLoud)    {

         if (g_dwWaveOutGain <= (0xffffffff - 0x1000))

              g_dwWaveOutGain+=0x1000;

         else

              g_dwWaveOutGain = 0xffffffff;

     }else    {

         if (g_dwWaveOutGain >= 0x1000)

              g_dwWaveOutGain-=0x1000;

         else

              g_dwWaveOutGain = 0;

     }

 

     // 准备buffer

     hr = waveOutPrepareHeader(hwo, &wh, sizeof(WAVEHDR));

     if(hr != MMSYSERR_NOERROR)

     {

         NKDbgPrintfW(L"waveOutPrepareHeader failed! Error number 0x%x/r/n", hr);

         return ;

     }

 

     // 写入buffer

     hr = waveOutWrite(hwo, &wh, sizeof(WAVEHDR));

 

     while( (!(wh.dwFlags&WHDR_DONE)) && ( dwTime<dwExpectedPlaytime+1000) )

     {

         Sleep( dwSleepInterval );

          dwTime += dwSleepInterval;

     }

 

     // ***********************************************************************

     // 3. 销毁所有资源

     // ***********************************************************************

     NKDbgPrintfW(L"Destroy all the resource/r/n");

     waveOutReset(hwo);

     if(hr != MMSYSERR_NOERROR)  {

         NKDbgPrintfW(L"waveOutUnprepareHeader failed! Error number 0x%x/r/n", hr);

         return ;

     }

 

     hr = waveOutUnprepareHeader(hwo,&wh,sizeof(WAVEHDR) );

     if(hr != MMSYSERR_NOERROR)  {

         NKDbgPrintfW(L"waveOutUnprepareHeader failed! Error number 0x%x/r/n", hr);

         return ;

     }

 

     waveOutClose(hwo);

     if(hr != MMSYSERR_NOERROR)  {

         NKDbgPrintfW(L"waveOutUnprepareHeader failed! Error number 0x%x/r/n", hr);

         return ;

     }

 

     if (data)

         delete []data;

 

     if( hEvent)

         CloseHandle(hEvent);

}

         前面提到,应用程序中有四个按钮,左边的两个按钮已经讲过,右边的两个按钮“Loud Stream”和“Soft Stream”的实现就是通过调用上面的函数PlayBackSound完成,这部分代码如下:

void CWaveAPI_TestDlg::OnBnClickedButton3()

{

     PlayBackSound(true);

}

 

void CWaveAPI_TestDlg::OnBnClickedButton4()

{

     // TODO: Add your control notification handler code here    

     PlayBackSound(false);

}

 

你可能感兴趣的:(api,Stream,null,buffer,callback,winapi)