之前在我的博客中有一篇关于编写录音器的代码,可保存为pcm和wav格式,说白了其实两者是一个东西,只不过wav比pcm多了一个文件头,这个文件头一共占了44个字节。此处这个不是重点,重点是如何编写程序实现播放pcm音频数据。我下面有这个程序的完整代码,可以通过粘贴复制实现即可使用。
一、 如果要播放pcm格式音频,我们需要几个windows API函数:
1. waveOutGetNumDevs()函数
函数原型:UINT waveOutGetNumDevs(VOID);
这个函数没有参数,返回设备个数。
函数简单使用:
BOOL getOutNumDrive()//音频数量
{
int count = waveOutGetNumDevs();
return count < 1 ? FALSE : TRUE;//1 音频输入数量
}
2. waveOutGetDevCaps()函数
函数原型:
MMRESULT waveOutGetDevCaps(
UINT uDeviceID,
LPWAVEOUTCAPS pwoc,
UINT cbwoc
);
功能:这个函数获取输出音频设备的相应信息。
函数的简单使用:
BOOL getOutDevCaps()
{
WAVEOUTCAPS waveOutcaps;
MMRESULT mmResult =waveOutGetDevCaps(0,&waveOutcaps,sizeof(WAVEINCAPS));//2设备描述
if ( MMSYSERR_NOERROR != mmResult )
{
return FALSE;
}
return TRUE;
}
3. waveOutOpen()函数
函数原型:
MMRESULT waveOutOpen(
LPHWAVEOUT phwo,
UINT uDeviceID,
LPWAVEFORMATEX pwfx,
DWORD dwCallback,
DWORD dwCallbackInstance,
DWORD fdwOpen
);
功能:打开一个音频设备。
函数简单使用:
LPHWAVEOUT phwo, LPCWAVEFORMATEX pwfx;
if (MMSYSERR_NOERROR != waveOutOpen(0, 0, pwfx, 0, 0,WAVE_FORMAT_QUERY))
{
//fprintf(stderr, "不支持文件格式!\n");
return FALSE;
}
4. waveOutPrepareHeader()函数
函数原型:
waveOutPrepareHeader(
hWaveOut: HWAVEOUT; {设备句柄}
lpWaveOutHdr: PWaveHdr; {TWaveHdr结构的指针}
uSize: UINT {TWaveHdr 结构大小}
): MMRESULT; {成功返回 0; 可能的错误值见下:}
MMSYSERR_INVALHANDLE = 5; {设备句柄无效}
MMSYSERR_NOMEM = 7; {不能分配或锁定内存}
MMSYSERR_HANDLEBUSY = 12;{其他线程正在使用该设备}
//TWaveHdr 是 wavehdr_tag 结构的重定义
wavehdr_tag = record
lpData: PChar; {指向波形数据缓冲区}
dwBufferLength: DWORD; {波形数据缓冲区的长度}
dwBytesRecorded: DWORD; {若首部用于输入,指出缓冲区中的数据量}
dwUser: DWORD; {指定用户的32位数据}
dwFlags: DWORD; {缓冲区标志}
dwLoops: DWORD; {循环播放次数,仅用于输出缓冲区}
lpNext: PWaveHdr; {保留}
reserved: DWORD; {保留}
end;
//TWaveHdr 中的 dwFlags 的可选值:
WHDR_DONE = $00000001; {设备已使用完缓冲区, 并返回给程序}
WHDR_PREPARED = $00000002;{waveInPrepareHeader 或 waveOutPrepareHeader 已将缓冲区准备好}
WHDR_BEGINLOOP = $00000004; {缓冲区是循环中的第一个缓冲区,仅用于输出}
WHDR_ENDLOOP = $00000008;{缓冲区是循环中的最后一个缓冲区, 仅用于输出}
WHDR_INQUEUE = $00000010; { reserved fordriver }
功能:准备一个波形数据块用于播放。
函数的简单使用:
BOOL writeOutAudioBlock(HWAVEOUT phwo, LPSTR block, DWORDsize)
{
WAVEHDR wHdr = { 0 };
wHdr.dwBufferLength =size;
wHdr.lpData = block;
if (MMSYSERR_NOERROR !=waveOutPrepareHeader(phwo, &wHdr, sizeof(WAVEHDR)))
{
//fprintf(stderr,"文件缓冲区准备失败!\n");
return FALSE;
}
}
5. waveOutWrite()函数
函数原型:
MMRESULT waveOutWrite(
HWAVEOUT hwo,
LPWAVEHDRpwh,
UINT cbwh
);
函数功能:发送音频数据块到指定设备。
函数使用:
BOOL writeOutAudioBlock(HWAVEOUT phwo, LPSTR block, DWORDsize)
{
WAVEHDR wHdr = { 0 };
wHdr.dwBufferLength = size;
wHdr.lpData = block;
if (MMSYSERR_NOERROR != waveOutWrite(phwo, &wHdr,sizeof(WAVEHDR)) )
{
//fprintf(stderr, "写文件缓冲区失败!\n");
return FALSE;
}
}
6. waveOutUnprepareHeader()函数
函数原型:
MMRESULT waveOutUnprepareHeader(
HWAVEOUT hwo,
LPWAVEHDRpwh,
UINT cbwh
);
函数功能:清除由 waveOutPrepareHeader 完成的准备
函数使用:
BOOL writeOutAudioBlock(HWAVEOUT phwo, LPSTR block, DWORDsize)
{
WAVEHDR wHdr = { 0 };
wHdr.dwBufferLength = size;
wHdr.lpData = block;
while ( WAVERR_STILLPLAYING == waveOutUnprepareHeader(phwo,&wHdr, sizeof(WAVEHDR)))
{
Sleep(100);
}
return TRUE;
}
7. waveOutClose()函数的使用
函数原型:
MMRESULT waveOutWrite(
HWAVEOUT hwo,
LPWAVEHDRpwh,
UINT cbwh
);
函数功能:关闭音频设备。
函数使用:
BOOL closeOutAudioBlock(HWAVEOUT phwo)
{
if (MMSYSERR_NOERROR != waveOutClose(phwo))
{
//fprintf(stderr, "写文件缓冲区失败!\n");
return FALSE;
}
return TRUE;
}
要使用以上七个函数才能够完成pcm文件的播放。下面让我们看看代码是怎么写成的。
二、 播放录音,播放pcm文件。
首先我贴出来头文件。文件名为:PlayPcm.h
#ifndef _PLAYPCM_H_
#define _PLAYPCM_H_
#include "stdio.h"//此处写成双引号是因为新浪博客的原因,如果写成尖括号就不能显示。
#include "windows.h"
#include "MMSystem.h"
#define LENGTH 10240
#pragma comment(lib, "winmm.lib")
#pragma warning(disable:4996)
BOOL getOutNumDrive(); //判断是否有输出设备
BOOL getOutDevCaps(); //判断时候能够获取设备描述
//BOOL openOutFile(const char *cFileName, FILE **fp);//打开文件
BOOL initOutDrive(const char *cFileName); //初始化设备
BOOL openOutPcm(LPHWAVEOUT, LPCWAVEFORMATEX, DWORD_PTR );//打开相应的设备
LPSTR loadOutAudioBlock(const char* filename, DWORD*blockSize); //加载相应的文件信息
BOOL writeOutAudioBlock(HWAVEOUT hWaveOut, LPSTR block,DWORD size); //写声音文件到设备
BOOL closeOutAudioBlock(HWAVEOUT phwo); //关闭设备
#endif
然后贴出函数的实现文件:PlayPcm.cpp
#include "PlayPcm.h"
BOOL getOutNumDrive()//音频数量
{
int count = waveOutGetNumDevs();
return count < 1 ? FALSE : TRUE;//1 音频输入数量
}
BOOL getOutDevCaps()
{
WAVEOUTCAPS waveOutcaps;
MMRESULT mmResult =waveOutGetDevCaps(0,&waveOutcaps,sizeof(WAVEINCAPS));//2设备描述
if ( MMSYSERR_NOERROR != mmResult )
{
return FALSE;
}
return TRUE;
}
BOOL openOutFile(const char *cFileName, FILE**fp)
{
FILE *fpRead = NULL;
fpRead = fopen(cFileName, "rb");
if (NULL == fpRead)
{
fp = NULL;
return FALSE;
}
*fp = fpRead;
fclose(fpRead);
return TRUE;
}
BOOL initOutDrive()//BOOL initDrive(const char*cFileName)
{
if ( FALSE == getOutNumDrive() )
{
return FALSE;
}
if ( FALSE == getOutDevCaps() )
{
return FALSE;
}
return TRUE;
}
BOOL openOutPcm(LPHWAVEOUT phwo, LPCWAVEFORMATEX pwfx,DWORD_PTR dwCallback)
{
if (MMSYSERR_NOERROR != waveOutOpen(0, 0, pwfx, 0, 0,WAVE_FORMAT_QUERY))
{
//fprintf(stderr, "不支持文件格式!\n");
return FALSE;
}
if ( MMSYSERR_NOERROR != waveOutOpen(phwo, WAVE_MAPPER,pwfx, 0, 0,CALLBACK_NULL))
{
//fprintf(stderr, "文件打开失败!\n");
return FALSE;
}
return TRUE;
}
LPSTR loadOutAudioBlock(const char*filename, DWORD*blockSize)
{
HANDLE hFile= INVALID_HANDLE_VALUE;
DWORD size = 0;
DWORD readBytes = 0;
void* block = NULL;
if((hFile =CreateFile(filename,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL))== INVALID_HANDLE_VALUE)
{
return NULL;
}
do
{
if((size = GetFileSize(hFile, NULL)) ==0)
{
break;
}
if((block = HeapAlloc(GetProcessHeap(),0, size)) ==NULL)
{
break;
}
ReadFile(hFile, block, size,&readBytes,NULL);
} while(0);
CloseHandle(hFile);
*blockSize = size;
return (LPSTR)block;
}
BOOL writeOutAudioBlock(HWAVEOUT phwo, LPSTR block, DWORDsize)
{
WAVEHDR wHdr = { 0 };
wHdr.dwBufferLength = size;
wHdr.lpData = block;
if (MMSYSERR_NOERROR != waveOutPrepareHeader(phwo,&wHdr, sizeof(WAVEHDR)))
{
//fprintf(stderr, "文件缓冲区准备失败!\n");
return FALSE;
}
if (MMSYSERR_NOERROR != waveOutWrite(phwo, &wHdr,sizeof(WAVEHDR)) )
{
//fprintf(stderr, "写文件缓冲区失败!\n");
return FALSE;
}
Sleep(500);
while ( WAVERR_STILLPLAYING == waveOutUnprepareHeader(phwo,&wHdr, sizeof(WAVEHDR)))
{
Sleep(100);
}
return TRUE;
}
BOOL closeOutAudioBlock(HWAVEOUT phwo)
{
if (MMSYSERR_NOERROR != waveOutClose(phwo))
{
//fprintf(stderr, "写文件缓冲区失败!\n");
return FALSE;
}
return TRUE;
}
这里贴出main函数所在的文件:demoPlayPcm.cpp
#include "PlayPcm.h"
int main(int argc, char* argv[])
{
const char cFileName[] = "MyRecord.pcm";
LPSTR block;
DWORD blockSize;
if (NULL == (block = loadOutAudioBlock( cFileName ,&blockSize)))
{
fprintf(stderr, "文件初始化失败!\n");
return -1;
}
if (FALSE == initOutDrive( cFileName))
{
fprintf(stderr, "设备初始化失败!\n");
return -1;
}
WAVEFORMATEX pwfx ={WAVE_FORMAT_PCM,//wFormatTag,格式标志
1, // nChannels,通道数,单声道数据用单通道,立体声通道用双通道
16000, // nSamplesPerSec,采样率(HZ),每秒钟采取样本的次数
32000, // nAvgBytesPerSec,每秒转换数据的字节数,forWAVE_FORMAT_PCM,nAvgBytesPerSec = nSamplesPerSec *nBlockAlign,此处的大小与waveInOopen回调函数中写入数据的大小应该一直,否则会出现问题
2, // nBlockAlign,每个样本的字节数,for WAVE_FORMAT_PCM,nBlockAlign= (nChannels × wBitsPerSample) / 8
16, // wBitsPerSample,每个样本的位数,forWAVE_FORMAT_PCM,wBitsPerSample必须等于8或者16
0 // cbSize,附加在该结构体后面的格式信息的大小
};
HWAVEOUT phwo;
if ( FALSE == openOutPcm(&phwo, &pwfx,NULL))
{
fprintf(stderr, "文件打开失败!\n");
return -1;
}
if (FALSE == writeOutAudioBlock(phwo, block,blockSize))
{
fprintf(stderr, "写音频设备失败!\n");
return -1;
}
if ( FALSE == closeOutAudioBlock(phwo))
{
fprintf(stderr, "关闭音频设备失败!\n");
return -1;
}
return 0;
}
这个代码是完全可以用的,但是大家注意,我这是pcm文件名为“MyRecord.pcm”,并且是16K16bit的PCM文件,你在使用时注意这点。把文件名换成你的pcm文件名。然后如果要打开其他格式的pcm,那么就在demoPlayPcm.cpp中设置相应的格式信息(注意设置成你需要的采样信息):
WAVEFORMATEX pwfx ={WAVE_FORMAT_PCM,//wFormatTag,格式标志
1, // nChannels,通道数,单声道数据用单通道,立体声通道用双通道
16000, // nSamplesPerSec,采样率(HZ),每秒钟采取样本的次数
32000, // nAvgBytesPerSec,每秒转换数据的字节数,forWAVE_FORMAT_PCM,nAvgBytesPerSec = nSamplesPerSec *nBlockAlign,此处的大小与waveInOopen回调函数中写入数据的大小应该一直,否则会出现问题
2, // nBlockAlign,每个样本的字节数,for WAVE_FORMAT_PCM,nBlockAlign= (nChannels × wBitsPerSample) / 8
16, // wBitsPerSample,每个样本的位数,forWAVE_FORMAT_PCM,wBitsPerSample必须等于8或者16
0 // cbSize,附加在该结构体后面的格式信息的大小
};
window API播放pcm格式音频文件,函数waveOutOpen等相关推荐
- AudioTrack播放pcm格式音频
AudioTrack播放pcm格式音频 package com.zero.demo;import android.content.Context; import android.media.Audio ...
- Java如何播放MP3格式音频文件,以及如何循环播放音频?
首选,感谢你能在百忙之中阅读我的博客,在这里我告诉大家2中播放MP3格式音频的方法.第一种,使用自带的JavaFX技术来播放(现再最新的JDK版本中已被移除).第二种我们使用第三方库来进行播放. 点击 ...
- 【转】PCM Audio,PCM格式音频文件 详解
PCM文件:模拟音频信号经模数转换(A/D变换)直接形成的二进制序列,该文件没有附加的文件头和文件结束标志.Windows的Convert工具可以把PCM音频格式的文件转换成Microsoft的WAV ...
- java播放mp3格式音频文件
下载第三方jar包,网址:http://www.javazoom.net/javalayer/javalayer.html 下载完成之后解压提取jl1.0.0.1.jar 将jl1.0.0.1.jar ...
- Qt播放WAV格式音频文件的两种方法
这两种方法都需要在.pro文件中加入multimedia模块. 方法一.使用QAudioOutput #include <QApplication> #include <QFile& ...
- Java实现PCM格式音频转MP3或WAV
最近做语音合成的项目,需要把PCM格式的音频文件转换成MP3或WAV,记录一下. Java实现的PCM格式音频文件转换MP3格式 import java.io.FileInputStream; imp ...
- Android 音视频开发(一):PCM 格式音频的播放与采集
什么是 PCM 格式 声音从模拟信号转化为数字信号的技术,经过采样.量化.编码三个过程将模拟信号数字化. 采样 顾名思义,对模拟信号采集样本,该过程是从时间上对信号进行数字化,例如每秒采集 44100 ...
- vue.js用benz-amr-recorder实现播放amr格式音频
vue.js实现播放amr格式音频 安装 引用 实操 html部分 初始化对象 调用方法 预览效果 纯前端解码.播放.录音.编码 AMR 音频,无须服务器支持,基于 [amr.js] 注意:由于使用了 ...
- java 播放amr_amr格式转mp3和直接播放amr格式的文件-sunziren
原创文章,转载请注明出处! 前言: amr作为一种高压缩比的音频格式,受到很多客户的青睐.本文主要涉及两部分的内容,一是amr如何转为mp3格式,二是如何直接播放amr格式的文件. 1. 如何使用Ja ...
最新文章
- 深度学习Anchor Boxes原理与实战技术
- bug带来的兄弟感情
- mfc 制作不同的文档模板mdi不同的子窗体_制作ACK集群自定义节点镜像的正确姿势...
- java对象赋值给另一个对象_java面向对象编程
- js错误:对象不支持此属性或方法
- 【Boost】boost库中智能指针——shared_ptr
- vector作为参数传递到dll问题
- PyCharm调试错误
- The word is not correctly spelled问题解决
- Qt使用QPainter实现雷达图(玫瑰图)
- java数字处理_Java数字处理类(上)
- 实验二 固件设计(小组)
- MS08-067漏洞渗透测试
- PPT文件太大?如何压缩PPT?这几招教你搞定
- 面试中单例模式有几种写法?
- 如何使用Redis Streams
- 主编推荐 | 深度学习如何影响运筹学?
- (android文档原创翻译)管理Activity的生命周期一
- 实现百度第三方登陆详细解答
- 互联网常用的网络用语
热门文章
- JavaScript高级编程语言基础语法
- 【企业架构设计实战】业务架构设计
- 全志h3芯片刷机包_全志h3详细刷机教程
- 可商用字体在哪里找?2022可商用字体汇总
- OpenFeign中动态URl、动态传递接口地址
- Kademlia算法 理解 总结
- 国债是什么?国债逆回购是什么?国债逆回购是低风险高回报的投资方式之一
- 基于8255A接口芯片的跑马灯程序
- 【贪心算法】Leetcode 714. 买卖股票的最佳时机含手续费
- lambda表达式与6种方法引用格式