不怎么喜欢windows,一个windows创建线程的API让我找了半天,在linux上分分钟的事。。。但由于需要用,所以。。。。
这个录音程序用的是waveform audioAPI。给个官方链接:http://home.elka.pw.edu.pl/~mroj/h323/homepage/works/mroj/html/audio/waveform-audio.htm
我在代码中写了注释,看懂应该没问题。
代码如下:程序作用在当前目录下,产生一个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之后要阻塞,在回调函数中处理数据。
如果仅仅是录制一个音频文件的话就不需要重新创建一个线程,只要在回调函数中写入就行了。