使用WaveIn录制音频并且使用WaveOut播放音频

article/2025/11/9 15:57:16

在Windows下开发音频的方式有多种,但是最简单,也是最灵活的就是Wave系列API,今天我们一起用WaveIn和WaveOut实现一个音频录制和音频播放器,具体界面如下:

录制步骤如下:

void CcbdDlg::OnBnClickedButtonStartRec()
{// TODO:  在此添加控件通知处理程序代码m_iHour = 0;m_iMinute = 0;m_iSec = 0;m_pDataBuff1 = NULL;m_pDataBuff2 = NULL;m_hWaveDev = NULL;m_dwFileSize = 0;CStringA strPathA;strPathA = m_strPath;m_fSave = fopen(strPathA.GetBuffer(strPathA.GetLength()), "ab+");if (!m_fSave){AfxMessageBox(_T("文件打开失败!"));return;}WAVEFORMATEX waveCtx;waveCtx.nSamplesPerSec = 44100; /* sample rate */waveCtx.wBitsPerSample = 16; /* sample size */waveCtx.nChannels = 2; /* channels*/waveCtx.cbSize = 0; /* size of _extra_ info */waveCtx.wFormatTag = WAVE_FORMAT_PCM;waveCtx.nBlockAlign = (waveCtx.wBitsPerSample * waveCtx.nChannels) >> 3;waveCtx.nAvgBytesPerSec = waveCtx.nBlockAlign * waveCtx.nSamplesPerSec;MMRESULT iRet = waveInOpen(&m_hWaveDev, m_comboDevList.GetCurSel()/*WAVE_MAPPER*/, &waveCtx, (DWORD)(UINT)this->m_hWnd, NULL, CALLBACK_WINDOW);if (MMSYSERR_NOERROR == iRet){TRACE("yes,waveInOpen OK!\n");}else{AfxMessageBox(_T("设备打开失败!"));return;}//准备第一个缓冲m_pDataBuff1 = new BYTE[waveCtx.nAvgBytesPerSec];memset(m_pDataBuff1, 0, waveCtx.nAvgBytesPerSec);m_waveHdr1.lpData = (LPSTR)m_pDataBuff1;m_waveHdr1.dwBufferLength = waveCtx.nAvgBytesPerSec;m_waveHdr1.dwBytesRecorded = 0;m_waveHdr1.dwFlags = 0;iRet = waveInPrepareHeader(m_hWaveDev, &m_waveHdr1, sizeof(WAVEHDR));if (MMSYSERR_NOERROR == iRet){TRACE("yes,waveInPrepareHeader OK!\n");}else{if (m_pDataBuff1){delete [] m_pDataBuff1;m_pDataBuff1 = NULL;}AfxMessageBox(_T("内存分配失败!"));return;}iRet = waveInAddBuffer(m_hWaveDev, &m_waveHdr1, sizeof(WAVEHDR));if (MMSYSERR_NOERROR == iRet){TRACE("yes,waveInAddBuffer OK!\n");}else{if (m_pDataBuff1){delete [] m_pDataBuff1;m_pDataBuff1 = NULL;}AfxMessageBox(_T("录制准备失败!"));return;}//准备第二个缓冲m_pDataBuff2 = new BYTE[waveCtx.nAvgBytesPerSec];memset(m_pDataBuff2, 0, waveCtx.nAvgBytesPerSec);m_waveHdr2.lpData = (LPSTR)m_pDataBuff2;m_waveHdr2.dwBufferLength = waveCtx.nAvgBytesPerSec;m_waveHdr2.dwBytesRecorded = 0;m_waveHdr2.dwFlags = 0;iRet = waveInPrepareHeader(m_hWaveDev, &m_waveHdr2, sizeof(WAVEHDR));if (MMSYSERR_NOERROR == iRet){TRACE("yes,waveInPrepareHeader OK!\n");}else{if (m_pDataBuff1){delete[] m_pDataBuff1;m_pDataBuff1 = NULL;}if (m_pDataBuff2){delete [] m_pDataBuff2;m_pDataBuff2 = NULL;}AfxMessageBox(_T("准备录制声音失败!"));return;}iRet = waveInAddBuffer(m_hWaveDev, &m_waveHdr2, sizeof(WAVEHDR));if (MMSYSERR_NOERROR == iRet){TRACE("yes,waveInAddBuffer OK!\n");}else{if (m_pDataBuff1){delete[] m_pDataBuff1;m_pDataBuff1 = NULL;}if (m_pDataBuff2){delete [] m_pDataBuff2;m_pDataBuff2 = NULL;}AfxMessageBox(_T("缓冲添加失败!"));return;}iRet = waveInStart(m_hWaveDev);if (MMSYSERR_NOERROR == iRet){TRACE("yes,waveInStart OK!\n");}else{if (m_pDataBuff1){delete [] m_pDataBuff1;m_pDataBuff1 = NULL;}if (m_pDataBuff2){delete [] m_pDataBuff2;m_pDataBuff2 = NULL;}AfxMessageBox(_T("录制启动失败!"));return;}GetDlgItem(IDC_BUTTON_START_REC)->EnableWindow(FALSE);GetDlgItem(IDC_STATIC_REC_TIMER)->ShowWindow(SW_SHOW);GetDlgItem(IDC_STATIC_REC_TIMER)->SetWindowText(_T("00:00:00"));SetTimer(MM_AUDIO_RECORD_TIMER, 1000, NULL);SetTimer(SCREEN_RECORD_TIMER, 33, NULL);
}

上面的代码我们已经准备好了录制,当麦克风有音频信息传入的时候,我们会收到MM_WIM_DATA消息,在这里我们把收到的数据保存为文件,具体代码如下:

LRESULT CcbdDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{MMRESULT iRet = 0;switch (message){case MM_WIM_OPEN:TRACE("Open!\n");break;case MM_WIM_CLOSE:TRACE("Close!\n");break;case MM_WIM_DATA:if (m_hWaveDev == NULL)break;LPWAVEHDR waveHdr = (LPWAVEHDR)lParam;if (!m_fSave){TRACE("Error!\n");break;}BYTE bType = 1;//音频DWORD dwSize = waveHdr->dwBytesRecorded; //大小m_dwFileSize += dwSize;fwrite(&bType, 1, 1, m_fSave);fwrite(&dwSize, 1, 4, m_fSave);int nTotal = 0;while (nTotal < waveHdr->dwBytesRecorded){int nTmp = fwrite(waveHdr->lpData, 1, waveHdr->dwBytesRecorded, m_fSave);nTotal += nTmp;}iRet = waveInAddBuffer(m_hWaveDev, waveHdr, sizeof(WAVEHDR));if (MMSYSERR_NOERROR != iRet){if (m_pDataBuff1){delete m_pDataBuff1;m_pDataBuff1 = NULL;}if (m_pDataBuff2){delete m_pDataBuff2;m_pDataBuff2 = NULL;}}	break;}return CDialog::WindowProc(message, wParam, lParam);
}

当用户点击结束录制的时候,我们需要做一些善后的工作,具体如下:

void CcbdDlg::OnBnClickedButtonEndRec()
{// TODO:  在此添加控件通知处理程序代码waveInUnprepareHeader(m_hWaveDev, &m_waveHdr1, sizeof(WAVEHDR));waveInUnprepareHeader(m_hWaveDev, &m_waveHdr2, sizeof(WAVEHDR));MMRESULT iRet = waveInStop(m_hWaveDev);if (MMSYSERR_NOERROR == iRet){TRACE("yes,waveInStop OK!\n");}else{TRACE("no,waveInStop Wrong!\n");}iRet = waveInReset(m_hWaveDev);if (MMSYSERR_NOERROR == iRet){TRACE("yes,waveInReset OK!\n");}else{TRACE("no,waveInReset Wrong!\n");}iRet = waveInClose(m_hWaveDev);if (MMSYSERR_NOERROR == iRet){TRACE("yes,waveInClose OK!\n");}else{TRACE("no,waveInClose Wrong!\n");}m_hWaveDev = NULL;if (m_pDataBuff1){delete m_pDataBuff1;m_pDataBuff1 = NULL;}if (m_pDataBuff2){delete m_pDataBuff2;m_pDataBuff2 = NULL;}GetDlgItem(IDC_BUTTON_START_REC)->EnableWindow(TRUE);GetDlgItem(IDC_STATIC_REC_TIMER)->ShowWindow(SW_HIDE);KillTimer(MM_AUDIO_RECORD_TIMER);if (m_fSave){fwrite(&m_dwFileSize, 1, 4, m_fSave);}if (m_fSave){fclose(m_fSave);return;}
}

上面我们完成了录制,下面我们一起看一下播放。

播放器的具体开发步骤如下:

当用户点击播放的时候,我们首先需要读取音频文件,用音频文件的数据初始化两个缓冲区,然后提交给声卡,让其播放,具体代码如下:

void CcbdplayerDlg::OnBnClickedButtonPlay()
{// TODO:  在此添加控件通知处理程序代码m_bStartPlay = true;CStringA strTmpA;strTmpA = m_strPath;m_fTmp = fopen(strTmpA.GetBuffer(strTmpA.GetLength()), "rb+");if (!m_fTmp){AfxMessageBox(_T("文件打开失败!"));return;}fseek(m_fTmp, -4, SEEK_END);fread(&m_dwFileSize, 1, 4, m_fTmp);fseek(m_fTmp, 0, SEEK_SET);int nTmp = m_dwFileSize;m_PlayProgress.SetRange32(0, nTmp);m_PlayProgress.SetPos(0);MMRESULT mmresult = 0;mmresult = waveOutOpen(&m_hWaveOut, WAVE_MAPPER, &m_WaveFormat, (DWORD)m_hWnd, (DWORD_PTR)0, CALLBACK_WINDOW);if (mmresult != MMSYSERR_NOERROR){AfxMessageBox(_T("打开设备失败!"));return;}if (!m_pDataBuff1){AfxMessageBox(_T("内存分配失败!"));return;}int nReadSize = 0;BYTE bType = 0;DWORD dwSize = 0;fread(&bType, 1, 1, m_fTmp);fread(&dwSize, 1, 4, m_fTmp);memset(m_pDataBuff1, 0, m_WaveFormat.nAvgBytesPerSec);nReadSize = fread(m_pDataBuff1, 1, dwSize, m_fTmp);m_WaveHead1.lpData = (LPSTR)m_pDataBuff1;m_WaveHead1.dwBufferLength = nReadSize;m_WaveHead1.dwBytesRecorded = 0;m_WaveHead1.dwUser = 0;m_WaveHead1.dwFlags = 0;m_WaveHead1.dwLoops = 1;m_WaveHead1.lpNext = NULL;m_WaveHead1.reserved = 0;mmresult = waveOutPrepareHeader(m_hWaveOut, &m_WaveHead1, sizeof(WAVEHDR));if (mmresult != MMSYSERR_NOERROR){if (m_pDataBuff1)delete[] m_pDataBuff1;if (m_pDataBuff2)delete[] m_pDataBuff2;AfxMessageBox(_T("准备失败!"));return;}memset(m_pDataBuff2, 0, m_WaveFormat.nAvgBytesPerSec);fread(&bType, 1, 1, m_fTmp);fread(&dwSize, 1, 4, m_fTmp);nReadSize = fread(m_pDataBuff2, 1, dwSize, m_fTmp);if (!m_pDataBuff2){if (m_pDataBuff1)delete[] m_pDataBuff1;AfxMessageBox(_T("内存分配失败!"));return;}m_WaveHead2.lpData = (LPSTR)m_pDataBuff2;m_WaveHead2.dwBufferLength = nReadSize;m_WaveHead2.dwBytesRecorded = 0;m_WaveHead2.dwUser = 0;m_WaveHead2.dwFlags = 0;m_WaveHead2.dwLoops = 1;m_WaveHead2.lpNext = NULL;m_WaveHead2.reserved = 0;	mmresult = waveOutPrepareHeader(m_hWaveOut, &m_WaveHead2, sizeof(WAVEHDR));if (mmresult != MMSYSERR_NOERROR){if (m_pDataBuff1)delete[] m_pDataBuff1;if (m_pDataBuff2)delete[] m_pDataBuff2;AfxMessageBox(_T("准备失败!"));return;}waveOutWrite(m_hWaveOut, &m_WaveHead1, sizeof(WAVEHDR));waveOutWrite(m_hWaveOut, &m_WaveHead2, sizeof(WAVEHDR));GetDlgItem(IDC_BUTTON_PLAY)->EnableWindow(FALSE);GetDlgItem(IDC_BUTTON_STOP_PLAY)->EnableWindow(TRUE);
}

当声卡播放完我们提交给它的数据后,它会发送通知WOM_DONE给我们,我们需要做的是继续从文件读取音频,然后再次提交给声卡,具体代码如下:

LRESULT CcbdplayerDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{MMRESULT iRet = 0;switch (message){case WOM_OPEN:break;case WOM_DONE:{if (m_bStartPlay == false)break;LPWAVEHDR pWaveHeader = (LPWAVEHDR)lParam;if (pWaveHeader){int nPos = m_PlayProgress.GetPos();if (nPos >= m_dwFileSize){break;}nPos += pWaveHeader->dwBufferLength;m_PlayProgress.SetPos(nPos);waveOutUnprepareHeader(m_hWaveOut, pWaveHeader, sizeof(WAVEHDR));int nReadSize = 0;BYTE bType = 0;DWORD dwSize = 0;fread(&bType, 1, 1, m_fTmp);			if (feof(m_fTmp))break;if (bType != 1)break;fread(&dwSize, 1, 4, m_fTmp);if (feof(m_fTmp))break;memset(pWaveHeader->lpData, 0, m_WaveFormat.nAvgBytesPerSec);nReadSize = fread(pWaveHeader->lpData, 1, dwSize, m_fTmp);if (feof(m_fTmp))break;pWaveHeader->dwBufferLength = nReadSize;MMRESULT mmresult = waveOutPrepareHeader(m_hWaveOut, pWaveHeader, sizeof(WAVEHDR));if (mmresult != MMSYSERR_NOERROR){AfxMessageBox(_T("准备失败!"));break;}waveOutWrite(m_hWaveOut, pWaveHeader, sizeof(WAVEHDR));}}break;}return CDialog::WindowProc(message, wParam, lParam);
}

当用户点击停止播放的时候,我们需要做的是释放资源,具体如下:

void CcbdplayerDlg::OnBnClickedButtonStopPlay()
{// TODO:  在此添加控件通知处理程序代码waveOutReset(m_hWaveOut);waveOutUnprepareHeader(m_hWaveOut, &m_WaveHead1, sizeof(WAVEHDR));waveOutUnprepareHeader(m_hWaveOut, &m_WaveHead2, sizeof(WAVEHDR));waveOutClose(m_hWaveOut);if (m_fTmp){fclose(m_fTmp);m_fTmp = NULL;}GetDlgItem(IDC_BUTTON_PLAY)->EnableWindow(TRUE);GetDlgItem(IDC_BUTTON_STOP_PLAY)->EnableWindow(FALSE);m_bStartPlay = false;return;
}

需要代码的小伙伴,可以从下面的地址下载:

https://download.csdn.net/download/u011711997/10478662




http://chatgpt.dhexx.cn/article/IsyCRCF3.shtml

相关文章

WaveOut播放声音死锁问题原因

1.首先我们复习下造成死锁的几个充要条件&#xff1a; 1&#xff09;互斥&#xff1a;互斥资源&#xff0c;只能被一个进程使用 2&#xff09;不剥夺&#xff1a;非抢占式调度&#xff0c;不能强行抢用其他进程资源 3&#xff09;请求和保持&#xff1a;占有着资源不释放&am…

C++ 使用waveOut实现声音播放

文章目录 前言一、需要的对象及方法1.对象2.方法 二、整体流程三、关键实现1.声音格式2.对象池 四、封装成对象1.接口设计2.具体实现 五、使用示例总结 前言 在Windows上实现声音播放比较简单的方法是使用winmm&#xff0c;其中的waveOut模块就可以打开声音设备&#xff0c;播…

windows下使用waveOut播放音频pcm

目录 一、前言 二、waveOut主要API介绍 三、waveOut播放PCM音频框架 《Windows平台使用waveOut播放PCM数据代码实现》链接&#xff1a; https://edu.csdn.net/learn/38258/606147?spm1003.2001.3001.4157 一、前言 在<windows下directShow音视频采集>一篇文章中就介…

双缓冲方法实现waveOut的连续无卡顿播放以及即收即放

1. waveOut基本使用方法 waveOut是一套历史悠久的Windows音频API&#xff0c;虽然古老&#xff0c;但至今仍运行良好&#xff0c;且支持老旧系统&#xff08;原生支持Windows XP&#xff09;。 waveOut虽然不像DirectSound那样自带混音功能&#xff0c;但也可以通过同时开多个…

清华牛逼!

如果让你用尽可能多的词语形容一名女子的美貌&#xff0c;你会怎么说&#xff1f; “这女孩太美了&#xff0c;闭月羞花、沉鱼落雁不足以形容起美貌。那身段令貂婵蒙羞&#xff0c;那脸蛋令月亮失色。那眼睛&#xff0c;那鼻子&#xff0c;那小嘴&#xff0c;那胸脯&#xff0c…

博士申请 | 上海交通大学叶南阳老师招收计算机视觉/机器学习方向博士生

合适的工作难找&#xff1f;最新的招聘信息也不知道&#xff1f; AI 求职为大家精选人工智能领域最新鲜的招聘信息&#xff0c;助你先人一步投递&#xff0c;快人一步入职&#xff01; 上海交通大学 上海交通大学约翰霍普克罗夫特计算机科学中心于 2017 年 1 月正式成立&#x…

硬核实力!西安交大人工智能学科到底有多强?

人工智能 是时下最火的科技词汇之一 人工智能 作为引领未来的战略性技术 正默默改变着人类的生产生活 西安交大人工智能研究是国内高校的先驱 拥有强大的学术实力 科研成绩斐然 斩获国内外多项荣誉 今天 跟着小编一起走进交大人工智能 漫步科学殿堂 西安交通大学人工智能学科发…

清华大学交叉信息研究院姚班修改培养模式:姚班、智班、量信班全合并

机器之心报道 编辑&#xff1a;杜伟、泽南 拓宽优秀学生的选择方向。 清华大学交叉信息研究院宣布了优化本科培养模式的新计划&#xff1a; 为进一步拓宽学生基础学养&#xff0c;并优化专业方向选择&#xff0c;特调整本科生培养模式&#xff0c;具体要点如下&#xff1a; 1、…

我的2020推免之路:清叉、TBSI、贵系、中山、国防科大、自动化所

我的2020推免之路&#xff1a;清叉、TBSI、贵系、中山、国防科大、自动化所 一.前言二.个人情况三.夏令营清华大学交叉信息学院&#xff08;6.13~6.14&#xff09;清华-伯克利深圳学院&#xff08;7.2~7.9&#xff09;清华大学计算机系&#xff08;7.8~7.9&#xff09;中山大学…

清北浙交大比拼,南大强势上榜,AI到底哪家强?

作者 | 阿司匹林 编者注&#xff1a;近日&#xff0c;上海交通大学和武书连编制的两份比较有影响力的大学排行榜先后发布。这两份榜单的最终排名虽然存在一定的差异&#xff0c;但是都将清华大学、北京大学、浙江大学、上海交通大学、以及复旦大学排在了前五&#xff08;先后顺…

2022 ACM杰出会员揭榜:清华黄隆波、刘世霞,北大郝丹、刘譞哲等23位华人学者入选!...

来源&#xff1a;新智元 2022年度ACM杰出会员&#xff08;Distinguished Member&#xff09;名单公布&#xff01; 本次评选设有三个奖项&#xff0c;分别表彰在计算机领域做出的教育贡献、工程贡献和科学贡献。 今年共有67名会员入选。其中有23位是华人&#xff08;中国大陆6人…

科研实习 | 清华大学交叉信息研究院弋力老师课题组招收科研实习生

合适的工作难找&#xff1f;最新的招聘信息也不知道&#xff1f; AI 求职为大家精选人工智能领域最新鲜的招聘信息&#xff0c;助你先人一步投递&#xff0c;快人一步入职&#xff01; 清华大学 弋力博士于2021年7月加入清华大学交叉信息研究院任助理教授&#xff0c;博士生导师…

数说CS | 北大叉院推免生源大起底!

写在前面 北京大学前沿交叉学科研究院是怎样的一个存在&#xff1f;研究方向与其他学院有什么不同&#xff1f;学术氛围如何&#xff1f; 今天&#xff0c;岛主将会带领各位一同了解北大叉院&#xff0c;从学院介绍、培养特色、优营生源分析等板块为大家深入剖析&#xff01;…

北理工通报方岱宁院士处理结果

点击进入—>3D视觉工坊学习交流群 12月7日&#xff0c;北京理工大学发布《情况通报》&#xff0c;通报“教师方岱宁相关视频事件”处理结果。 据通报&#xff0c;经查核&#xff0c;视频内容是方岱宁今年7月初在线参加学术会议时发生的事情&#xff0c;因其行为失范&#xf…

2020cs线上保研之路 清华北大交大复旦自动化所计算所

2020计算机线上保研 清华北大交大复旦自动化所计算所 无聊在飞机上编辑的这几个月来的经历吧。我真的感觉好累&#xff0c;巨累。该死的疫情。 今年保研&#xff0c;往届的学长学姐的去路都没有什么参考性了&#xff0c;一个原因在于cs内卷更严重了&#xff0c;另一个因为疫情…

直播预告 | 清华叉院助理教授赵行、在读博士生孙桥:自动驾驶中的行为交互

本期为TechBeat人工智能社区第421期线上Talk&#xff0c;响应大家希望实时交流的需求&#xff0c;本次Talk是&#xff01;直&#xff01;播&#xff01; 北京时间7月13日(周三)20:00&#xff0c;我们很开心邀请到老朋友——清华叉院助理教授赵行老师和他的学生孙桥&#xff0c;…

清华大学交叉信息研究院招收计算机图形学、机器学习方向博士

来源&#xff1a;机器之心 课题组简介杜韬博士于 2022 年秋季加入清华大学交叉信息研究院担任助理教授&#xff0c;领导课题组开展计算机图形学和机器学习相关方向的研究。课题组常年招收博士生、科研助理、实习生、访问学生、助理研究员等&#xff0c;部分职位工作地点可选北京…

生源985占比100%,北大叉院这个专业本校学生也心仪~

1、院校介绍 北京大学前沿交叉学科研究院下的大数据研究中心是中国交叉学科的先锋&#xff0c;主要学科为数据科学&#xff0c;现设数据挖掘方法、大数据软件技术、大数据安全技术、健康医疗大数据以及交通大数据等9个研究方向。 北大叉院大数据研究中心科研实力强大&#xf…

【2019保研经验】清华贵系、清华软院、北大叉院、中科院自动化所等

2019年9月29日23点00分&#xff0c;我收到了清华大学计算机科学与技术系的待录取通知&#xff0c;也意味着我几个月的保研工作彻底结束&#xff0c;并且取得了我所能取得的最好成绩。如今&#xff0c;全部的保研工作已全部结束&#xff0c;我也是时候要摆脱懒惰的借口&#xff…

清华数据女神评选结果:第一竟然是叉院大神...?

在刚刚过去的3月7日女生节&#xff0c;清华学大数据的男生们发起了一个数据女神评选活动&#xff0c;希望在大家的参与下评选出数据女神&#xff0c;送上女生节的礼物。 今天是公布评选结果的时刻&#xff1a; 恭喜来自交叉信息研究院的陈立杰和来自美术学院的张玉萍同学成为我…