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
原创粉丝点击