栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++ 使用waveIn实现声音采集

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++ 使用waveIn实现声音采集

文章目录
  • 前言
  • 一、需要的对象及方法
    • 1.对象
    • 2.方法
  • 二、关键实现
    • 1.设置声音格式
    • 2.子线程中打开设备
    • 3.停止采集
  • 三、封装成对象
    • 1.接口设计
    • 2.具体实现
  • 四、使用示例
  • 总结


前言

在Windows上实现录音比较简单的方法是使用winmm,其中的waveIn模块就可以打开录音设备,获取PCM数据,进行声音录制。本文将介绍waveIn录音的具体实现,以及如何避免死锁。


一、需要的对象及方法

需要用到的头文件

#include"windows.h"
#include 
#pragma comment(lib, "winmm.lib ")  
1.对象
//声音采集对象
HWAVEIN _waveIn;
//声音数据的缓存
WAVEHDR  _wavehdrs[2];
//声音格式
WAVEFORMATEX _waveFormat;
2.方法
//打开声音输入设备
waveInOpen
//注册缓冲区
waveInPrepareHeader
//注销缓冲区
waveInUnprepareHeader
//缓冲区加入使用
waveInAddBuffer
//开始采集
waveInStart
//停止采集
waveInStop
//停止采集
waveInReset
//关闭设备
waveInClose

二、关键实现 1.设置声音格式
 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.子线程中打开设备

使用子线程开启的方式可以有效避免死锁问题。

//子线程,通过CreateThread启动下列线程
DWORD WINAPI ThreadProc(LPVOID p)
{
MMRESULT result = waveInOpen(&_waveIn, 0, &waveFormat, GetCurrentThreadId(), (DWORD)NULL, CALLBACK_THREAD);
//开启消息循环
//略
//消息循环中监听3个消息
switch (msg.message){
case WIM_OPEN:break;
case WIM_DATA:break;
case WIM_CLOSE:break;
}
//开启消息循环--end
return 0;
}
3.停止采集

通过发送WIM_CLOSE消息停止采集。

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

在子线程的消息循环中:

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

三、封装成对象

将采集功能封装成一个通用工具,方便在任意地方使用。

1.接口设计

接口设计如下:

#pragma once  
#include
#include
#include

namespace AC {
	/// 
	/// 声音格式
	/// 
	class SoundFormat {
	public:
		/// 
		/// 声道数
		/// 
		int  Channels;
		/// 
		/// 采样率
		/// 
		int  SampleRate;
		/// 
		/// 位深
		/// 
		int  BitsPerSample;
	};
	/// 
	/// 声音采集设备
	/// 
	class  SoundDevice {
	public:
		/// 
		/// 设备Id
		/// 
		int Id;
		/// 
		/// 设备名称
		/// 
		std::string Name;
		/// 
		/// 声道数
		/// 
		int Channels;
		/// 
		/// 支持的格式
		/// 
		std::vector SupportedFormats;
	};
	/// 
	/// 声音采集对象
	/// 
	class  SoundCollector {
	public:
		/// 
		/// 采集开始事件参数
		/// 
		class  StartedEventArgs
		{
		public:
			/// 
			/// 采集声音数据的格式
			/// 
			SoundFormat Format;
		};
		/// 
		/// 采集数据到达事件
		/// 
		class  DataArrivedEventArgs :public StartedEventArgs
		{
		public:
			/// 
			/// 声音数据
			/// 
			unsigned char* Data;
			/// 
			/// 数据长度
			/// 
			int DataLength;
		};
		/// 
		/// 错误事件参数
		/// 
		class  ErrorEventArgs 
		{
		public:
			/// 
			/// 错误内容
			/// 
			std::string Message;
		};
		/// 
		/// 采集开始事件
		/// 
		std::function Started;
		/// 
		/// 采集数据到达事件
		/// 
		std::function DataArrived;
		/// 
		/// 采集结束事件
		/// 
		std::function Stoped;
		/// 
		/// 错误事件
		/// 
		std::function Error;
		/// 
		/// 构造方法
		/// 
		SoundCollector();
		/// 
		/// 构造方法
		/// 
		/// 声音设备Id,0为默认设备
		SoundCollector(int deviceId);
		/// 
		///  构造方法
		/// 
		/// 声音设备Id,0为默认设备
		/// 采集的声道数
		/// 采集的采样率
		/// 采集的位深
		SoundCollector(int deviceId, int channels, int  sampleRate, int  bitsPerSample);
		/// 
		/// 析构方法
		/// 
		~SoundCollector();
		/// 
		/// 开始采集
		/// 		
		void Start();
		/// 
		/// 停止采集
		/// 
		void Stop();
		/// 
        /// 异步停止采集
        /// 
		void BeginStop();
		/// 
		/// 设置采集速率
		/// 
		/// 采集速率单位:次/秒
		void SetFrequency(int timesPerSecond);
		/// 
		/// 获取采集速率,数据回调频率。
		/// 
		/// 采集速率,单位:次/秒
		int GetFrequency();
		/// 
	    /// 获取声道数
	    /// 
	    /// 声道数
		int GetChannels();
		/// 
        /// 获取采样率
        /// 
        /// 采样率,单位:hz
		int GetSampleRate();
		/// 
		/// 获取位深
		/// 
		/// 位深,单位:bits
		int GetBitsPerSample();
		/// 
		/// 获取当前是否在采集中
		/// 
		/// 是否已停止
		bool GetIsStoped();
		/// 
		/// 获取声音设备列表
		/// 
		/// 
		static std::vector GetDeives();
	private:
		void* _implement = nullptr;
	};
}
2.具体实现

https://download.csdn.net/download/u013113678/72818898


四、使用示例

采集声音并保存为wav文件,其中的WavWapper对象参考《C++ 将音频PCM数据封装成wav文件》

#include"SoundCollector.h"
#include "WavWapper.h"
#include
int main()
{
	//获取设备列表
	auto devices = AC::SoundCollector::GetDeives();
	if (devices.size() > 0)
	{
		printf("设备名称:%sn", devices[0].Name.c_str());
		//初始化采集对象
		AC::SoundCollector sc(devices[0].Id,2,44100,16);
		//wav封装对象
		AC::WavWapper ww;
		//注册事件
		sc.Started = [&](auto s, auto e) {
			printf("开始录制:Channels %d SampleRate %d BitsPerSample %dn", e->Format.Channels, e->Format.SampleRate, e->Format.BitsPerSample);
			//根据声音格式创建wav文件
			ww.CreateWavFile("sound.wav", e->Format.Channels, e->Format.SampleRate, e->Format.BitsPerSample);
		};
		sc.DataArrived = [&](auto s, auto* e) {
			//采集的数据写入wav文件
			ww.WriteToFile(e->Data, e->DataLength);
		};
		sc.Stoped = [&](auto s, auto e) {	
			//关闭文件
			ww.CloseFile();
			printf("录制完成!n");
		};
		//开始采集
		sc.Start();
		Sleep(20000);
		//结束采集
		sc.Stop();
	}
}

总结

以上就是今天要讲的内容,使用waveIn实现声音采集,实现过程还是相对较简单的,但还是有些细节需要注意,比如使用方法回调的方式打开设备,关闭时很容易造成死锁,经过一番尝试发现子线程中打开设备才是比较好的方式。总的来说,waveIn实现的声音采集模块是能够支持音频实时流和录制开发的。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/691719.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号