自己封装的在Windows平台上的录音
支持:
1.打开录音设备
2.关闭录音设备
3.开始录音
4.停止录音
5.重置录音
6.保存录音为wav音频文件
7.保存录音为pcm裸流音频文件
8.可回调录音状态与录音数据

1 头文件 AudioRecordWindows.h
#ifndef _AUDIO_RECORD_WINDOWS_H_
#define _AUDIO_RECORD_WINDOWS_H_

#include <Windows.h>
#include <iostream>
#include <array>
#include <vector>

#pragma comment(lib,"winmm.lib")

#define CHANNEL_NUM  1                                           // 声道数
#define SAMPLE_RATE 16000                                        // 每秒采样率
#define SAMPLE_BITS 16                                           // 采样位深
#define AUDIO_DATA_BLOCK_SIZE (SAMPLE_RATE*SAMPLE_BITS / 8 * 1)  // 缓存数据块大小 = 采样率*位深/2*秒(字节)                                
#define BUFFER_NUM 10                                            // 缓冲区层数

//!
//! @brief 录音状态枚举 
//! 
enum RecordStatus
{
    OpenDevice,
    RecordStart,
    RecordWriteData,
    RecordStop,
    CloseDevice
};

//!
//! @brief 回调函数
//! 
typedef void( *AudioRecordCallback)(std::array <char, AUDIO_DATA_BLOCK_SIZE> audioDataBlock, RecordStatus recordStatus);

namespace AudioRecordSpace
{

    //!
    //! @brief wav文件头
    //! 
    typedef struct WavHeader
    {
        char            riff[4];                    // = "RIFF"
        UINT32          size_8;                     // = FileSize - 8
        char            wave[4];                    // = "WAVE"
        char            fmt[4];                     // = "fmt "
        UINT32          fmt_size;                   // = PCMWAVEFORMAT的大小 : 
                                                    //PCMWAVEFORMAT
        UINT16          format_tag;                 // = PCM : 1
        UINT16          channels;                   // = 通道数 : 1
        UINT32          samples_per_sec;            // = 采样率 : 8000 | 6000 | 11025 | 16000
        UINT32          avg_bytes_per_sec;          // = 每秒平均字节数 : samples_per_sec * bits_per_sample / 8
        UINT16          block_align;                // = 每采样点字节数 : wBitsPerSample / 8
        UINT16          bits_per_sample;            // = 量化精度: 8 | 16
        char            data[4];                    // = "data";
                                                    //DATA
        UINT32          data_size;                  // = 裸数据长度 
    };

    class AudioRecordWindows
    {
    public:
        AudioRecordWindows();
        virtual~AudioRecordWindows();

        //!
        //! @brief 打开录音设备
        //!
        bool OpenAudioDevice(); 

        //!
        //! @brief 关闭录音设备
        //!
        void CloseAudioDevice();

        //!
        //! @brief 初始化录音参数
        //!
        void InitWaveFormat(LPWAVEFORMATEX WaveFormat, WORD ChannelNum, DWORD SampleRate, WORD BitsPerSample);

        //!
        //! @brief 开始录音
        //!
        void StartRecord();

        //!
        //! @brief 停止录音
        //!
        void StopRecord();

        //!
        //! @brief 重置录音
        //!
        void ResetRecord();

        //!
        //! @brief 设置需要录音wav文件名称
        //!
        void SetWavFileName(const char* filePath);

        //!
        //! @brief 设置需要录音wav文件名称
        //!
        void SetPcmFileName(const char* filePath);

        //!
        //! @brief 写录音wav文件
        //!
        void WriteWavFile();

        //!
        //! @brief 写录音pcm文件
        //!
        void WritePcmFile();

        //!
        //! @brief 注册回调函数
        //!
        void RegisterCallback(AudioRecordCallback audioCallback);

        //!
        //! @brief 系统录音回调函数
        //!
        static DWORD(CALLBACK WaveAPI_Callback)(        // WaveAPI回调函数
            HWAVEIN hWaveIn,                            //  输入设备
            UINT uMsg,                                  //  消息
            DWORD_PTR dwInstance,                       //  保留
            DWORD_PTR dwParam1,                         //  刚填充好的缓冲区指针
            DWORD_PTR dwParam2                          //  保留
            );                      
    private:
        HWAVEIN m_AudioDevice;                                                   // 音频输入设备
        WAVEHDR m_WaveHdrBuffer[BUFFER_NUM];                                     // 声明缓冲区
        static std::array<char, AUDIO_DATA_BLOCK_SIZE> m_AudioDataBlock;       // 当前录制一帧音频数据
        static std::vector<std::array<char, AUDIO_DATA_BLOCK_SIZE>> m_AudioData; // 存储所有录制的音频数据
        static bool m_bStopRecord;                                               // 是否停止录音
        static bool m_bPushData;                                                 // 是否向m_AudioDataBlock中push数据
        std::string m_WavFilePath;                                               // Wav录音文件名称
        std::string m_PcmFilePath;                                               // Pcm录音文件名称
        bool m_bSaveWavFile;                                                     // 是否保存wav文件
        FILE* m_WavFileOpen;                                                     // 录音wav文件指针
        bool m_bSavePcmFile;                                                     // 是否保存Pcm文件
        FILE* m_PcmFileOpen;                                                     // 录音Pcm文件指针
        WavHeader m_WavHeader;                                                   // wav文件头
        static AudioRecordCallback m_Callback;                                   // 外部回调函数
        static bool m_bCallback;                                                 // 是否回调外部回调函数
    };
}

#endif // !_AUDIO_RECORD_WINDOWS_H_
2 实现文件 AudioRecordWindows.cpp
#include "AudioRecordWindows.h"

namespace AudioRecordSpace
{
    // 静态变量初始化
    std::array <char, AUDIO_DATA_BLOCK_SIZE> AudioRecordWindows::m_AudioDataBlock = {};
    std::vector<std::array<char, AUDIO_DATA_BLOCK_SIZE>> AudioRecordWindows::m_AudioData = { {} };
    bool AudioRecordWindows::m_bStopRecord = false;
    bool AudioRecordWindows::m_bPushData = true;
    bool AudioRecordWindows::m_bCallback = false;;
    AudioRecordCallback AudioRecordWindows::m_Callback = NULL;

    AudioRecordWindows::AudioRecordWindows()
    {
        m_WavFileOpen = NULL;
        m_PcmFileOpen = NULL;
        m_bSaveWavFile = false;
        m_bSavePcmFile = false;

        m_WavFilePath = "";
        m_PcmFilePath = "";

        m_WavHeader =
        {
            { 'R', 'I', 'F', 'F' },
            0,
            { 'W', 'A', 'V', 'E' },
            { 'f', 'm', 't', ' ' },
            sizeof(PCMWAVEFORMAT) ,
            WAVE_FORMAT_PCM,
            1,
            SAMPLE_RATE,
            SAMPLE_RATE*(SAMPLE_BITS / 8),
            SAMPLE_BITS / 8,
            SAMPLE_BITS,
            { 'd', 'a', 't', 'a' },
            0
        };
    }
    AudioRecordWindows::~AudioRecordWindows()
    {
    }
    bool AudioRecordWindows::OpenAudioDevice()
    {
        int audioDeviceNum = waveInGetNumDevs();
        if (audioDeviceNum <= 0)
        {
            std::cout << "Windows没有找到录音设备,请确认Windows找到了录音设备" << std::endl;
            return false;
        }
        else
        {
            for (unsigned int i = 0; i < audioDeviceNum; ++i)
            {
                WAVEINCAPS waveInCaps;
                MMRESULT mmResult = waveInGetDevCaps(i, &waveInCaps, sizeof(WAVEINCAPS));
                if (i == 0)
                {
                    std::cout << "当前默认的录音设备信息描述:" << waveInCaps.szPname << std::endl;
                }
                else
                {
                    std::cout << "其他录音设备信息描述" << waveInCaps.szPname << std::endl;
                }
            }
        }

        WAVEFORMATEX waveFormate;
        InitWaveFormat(&waveFormate, CHANNEL_NUM, SAMPLE_RATE, SAMPLE_BITS);
//#ifndef _WIN64
//  waveInOpen(&m_AudioDevice, WAVE_MAPPER, &waveFormate, (DWORD)WaveAPI_Callback, DWORD(this), CALLBACK_FUNCTION);
//#else
//  waveInOpen(&m_AudioDevice, WAVE_MAPPER, &waveFormate, (DWORD_PTR)WaveAPI_Callback, DWORD_PTR(this), CALLBACK_FUNCTION);
//#endif // !1
        waveInOpen(&m_AudioDevice, WAVE_MAPPER, &waveFormate, (DWORD_PTR)WaveAPI_Callback, DWORD_PTR(this), CALLBACK_FUNCTION);

        if (m_bCallback)
        {
            m_Callback(m_AudioDataBlock, RecordStatus::OpenDevice);
        }

        return true;
    }
    void AudioRecordWindows::CloseAudioDevice()
    {
        if (m_bCallback)
        {
            m_Callback(m_AudioDataBlock, RecordStatus::CloseDevice);
        }
        waveInClose(m_AudioDevice);
    }
    void AudioRecordWindows::InitWaveFormat(LPWAVEFORMATEX WaveFormat, WORD ChannelNum, DWORD SampleRate, WORD BitsPerSample)
    {
        // 配置音频波形参数
        WaveFormat->wFormatTag = WAVE_FORMAT_PCM;
        WaveFormat->nChannels = ChannelNum;
        WaveFormat->nSamplesPerSec = SampleRate;
        WaveFormat->nAvgBytesPerSec = SampleRate * ChannelNum * BitsPerSample / 8;
        WaveFormat->nBlockAlign = ChannelNum * BitsPerSample / 8;
        WaveFormat->wBitsPerSample = BitsPerSample;
        WaveFormat->cbSize = 0;

        std::cout << "采样参数:" << std::endl;
        std::cout << "声道数" << ChannelNum << std::endl;
        std::cout << "采样率" << SampleRate << "Hz" << std::endl;
        std::cout << "位深" << BitsPerSample << std::endl;

    }
    void AudioRecordWindows::StartRecord()
    {
        for (unsigned int i = 0; i < BUFFER_NUM; ++i)
        {
            m_WaveHdrBuffer[i].lpData = new char[AUDIO_DATA_BLOCK_SIZE];
            m_WaveHdrBuffer[i].dwBufferLength = AUDIO_DATA_BLOCK_SIZE;
            m_WaveHdrBuffer[i].dwBytesRecorded = 0;
            m_WaveHdrBuffer[i].dwUser = i;
            m_WaveHdrBuffer[i].dwFlags = 0;
            m_WaveHdrBuffer[i].dwLoops = 0;
            m_WaveHdrBuffer[i].lpNext = NULL;
            m_WaveHdrBuffer[i].reserved = 0;
            // 排进缓冲区
            waveInPrepareHeader(m_AudioDevice, &m_WaveHdrBuffer[i], sizeof(WAVEHDR));
            waveInAddBuffer(m_AudioDevice, &m_WaveHdrBuffer[i], sizeof(WAVEHDR));
        }

        // 清除视频缓冲
        m_AudioData.clear();
        m_AudioData.shrink_to_fit();
        m_AudioData.resize(0);

        // 开始录音
        waveInStart(m_AudioDevice);

        if (m_bCallback)
        {
            m_Callback(m_AudioDataBlock, RecordStatus::RecordStart);
        }

    }
    void AudioRecordWindows::StopRecord()
    {
        m_bStopRecord = true;
        // 停止录音设备
        waveInStop(m_AudioDevice);
        waveInReset(m_AudioDevice);

        if (m_bCallback)
        {
            m_Callback(m_AudioDataBlock, RecordStatus::RecordStop);
        }

        // 释放缓冲区
        for (unsigned int i = 0; i < BUFFER_NUM; ++i)
        {
            waveInUnprepareHeader(m_AudioDevice,&m_WaveHdrBuffer[i], sizeof(WAVEHDR));
            delete m_WaveHdrBuffer[i].lpData;
            m_WaveHdrBuffer[i].lpData = NULL;
        }

        // 保存wav文件
        if (m_bSaveWavFile)
        {
            WriteWavFile();
        }

        // 保存pcm文件
        if (m_bSavePcmFile)
        {
            WritePcmFile();
        }

    }
    void AudioRecordWindows::ResetRecord()
    {
        m_AudioData.clear();
        m_AudioData.shrink_to_fit();

        m_bSaveWavFile = false;
        m_bSavePcmFile = false;
        m_bPushData = true;
        m_bStopRecord = false;
        m_bCallback = false;
        m_Callback = NULL;
        m_WavFileOpen = NULL;
        m_PcmFileOpen = NULL;

        m_WavFilePath = "";
        m_PcmFilePath = "";
    }
    void AudioRecordWindows::SetWavFileName(const char * filePath)
    {
        m_bSaveWavFile = true;

        m_WavFilePath = filePath;

        // 尝试打开文件,创建文件
        errno_t err = fopen_s(&m_WavFileOpen, m_WavFilePath.c_str(), "wb");
        if (err > 0)
        {
            std::cout << "文件创建失败:" << err << " 检查文件名和占用" << std::endl;
            m_bSaveWavFile = false;
        }

    }
    void AudioRecordWindows::SetPcmFileName(const char * filePath)
    {
        m_bSavePcmFile = true;

        m_PcmFilePath = filePath;

        // 尝试打开文件,创建文件
        errno_t err = fopen_s(&m_PcmFileOpen, m_PcmFilePath.c_str(), "wb");
        if (err > 0)
        {
            std::cout << "文件创建失败:" << err << " 检查文件名和占用" << std::endl;
            m_bSavePcmFile = false;
        }
    }
    void AudioRecordWindows::WriteWavFile()
    {
        // 编辑并写入Wave头信息
        m_WavHeader.data_size = AUDIO_DATA_BLOCK_SIZE * m_AudioData.size();
        m_WavHeader.size_8 = m_WavHeader.data_size + 32;
        fwrite(&m_WavHeader, sizeof(m_WavHeader), 1, m_WavFileOpen);

        // 追加RawData
        fwrite(m_AudioData.data(), AUDIO_DATA_BLOCK_SIZE * m_AudioData.size(), 1, m_WavFileOpen);

        // 写入结束
        fclose(m_WavFileOpen);
    }
    void AudioRecordWindows::WritePcmFile()
    {
        // 追加RawData
        fwrite(m_AudioData.data(), AUDIO_DATA_BLOCK_SIZE * m_AudioData.size(), 1, m_PcmFileOpen);

        // 写入结束
        fclose(m_PcmFileOpen);
    }
    void AudioRecordWindows::RegisterCallback(AudioRecordCallback audioCallback)
    {
        m_bCallback = true;
        m_Callback = audioCallback;
    }
    DWORD(AudioRecordWindows::WaveAPI_Callback)(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
    {
        // 消息switch
        switch (uMsg)
        {
        case WIM_OPEN:      // 设备成功已打开
            std::cout << "设备成功打开" << std::endl;
            break;

        case WIM_DATA:      // 缓冲区数据填充完毕

            // 停止后会频繁发出WIM_DATA,已经将数据转移所以不必理会后继数据【后继数据在这里来看是是重复的】

            if (m_bPushData)
            {
                std::cout << "缓冲区数据填充完毕" << std::endl;

                // 把缓冲区数据拷贝出来
                memcpy(m_AudioDataBlock.data(), ((LPWAVEHDR)dwParam1)->lpData, AUDIO_DATA_BLOCK_SIZE);

                // 没有录进去的被填充为0xcd,改成0来避免末尾出现爆音【只在结束录音时进行,不影响添加缓存效率】
                if (((LPWAVEHDR)dwParam1)->dwBytesRecorded < AUDIO_DATA_BLOCK_SIZE)
                {
                    for (size_t i = ((LPWAVEHDR)dwParam1)->dwBytesRecorded; i < AUDIO_DATA_BLOCK_SIZE; i++)
                    {
                        m_AudioDataBlock.at(i) = 0;
                    }
                }
                // 添加这一帧
                m_AudioData.push_back(m_AudioDataBlock);

                // 如果你设置了回调函数
                if (m_bCallback)
                {
                    m_Callback(m_AudioDataBlock,RecordStatus::RecordWriteData);
                }
            }

            // 如果需要停止录音则不继续添加缓存
            if (!m_bStopRecord)
            {
                waveInAddBuffer(hWaveIn, (LPWAVEHDR)dwParam1, sizeof(WAVEHDR));//添加到缓冲区
            }
            else
            {
                // 如果已经停止了录制,就不要再写入数据
                m_bPushData = false;
            }

            break;

        case WIM_CLOSE:
            // 操作成功完成
            std::cout << "录音设备已经关闭..." << std::endl;
            break;

        default:
            break;
        }
        return 0;
    }
}
3 调用Demo AudioRecordWindowsTest.cpp
#include "AudioRecordWindows.h"
#include "conio.h"

using namespace std;
using namespace AudioRecordSpace;

void Callback(std::array <char, AUDIO_DATA_BLOCK_SIZE> audioDataBlock, RecordStatus recordStatus)
{
    if (recordStatus == RecordStatus::OpenDevice)
    {
        cout << "回调 打开设备" << endl;
    }
    else if (recordStatus == RecordStatus::RecordStart)
    {
        cout << "回调 开始录音" << endl;
    }
    else if (recordStatus == RecordStatus::RecordWriteData)
    {
        cout << "回调 正在写入数据" << endl;
    }
    else if (recordStatus == RecordStatus::RecordStop)
    {
        cout << "回调 停止录音" << endl;
    }
    else if (recordStatus == RecordStatus::CloseDevice)
    {
        cout << "回调 关闭设备" << endl;
    }
}

int main()
{
    char ch = 0;
    AudioRecordWindows sound_gbr;
    while (ch != 'q')
    {
        ch = _getch();
        switch (ch)
        {
        case 's':
            cout << "开始录音" << endl;
            sound_gbr.RegisterCallback(Callback);
            sound_gbr.OpenAudioDevice();
            sound_gbr.SetPcmFileName("test.pcm");
            sound_gbr.StartRecord();
            break;
        case 'q':
            cout << "结束录音" << endl;
            sound_gbr.StopRecord();
            sound_gbr.CloseAudioDevice();
            sound_gbr.ResetRecord();
            break;
        default:
            break;
        }
    }

    getchar();

    return 0;
}

测试结果如下:
Windows平台录音类封装:AudioRecordWindows-StubbornHuang Blog

如果您觉得对您有帮助,可以请站长喝一杯咖啡哦!

记得在赞赏备注里写上您的昵称

您可在本站资助名单中查看你的打赏记录哦!

支付宝扫一扫

微信扫一扫

金额随意,礼清义重

当前分类随机文章推荐

全站随机文章推荐

关于本站站长 StubbornHuang
Windows平台录音类封装:AudioRecordWindows-StubbornHuang Blog纵使晴明无雨色,入云深处亦沾衣。