一、录制音频

  在windows中提供了相应的API函数(waveIn这个族的函数)实现录音功能;在使用这些函数时,一定要引入相应的头文件

#include <windows.h>

#include <Mmsystem.h>

#pragram comment(lib, "Winmm.lib")

1、在开始录音之前,需要首先定义音频的相关信息:使用WAVEFORMATEX结构,设置相关的音频流信息。以下是MSDN中的定义:

typedef struct
{
WORD wFormatTag; // 波形音频的格式,一般情况下设置为WAVE_FORMAT_PCM
WORD nChannels; // 音频声道的数量。可以是1或者2(现在电脑基本上都是左右两个声道,因此一般设置为2)
DWORD nSamplesPerSec; // 每个声道播放和接收的音频的样本频率(一般的频率为8khz, 11.025khz, 22.05khz,44.1khz)
DWORD nAvgBytesPerSec; // 平均的数据传输率,单位为byte/s
WORD nBlockAlign; // 以字节为单位的块对齐的大小,一般为:(nChannels * wBitsPerSample)/8
WORD wBitsPerSample; // 根据wFormatTag设置的类型,设置采样率的大小,如果设置为WAVE_FORMAT_PCM,则大小为8的整倍数
WORD cbSize; // 额外的空间,一般不需要,设置为0
}WAVEFORMATEX, *PWAVEFORMATEX;

定义一个WAVEFORMATEX对象,根据自己的要求设置音频流的信息,如下:

WAVEFORMATEX waveFormat;
waveFormat.nSamplesPerSec = 44100;
waveFormat.wBitsPerSample = 16;
waveFormat.nChannels = 2;
waveFormat.cbSize = 0;
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nBlockAlign = (waveFormat.wBitsPerSample * waveFormat.nChannels)/8;
waveFormat.nAvgBytesPerSec = waveFormat.nBlockAlign * waveFormat.nSamplesPersec;

2、当音频流信息设置完成后,接下来需要启动录音设备:使用waveInOpen函数。

waveInOpen函数原型为:

WINMMAPI
MMRESULT
WINAPI
waveInOpen(
_Out_opt_ LPHWAVEIN phwi,    // 一个特定的录音设备指针,如果设备启动成功,该参数的值将会被赋值为启动的设备
_In_ UINT uDeviceID,       // 需要启动的设备ID。一般不会手动指定某个设备,而是通过设置WAVE_MAPPER,通过系统查找可用设备
_In_ LPCWAVEFORMATEX pwfx,   // 音频流信息对象的指针。这个参数就是我们第一步设置的对象
_In_opt_ DWORD_PTR dwCallback,  // 录音消息的处理程序,可以设置一个函数、事件句柄、窗口句柄、一个特定的线程。也就是说录音消息产生后,由这个参数对应的值来处理该消息。包括关闭录音、缓冲区已满、开启设备
_In_opt_ DWORD_PTR dwInstance, // dwCallback参数的参数列表
_In_ DWORD fdwOpen // 打开设备的标识符。对应dwCallback,如果第四个参数设置为函数,则这个参数的值为CALLBACK_FUNCTION;如果为线程,则为CALLBACK_THREAD
);

注意:要想该函数成功执行,必须在开始之前,有录音设备的存在(台式电脑一定要插入麦克风才可以被检测到)。

3、当录音设备启动后,接下来需要声明两个缓冲区和两个缓冲区头部结构体WAVEHDR对象,缓冲区用来存放录音音频,并用缓冲区初始化头部对象:

INT bufSize = 512;

BYTE *pBuffer1 = new BYTE[bufSize];
if (pBuffer1 == NULL) return;
memset(pBuffer1, 0, bufSize); WAVEHDR wHdr1;
wHdr1.lpData = (LPSTR)pBuffer1;
wHdr1.dwBufferLength = bufSize;
wHdr1.dwBytesRecorded = 0;
wHdr1.dwUser = 0;
wHdr1.dwFlags = 0;
wHdr1.dwLoops = 1; BYTE *pBuffer2 = new BYTE[bufSize];
if (pBuffer2 == NULL) return;
memset(pBuffer2,0, bufSize); WAVEHDR wHdr2;
wHdr2.lpData = (LPSTR)pBuffer2;
wHdr2.dwBufferLength = bufSize;
wHdr2.dwBytesRecorded = 0;
wHdr2.dwUser = 0;
wHdr2.dwFlags = 0;
wHdr2.dwLoops = 1;

WAVEHDR对象定义如下:

typedef struct
{
LPSTR lpData; // 缓冲区存放的内容
DWORD dwBufferLength; // 缓冲区的大小
DWORD dwButesRecorded; // 缓冲区中存放的字节数
DWORD_PTR dwUser;
DWORD dwFlags;
DWORD dwLoops;
struct wavehdr_tag *lpNext;
DWORD_PTR reserved;
} WAVEHDR;

4、接下来将这两个头部对象,加入到准备的录音缓冲区中。该过程使用waveInPrepareHeader函数。

waveInPrepareHeader(hWaveIn, &wHdr1, sizeof(WAVEHDR)); // 准备第一个波形数据块用于录音
waveInPrepareHeader(hWaveIn, &wHdr2, sizeof(WAVEHDR)); // 准备第二个数据块用于录音

 waveInPrepareHeader的第一个参数表示:录音设备句柄;第二个参数表示:录音的缓冲区对象;第三个参数表示:录音缓冲区结构体的大小。

5、当准备好录音缓冲区,就可以将录音缓冲区加入到指定的录音设备中。该步骤使用waveInAddBuffer函数:

waveInAddBuffer(hWaveIn, &wHdr1, sizeof(WAVEHDR)); // 指定波形数据块为录音输入缓存
waveInAddBuffer(hWaveIn, &wHdr2, sizeof(WAVEHDR)); // 指定波形数据块为录音缓存

分别将缓冲区1和2设置为录音缓冲区。这些缓冲区将被加入到录音缓冲队列中,缓冲区循环执行。

6、开始录音,使用waveInStart函数

waveInStart(hWaveIn); // 开始录音

这个函数的意思就是,通过hWaveIn录音设备,将波形音频放入录音缓冲区(前面已经指定了缓冲区)

7、当缓冲区满时,waveInStart函数,就会自动的调用waveInOpen函数中指定的函数/窗体/事件;通过该函数,用户可以将缓冲区的波形文件发给其它的用户,也可以将缓冲区的文件保存起来,即就是用户对缓冲区的拷贝。声卡自动将音频缓冲区从缓冲队列中删除。拷贝完成后,就将该缓冲区以及对应的音频头文件初始化,并通过waveInAddBuffer函数重新加入录音缓冲队列中。

DWORD CIP_PHONEDlg::MicCallBack(HWAVEIN hWaveIn, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
// 所有的这些录音缓冲区都是由录音函数自动触发的,开发这不需要自己触发
CIP_PHONEDlg *pwnd = (CIP_PHONEDlg*)dwInstace; // 表示录音的窗体
PWAVEHDR whd = (PWAVEHDR)dwParam1; // 录音的头结构体对象
switch(uMsg)
{
case WIM_OPEN: // 打开录音设备,这里不做处理
break;
case WIM_DATA: // 表示缓冲区已满,我们将信息写入一个pcm文件
{
// 保存数据
pwnd->pf = fopen(pwnd->soundName, "ab+"); // 一定要以二进制数据写入,否则录音的音频会出现杂音
Sleep(1000); // 等待声音录制1s
fwrite(whd->lpData, 1, whd->dwBufferLength, pwnd->pf);
if (pwnd->isGetSound)
{
waveInAddBuffer(hWaveIn, whd, sizeof(WAVEHDR));
}
fclose(pwnd->pf);
}
break;
case WIM_CLOSE: // 停止录音
{
waveInStop(hWaveIn);
waveInReset(hWaveIn);
waveInClose(hWaveIn);
}
break;
default:
break;
}
return 0;
}

8、停止录音,使用waveInClose函数执行该操作

delete [] pBuffer1->lpData;
delete [] pBuffer2->lpData;
waveInClose(hWaveIn); // 停止录音

停止录音时,将会触发WIM_CLOSE消息。

在这个过程中首先执行waveInStop函数:表示禁止向输入缓冲区中输入波形数据;

然后执行waveInReset函数:表示停止波形数据的输入并且将当前的位置位0,将所有挂起的输入缓冲区设置为完成,并返回给应用程序(其实就是一个复位操作)

最后执行waveInClose函数:表示关闭录音设备。

  

MFC录制音频和播放音频的更多相关文章

  1. MFC中使用SDL播放音频没有声音的解决方法

    本文所说的音频是指的纯音频,不包含视频的那种. 在控制台中使用SDL播放音频,一般情况下不会有问题. 但是在MFC中使用SDL播放音频的时候,会出现没有声音的情况.经过长时间探索,没有找到特别好的解决 ...

  2. iOS 9音频应用播放音频之音量设置与声道设置

    iOS 9音频应用播放音频之音量设置与声道设置 iOS 9音频应用音量设置 音量又称响度.音强,是指人耳对所听到的声音大小强弱的主观感受,其客观评价尺度是声音的振幅大小.在iOS 9音频应用的应用中, ...

  3. iOS 9音频应用播放音频之iOS 9音频播放进度

    iOS 9音频应用播放音频之iOS 9音频播放进度 iOS 9音频应用开发播放进度 音频文件在播放后经过了多久以及还有多久才可以播放完毕,想必是用户所关注的问题.为了解决这一问题,在很多的音乐播放器中 ...

  4. iOS 9音频应用播放音频之控制播放速度

    iOS 9音频应用播放音频之控制播放速度 iOS 9音频控制播放速度 iOS9音频文件在播放时是以一定的速度进行的.这个速度是可以进行更改的,从而实现iOS9音频文件的快速播放和慢速播放功能.要实现i ...

  5. iOS 9音频应用播放音频之播放控制暂停停止前进后退的设置

    iOS 9音频应用播放音频之播放控制暂停停止前进后退的设置 ios9音频应用播放控制 在“iOS 9音频应用播放音频之ios9音频基本功能”一文可以看到AVAudioPlayer类有很多的属性以及方法 ...

  6. iOS 9音频应用播放音频之第一个ios9音频实例2

    iOS 9音频应用播放音频之第一个ios9音频实例2 ios9音频应用关联 iOS9音频应用中对于在主视图上添加的视图或控件,在使用它们时必须要与插座变量进行关联.ios9插座变量其实就是为主视图中的 ...

  7. iOS 9音频应用播放音频之第一个ios9音频实例

    iOS 9音频应用播放音频之第一个ios9音频实例 第一个ios9音频实例 为了让开发者可以对上面的内容有更加深入的了解,本节将实现播放音频的第一个实例.在此实例中会涉及到项目的创建.界面设计.关联以 ...

  8. iOS 9音频应用播放音频之ios9音频基本功能

    iOS 9音频应用播放音频之ios9音频基本功能 在iOS 9音频应用开发中最为简单和常用的就是AVFoundation框架中的AVAudioPlayer类.虽然AVAudioPlayer类不能播放网 ...

  9. 微信小程序--录制音频,播放音频

    1.在pages创建一个main文件夹2.在main文件夹下创建一个miain.js文件.添加代码: const constant = require('../../utils/constant.js ...

随机推荐

  1. 【Orange Pi Lite2】 ——2《在使用之前的配置》(未完)

    [Orange Pi Lite2] --2<在使用之前的配置> 本文只在博客园发布 在开始前你需要准备的材料与软件 filezilla/或者不 声明 : 本教程适合0基础新手,本章将会介绍 ...

  2. IOS 创建一个可以随意拉伸不变形的图片

    创建一个扩展 UIImage的类 #import "UIImage_Extension.h" @implementation UIImage+Extension /** *返回一张 ...

  3. 显示 Mac隐藏的文件夹 命令语句

    默认情况下,模拟器的目录是隐藏的,要想显示出来,需要在Mac终端输入下面的命令 显示Mac隐藏文件的命令:defaults write com.apple.finder AppleShowAllFil ...

  4. autoreleasing on a thread

    So basically, if you are running on OS X 10.9+ or iOS 7+, autoreleasing on a thread without a pool s ...

  5. Mybatis-延迟加载和缓存

    延迟加载 概念: 就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据.延迟加载也称懒加载. 好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表 ...

  6. SSH框架快速搭建(Maven)

    1.新建Maven项目ssh 2.pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi=& ...

  7. java设计模式——单例模式(一)

    一. 定义与类型 定义:保证一个类仅有一个实例,并提供一个全局访问点 类型:创建型 二. 适用场景 想确保任何情况下都绝对只用一个实例 三. 优缺点 优点: 在内存里只有一个实例,减少了内存开销 可以 ...

  8. macbook pro开机键盘键盘和触摸板没反应问题

    今天遇到开机键盘和触摸板没反应的问题,打电话给售后,他叫我插一个usb外置键盘,开机时按shift+alt+control+电源键开机,突然发现可以了,这bug我也是醉了

  9. vue-cli npm run build 打包问题 webpack@3.6

    1, vue-router 路由 有两个模式 (mode) hash (默认模式) 使用URL来模拟一个完整的URL 但是没个URL都会带上 "#/'' 支持所有浏览器 这个模式使用 red ...

  10. SQL数据库从高版本导入低版本

    1. 打开高版本数据库右键–>任务–>生成脚本–>高级–>选择脚本兼容的版本(也就是低版本)–>拉倒最下面选择架构和数据 2. 在低版本里面,先新建一个数据库,名称要和脚 ...