Google Code Prettify

2014年3月27日 星期四

windows 使用 winmm.lib 錄音(windows sound recording using winmm.lib)


在windows 下有一個 winAPI library 可以實現錄音的功能

API 說明可以參考 MSDN 網站:

http://msdn.microsoft.com/en-us/library/windows/desktop/dd743833(v=vs.85).aspx


這裡主要是介紹如何將電腦麥克風的聲音錄起來,

並存成 wav 檔



1.Audio 結構

在聲音的格式中,定義在 WAVEFORMATEX 這個結構中:


    typedef struct { 

    WORD wFormatTag;       //audio format,這裡使用WAVE_FORMAT_PCM

    WORD nChannels;         //1為單聲道,2為雙聲道

    DWORD nSamplesPerSec;   //每秒採集的聲音sample數量

    DWORD nAvgBytesPerSec;  //每秒傳送byte數量

    WORD nBlockAlign;       //nBlockAlign = (nChannels*wBitsPerSample)/8;

    WORD wBitsPerSample;    //每個sample儲存多少bit的資料,一般為8bit or 16bit

    WORD cbSize;            //額外資訊的大小,通常是non-PCM時,才會用到,這裡設0

} WAVEFORMATEX; 





聲音的採集資料,定義在 WAVEHDR 這個結構中:

typedef struct { 
    LPSTR lpData;              //buffer的資料

    DWORD dwBufferLength;      //buffer的長度

    DWORD dwBytesRecorded;     //buffer的資料大小(bytes)

    DWORD dwUser;              //通常為0

    DWORD dwFlags;             //描述額外的資料

    DWORD dwLoops;             //在播放的時候使用

    struct wavehdr_tag* lpNext;//應該給link list用

    DWORD reserved;            //保留

} WAVEHDR;





2.使用API

  使用windows API錄音 主要利用到下面函式:


  waveInOpen()            //開啟錄音裝置

  waveInPrepareHeader()   //buffer header 準備

  waveInAddBuffer()       //輸入buffer

  waveInStart()           //開始錄音

  waveInStop()            //停止錄音

  waveInUnprepareHeader() //buffer header 清空

  waveInClose()           //關閉錄音裝置



3.code ( 含儲存成wav檔 )

 

#include "stdafx.h"
#include "windows.h"

#pragma comment(lib, "winmm.lib") //windows API library for wave

#define DATASIZE 22050*2*5 //SamplesPerSec * byte per sample * duration time

int _tmain(int argc, _TCHAR* argv[])
{
	HWAVEIN audio_in; 
	WAVEFORMATEX audio_format;  //structure of wave format      
	WAVEHDR audio_header;   

	int log;
	char* soundBuffer[DATASIZE];

	audio_format.wFormatTag=WAVE_FORMAT_PCM;
	audio_format.nChannels=1;
	audio_format.nSamplesPerSec=22050;
	audio_format.wBitsPerSample=16;
	audio_format.cbSize=0;
	audio_format.nBlockAlign  = (audio_format.nChannels * audio_format.wBitsPerSample)/8;
	audio_format.nAvgBytesPerSec = (audio_format.nSamplesPerSec*audio_format.nBlockAlign);

	log = waveInOpen(&audio_in,WAVE_MAPPER,&audio_format,NULL,NULL,NULL);    
        //open input device

	if (log == MMSYSERR_NOERROR) 
		printf("waveInOpen success\n");
	else
		printf("waveInOpen fail\n");	
	                  
	audio_header.dwFlags=0;
	audio_header.dwUser=0;
	audio_header.dwLoops=0;
	audio_header.dwBytesRecorded=0;
	audio_header.lpData = (LPSTR)soundBuffer; 
	audio_header.dwBufferLength = DATASIZE; 
        
        //prepare buffer header
	log = waveInPrepareHeader( audio_in, &audio_header, sizeof(WAVEHDR) );  

	if (log == MMSYSERR_NOERROR) 
		printf("waveInPrepareHeader success\n");
	else
		printf("waveInPrepareHeader fail\n");
        //send buffer to input device
	log = waveInAddBuffer( audio_in, &audio_header, sizeof(WAVEHDR) );      

	if (log == MMSYSERR_NOERROR) 
		printf("waveInAddBuffer success\n");
	else
		printf("waveInAddBuffer fail\n");

	if (! waveInStart(audio_in) )	//start recording										
		printf("recording start for 5 sec...\nenter any char to stop\n"); 
	else 
		printf("recording fail...\n"); 

	getchar();
	if (! waveInStop(audio_in) )	//stop recording										
		printf("recording stop\n"); 
	else 
		printf("recording stop fail\n"); 

        //clean buffer header
	if(waveInUnprepareHeader(audio_in, &audio_header, sizeof(WAVEHDR)))     
		printf("UnPrepare Header fail\n"); 
	else 
		printf("UnPrepare Header success\n"); 

	Sleep(500);

	if (waveInClose(audio_in)==MMSYSERR_NOERROR)
		printf("waveInClose success\n"); 
	else 
		printf("waveInClose fail\n"); 

	//saving as wave file
	MMCKINFO wav_file_header1;
	MMCKINFO wav_file_header2;
	HMMIO wav_file;
	MMRESULT result;
	long wav_log;

        //create wav file
	wav_file = mmioOpen(L"wavfile.wav",NULL,MMIO_CREATE|MMIO_WRITE|MMIO_EXCLUSIVE | MMIO_ALLOCBUF);   
	if(wav_file == NULL)
		 printf("mmioOpen fail\n");

	ZeroMemory(&wav_file_header1, sizeof(MMCKINFO));
	wav_file_header1.fccType=mmioFOURCC('W', 'A', 'V', 'E');

	result = mmioCreateChunk(wav_file ,&wav_file_header1, MMIO_CREATERIFF);  //create RIFF chunk 
	if (result==MMSYSERR_NOERROR)
		printf("mmioCreateChunk: wav_file_header1 %s ok\n",result);
	else
		printf("mmioCreateChunk: wav_file_header1 fail\n");

	
	ZeroMemory(&wav_file_header2, sizeof(MMCKINFO));
	wav_file_header2.ckid=mmioFOURCC('f', 'm', 't', ' ');
	wav_file_header2.cksize=sizeof(WAVEFORMATEX)+audio_format.cbSize;

        //create fmt chunk
	result = mmioCreateChunk(wav_file ,&wav_file_header2, 0);	
	if (result==MMSYSERR_NOERROR)
		printf("mmioCreateChunk wav_file_header2  %s ok\n",result);
	else
		printf("mmioCreateChunk wav_file_header2 fail\n");
        
        //輸入fmt chunk的data
	wav_log = mmioWrite(wav_file ,(char*)&audio_format,sizeof(WAVEFORMATEX)+audio_format.cbSize);   
	if(wav_log==-1)
		printf(  "mmioWrite audio_format fail\n");
	else
		printf(  "mmioWrite write %d bytes\n",wav_log);

        //跳出fmt chunk
        result = mmioAscend(wav_file,&wav_file_header2,0);                   
	if (result==MMSYSERR_NOERROR )
		printf(  "mmioAscend wav_file_header2 %s ok\n",result);
	else
		printf(  "mmioAscend wav_file_header2 %s fail\n",result);

	wav_file_header2.ckid=mmioFOURCC('d', 'a', 't', 'a');

        //create data Chunk
	result = mmioCreateChunk(wav_file ,&wav_file_header2,0);             
	if (result==MMSYSERR_NOERROR)
		printf(  "mmioCreateChunk wav_file_header2 %s ok\n",result);
	else
		printf(  "mmioCreateChunk wav_file_header2 %s fail\n",result);
	printf("audio_header %d BytesRecorded\n",audio_header.dwBytesRecorded);
        
        //輸入audio data 
        wav_log = mmioWrite(wav_file,(char*)audio_header.lpData,DATASIZE);       

	if(wav_log==-1)
		printf( "mmioWrite audio data fail\n");
	else
		printf( "mmioWrite success : write %d bytes\n",wav_log);
	//跳出data chunk 
        result = mmioAscend(wav_file,&wav_file_header2,0);                   
	if (result==MMSYSERR_NOERROR)
		printf(  "mmioAscend wav_file_header2 %s ok\n",result);
	else
		printf(  "mmioAscend wav_file_header2 %s fail\n",result); 
 
        //跳出RIFF chunk
        result = mmioAscend(wav_file,&wav_file_header1,0);                   
	if (result==MMSYSERR_NOERROR)
		printf(  "mmioAscend wav_file_header1 %s ok\n",result);
	else
		printf(  "mmioAscend wav_file_header1 %s fail\n",result);

	result = mmioClose(wav_file,0);	//close the wav file 
	if (result==MMIOERR_CANNOTWRITE){
		printf( "mmioClose fail\n");
	}
	return 0;
}



可以看到儲存下來的wav檔


2 則留言:

  1. 作者已經移除這則留言。

    回覆刪除
  2. 請問這只能錄5秒嗎,我用只能錄5秒就結束了

    如果要改要改哪裡呢?

    感恩

    回覆刪除