亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C++使用waveIn實現(xiàn)聲音采集

 更新時間:2023年10月24日 15:34:35   作者:CodeOfCC  
在Windows上實現(xiàn)錄音比較簡單的方法是使用winmm,其中的waveIn模塊就可以打開錄音設(shè)備,這篇文章主要為大家介紹了C++如何使用waveIn實現(xiàn)聲音采集,需要的可以了解下

前言

在Windows上實現(xiàn)錄音比較簡單的方法是使用winmm,其中的waveIn模塊就可以打開錄音設(shè)備,獲取PCM數(shù)據(jù),進(jìn)行聲音錄制。本文將介紹waveIn錄音的具體實現(xiàn),以及如何避免死鎖。

一、需要的對象及方法

需要用到的頭文件

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

1.對象

//聲音采集對象
HWAVEIN _waveIn;
//聲音數(shù)據(jù)的緩存
WAVEHDR  _wavehdrs[2];
//聲音格式
WAVEFORMATEX _waveFormat;

2.方法

//打開聲音輸入設(shè)備
waveInOpen
//注冊緩沖區(qū)
waveInPrepareHeader
//注銷緩沖區(qū)
waveInUnprepareHeader
//緩沖區(qū)加入使用
waveInAddBuffer
//開始采集
waveInStart
//停止采集
waveInStop
//停止采集
waveInReset
//關(guān)閉設(shè)備
waveInClose

二、整體流程

整體流程大致如下:

三、關(guān)鍵實現(xiàn)

1.設(shè)置聲音格式

 WAVEFORMATEX WaveInitFormat(WORD nCh, DWORD  nSampleRate, WORD  bitsPerSample)
{
    WAVEFORMATEX waveFormat;
    waveFormat.wFormatTag = WAVE_FORMAT_PCM;
    waveFormat.nChannels = nCh;
    waveFormat.nSamplesPerSec = nSampleRate;
    waveFormat.nAvgBytesPerSec = nSampleRate * nCh * bitsPerSample / 8;
    waveFormat.nBlockAlign = nCh * bitsPerSample / 8;
    waveFormat.wBitsPerSample = bitsPerSample;
    waveFormat.cbSize = 0;
    return waveFormat;
}

2.子線程中打開設(shè)備

使用子線程開啟的方式可以有效避免死鎖問題。

//子線程,通過CreateThread啟動下列線程
DWORD WINAPI ThreadProc(LPVOID p)
{
MMRESULT result = waveInOpen(&_waveIn, 0, &waveFormat, GetCurrentThreadId(), (DWORD)NULL, CALLBACK_THREAD);
//開啟消息循環(huán)
//略
//消息循環(huán)中監(jiān)聽3個消息
switch (msg.message){
case WIM_OPEN:break;
case WIM_DATA:break;
case WIM_CLOSE:break;
}
//開啟消息循環(huán)--end
return 0;
}

3.停止采集

通過發(fā)送WIM_CLOSE消息停止采集。

PostThreadMessage(GetThreadId(_hThread), WIM_CLOSE, 0, 0);

在子線程的消息循環(huán)中:

case WIM_CLOSE:
waveInStop(_waveIn);
waveInReset(_waveIn);
waveInClose(_waveIn);
break;

四、封裝成對象

將采集功能封裝成一個通用工具,方便在任意地方使用。

1.接口設(shè)計

接口設(shè)計如下:

#pragma once 
#include<vector>
#include<string>
#include<functional>
/************************************************************************
* @Project:      AC::SoundCollector
* @Decription:  音頻采集工具
* @Verision:      v1.0.0.1
* @Author:      Xin Nie
* @Create:      2021/12/30 13:34:00
* @LastUpdate:  2021/1/7 23:06:00
************************************************************************
* Copyright @ 2020. All rights reserved.
************************************************************************/
namespace AC {
    /// <summary>
    /// 聲音格式
    /// </summary>
    class SoundFormat {
    public:
        /// <summary>
        /// 聲道數(shù)
        /// </summary>
        int  Channels;
        /// <summary>
        /// 采樣率
        /// </summary>
        int  SampleRate;
        /// <summary>
        /// 位深
        /// </summary>
        int  BitsPerSample;
    };
    /// <summary>
    /// 聲音采集設(shè)備
    /// </summary>
    class  SoundDevice {
    public:
        /// <summary>
        /// 設(shè)備Id
        /// </summary>
        int Id;
        /// <summary>
        /// 設(shè)備名稱
        /// </summary>
        std::string Name;
        /// <summary>
        /// 聲道數(shù)
        /// </summary>
        int Channels;
        /// <summary>
        /// 支持的格式
        /// </summary>
        std::vector<SoundFormat> SupportedFormats;
    };
    /// <summary>
    /// 聲音采集對象
    /// </summary>
    /// <summary>
    /// 聲音采集對象
    ///這是一個功能完整聲音采集對象,所有接口通過了測試。
    ///SoundCollectorTest方法包含了所有接口的測試,修改代碼后,用其驗證功能是否正常。
    ///非線程安全,所有方法需確保在單線程中調(diào)用,即比如:Start和Stop不能在兩個線程中同時調(diào)用。
    /// </summary>
    class  SoundCollector {
    public:
        /// <summary>
        /// 采集開始事件參數(shù)
        /// </summary>
        class  StartedEventArgs
        {
        public:
            /// <summary>
            /// 采集聲音數(shù)據(jù)的格式
            /// </summary>
            SoundFormat Format;
        };
        /// <summary>
        /// 采集數(shù)據(jù)到達(dá)事件
        /// </summary>
        class  DataArrivedEventArgs :public StartedEventArgs
        {
        public:
            /// <summary>
            /// 聲音數(shù)據(jù)
            /// </summary>
            unsigned char* Data;
            /// <summary>
            /// 數(shù)據(jù)長度
            /// </summary>
            int DataLength;
        };
        /// <summary>
        /// 錯誤事件參數(shù)
        /// </summary>
        class  ErrorEventArgs 
        {
        public:
            /// <summary>
            /// 錯誤內(nèi)容
            /// </summary>
            std::string Message;
        };
        /// <summary>
        /// 采集開始事件
        /// </summary>
        std::function<void(void*, StartedEventArgs*)> Started;
        /// <summary>
        /// 采集數(shù)據(jù)到達(dá)事件
        /// </summary>
        std::function<void(void*, DataArrivedEventArgs*)> DataArrived;
        /// <summary>
        /// 采集結(jié)束事件
        /// </summary>
        std::function<void(void*, void*)>Stoped;
        /// <summary>
        /// 錯誤事件
        /// </summary>
        std::function<void(void*, ErrorEventArgs*)> Error;
        /// <summary>
        /// 構(gòu)造方法
        /// </summary>
        SoundCollector();
        /// <summary>
        /// 構(gòu)造方法
        /// </summary>
        /// <param name="deviceId">聲音設(shè)備Id,0為默認(rèn)設(shè)備</param>
        SoundCollector(int deviceId);
        /// <summary>
        ///  構(gòu)造方法
        /// </summary>
        /// <param name="deviceId">聲音設(shè)備Id,0為默認(rèn)設(shè)備</param>
        /// <param name="channels">采集的聲道數(shù)</param>
        /// <param name="sampleRate">采集的采樣率</param>
        /// <param name="bitsPerSample">采集的位深</param>
        SoundCollector(int deviceId, int channels, int  sampleRate, int  bitsPerSample);
        /// <summary>
        /// 析構(gòu)方法
        /// </summary>
        ~SoundCollector();
        /// <summary>
        /// 開始采集
        /// </summary>        
        bool Start(int channels, int sampleRate, int bitsPerSample);
        /// <summary>
        /// 停止采集
        /// </summary>
        void Stop();
        /// <summary>
        /// 異步停止采集
        /// </summary>
        void BeginStop();
        /// <summary>
        /// 設(shè)置采集速率
        /// </summary>
        /// <param name="timesPerSecond">采集速率單位:次/秒</param>
        void SetFrequency(int timesPerSecond);
        /// <summary>
        /// 獲取采集速率,數(shù)據(jù)回調(diào)頻率。
        /// </summary>
        /// <returns>采集速率,單位:次/秒</returns>
        int GetFrequency();
        /// <summary>
        /// 獲取聲道數(shù)
        /// </summary>
        /// <returns>聲道數(shù)</returns>
        int GetChannels();
        /// <summary>
        /// 獲取采樣率
        /// </summary>
        /// <returns>采樣率,單位:hz</returns>
        int GetSampleRate();
        /// <summary>
        /// 獲取位深
        /// </summary>
        /// <returns>位深,單位:bits</returns>
        int GetBitsPerSample();
        /// <summary>
        /// 獲取當(dāng)前是否在采集中
        /// </summary>
        /// <returns>是否已停止</returns>
        bool GetIsStoped();
        /// <summary>
        /// 獲取聲音設(shè)備列表
        /// </summary>
        /// <returns></returns>
        static std::vector<SoundDevice> GetDeives();
    private:
        void* _implement = nullptr;
    };
}

五、使用示例

采集聲音并保存為wav文件,其中的WavWapper對象參考《C++ 將音頻PCM數(shù)據(jù)封裝成wav文件

#include"SoundCollector.h"
#include "WavWapper.h"
#include<Windows.h>
int main()
{
    //運行測試程序
    //AC::SoundCollectorTest();
    //獲取設(shè)備列表
    auto devices = AC::SoundCollector::GetDeives();
    if (devices.size() > 0)
    {    
        printf("設(shè)備名稱:%s\n", devices[0].Name.c_str());
        //初始化采集對象
        AC::SoundCollector sc(devices[0].Id);
        //wav封裝對象
        AC::WavWapper ww;
        //注冊事件
        sc.Started = [&](auto s, auto e) {
            printf("開始錄制:Channels %d SampleRate %d BitsPerSample %d\n", e->Format.Channels, e->Format.SampleRate, e->Format.BitsPerSample);
            //根據(jù)聲音格式創(chuàng)建wav文件
            ww.CreateWavFile("sound.wav", e->Format.Channels, e->Format.SampleRate, e->Format.BitsPerSample);
        };
        sc.DataArrived = [&](auto s, auto* e) {
            //采集的數(shù)據(jù)寫入wav文件
            ww.WriteToFile(e->Data, e->DataLength);
        };
        sc.Stoped = [&](auto s, auto e) {
            //關(guān)閉文件
            ww.CloseFile();
            printf("錄制完成!\n");
        };
        sc.Error = [&](auto s, auto e) {            
            printf("%s\n",e->Message.c_str());
        };
        //開始采集
        if (sc.Start(2, 44100, 16))
        {
            Sleep(20000);
            //結(jié)束采集
            sc.Stop();
        }
    }
}

總結(jié)

使用waveIn實現(xiàn)聲音采集,實現(xiàn)過程還是相對較簡單的,但還是有些細(xì)節(jié)需要注意,比如使用方法回調(diào)的方式打開設(shè)備,關(guān)閉時很容易造成死鎖,經(jīng)過一番嘗試發(fā)現(xiàn)子線程中打開設(shè)備才是比較好的方式??偟膩碚f,waveIn實現(xiàn)的聲音采集模塊是能夠支持音頻實時流和錄制開發(fā)的。

以上就是C++使用waveIn實現(xiàn)聲音采集的詳細(xì)內(nèi)容,更多關(guān)于C++ waveIn聲音采集的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C語言 詳細(xì)分析結(jié)構(gòu)體的內(nèi)存對齊

    C語言 詳細(xì)分析結(jié)構(gòu)體的內(nèi)存對齊

    C 數(shù)組允許定義可存儲相同類型數(shù)據(jù)項的變量,結(jié)構(gòu)是 C 編程中另一種用戶自定義的可用的數(shù)據(jù)類型,它允許你存儲不同類型的數(shù)據(jù)項,本篇讓我們來了解C 的結(jié)構(gòu)體內(nèi)存對齊
    2022-03-03
  • C語言計算Robots機器人行走路線

    C語言計算Robots機器人行走路線

    這篇文章介紹了C語言計算Robots機器人行走路線,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • 詳細(xì)聊聊c語言中的緩沖區(qū)問題

    詳細(xì)聊聊c語言中的緩沖區(qū)問題

    緩沖區(qū)又稱為緩存,它是內(nèi)存空間的一部分,也就是說在內(nèi)存空間中預(yù)留了一定的存儲空間,這些存儲空間用來緩沖輸入或輸出的數(shù)據(jù),這部分預(yù)留的空間就叫做緩沖區(qū),這篇文章主要給大家介紹了關(guān)于c語言中緩沖區(qū)問題的相關(guān)資料,需要的朋友可以參考下
    2021-11-11
  • C++實現(xiàn)LeetCode(37.求解數(shù)獨)

    C++實現(xiàn)LeetCode(37.求解數(shù)獨)

    這篇文章主要介紹了C++實現(xiàn)LeetCode(37.求解數(shù)獨),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • VC++的combobox控件用法匯總

    VC++的combobox控件用法匯總

    這篇文章主要介紹了VC++的combobox控件用法,對VC++初學(xué)者來說尤為重要,需要的朋友可以參考下
    2014-08-08
  • 淺談C++中virtual的三種用法

    淺談C++中virtual的三種用法

    這篇文章主要介紹了淺談C++中virtual的三種用法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Windows系統(tǒng)下使用C語言編寫單線程的文件備份程序

    Windows系統(tǒng)下使用C語言編寫單線程的文件備份程序

    這篇文章主要介紹了Windows系統(tǒng)下使用C語言編寫單線程的文件備份程序,文中給出了實現(xiàn)的幾個關(guān)鍵代碼片段,剩下的只要套上main和線程調(diào)用的相關(guān)函數(shù)即可,非常詳細(xì),需要的朋友可以參考下
    2016-02-02
  • 如何給隨機數(shù)加密

    如何給隨機數(shù)加密

    隨機數(shù)加密的簡單算法,需要的朋友可以參考一下
    2013-03-03
  • 詳解C++中賦值和輸入輸出語句的用法

    詳解C++中賦值和輸入輸出語句的用法

    這篇文章主要介紹了詳解C++中賦值和輸入輸出語句的用法,是C++入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-09-09
  • C++圖文并茂講解類型轉(zhuǎn)換函數(shù)

    C++圖文并茂講解類型轉(zhuǎn)換函數(shù)

    類型轉(zhuǎn)換(type cast),是高級語言的一個基本語法。它被實現(xiàn)為一個特殊的運算符,以小括號內(nèi)加上類型名來表示,接下來讓我們一起來詳細(xì)了解
    2022-05-05

最新評論