不怎麼喜歡windows,一個windows建立線程的API讓我找了半天,在linux上分分鐘的事。。。但因爲須要用,因此。。。。
html
這個錄音程序用的是waveform audioAPI。給個官方連接:http://home.elka.pw.edu.pl/~mroj/h323/homepage/works/mroj/html/audio/waveform-audio.htm linux
我在代碼中寫了註釋,看懂應該沒問題。windows
代碼以下:程序做用在當前目錄下,產生一個test.wav的音頻文件保存錄音。數據結構
#include <stdlib.h> #include <stdio.h> #include <windows.h> #include <conio.h> #include <errno.h> #include <Windows.h> #include <stdlib.h> #include <stdio.h> #include "mmsystem.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #pragma comment(lib, "winmm.lib")//必須包含這個lib #define BUFFSIZE 1024 * 1024 //環形緩衝區的大小,你能夠定義大一些 /*WAV音頻文件頭*/ #define LENGTH 10 //錄音時間,秒 #define RATE 16000 //採樣頻率 #define SIZE 16 //量化位數 #define CHANNELS 1 //聲道數目 #define RSIZE 8 //buf的大小, #define BUFFER_SIZE 4096 #define FRAME_LEN 640 #define HINTS_SIZE 100 //#define min(x, y) ((x) < (y) ? (x) : (y))//這個函數在VS中有一個一樣的宏,因此註釋掉了。 typedef int SR_DWORD; typedef short int SR_WORD ; typedef long long SR_LWORD; //wav的音頻頭 typedef struct wave_pcm_hdr { char riff[4]; // = "RIFF" SR_DWORD size_8; // = FileSize - 8 char wave[4]; // = "WAVE" char fmt[4]; // = "fmt " SR_DWORD dwFmtSize; // = 下一個結構體的大小 : 16 SR_WORD format_tag; // = PCM : 1 SR_WORD channels; // = 通道數 : 1 SR_DWORD samples_per_sec; // = 採樣率 SR_DWORD avg_bytes_per_sec; // = 每秒字節數 SR_WORD block_align; // = 每採樣點字節數 SR_WORD bits_per_sample; // = 量化比特數: 8 | 16 char data[4]; // = "data"; SR_DWORD data_size; // = 純數據長度 : FileSize - 44 } wave_header; //默認音頻頭部數據 struct wave_pcm_hdr wav_hdr= { { 'R', 'I', 'F', 'F' }, LENGTH*RATE*CHANNELS*SIZE/8+36, {'W', 'A', 'V', 'E'}, {'f', 'm', 't', ' '}, 16, 1, CHANNELS, RATE, RATE*CHANNELS*SIZE/8, CHANNELS*SIZE/8, SIZE, {'d', 'a', 't', 'a'}, LENGTH*RATE*CHANNELS*SIZE/8 }; //環形緩衝區的的數據結構 struct cycle_buffer { char *buf; unsigned int size; unsigned int in; unsigned int out; }; static struct cycle_buffer *fifo = NULL;//定義全局FIFO FILE *fp; CRITICAL_SECTION cs; //初始化環形緩衝區 static int init_cycle_buffer(void) { int size = BUFFSIZE, ret; ret = size & (size - 1); if (ret) return ret; fifo = (struct cycle_buffer *) malloc(sizeof(struct cycle_buffer)); if (!fifo) return -1; memset(fifo, 0, sizeof(struct cycle_buffer)); fifo->size = size; fifo->in = fifo->out = 0; fifo->buf = (char *) malloc(size); if (!fifo->buf) free(fifo); else memset(fifo->buf, 0, size); return 0; } unsigned int fifo_get(char *buf, unsigned int len) //從環形緩衝區中取數據 { unsigned int l; len = min(len, fifo->in - fifo->out); l = min(len, fifo->size - (fifo->out & (fifo->size - 1))); memcpy(buf, fifo->buf + (fifo->out & (fifo->size - 1)), l); memcpy(buf + l, fifo->buf, len - l); fifo->out += len; return len; } unsigned int fifo_put(char *buf, unsigned int len) //將數據放入環形緩衝區 { unsigned int l; len = min(len, fifo->size - fifo->in + fifo->out); l = min(len, fifo->size - (fifo->in & (fifo->size - 1))); memcpy(fifo->buf + (fifo->in & (fifo->size - 1)), buf, l); memcpy(fifo->buf, buf + l, len - l); fifo->in += len; return len; } void WaveInitFormat(LPWAVEFORMATEX m_WaveFormat, WORD nCh,DWORD nSampleRate,WORD BitsPerSample)//初始化音頻格式 { m_WaveFormat->wFormatTag = WAVE_FORMAT_PCM; m_WaveFormat->nChannels = nCh; m_WaveFormat->nSamplesPerSec = nSampleRate; m_WaveFormat->nAvgBytesPerSec = nSampleRate * nCh * BitsPerSample/8; m_WaveFormat->nBlockAlign = m_WaveFormat->nChannels * BitsPerSample/8; m_WaveFormat->wBitsPerSample = BitsPerSample; m_WaveFormat->cbSize = 0; } DWORD CALLBACK MicCallback(HWAVEIN hwavein, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)//回調函數當數據緩衝區慢的時候就會觸發,回調函數,執行下面的RecordWave函數以後至關於建立了一個線程 { int len=0; switch(uMsg) { case WIM_OPEN://打開設備時這個分支會執行。 printf("\n設備已經打開...\n"); break; case WIM_DATA://當緩衝區滿的時候這個分支會執行,不要再這個分支中出現阻塞語句,會丟數據 ,waveform audio自己沒有緩衝機制。 printf("\n緩衝區%d存滿...\n",((LPWAVEHDR)dwParam1)->dwUser); waveInAddBuffer(hwavein, (LPWAVEHDR)dwParam1, sizeof(WAVEHDR)); EnterCriticalSection(&cs); //進入臨界區 len=fifo_put(((LPWAVEHDR)dwParam1)->lpData, 10240);將緩衝區的數據寫入環形fifo LeaveCriticalSection(&cs);//退出臨界區 //fwrite(((LPWAVEHDR)dwParam1)->lpData,10240, 1, fp); //printf("lens=%d", len); break; case WIM_CLOSE: printf("\n設備已經關閉...\n"); break; default: break; } return 0; } void RecordWave() { HWAVEIN phwi; WAVEINCAPS waveIncaps; int count=0; MMRESULT mmResult; count= waveInGetNumDevs();//獲取系統有多少個聲卡 mmResult = waveInGetDevCaps(0, &waveIncaps, sizeof(WAVEINCAPS));//查看系統聲卡設備參數,不用太在乎這兩個函數。 printf("\ncount = %d\n", count); printf("\nwaveIncaps.szPname=%s\n",waveIncaps.szPname); if(MMSYSERR_NOERROR==mmResult) { WAVEFORMATEX pwfx; WaveInitFormat(&pwfx,1,16000,16); printf("\n請求打開音頻輸入設備"); printf("\n採樣參數:單聲道 8kHz 8bit\n"); mmResult=waveInOpen(&phwi,WAVE_MAPPER,&pwfx,(DWORD)(MicCallback),NULL,CALLBACK_FUNCTION);//打開音頻設備,設置回調函數 if(MMSYSERR_NOERROR==mmResult) { WAVEHDR pwh1; char buffer1[10240]; WAVEHDR pwh2; char buffer2[10240]; pwh1.lpData = buffer1; pwh1.dwBufferLength = 10240; pwh1.dwUser = 1; pwh1.dwFlags = 0; mmResult = waveInPrepareHeader(phwi,&pwh1,sizeof(WAVEHDR));//準備緩衝區 printf("\n準備緩衝區1"); pwh2.lpData=buffer2; pwh2.dwBufferLength=10240; pwh2.dwUser=2; pwh2.dwFlags=0; mmResult=waveInPrepareHeader(phwi,&pwh2,sizeof(WAVEHDR));// printf("\n準備緩衝區2\n"); if(MMSYSERR_NOERROR==mmResult) { mmResult=waveInAddBuffer(phwi,&pwh1,sizeof(WAVEHDR));//添加緩衝區 mmResult=waveInAddBuffer(phwi,&pwh2,sizeof(WAVEHDR)); printf("\n將緩衝區2加入音頻輸入設備\n"); if(MMSYSERR_NOERROR==mmResult) { mmResult=waveInStart(phwi); printf("\n請求開始錄音\n"); /* Sleep(10000); waveInStop(phwi);//中止錄音 //waveInReset(phwi); waveInClose(phwi);//關閉音頻設備 waveInUnprepareHeader(phwi,&pwh1, sizeof(WAVEHDR));//釋放buffer waveInUnprepareHeader(phwi,&pwh2, sizeof(WAVEHDR)); printf("stop capture!\n"); fflush(stdout); */ } } } } } void forRec(void * ptr)//新建的另外一個線程用於將數據寫入文件 { char buff[10240]={0}; int len=0; while(1) { //printf("sdafsadf"); //if() EnterCriticalSection(&cs); //進入臨界區 len=fifo_get(buff, 10240);//從fifo中獲取數據 LeaveCriticalSection(&cs);//離開臨界區 //printf("len=%d\n", len); fwrite(buff,len, 1, fp);//將音頻數據寫入音頻文件 Sleep(100); } } int main(int argc, char* argv[]) { int len=0; char buff[10240]={0}; InitializeCriticalSection(&cs);//初始化臨界區 init_cycle_buffer();//初始化緩衝區 fp = fopen("test.wav","wb");//打開音頻文件 if (NULL == fp) { printf("open %s error.\n", "test.wav"); return 1; } fwrite(&wav_hdr, sizeof(wav_hdr) ,1, fp); //添加寫入wav音頻頭,使用採樣率爲16000 _beginthread(forRec, 0, NULL);//建立線程 RecordWave();//開啓錄音,一旦錄音數據buffer滿,就會觸發回調函數,因此下面要有while阻塞否則程序就會結束掉。 while(1) { } return 0; }
每一個函數參數的意義就去MSDN上查一下吧,會用就好。函數
MicCallback是一個回調函數這一點要注意,還有在MicCallback中不要出現阻塞的語句,會丟數據。學習
環行緩衝區的思想值得學習。線程
RecotdWave以後要阻塞,在回調函數中處理數據。code
若是僅僅是錄製一個音頻文件的話就不須要從新建立一個線程,只要在回調函數中寫入就好了。orm