C++基于Windows的音频采集

#include "stdafx.h"

#include <time.h>
#include <windows.h>
#include<mmsystem.h>
#include<mmreg.h>
#pragma  comment(lib, "winmm.lib")

struct RIFF_HEADER//文件类型
{
    char szRiffID[4]; // 'R','I','F','F'
    DWORD dwRiffSize;//DWORD最大可容纳4GB的文件大小=FILE_LENGTH-8(即减去此数据结构大小)
    char szRiffFormat[4]; // 'W','A','V','E'
};

struct WAVE_FORMAT
{
    WORD wFormatTag;
    WORD wChannels;
    DWORD dwSamplesPerSec;
    DWORD dwAvgBytesPerSec;
    WORD wBlockAlign;
    WORD wBitsPerSample;
};

struct FMT_BLOCK
{
    char szFmtID[4]; // 'f','m','t',' '
    DWORD dwFmtSize;
    WAVE_FORMAT wavFormat;
};

struct DATA_BLOCK
{
    char szDataID[4]; // 'd','a','t','a'
    DWORD dwDataSize;
};

//struct FACT_BLOCK//可先项 一般可不用
//{
//    char szFactID[4]; // 'f','a','c','t'
//    DWORD dwFactSize;
//};

WAVEFORMATEX pwfx; //声音格式


#define MAX_RUN_COUNT 4
#define BUFFER_COUNT 4
#define MAX_BUFF_SOUNDSIZE 10240
FILE *fptmp;
CHAR m_cBufferIn[BUFFER_COUNT][MAX_BUFF_SOUNDSIZE];
WAVEHDR m_pWaveHdrIn[BUFFER_COUNT];
HWAVEIN phwi; //设备句柄

bool is_stop = false;


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)
{
    switch (uMsg)
    {
    case WIM_OPEN: //打开设备时消息,在此期间我们可以进行一些初始化工作
        printf("\n设备已经打开...\n");
        break;

    case WIM_DATA: //当缓存已满或者停止录音时的消息,处理这个消息可以对缓存进行重新分配,实现不限长度录音
    {
        WAVEHDR*p = ((PWAVEHDR)dwParam1); // 这就是采集到的数据指针
        DWORD len = p->dwBytesRecorded; // 这就是采集到的数据长度
       

        int i = p->dwUser;
        if (is_stop) //停止
        {
            waveInUnprepareHeader(phwi, p, sizeof(WAVEHDR)); //释放
            return 0;
        }
        
        printf("\n缓冲区%d存满...\n", p->dwUser);
        fwrite(&m_cBufferIn[i], 1, p->dwBytesRecorded, fptmp);
        waveInUnprepareHeader(phwi, p, sizeof(WAVEHDR)); //释放
        


        p->lpData = m_cBufferIn[i];
        p->dwBufferLength = MAX_BUFF_SOUNDSIZE;
        p->dwBytesRecorded = 0;
        p->dwUser = i;
        p->dwFlags = 0;
        waveInPrepareHeader(phwi, p, sizeof(WAVEHDR)); //准备内存块录音
        waveInAddBuffer(phwi, p, sizeof(WAVEHDR)); //增加内存块

        break;
    }
    case WIM_CLOSE: //关闭录音设备时的消息
        printf("\n设备已经关闭...\n");
        break;
    default:
        break;
    }
    return 0;
}

//录音函数
void Start()
{
    //获取声卡数
    int count = waveInGetNumDevs();//1
    printf("\n音频输入数量:%d\n", count);

    //获取声音输入设备
    WAVEINCAPS waveIncaps;
    MMRESULT mmResult = waveInGetDevCaps(0, &waveIncaps, sizeof(WAVEINCAPS));//2
    printf("\n音频输入设备:%s\n", waveIncaps.szPname);

    //如果正确获取到输入设备
    if (MMSYSERR_NOERROR == mmResult)
    {
        /// HWAVEIN phwi; //设备句柄
        //  WAVEFORMATEX pwfx;
        WaveInitFormat(&pwfx, 1, 8000, 8);
        printf("\n请求打开音频输入设备");
        printf("\n采样参数:单声道 8kHz 8bit\n");
        //打开录音设备
        mmResult = waveInOpen(&phwi, WAVE_MAPPER, &pwfx, (DWORD)(MicCallback), NULL, CALLBACK_FUNCTION);//3

        //如果正确打开了设备
        if (MMSYSERR_NOERROR == mmResult)
        {
            for (int i = 0; i < BUFFER_COUNT; i++)
            {
                m_pWaveHdrIn[i].lpData = m_cBufferIn[i];
                m_pWaveHdrIn[i].dwBufferLength = MAX_BUFF_SOUNDSIZE;
                m_pWaveHdrIn[i].dwBytesRecorded = 0;
                m_pWaveHdrIn[i].dwUser = i;
                m_pWaveHdrIn[i].dwFlags = 0;
                mmResult = waveInPrepareHeader(phwi, &m_pWaveHdrIn[i], sizeof(WAVEHDR));//4
                printf("\n准备缓冲区%d\n", i + 1);
            }

            //数据头准备完成
            if (MMSYSERR_NOERROR == mmResult)
            {
                for (int i = 0; i < BUFFER_COUNT; i++)
                {
                    mmResult = waveInAddBuffer(phwi, &m_pWaveHdrIn[i], sizeof(WAVEHDR));//5
                    printf("\n将缓冲区%d加入音频输入设备\n", i + 1);
                }

                //数据块准备完成
                if (MMSYSERR_NOERROR == mmResult)
                {
                    //开始录音
                    mmResult = waveInStart(phwi);//6
                    printf("\n请求开始录音\n");
                }
            }
        }
    }
}

void Stop()
{
    is_stop = true;
    Sleep(3000); //等待录音完成

    printf("Start Stop\n");
    waveInStop(phwi);

    printf("Start Reset\n");
    waveInReset(phwi);

    printf("Start Close\n");
    waveInClose(phwi);

    fclose(fptmp);
}

void Save()
{
    long len = 0;
    char *buf = NULL;
    FILE *fp;
    if (fopen_s(&fp, "temp.dat", "rb") == 0)
    {
        fseek(fp, 0, SEEK_END);;
        len = ftell(fp);
        fseek(fp, 0, SEEK_SET);
        buf = new char[len + 1];
        memset(buf, 0, sizeof(char)*(len + 1));
        fread(buf, len, 1, fp);
        fclose(fp);
    }
    if (buf == NULL)
    {
        printf("save error\n");
        return;
    }

    if (fopen_s(&fp, "output.wav", "wb") == 0)
    {
        RIFF_HEADER riff;
        WAVE_FORMAT wform;
        FMT_BLOCK fmt;
        //FACT_BLOCK fact;
        DATA_BLOCK data;
        memset(&riff, 0, sizeof(RIFF_HEADER));
        memset(&wform, 0, sizeof(WAVE_FORMAT));
        memset(&fmt, 0, sizeof(FMT_BLOCK));
        //memset(&fact, 0, sizeof(FACT_BLOCK));
        memset(&data, 0, sizeof(DATA_BLOCK));

        fmt.wavFormat.wFormatTag = WAVE_FORMAT_PCM;//1
        fmt.wavFormat.dwAvgBytesPerSec = pwfx.nAvgBytesPerSec;
        fmt.wavFormat.dwSamplesPerSec = pwfx.nSamplesPerSec;
        fmt.wavFormat.wBitsPerSample = pwfx.wBitsPerSample;
        fmt.wavFormat.wBlockAlign = pwfx.nBlockAlign;
        fmt.wavFormat.wChannels = pwfx.nChannels;

        memcpy(fmt.szFmtID, "fmt ", 4);
        fmt.dwFmtSize = 16;//一般情况下为16,如有附加信息为18
        memcpy(data.szDataID, "data", 4);
        data.dwDataSize = len;


        riff.dwRiffSize = (len + sizeof(FMT_BLOCK) + sizeof(DATA_BLOCK));
        memcpy(riff.szRiffFormat, "WAVE", 4);
        memcpy(riff.szRiffID, "RIFF", 4);

        fwrite(&riff, sizeof(RIFF_HEADER), 1, fp);//写RIFF_HEADER
        fwrite(&fmt, sizeof(FMT_BLOCK), 1, fp);//写FMT_BLOCK
        fwrite(&data, sizeof(DATA_BLOCK), 1, fp);//写数据头 DATA_BLOCK

        fwrite(buf, len, 1, fp);

        fclose(fp);
    }
}


DWORD WINAPI TimeThread(LPVOID wPame)
{
    Sleep(10000);
    return 0;
}


int main(void)
{
    errno_t errno;
    errno = fopen_s(&fptmp, "temp.dat", "wb");
    if (errno != 0)
    {
        printf("临时文件创建失败..\n");
        return 0;
    }

    Start();

    HANDLE hThread = CreateThread(NULL, NULL, TimeThread, NULL, NULL, NULL);
    WaitForSingleObject(hThread, INFINITE); //等待计时线程执行完成

    Stop();
    Save();

    system("pause");
    return 0;
}

本文为“老吴笔记”的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注