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;
}