直接上代碼。假設有須要能夠直接建一個win32控制台程序然后將代碼拷過去改個文件名稱就能夠用了(注意將聲道和頻率與你自己的文件相應)。當然我自己也用VS2008寫了個樣例上傳了,假設有須要下載地址例如以下:點擊打開鏈接。
這份代碼是打開文件截取一段數據然后播放的,能夠輕松的經過加一條線程的方式改成網絡傳輸的形式,但經過本人測試,由於沒有緩存機制會有“噠噠”的噪聲,也就是說這份代碼在網絡實時音頻上的表現並不太好。為了解決問題,能夠加上緩存機制,本人由於一開始用的是事件響應方式。所以一直困在這個框架里,不能非常好的利用緩存的機制解決上面提到的問題,后來嘗試了用回調函數的方式來響應數據播放完畢的消息。問題就輕松的攻克了。
那部分的代碼會在稍候放上去。
#include "stdafx.h" #include <stdio.h> #include <Windows.h> #pragma comment(lib, "winmm.lib") char buf[1024 * 1024 * 4]; int _tmain(int argc, _TCHAR* argv[]) { FILE* thbgm;//文件 int cnt; HWAVEOUT hwo; WAVEHDR wh; WAVEFORMATEX wfx; HANDLE wait; wfx.wFormatTag = WAVE_FORMAT_PCM;//設置波形聲音的格式 wfx.nChannels = 1;//設置音頻文件的通道數量 wfx.nSamplesPerSec = 8000;//設置每一個聲道播放和記錄時的樣本頻率 wfx.nAvgBytesPerSec = 16000;//設置請求的平均傳輸數據率,單位byte/s。這個值對於創建緩沖大小是非常實用的 wfx.nBlockAlign = 2;//以字節為單位設置塊對齊 wfx.wBitsPerSample = 16; wfx.cbSize = 0;//額外信息的大小 wait = CreateEvent(NULL, 0, 0, NULL); waveOutOpen(&hwo, WAVE_MAPPER, &wfx, (DWORD_PTR)wait, 0L, CALLBACK_EVENT);//打開一個給定的波形音頻輸出裝置來進行回放 fopen_s(&thbgm, "paomo.pcm", "rb"); cnt = fread(buf, sizeof(char), 1024 * 1024 * 4, thbgm);//讀取文件4M的數據到內存來進行播放。通過這個部分的改動,添加線程可變成網絡音頻數據的實時傳輸。當然假設希望播放完整的音頻文件,也是要在這里略微改一改 int dolenght = 0; int playsize = 1024; while (cnt) {//這一部分須要特別注意的是在循環回來之后不能花太長的時間去做讀取數據之類的工作,不然在每一個循環的間隙會有“噠噠”的噪音 wh.lpData = buf + dolenght; wh.dwBufferLength = playsize; wh.dwFlags = 0L; wh.dwLoops = 1L; waveOutPrepareHeader(hwo, &wh, sizeof(WAVEHDR));//准備一個波形數據塊用於播放 waveOutWrite(hwo, &wh, sizeof(WAVEHDR));//在音頻媒體中播放第二個函數wh指定的數據 WaitForSingleObject(wait, INFINITE);//用來檢測hHandle事件的信號狀態,在某一線程中調用該函數時,線程臨時掛起,假設在掛起的INFINITE毫秒內。線程所等待的對象變為有信號狀態。則該函數馬上返回 dolenght = dolenght + playsize; cnt = cnt - playsize; } waveOutClose(hwo); fclose(thbgm); return 0; }
離寫上面部分已經過了快一年。如今回看之前寫的代碼感覺略為坑爹,也許是進步了吧。
之前說要把雙緩存的代碼放出來。哪知道后來忙別的項目去了,這部分就丟到一邊,去老項目中提代替碼感覺好煩一直沒弄。在這一年中不少人發私信問我關於這部分代碼怎樣寫的事,沒想到如今做音頻的人還真不少呢。Ok。既然挖了坑就要填,今天乘着周末寫了一個雙緩存的Demoproject,代碼例如以下:
#include <stdio.h> #include <Windows.h> #pragma comment(lib, "winmm.lib") #define DATASIZE 1024*512 //分次截取數據大小 FILE* pcmfile; //音頻文件 HWAVEOUT hwo; void CALLBACK WaveCallback(HWAVEOUT hWave, UINT uMsg, DWORD dwInstance, DWORD dw1, DWORD dw2)//回調函數 { switch (uMsg) { case WOM_DONE://上次緩存播放完畢,觸發該事件 { LPWAVEHDR pWaveHeader = (LPWAVEHDR)dw1; pWaveHeader->dwBufferLength = fread(pWaveHeader->lpData, 1, DATASIZE, pcmfile);; waveOutPrepareHeader(hwo, pWaveHeader, sizeof(WAVEHDR)); waveOutWrite(hwo, pWaveHeader, sizeof(WAVEHDR)); break; } } } void main() { int cnt; WAVEHDR wh1; WAVEHDR wh2; WAVEFORMATEX wfx; fopen_s(&pcmfile, "paomo.pcm", "rb");//打開文件 wfx.wFormatTag = WAVE_FORMAT_PCM;//設置波形聲音的格式 wfx.nChannels = 1;//設置音頻文件的通道數量 wfx.nSamplesPerSec = 8000;//設置每一個聲道播放和記錄時的樣本頻率 wfx.nAvgBytesPerSec = 16000;//設置請求的平均傳輸數據率,單位byte/s。這個值對於創建緩沖大小是非常實用的 wfx.nBlockAlign = 2;//以字節為單位設置塊對齊 wfx.wBitsPerSample = 16; wfx.cbSize = 0;//額外信息的大小 waveOutOpen(&hwo, WAVE_MAPPER, &wfx, (DWORD)WaveCallback, 0L, CALLBACK_FUNCTION);//打開一個給定的波形音頻輸出裝置來進行聲音播放,方式為回調函數方式。假設是對話框程序,能夠將第五個參數改為(DWORD)this。操作跟本Demo程序類似 wh1.dwLoops = 0L;//播放區一 wh1.lpData = new char[DATASIZE]; wh1.dwBufferLength = DATASIZE; fread(wh1.lpData, 1, DATASIZE, pcmfile); wh1.dwFlags = 0L; waveOutPrepareHeader(hwo, &wh1, sizeof(WAVEHDR));//准備一個波形數據塊用於播放 waveOutWrite(hwo, &wh1, sizeof(WAVEHDR));//在音頻媒體中播放第二個參數指定的數據,也相當於開啟一個播放區的意思 wh2.dwLoops = 0L;//播放區二,基本同上 wh2.lpData = new char[DATASIZE]; wh2.dwBufferLength = DATASIZE; fread(wh2.lpData, 1, DATASIZE, pcmfile); wh2.dwFlags = 0L; waveOutPrepareHeader(hwo, &wh2, sizeof(WAVEHDR)); waveOutWrite(hwo, &wh2, sizeof(WAVEHDR)); while (wh1.dwBufferLength != 0 || wh2.dwBufferLength != 0)//假設文件還在沒播放完則等待500ms { Sleep(500); } waveOutUnprepareHeader(hwo, &wh1, sizeof(WAVEHDR));//清理數據 waveOutUnprepareHeader(hwo, &wh2, sizeof(WAVEHDR)); delete []wh1.lpData; delete []wh2.lpData; fclose(pcmfile);//關閉文件 return; }
同上面一樣。假設想要這個project的能夠到這個鏈接去下載。
只是提醒下,本人已然拋棄了VS2008。直接用VS2013,假設還在用老平台的話要用還是要折騰一會的。