[C++]录音与播放类

马臻
2023-12-01

MicroPhone.h

#pragma once
#include <string>
#include <windows.h>
#include <mmsystem.h>
#include <mmreg.h>
#include <msacm.h>

#define INIT_BUFFER_SIZE 512
#define IPUT_BUFFER_SIZE 256
#define UNUSE_BUFFER_SIZE 80000
#define THRESHOLD_DB      450

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

class MicroPhone
{
protected:
	MicroPhone();
	~MicroPhone();
public:
	static MicroPhone * getMic();

	bool Open();
	bool Close();

	bool StartListen();
	bool PauseListen();
	bool StopListen(bool save = false);

	bool StartSpeak();
	bool StartSpeak(BYTE *buffer, DWORD len);
	bool PauseSpeak();
	bool StopSpeak();
protected:
	static void CALLBACK Callback(HWAVEIN hwi, UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2);

	float getdBValue(SHORT *RecordSrc, DWORD Size);
private:
	HANDLE       m_SpeakHandle;//播放结束的一个事件

	HWAVEIN      m_hWaveIn;  //输入设备
	HWAVEOUT     m_hWaveOut; //输出设备

	WAVEHDR      m_waveInputHdrL;//输入
	WAVEHDR      m_waveInputHdrR;//输入
	WAVEHDR      m_waveOutputHdr;//输出

	WAVEINCAPS    m_waveInCaps;//声音设备功能信息

	WAVEFORMATEX  m_waveForm; //采集音频的格式,结构体
	
	BYTE *        m_inPutBufferL;//输入buffer
	BYTE *        m_inPutBufferR;//输入buffer
	
	bool          m_IsOpened;//是否打开设备

	bool          m_IsSpeaking;//是否正在说
	bool          m_IsListening;//是否正在听

	bool          m_IsRecycle;//是否循环加入buffer
	bool          m_IsNeedWakeUp;//是否需要唤醒
	
	double        m_StopTalkTime;//用于睡眠

	std::string   m_FilePath;//测试保存文件路径
public:
	BYTE *        m_AudioBuffer;//收集到的音频数据
	DWORD         m_RecordedBytes;//收集到的音频数据大小
	bool          m_IsAudioReady;//音频数据是否准备好
	bool          m_IsRecving;//是否正在阻塞
};

MicroPhone.cpp

#include "stdafx.h"
#include "MicroPhone.h"
#include <iostream> 


MicroPhone::MicroPhone()
{
	m_IsOpened = false;
	m_IsRecving = false;
	m_IsSpeaking = false;

	m_IsListening = false;
	m_IsNeedWakeUp = false;
	m_IsAudioReady = false;

	m_FilePath = "1.wav";
	m_SpeakHandle = CreateEvent(NULL, 0, 0, NULL);
	
	m_RecordedBytes = 0;
	m_IsRecycle = true;
	m_AudioBuffer = (BYTE*)malloc(sizeof(BYTE) * INIT_BUFFER_SIZE);
}


MicroPhone::~MicroPhone()
{
	Close();
}

MicroPhone * MicroPhone::getMic()
{
	static MicroPhone mic;
	return &mic;
}

bool MicroPhone::Open()
{
	//get info
	MMRESULT result = waveInGetNumDevs();//获取设备数量
	if (result == 0)
		return false;

	//获取指定波形音频设备的功能,第一个参数为设备id,第二个参数保存设备功能信息,第三个参数为设备结构体大小
	result = waveInGetDevCaps(0, &m_waveInCaps, sizeof(WAVEINCAPS));
	if (result != MMSYSERR_NOERROR)
		return false;

	//init form
	m_waveForm.wFormatTag = WAVE_FORMAT_PCM;
	m_waveForm.nChannels = 1;//声道,1为单声道,2为立体声
	m_waveForm.nSamplesPerSec = 16000;//采样频率,
	m_waveForm.nAvgBytesPerSec = 32000;//设置请求的平均数据传输率,单位byte/s
	m_waveForm.nBlockAlign = 2;//以字节为单位设置块对齐。块对齐是指最小数据的原子大小。如果wFormatTag= WAVE_FORMAT_PCM,nBlockAlign为(nChannels*wBitsPerSample)/8。
	m_waveForm.wBitsPerSample = 16;//根据wFormatTag的类型设置每个样本的位深(即每次采样样本的大小,以bit为单位)。如果wFormatTag= WAVE_FORMAT_PCM,此值应该设为8或16,对于非PCM格式,根据厂商的说明设置。一些压缩的架构不能设置此值,此时wBitsPerSample应该为零。
	m_waveForm.cbSize = 18;//额外信息的大小,以字节为单位,额外信息添加在WAVEFORMATEX结构的结尾。这个信息可以作为非PCM格式的wFormatTag额外属性,如果wFormatTag不需要额外的信息,此值必需为0,对于PCM格式此值被忽略。
	// Open Input
	//打开指定麦克风设备
	//第一个参数为设备句柄
	//第二个参数为设备id
	//第三个参数为格式化声音数据
	//第四个参数为回掉函数指针
	//第五个参数为传递给回调句柄的用户实例数据
	//第六个参数为设备打开参数
	result = waveInOpen(&m_hWaveIn, WAVE_MAPPER, &m_waveForm, (DWORD_PTR)Callback, (DWORD_PTR)this, CALLBACK_FUNCTION);
	if (result != MMSYSERR_NOERROR)
		return false;

	//打开音频输出设备
	result = waveOutOpen(&m_hWaveOut, WAVE_MAPPER, &m_waveForm, (DWORD_PTR)m_SpeakHandle, 0L, CALLBACK_EVENT);
	if (result != MMSYSERR_NOERROR)
	{
		waveInClose(m_hWaveIn);
		return false;
	}
	m_IsOpened = true;
	return m_IsOpened;
}

bool MicroPhone::Close()
{
	if (!m_IsOpened)return false;

	waveInReset(m_hWaveIn);
	waveInClose(m_hWaveIn);

	waveOutReset(m_hWaveOut);
	waveOutClose(m_hWaveOut);
	return true;
}

bool MicroPhone::StartListen()
{
	if (!m_IsOpened)return false;

	if (m_inPutBufferL)
	{
		delete[]m_inPutBufferL;
		m_inPutBufferL = nullptr;
	}

	if (m_inPutBufferR)
	{
		delete[]m_inPutBufferR;
		m_inPutBufferR = nullptr;
	}

	if (m_AudioBuffer)
	{
		free(m_AudioBuffer);
		m_AudioBuffer = (BYTE*)malloc(sizeof(BYTE) * INIT_BUFFER_SIZE);
		m_RecordedBytes = 0;
	}

	//buffer
	m_inPutBufferL = new BYTE[IPUT_BUFFER_SIZE];
	memset(m_inPutBufferL, 0, IPUT_BUFFER_SIZE);

	m_inPutBufferR = new BYTE[IPUT_BUFFER_SIZE];
	memset(m_inPutBufferR, 0, IPUT_BUFFER_SIZE);
	//head
	m_waveInputHdrL.lpData = (LPSTR)m_inPutBufferL; // 指向buffer
	m_waveInputHdrL.dwBufferLength = IPUT_BUFFER_SIZE;     // buffer大小
	m_waveInputHdrL.dwBytesRecorded = 0;      // buffer存放大小
	m_waveInputHdrL.dwUser = 0;
	m_waveInputHdrL.dwFlags = 0;
	m_waveInputHdrL.dwLoops = 1;

	m_waveInputHdrR.lpData = (LPSTR)m_inPutBufferR; // 指向buffer
	m_waveInputHdrR.dwBufferLength = IPUT_BUFFER_SIZE;     // buffer大小
	m_waveInputHdrR.dwBytesRecorded = 0;      // buffer存放大小
	m_waveInputHdrR.dwUser = 0;
	m_waveInputHdrR.dwFlags = 0;
	m_waveInputHdrR.dwLoops = 1;

	//准备buffer
	waveInPrepareHeader(m_hWaveIn, &m_waveInputHdrL, sizeof(WAVEHDR));
	waveInPrepareHeader(m_hWaveIn, &m_waveInputHdrR, sizeof(WAVEHDR));
	//添加buffer
	waveInAddBuffer(m_hWaveIn, &m_waveInputHdrL, sizeof(WAVEHDR));
	waveInAddBuffer(m_hWaveIn, &m_waveInputHdrR, sizeof(WAVEHDR));
	//开启循环收集
	m_IsRecycle = true;
	
	waveInStart(m_hWaveIn);
	return true;
}
bool MicroPhone::PauseListen()
{
	if (!m_IsOpened)return false;

	m_IsRecycle = false;
	m_IsListening = false;

	waveInReset(m_hWaveIn);
	return true;
}
bool MicroPhone::StopListen(bool save)
{
	if (!m_IsOpened)return false;

	m_IsRecycle = false;
	m_IsListening = false;
	waveInReset(m_hWaveIn);
	waveInClose(m_hWaveIn);

	if (save)
	{
		FILE *pf;
		fopen_s(&pf, "录音测试.pcm", "wb");
		fwrite(m_AudioBuffer, 1, m_RecordedBytes, pf);
		fclose(pf);
	}
	
	return true;
}
bool MicroPhone::StartSpeak()
{
	if (!m_IsOpened)return false;

	m_IsSpeaking = true;
	m_waveOutputHdr.lpData = (LPSTR)m_AudioBuffer;// 指向buffer
	m_waveOutputHdr.dwBufferLength = m_RecordedBytes;// buffer大小
	m_waveOutputHdr.dwBytesRecorded = m_RecordedBytes;
	m_waveOutputHdr.dwFlags = 0;
	m_waveOutputHdr.dwLoops = 1;

	ResetEvent(m_SpeakHandle);

	waveOutPrepareHeader(m_hWaveOut, &m_waveOutputHdr, sizeof(WAVEHDR));
	waveOutWrite(m_hWaveOut, &m_waveOutputHdr, sizeof(WAVEHDR));

	DWORD dw = WaitForSingleObject(m_SpeakHandle, INFINITE);

	if (dw == WAIT_OBJECT_0)
	{
		std::cout << "jieshu" << std::endl;
		m_IsSpeaking = false;
	}
	return true;
}

bool MicroPhone::StartSpeak(BYTE * buffer, DWORD len)
{
	if (!m_IsOpened|| m_IsSpeaking)return false;

	m_IsSpeaking = true;

	waveOutReset(m_hWaveOut);

	m_waveOutputHdr.lpData = (LPSTR)buffer;// 指向buffer
	m_waveOutputHdr.dwBufferLength = len;// buffer大小
	m_waveOutputHdr.dwBytesRecorded = len;
	m_waveOutputHdr.dwFlags = 0;
	m_waveOutputHdr.dwLoops = 1;

	ResetEvent(m_SpeakHandle);

	waveOutPrepareHeader(m_hWaveOut, &m_waveOutputHdr, sizeof(WAVEHDR));

	waveOutWrite(m_hWaveOut, &m_waveOutputHdr, sizeof(WAVEHDR));

	DWORD dw = WaitForSingleObject(m_SpeakHandle, INFINITE);

	if (dw == WAIT_OBJECT_0)
	{
		printf("---------------Speak Stop\n");
		m_IsSpeaking = false;
		m_IsRecving = false;

		FILE *pf;
		fopen_s(&pf, "recv.pcm", "wb");
		fwrite(buffer, 1, len, pf);
		fclose(pf);
	}
	return true;
}

bool MicroPhone::PauseSpeak()
{
	if (!m_IsOpened)return false;
	waveOutReset(m_hWaveOut);
	return true;
}

bool MicroPhone::StopSpeak()
{
	if (!m_IsOpened)return false;
	waveOutReset(m_hWaveOut);
	waveOutClose(m_hWaveOut);
	return true;
}

int WaitTime = 0;

void MicroPhone::Callback(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
	// 获取对象
	MicroPhone* micPhone = (MicroPhone*)dwInstance;
	// 获取音频头
	PWAVEHDR  waveHdr = (PWAVEHDR)dwParam1;

	// 处理消息
	switch (uMsg)
	{
		case WIM_OPEN:                                 // 打开录音设备
			printf("打开麦克风设备[成功]\n");
			break;
		case WIM_DATA:                                 // 缓冲区已满
		{
			//printf("缓冲池已满..\n");
			// 缓冲池信息
			DWORD buflen = waveHdr->dwBufferLength;
			DWORD bytrecd = waveHdr->dwBytesRecorded;

			float dB=micPhone->getdBValue((SHORT *)waveHdr->lpData, bytrecd);

			if (dB>THRESHOLD_DB)
			{
				//printf("dB:%f\n",dB);
				WaitTime = 0;
				if (!micPhone->m_IsNeedWakeUp&&!micPhone->m_IsAudioReady)
				{
					if (!micPhone->m_IsSpeaking&&!micPhone->m_IsRecving)
					{
						micPhone->m_IsNeedWakeUp = true;
						if (micPhone->m_AudioBuffer)
						{
							free(micPhone->m_AudioBuffer);
							micPhone->m_AudioBuffer = (BYTE*)malloc(sizeof(BYTE) * INIT_BUFFER_SIZE);
							micPhone->m_RecordedBytes = 0;	
						}
						micPhone->m_IsListening = true;
						printf("---------------Listen Start\n");
					}
				}
			}
			else
			{
				if (GetTickCount() - micPhone->m_StopTalkTime >= 1000)
				{
					micPhone->m_StopTalkTime = GetTickCount();
					WaitTime++;
				}
				if (WaitTime > 2)
				{
					if (micPhone->m_IsNeedWakeUp)
					{
						WaitTime = 0;
						micPhone->m_IsListening = false;
						micPhone->m_IsAudioReady = true;
						micPhone->m_IsNeedWakeUp = false;
						printf("---------------Listen Stop\n");
					}
				}
			}
			if (micPhone->m_IsListening)
			{
				micPhone->m_RecordedBytes += bytrecd;
				// 缓冲扩增
				micPhone->m_AudioBuffer = (BYTE*)realloc(micPhone->m_AudioBuffer, micPhone->m_RecordedBytes * sizeof(BYTE));
				// 存储新内容
				if (micPhone->m_AudioBuffer)
				{
					memcpy(&micPhone->m_AudioBuffer[micPhone->m_RecordedBytes - bytrecd], waveHdr->lpData, bytrecd);
					//printf("已存储:%d byte\n", micPhone->m_RecordedBytes);
				}
			}
			// 循环 	
			if (micPhone->m_IsRecycle)
			{
				// 加入缓存
				waveInAddBuffer(hwi, waveHdr, sizeof(WAVEHDR));
			}
			break;
		}
		case WIM_CLOSE:                               // 关闭录音设备
		{
			printf("停止录音..\n");
			break;
		}
		default:
			break;
	}
}

float MicroPhone::getdBValue(SHORT *RecordSrc, DWORD Size)
{
	//计算
	SHORT *pSrc = new SHORT[Size];
	memcpy(pSrc, RecordSrc, Size);

	DWORD SizeRecord = Size / 2;

	double dbVal = pSrc[0]; //第一个点  
	DWORD  dwPtSize = SizeRecord; //点数  
	double fWave_Frame_Avg = dbVal; //平均值  
	double fWave_Frame_RMS = pow(dbVal, 2); //有效值  
	double fWave_Frame_Max = dbVal; //最大值  
	double fWave_Frame_Min = dbVal; //最小值  
	double fWave_Frame_AbsMax = fabs(dbVal); //绝对最大值  
	double fWave_Frame_AbsMin = fabs(dbVal); //绝对最小值  

	for (DWORD i = 1; i<SizeRecord; i++)
	{
		dbVal = pSrc[i];//单声道  
		fWave_Frame_Avg += dbVal; //平均值  
		fWave_Frame_RMS += pow(dbVal, 2); //有效值  
		fWave_Frame_Max = max(dbVal, fWave_Frame_Max); //最大值  
		fWave_Frame_Min = min(dbVal, fWave_Frame_Min); //最小值  
		fWave_Frame_AbsMax = max(fabs(dbVal), fWave_Frame_AbsMax); //绝对最大值  
		fWave_Frame_AbsMin = min(fabs(dbVal), fWave_Frame_AbsMin); //绝对最小值  
	}

	fWave_Frame_Avg /= SizeRecord; //平均值  
	fWave_Frame_RMS = sqrt(fWave_Frame_RMS / SizeRecord);//有效值  
	if (fWave_Frame_Avg > 0)
	{
		//printf("dB:%f\n", fWave_Frame_Avg);
	}

	delete[]pSrc;

	return fWave_Frame_Avg;
}

 

 类似资料: