搜索
查看
编辑修改
首页
UNITY
NODEJS
PYTHON
AI
GIT
PHP
GO
CEF3
JAVA
HTML
CSS
搜索
木道寻08
这个屌丝很懒,什么也没留下!
关注作者
热门标签
jquery
HTML
CSS
PHP
ASP
PYTHON
GO
AI
C
C++
C#
PHOTOSHOP
UNITY
iOS
android
vue
xml
爬虫
SEO
LINUX
WINDOWS
JAVA
MFC
CEF3
CAD
NODEJS
GIT
Pyppeteer
article
热门文章
1
unbutu20.04搭建ardupilot环境_ubuntu20.04安装和配置ardupilot
2
什么是NLP分词(Tokenization)
3
mac切换root用户的方法_mac 免密切换root
4
FFMPEG Mac版本编译
5
动态计算el-table高度_el-table动态高度
6
Flink高手之路:Flink的环境搭建_flink环境搭建
7
fasttext工具的使用及迁移学习_fasttext 已有分类模型 迁移学习
8
基于python+vue的ITS 信息平台的设计与实现flask-django-nodejs-php
9
XPM小结_xpm fifo
10
通俗易懂,车载显示屏简单介绍!_车载显示器结构图解
当前位置:
article
> 正文
window API播放pcm格式音频文件,函数waveOutOpen等
作者:木道寻08 | 2024-08-18 00:38:44
赞
踩
window API播放pcm格式音频文件,函数waveOutOpen等
之前在我的博客中有一篇关于编写录音器的代码,可保存为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,附加在该结构体后面的格式信息的大小
};
本文内容由网友自发贡献,转载请注明出处:
【wpsshop博客】
推荐阅读
article
RabbitMQ
3.8
.2版本
window
安装
教程...
本文详细介绍了如何在Windows上
安装
RabbitMQ
3.8
.2版本。首先,需要下载并
安装
Erlang,它是Rabb...
赞
踩
article
Charles
安装
、配置、(
window
、
iOS
、
Android
)抓包...
Help ==> SSL Proxying ==> Install
Charles
Root Certificate =...
赞
踩
article
clickhouse
--
Window
Functions
窗口
函数
概念讲解及实际使用示例_click...
Window
Functions
在
clickhouse
的需求和呼声很高,早期的版本需要借助array类
函数
【如`gro...
赞
踩
article
阿里云ECS服务
安装
Docker
_
ecs
window
安装
docker
...
依次运行以下命令添加yum源。yum updateyum install epel-release -yyum clea...
赞
踩
article
Window
窗口
函数 (
Spark
Sql)_
sparksql
window
...
在
Spark
SQL 中,
Window
函数是一种用于在查询结果集中执行聚合、排序和分析操作的强大工具。它允许你在查询...
赞
踩
article
Spark
在
Window
环境
下
的搭建...
1.java/scala的安装 - 安装JDK
下
载: http://www.oracle.com/technetwor...
赞
踩
article
Yarn
的
安装
和使用详细教程(
Mac
/
Window
)_mac
安装
yarn
...
Yarn
的
安装
和使用详细教程(
Mac
/
Window
).
Mac
安装
Yarn
.
Window
s
安装
Yarn
Yarn
使用....
赞
踩
article
unity
新建项目后提示: [
Package
Manager
Window
] Cannot perf...
[
Package
Manager
Window
] Error
search
ing for packages._[pack...
赞
踩
article
[Unity] Unable to add
package
[
git
地址] Open
SSL
SSL
_...
Unity导入
git
hub库遇到Open
SSL
SSL
_
read
:
Connection
was
reset
, errn...
赞
踩
article
Unity
报错[
Package
Manager
Window
] Error
searching
fo...
我是创建一个3D项目时,遇到了这个问题。[
Package
Manager
Window
] Cannot perform ...
赞
踩
article
Unity 通过url 下载插件失败_[
package
manager
window
] cannot...
最近使用Add
package
from git URL安装插件时老是报错,搜了好久的解决方案,最终找到了解决方法,然后...
赞
踩
article
Unable to
perform
online
search
的解决方案_[
package
mana...
unable to
perform
search
:
cannot
perform
upm
operat
ion:unable...
赞
踩
相关标签
rabbitmq
分布式
ios
android
测试工具
压力测试
clickhouse
Window Function
窗口函数
SQL示例
docker
ajax
前端
javascript
scala
java
大数据
yarn
unity
git
ssl
游戏引擎