目录
音频播放
音频播放这里提供两种方式,直接播放音频文件和播放音频流的方式,可以根据实际情况选择使用方法
播放MP3与wav文件
操作步骤包括,装载文件,播放,暂停,恢复,停止
- 装载文件
int Load(HWND hWnd, CString strFilepath)
{
m_hWnd = hWnd;
DWORD dwReturn;
mciSendCommand(DeviceID, MCI_CLOSE, 0, 0);//在加载文件前先清空上一次播放的设备
mciopenparms.lpstrElementName = strFilepath;//将音乐文件路径传给设备
mciopenparms.lpstrDeviceType = _T("mpegvideo");
if (dwReturn = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_ELEMENT | MCI_OPEN_TYPE, (DWORD_PTR)&mciopenparms))
{
//如果打开文件失败,则将出错信息储存在buffer,并显示出错警告
CString s;
s.Format(_T("error:%d!"), dwReturn);
MessageBox(hWnd, s, _T("player"), MB_OKCANCEL);
}
DeviceID = mciopenparms.wDeviceID;
return dwReturn;
}
- 播放
void play()
{
MCI_PLAY_PARMS mciplayparms;
mciplayparms.dwCallback = (DWORD)m_hWnd;
mciplayparms.dwFrom = 0;//每次播放都是从0开始播放 dwFrom用于设置音乐播放的起始位置
mciSendCommand(DeviceID, MCI_PLAY, MCI_FROM | MCI_NOTIFY, (DWORD_PTR)&mciplayparms);//dwCallback与MCI_NOTIFY是配对出现的
}
- 暂停
void pause()
{
mciSendCommand(DeviceID, MCI_PAUSE, 0, 0);
}
- 恢复
void resume()
{
mciSendCommand(DeviceID, MCI_RESUME, 0, 0);
}
- 停止
void stop()
{
mciSendCommand(DeviceID, MCI_STOP, 0, 0);//当点击停止按钮时,将所有的信息都清除掉
mciSendCommand(DeviceID, MCI_CLOSE, 0, 0);//点关闭按钮的时候,清除设备信息,再点播放按钮,就播放不了音乐了
}
- 设置播放音量
DWORD setvolume(DWORD vol)
{
MCI_DGV_SETAUDIO_PARMS setvolume;//这是设置音量的参数数据结构
setvolume.dwCallback = NULL;
setvolume.dwItem = MCI_DGV_SETAUDIO_VOLUME;//动作是设置音量
setvolume.dwValue = vol;//音量值是vol
mciSendCommand(DeviceID, MCI_SETAUDIO, MCI_DGV_SETAUDIO_ITEM | MCI_DGV_SETAUDIO_VALUE, (DWORD)(LPVOID)&setvolume);
return 0;
}
DeviceID见装载文件步骤
播放音频流
播放音频流,我已经封装成一个简单的类WaveOut,可直接移植使用,类中包括打开,关闭,push音频数据等,详细见源码
- 打开一个播放实体
HWAVEOUT hWaveOut;
WAVEHDR wvHeader[2];
int WaveOut::open(DWORD nSamplesPerSec, WORD wBitsPerSample, WORD nChannels)
{
/*
...
详细见源码
*/
/* 'waveOutOpen' will call 'SetEvent'. */
if (waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, (DWORD_PTR)hEventPlay, 0, CALLBACK_EVENT))
{
return -1;
}
waveOutPrepareHeader(hWaveOut, &wvHeader[0], sizeof(WAVEHDR));
waveOutPrepareHeader(hWaveOut, &wvHeader[1], sizeof(WAVEHDR));
/*
...
*/
return 0;
}
在这个步骤中我们需要将两个buf添加到waveIn中,至于为什么是两个,是因为只有当队列中至少有个buf时才不会有断音的情况,实测属实。
- 关闭一个播放实体
void WaveOut::close()
{
waveOutUnprepareHeader(hWaveOut, &wvHeader[0], sizeof(WAVEHDR));
waveOutUnprepareHeader(hWaveOut, &wvHeader[1], sizeof(WAVEHDR));
waveOutClose(hWaveOut);
hWaveOut = NULL;
}
- Push数据
当队列中的某个buf播放完之后,使用waveOutWrite接口重新加载入数据
录音
播放音频流,我已经封装成一个简单的类WaveIn,可直接移植使用,类中包括打开,关闭,开始录音,暂停,详细见源码
- 打开一个录音实体
WAVEHDR wvHeader[2];
HWAVEIN hWaveIn;
int WaveIn::open(DWORD nSamplesPerSec, WORD wBitsPerSample, WORD nChannels)
{
/*
.
.详细见源码
.
*/
//waveInReset(hWaveIn);
waveInPrepareHeader(hWaveIn, &wvHeader[0], sizeof(WAVEHDR));
waveInPrepareHeader(hWaveIn, &wvHeader[1], sizeof(WAVEHDR));
//将两个wHdr添加到waveIn中去
waveInAddBuffer(hWaveIn, &wvHeader[0], sizeof(WAVEHDR));
waveInAddBuffer(hWaveIn, &wvHeader[1], sizeof(WAVEHDR));
iCurRecording = 0;
return 0;
}
- 关闭一个录音实体
void WaveIn::close()
{
waveInStop(hWaveIn);
waveInUnprepareHeader(hWaveIn, &wvHeader[0], sizeof(WAVEHDR));
waveInUnprepareHeader(hWaveIn, &wvHeader[1], sizeof(WAVEHDR));
waveInClose(hWaveIn);
hWaveIn = NULL;
}
- 录音开始
int WaveIn::record_start()
{
waveInStart(hWaveIn);
return 0;
}
- 录音停止
int WaveIn::record_stop()
{
waveInStop(hWaveIn);
return 0;
}
Pcm格式
存储pcm格式,直接将录音数据写入文件中即可
Wav格式
typedef struct wav_format_t {
char riff[4];//"RIFF"
unsigned int file_len;
char wav_type[4];//"WAVE"
char fmt[4];//"fmt "
unsigned int fmt_len;
unsigned short wFormatTag;
unsigned short nChannels;
unsigned int nSamplesPerSec;
unsigned int nAvgBytesPerSec;
unsigned short nBlockAlign;
unsigned short wBitsPerSample;
char dataFlag[4];//"data"
unsigned int data_len;
} wav_format;
存储wav格式时,需要将上述结构体存在文件的头部,其中需要注意的是file_len,其长度为文件大小减4,data_len为文件大小减上诉结构体的大小,即有效音频的大小。这两个都是在录音结束之后才能确定的值
将上诉头存储之后,后面的数据则与pcm存储方式一样
相关资料包括demo代码请到我<MFC实用技巧>篇最下的网盘链接中下载!