最近搞了一個用於播放網路上傳來的PCM資料的網路播放聲音的東東,這裡是主要是想說明一下如何使用waveXXX系列函數編寫聲音播放程式,同時要注意的就是為了保持聲音播放的連續性採用了雙緩衝的方式,可以先試單緩衝播放時聽聽效果,一播一卡一播一卡,採用了雙緩衝以後聲音馬上連續播放了。
下面符上代碼,直接在win32控制台程式下就可以運行:
// vc console
#include <stdio.h>
#include <windows.h>
#include <winsock.h>
#include <mmreg.h>
#pragma comment(lib, "Winmm.lib")
#pragma comment(lib, "Ws2_32.lib")
#define IP "192.168.1.131"
int _tmain(int argc, _TCHAR* argv[])
{
//初始化網路
WSADATA wsa;
DWORD nResult = WSAStartup( MAKEWORD(2,2), &wsa );
if( nResult != 0 )
{
printf("WSAStartup初始化網路失敗! 錯誤號碼: %d\n", WSAGetLastError());
return 0;
}
else
{
printf("WSAStartup初始化網路成功!\n");
}
//建立SOCKET通訊端
SOCKET ClientSock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if( ClientSock == INVALID_SOCKET )
{
printf( "socket建立失敗! 錯誤號碼: %d\n", WSAGetLastError() );
return 0;
}
else
{
printf( "socket建立成功!\n" );
}
//定義地址
SOCKADDR_IN addr;
addr.sin_family = AF_INET;
addr.sin_port = 8000;
addr.sin_addr.S_un.S_addr = inet_addr( IP ); // INADDR_ANY
//綁定地址
if ( SOCKET_ERROR == connect( ClientSock, (sockaddr*)&addr, sizeof(addr) ) )
{
printf( "connect函數連結地址失敗! 錯誤號碼: %d\n", WSAGetLastError() );
return 0;
}
else
{
printf( "connect函數連結地址成功!\n" );
}
//----------------------------------------------------------------------------
HWAVEOUT hWaveOut; //波形音訊輸出控制代碼
WAVEFORMATEX waveformat;
memset(&waveformat, 0x00, sizeof(WAVEFORMATEX));
// 典型參數設定方法
waveformat.nSamplesPerSec = 44100; /* sample rate */
waveformat.wBitsPerSample = 16; /* sample size */
waveformat.nChannels = 2; /* channels */
waveformat.cbSize = 0; /* size of _extra_ info */
waveformat.wFormatTag = WAVE_FORMAT_PCM;//WAVE_FORMAT_PCM;
waveformat.nBlockAlign = (waveformat.wBitsPerSample * waveformat.nChannels) >> 3;
waveformat.nAvgBytesPerSec = waveformat.nBlockAlign * waveformat.nSamplesPerSec;
waveOutOpen (&hWaveOut, WAVE_MAPPER,&waveformat,0, 0, CALLBACK_NULL);
/*
這裡要注意的就是對於音頻要採用雙緩衝技術來保證聲音的連續性,
所謂雙緩衝就是使用兩個WAVEHDR結構體與HWAVEOUT進行綁定,綁定
就是通過waveOutPrepareHeader (hWaveOut, waveHdr[i], sizeof (WAVEHDR));實現的,
然後在播放的時候分別播放兩個waveOutWrite (hWaveOut, waveHdr[i], sizeof (WAVEHDR));
*/
LPBYTE lpData[2]; // = { 0x80 };
LPWAVEHDR waveHdr[2]; //音頻資料資訊頭
//這裡是分配記憶體
for(int i=0; i<2; i++)
{
lpData[i] = new BYTE[65536];
memset(lpData[i], 0x80, 65536); //在音頻資料中0x80代表靜音符號
waveHdr[i] = new WAVEHDR;
memset(waveHdr[i], 0x00, sizeof(WAVEHDR));
}
//這裡是對上面的資料進行初始化
for(int i=0; i<2; i++)
{
waveHdr[i]->lpData = (LPSTR)lpData[i] ;
waveHdr[i]->dwBufferLength = 65536;
waveHdr[i]->dwFlags = WHDR_PREPARED; //#define WHDR_PREPARED 0x00000002 /* set if this header has been prepared */
waveHdr[i]->dwLoops = 0;
//準備資訊頭
waveOutPrepareHeader (hWaveOut, waveHdr[i], sizeof (WAVEHDR));
}
while(1)
{
for(int i=0; i<2; i++)
{
if(SOCKET_ERROR == recv(ClientSock, (char*)lpData[i], 65536, 0))
break;
waveOutWrite (hWaveOut, waveHdr[i], sizeof (WAVEHDR));
}
}
printf("播放完成!\n");
}