window 多声道音频采集
来源:互联网 发布:淘宝全屏海报轮播代码 编辑:程序博客网 时间:2024/05/17 22:38
做到一个项目用到需要采集windows端多个声道的音频数据,我上面是DANTE在PC上虚拟出来是输入声道.每一个都是立体声,需要分别采集8路dante音频通道的数据,然后把数据做一些处理,比如送到语音识别.
使用过程中,也是跌跌撞撞的到处查找window底层音频采集库,都说要使用waveInStart,waveInAddBuffer等函数,但是发现自己写的,总是不能很好满足需求! 发现 别人写的winrec.c,winre.h很好使用,于是这里就专门写一个了工程来获取音频数据
主程序使用很简洁
int main(){ //初始化本地MIC int errcode; WAVEFORMATEX wavfmt = DEFAULT_FORMAT_STERO; struct recorder sr = {0}; int devid = GetDanteDevID(1);//如果需要传递参数 user_cb_para sr.on_data_ind = ASR_on_data_ind; //user_cb_para errcode = open_recorder(&sr, devid, &wavfmt); start_record(&sr);printf("开始录音..."); while (1) { Sleep(10); } return 0;}
winrec.c
/*@file@brief 封装windows录音*/#include <stdio.h>#include <stdlib.h>#include <Windows.h>#include <mmsystem.h>#include <process.h>#include <errno.h>#include "winrec.h"#pragma comment(lib, "winmm.lib")#define DBG_ON 0#if DBG_ON#define dbg printf#else#define dbg#endif/* Do not change the sequence */enum { RECORD_STATE_CREATED, /* Init */ RECORD_STATE_READY, /* Opened */ RECORD_STATE_STOPPING, /* During Stop */ RECORD_STATE_RECORDING, /* Started */};#define SAMPLE_RATE 16000#define SAMPLE_BIT_SIZE 16#define FRAME_CNT 8#define BUF_COUNT 4static void free_rec_buffer(HWAVEIN wi, WAVEHDR *first_header, unsigned headercount);static void data_proc(struct recorder *rec, MSG *msg);static unsigned int __stdcall record_thread_proc ( void * para);static HANDLE msgqueue_ready_evt = NULL; /* signaled: the message queque has been created in the thread */static void dbg_wave_header(WAVEHDR * buf){ dbg("-----\n"); dbg("Buf %x: User= %d, Len=%d, Rec = %d, Flag = %x\n", buf, buf->dwUser, buf->dwBufferLength, buf->dwBytesRecorded, buf->dwFlags); dbg("-----\n");}/* * If format is NULL, the default 16K sample rate, 16 bit will be used * bufheader */static int create_callback_thread(void *thread_proc_para, HANDLE *thread_hdl_out){ int ret = 0; HANDLE rec_thread_hdl = 0; unsigned int rec_thread_id; /* For indicating the thread's life stage. . signaled after the call back thread started and create its message queue. . will be close after callback thread exit. . must be manual reset. once signaled, keep in signaled state */ msgqueue_ready_evt = CreateEvent(NULL, TRUE, FALSE, NULL); if(msgqueue_ready_evt == NULL) return -1; rec_thread_hdl = (HANDLE) _beginthreadex(NULL, 0, record_thread_proc, thread_proc_para, 0, &rec_thread_id); if(rec_thread_hdl == 0) { /* close the event handle */ CloseHandle(msgqueue_ready_evt); msgqueue_ready_evt = NULL; return -1; } *thread_hdl_out = rec_thread_hdl; /* wait the message queue of the new thread has been created */ WaitForSingleObject(msgqueue_ready_evt, INFINITE); return 0;}static void close_callback_thread(HANDLE thread){ if(thread == NULL) return; if(msgqueue_ready_evt) { /* if quit before the thread ready */ WaitForSingleObject(msgqueue_ready_evt, INFINITE); CloseHandle(msgqueue_ready_evt); msgqueue_ready_evt = NULL; PostThreadMessage(GetThreadId(thread), WM_QUIT, 0, 0); WaitForSingleObject(thread, INFINITE); CloseHandle(thread); }}static int open_rec_device(int dev, WAVEFORMATEX *format, HANDLE thread, HWAVEIN *wave_hdl_out){ MMRESULT res; HWAVEIN wi = NULL; WAVEFORMATEX fmt; WAVEFORMATEX *final_fmt; if(thread == NULL) return -RECORD_ERR_INVAL; if(format == NULL) { fmt.wFormatTag = WAVE_FORMAT_PCM; fmt.nChannels = 1; fmt.nSamplesPerSec = SAMPLE_RATE; fmt.nAvgBytesPerSec = SAMPLE_RATE * (SAMPLE_BIT_SIZE >> 3); fmt.nBlockAlign = 2; fmt.wBitsPerSample = SAMPLE_BIT_SIZE; fmt.cbSize = sizeof(WAVEFORMATEX); final_fmt = &fmt; } else { final_fmt = format; } res = waveInOpen((LPHWAVEIN)&wi, dev, final_fmt, GetThreadId(thread), (DWORD_PTR)0, CALLBACK_THREAD); if(res != MMSYSERR_NOERROR) { return 0-res; } *wave_hdl_out = wi; return 0;}static int prepare_rec_buffer(HWAVEIN wi, WAVEHDR ** bufheader_out, unsigned int headercount, unsigned int bufsize){ int ret = 0; unsigned int i = 0; char clearout = 0; WAVEHDR *header; MMRESULT res; /* at least doubel buffering */ if(headercount < 2 || bufheader_out == NULL) return -RECORD_ERR_INVAL; header = (WAVEHDR *)malloc(sizeof(WAVEHDR) * headercount); if(!header) return - RECORD_ERR_MEMFAIL; memset(header, 0, sizeof(WAVEHDR) * headercount); for(i = 0; i < headercount; ++i) { (header+i)->lpData = (LPSTR)malloc(bufsize); if((header+i)->lpData == NULL) { clearout = 1; goto exit; } (header+i)->dwBufferLength = bufsize; (header+i)->dwFlags = 0; (header+i)->dwUser = i+1; /* my usage: if 0, indicate it's not used */ res = waveInPrepareHeader( wi, header+i, sizeof(WAVEHDR )); if(res != MMSYSERR_NOERROR) { clearout = 1; goto exit; } } * bufheader_out = header;exit: if(clearout) { free_rec_buffer(wi, header, headercount); } return ret;}static void free_rec_buffer(HWAVEIN wi, WAVEHDR *first_header, unsigned headercount){ unsigned int i; WAVEHDR *header; if(first_header == NULL || headercount == 0) return; header = first_header; for(i = 0; i < headercount; ++i) { if(header->lpData) { if(WHDR_PREPARED & header->dwFlags) waveInUnprepareHeader(wi, header, sizeof(WAVEHDR )); free(header->lpData); } header++; } free(first_header);}static void close_rec_device(HWAVEIN wi){ if(wi != NULL) { waveInClose(wi); }}static int start_record_internal(HWAVEIN wi, WAVEHDR *header, unsigned int bufcount){ MMRESULT res; unsigned int i; if(bufcount < 2) return -1; /* must put at least one buffer into the driver first. and this buffer must has been allocated and prepared. */ for(i = 0; i < bufcount; ++i) { if( (header->dwFlags & WHDR_INQUEUE) == 0) { header->dwUser = i + 1; res = waveInAddBuffer(wi, header, sizeof(WAVEHDR)); if(res != MMSYSERR_NOERROR) { waveInReset(wi); return 0-res; } } header ++; } res = waveInStart(wi); if(MMSYSERR_NOERROR != res) { waveInReset(wi); return 0-res; } return 0;}static int stop_record_internal(HWAVEIN wi){ MMRESULT res; //res = waveInStop(wi); /* some buffer may be still in the driver's queue */ res = waveInReset(wi); if(MMSYSERR_NOERROR != res) { return 0-res; } return 0;}/* the recording callback thread procedure */static unsigned int __stdcall record_thread_proc ( void * para){ MSG msg; BOOL bRet; struct recorder *rec = (struct recorder *)para; /* trigger the message queue generator */ PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); SetEvent(msgqueue_ready_evt); while ((bRet = GetMessage( &msg, NULL, 0, 0 )) != 0) { if(bRet == -1) { continue; } switch(msg.message) { case MM_WIM_OPEN: dbg("opened....\n"); break; case MM_WIM_CLOSE: dbg("closed....\n"); PostQuitMessage(0); break; case MM_WIM_DATA: data_proc(rec, &msg); break; default: break; } } return 0;}//数据处理在这里static void data_proc(struct recorder *rec, MSG *msg){ HWAVEIN whdl; WAVEHDR *buf; whdl = (HWAVEIN)msg->wParam; buf = (WAVEHDR *)msg->lParam; dbg("data....\n"); dbg_wave_header(buf); /* dwUser should be index + 1 */ if(buf->dwUser > rec->bufcount) { dbg("data_proc: something wrong. maybe buffer is reset.\n"); return; } rec->on_data_ind(buf->lpData, buf->dwBytesRecorded, rec->user_cb_para); //添加函数 switch(rec->state) { case RECORD_STATE_RECORDING: // after copied, put it into the queue of driver again. waveInAddBuffer(whdl, buf, sizeof(WAVEHDR)); break; case RECORD_STATE_STOPPING: default: /* from this flag, can check if the whole data is processed after stopping */ buf->dwUser = 0; break; }}static int is_stopped_internal(struct recorder *rec){ unsigned int i; WAVEHDR *header; header = (WAVEHDR*)(rec->bufheader); /* after close, already free */ if(header == NULL || rec->bufcount == 0 /*|| rec->using_flags == NULL*/) return 1; for(i = 0; i < rec->bufcount; ++i) { if ((header)->dwFlags & WHDR_INQUEUE) return 0; /* after stop, we called the waveInReset to return all buffers */ /* dwUser, see data_proc; */ if(header->dwUser != 0) return 0; header ++; } return 1;}static int open_recorder_internal(struct recorder * rec, unsigned int dev, WAVEFORMATEX * fmt){ unsigned int buf_size; int ret = 0; rec->bufcount = BUF_COUNT; rec->wavein_hdl = NULL; rec->rec_thread_hdl = NULL; ret = create_callback_thread((void *)rec, &rec->rec_thread_hdl); if(ret != 0) goto fail; ret = open_rec_device(dev, fmt, (HANDLE)rec->rec_thread_hdl, (HWAVEIN *)&rec->wavein_hdl); if(ret != 0 ) { goto fail; } if (fmt) buf_size = fmt->nBlockAlign *(fmt->nSamplesPerSec / 50) * FRAME_CNT; // 200ms else buf_size = FRAME_CNT * 20 * 16 * 2; // 16khz, 16bit, 200ms; ret = prepare_rec_buffer((HWAVEIN)rec->wavein_hdl, (WAVEHDR **)&rec->bufheader, rec->bufcount , buf_size); if(ret != 0 ) { goto fail; } return 0;fail: if(rec->bufheader) { free_rec_buffer((HWAVEIN)rec->wavein_hdl,(WAVEHDR *)rec->bufheader, rec->bufcount ); rec->bufheader = NULL; rec->bufcount = 0; } if(rec->wavein_hdl) { close_rec_device((HWAVEIN)rec->wavein_hdl); rec->wavein_hdl = NULL; } if(rec->rec_thread_hdl) { close_callback_thread(rec->rec_thread_hdl); rec->rec_thread_hdl = NULL; } return ret;}static void close_recorder_internal(struct recorder *rec){ if(rec->wavein_hdl) { close_rec_device((HWAVEIN)rec->wavein_hdl); if(rec->rec_thread_hdl) { close_callback_thread((HANDLE)rec->rec_thread_hdl); rec->rec_thread_hdl = NULL; } if(rec->bufheader) { free_rec_buffer((HWAVEIN)rec->wavein_hdl, (WAVEHDR*)rec->bufheader, rec->bufcount); rec->bufheader = NULL; rec->bufcount = 0; } rec->wavein_hdl = NULL; }}/* ------------------------------------- * Interfaces --------------------------------------*/int get_default_input_dev(){ return WAVE_MAPPER;}unsigned int get_input_dev_num(){ return waveInGetNumDevs();}/* callback will be run on a new thread */int create_recorder(struct recorder ** out_rec, void (*on_data_ind)(char *data, unsigned long len, void *user_cb_para), void* user_cb_para){ struct recorder * myrec; myrec = (struct recorder *)malloc(sizeof(struct recorder)); if(!myrec) return -RECORD_ERR_MEMFAIL; memset(myrec, 0, sizeof(struct recorder)); myrec->on_data_ind = on_data_ind; myrec->user_cb_para = user_cb_para; myrec->state = RECORD_STATE_CREATED; *out_rec = myrec; return 0;}void destroy_recorder(struct recorder *rec){ if(!rec) return; free(rec);}int open_recorder(struct recorder * rec, unsigned int dev, WAVEFORMATEX * fmt){ int ret = 0; if(!rec ) return -RECORD_ERR_INVAL; if(rec->state >= RECORD_STATE_READY) return 0; ret = open_recorder_internal(rec, dev, fmt); if(ret == 0) rec->state = RECORD_STATE_READY; return 0;}void close_recorder(struct recorder *rec){ if(rec == NULL || rec->state < RECORD_STATE_READY) return; if(rec->state == RECORD_STATE_RECORDING) stop_record(rec); close_recorder_internal(rec); rec->state = RECORD_STATE_CREATED;}int start_record(struct recorder * rec){ int ret; if(rec == NULL) return -RECORD_ERR_INVAL; if( rec->state < RECORD_STATE_READY) return -RECORD_ERR_NOT_READY; if( rec->state == RECORD_STATE_RECORDING) return 0; ret = start_record_internal((HWAVEIN)rec->wavein_hdl, (WAVEHDR*)rec->bufheader, rec->bufcount); if(ret == 0) rec->state = RECORD_STATE_RECORDING; return ret;}int stop_record(struct recorder * rec){ int ret; if(rec == NULL) return -RECORD_ERR_INVAL; if( rec->state < RECORD_STATE_RECORDING) return 0; rec->state = RECORD_STATE_STOPPING; ret = stop_record_internal((HWAVEIN)rec->wavein_hdl); if(ret == 0) { rec->state = RECORD_STATE_READY; } return ret;}int is_record_stopped(struct recorder *rec){ if(rec->state == RECORD_STATE_RECORDING) return 0; return is_stopped_internal(rec);}
winrec.h
/* * @file * @brief a record interface in windows * * it encapsluate the windows API waveInxxx; * Common steps: * create_recorder, * open_recorder, * start_record, * stop_record, * close_recorder, * destroy_recorder * */#ifndef __IFLY_WINREC_H__#define __IFLY_WINREC_H__/* error code */enum { RECORD_ERR_BASE = 0, RECORD_ERR_GENERAL, RECORD_ERR_MEMFAIL, RECORD_ERR_INVAL, RECORD_ERR_NOT_READY};/* recorder object. */struct recorder { void (*on_data_ind)(char *data, unsigned long len, void *user_para); void * user_cb_para; volatile int state; /* internal record state */ void * wavein_hdl; void * rec_thread_hdl; void * bufheader; unsigned int bufcount;};#ifdef __cplusplusextern "C" {#endif /* C++ */ /** * @fn * @brief Get the default input device ID * * @return returns WAVE_MAPPER in windows. */ int get_default_input_dev(); /** * @fn * @brief Get the total number of active input devices. * @return the number. 0 means no active device. */ unsigned int get_input_dev_num(); /** * @fn * @brief Create a recorder object. * @return int - Return 0 in success, otherwise return error code. * @param out_rec - [out] recorder object holder * @param on_data_ind - [in] callback. called when data coming. * @param user_cb_para - [in] user params for the callback. * @see */ int create_recorder(struct recorder ** out_rec, void (*on_data_ind)(char *data, unsigned long len, void *user_para), void* user_cb_para); /** * @fn * @brief Destroy recorder object. free memory. * @param rec - [in]recorder object */ void destroy_recorder(struct recorder *rec); /** * @fn * @brief open the device. * @return int - Return 0 in success, otherwise return error code. * @param rec - [in] recorder object * @param dev - [in] device id, from 0. * @param fmt - [in] record format. * @see * get_default_input_dev() */ int open_recorder(struct recorder * rec, unsigned int dev, WAVEFORMATEX * fmt); /** * @fn * @brief close the device. * @param rec - [in] recorder object */ void close_recorder(struct recorder *rec); /** * @fn * @brief start record. * @return int - Return 0 in success, otherwise return error code. * @param rec - [in] recorder object */ int start_record(struct recorder * rec); /** * @fn * @brief stop record. * @return int - Return 0 in success, otherwise return error code. * @param rec - [in] recorder object */ int stop_record(struct recorder * rec); /** * @fn * @brief test if the recording has been stopped. * @return int - 1: stopped. 0 : recording. * @param rec - [in] recorder object */ int is_record_stopped(struct recorder *rec);#ifdef __cplusplus} /* extern "C" */#endif /* C++ */#endif
GetLocalSound.cpp中有GetDanteDevID(ch)函数来获取指定字符音频通道的dev id
// GetLocalSound.cpp : 定义 DLL 应用程序的导出函数。//#include "GetLocalSound.h"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;}//获取本地电脑声卡通道设备数量int GetWavNumDevs(void){ int count = waveInGetNumDevs(); printf("\n音频输入数量:%d\n", count); return count;}//获取所有通道对应的DevIDvoid GetDevCaps(pDevChID pGetId){ int nDevID = 0; int i = 0,j=0; int nGetDanteCh = 0; //Audio的通道号 int count = waveInGetNumDevs(); WAVEINCAPS waveIncaps; MMRESULT mmResult; if (NULL == pGetId) { return; } //设备ID是从0开始 for (nDevID = 0; nDevID < count; nDevID++) { mmResult = waveInGetDevCaps(nDevID, &waveIncaps, sizeof(WAVEINCAPS));//2 //printf("\n%d音频输入设备:%ls\n", nDevID, waveIncaps.szPname);// L"DVS Receive 15-16 (Dante Virtua" size_t len = wcslen(waveIncaps.szPname) + 1; size_t converted = 0; char CStr[100]; //wchar_t转char wcstombs_s(&converted, CStr, len, waveIncaps.szPname, _TRUNCATE); //在这里指定字符串来确定是使用Dante if (strstr(CStr, "Dante Virtua")) { //顺序与编号nDevID不一致 //nSelDevID = atoi(waveIncaps.szPname); for (i = 0; i < (int)strlen(CStr); i++) { if (CStr[i] == '-') { char tmp[2] = { 0 }; // "DVS Receive 15-16 (Dante Virtua" 把-后的2个字符取出来 memcpy(tmp, &CStr[i + 1], sizeof(char) * 2); nGetDanteCh = atoi(tmp) / 2; //获取得到系统DANTE显示的通道号 break; } } pGetId[j].nCh = nGetDanteCh; pGetId[j].nDevID = nDevID; j++; } }}//通过Dante Viual Sound通道号进行找出设备ID//返回:该立体声通道的设备ID//nSetCh 立体声的通道号int GetDanteDevID(int nSetCh){ sDevChID id[16] = { 0 }; int nDevId = -2; int j = 0; GetDevCaps(id);//根据获取声卡通道的字符来确定保存所有的DEVID for (j = 0; j < 16; j++) { if (id[j].nCh == nSetCh) { nDevId = id[j].nDevID; return nDevId; } } return nDevId;}
完整vs2015工程下载
http://download.csdn.net/detail/zz603976046/9912474
0 0
- window 多声道音频采集
- MS 多声道音频数据和 WAVE 文件
- 多声道音频数据和 WAVE 文件
- 多声道音频数据和 WAVE 文件
- 多声道音频数据和 WAVE 文件
- 多声道音频数据和 WAVE 文件
- 多声道音频指南(一):被声音包围的感觉
- Android 音频采集---原始音频
- DirectShow音频采集
- jmf 音频采集
- DirectShow 音频采集
- waveIn音频采集
- wave 音频采集
- linux音频采集技术
- C#音频采集代码
- Android音频采集
- alsa 麦克风采集音频
- Android的音频采集
- jQuery的内容过滤选择器
- Leetcode 121. Best Time to Buy and Sell Stock
- Spring提高篇(七):Web 相关工具类
- 对数据结构一点一小小的理解(三)——C++ 模板库
- Android--Gradle build finished with 232 error(s) in 1m 43s
- window 多声道音频采集
- 28. Implement strStr()
- 第15章 Android性能优化
- 机器学习基石笔记3——在何时可以使用机器学习(3)(修改版)
- C++11 新特性摘抄
- IoC模式简单学习
- UVALIVE 4287 Proving Equivalences Tarjan求强连通分量
- Error:Execution failed for task ':XXXX:processDebugManifest'.
- linux下安装redis