windows C語言錄音

    不怎麼喜歡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

相關文章
相關標籤/搜索