【c++】怎样才能使程序播放文件中的声音?

【c++】怎样才能使程序播放文件中的声音?,第1张

一.播放声音文件的简单方法

在VC++ 中的多媒体码中动态连接库中提供了一组与音频设备有关的函数。利用这些函数可以方便地播蠢模没放声音。最简单的播放声音方法就是直接调用VC++中提供的声音播放函数BOOL sndPlaySound ( LPCSTR lpszSound,UINT fuSound )或BOOL PlaySound( LPCSTR lpszSound, HMODULE hmod, DWORD fuSound )其中参数lpszSound是需要播放声音的.WAV文件的路径和文件名, hmod在这里为NULL,fuSound是播放声音的标志,详细说明请参考VC++中的帮助。 例如播放C:soundmusic.wav可以用sndPlaySound ("c:\sound\music.wav",SND_ASYNC)或PlaySound("c:\sound\music.wav",NULL, SND_ASYNC|SND_NODEFAULT )如果没有找到music.wav文件,第一种格式将播放系统默认的声音,第二种格式不会播放系统默认的声音。

二.将声音文件加入到程序中

在VC++的程序设计中,可以利用各种标准的资源,如位图,菜单,对话框等。同时VC++也允许用户自定义资源,因此我们可以将声音文件作为用户自定义资源加入程序资源文件中,经过编译连接生成EXE文件,实现无.WAV文件的声音播放。

要实现作为资源的声音文件的播放,首先要在资源管理器中加入待播放的声音文件(实现过程并不复杂,这里不在叙述)。假设生成的声音文件资源标识符为IDR_WAVE1。在播放时只需要调用下面的语句:

PlaySound(MAKEINTRESOURCE(IDR_WAVE1),AfxGetResourceHandle(), SND_ASYNC|SND_RESOURCE|SND_NODEFAULT|SND_LOOP)

其中MAKEINTRESOURCE()宏将整数资源标识符转变为字符串,AfxGetResourceHandle()函数返回包含资源的模块句柄,

SND_RESOURCE是必须的标志。

作为资源的声音文件的第二种播放方法是把资源读入内存后作为内存数据播放。具体步骤入下:

1.获得包带纳含资源的模块句柄:

HMODULE hmod=AfxGetResourceHandle()

2.检索资源块信息:

HRSRC hSndResource=FindResource(hmod,MAKEINTRESOURCE(IDR_WAVE1),_T("WAVE"))

3. 装载资源数据并加锁:

HGLOBAL hGlobalMem=LoadResource(hmod,hSndResource)

LPCTSTR lpMemSound=(LPCSTR)LockResource(hGlobalMem)

4.播放声音文件:

sndPlaySound(lpMemSound,SND_MEMORY));

5.释放资源句柄:

FreeResource(hGlobalMem)

三.播放声音文件的高级方法

在VC++中提供了一组对音频设备及多媒体文件直接进行 *** 作的函数。利用这些函数可以灵活地对声音文件进行各种处理。

首先介绍几个要用到的数据结构。WAVEFORMATEX结构定义了WAVE音频数据文件的格式。WAVEHDR结构定义了波形音频缓冲区。读出的数据首先要填充此缓冲区才能送音频设备播放。WAVEOUTCAPS结构描述了音频设备的性能。MMCKINFO结构包含了RIFF文件中一个块的信息。详细的说明请参考VC++中的帮助。

下面给出程序流程简图及程序源代码清单,在VC++环境下可直接使用:

源程序清单如下:

LPSTR szFileName//声音文件名

MMCKINFO mmckinfoParent

MMCKINFO mmckinfoSubChunk

DWORD dwFmtSize

HMMIO m_hmmio//音频文件句柄

DWORD m_WaveLong

HPSTR lpData//音频数据

HANDLE m_hData

HANDLE m_hFormat

WAVEFORMATEX * lpFormat

DWORD m_dwDataOffset

DWORD m_dwDataSize

WAVEHDR pWaveOutHdr

WAVEOUTCAPS pwoc

HWAVEOUT hWaveOut

//打开波形文件

if(!(m_hmmio=mmioOpen(szFileName,NULL,MMIO_READ|MMIO_ALLOCBUF)))

{

//File open Error

Error("Failed to open the file.")//错误处理函数

return false

}

//检查打开文件是否是声音文件

mmckinfoParent.fccType =mmioFOURCC(’W’,’A’,’V’,’E’)

if(mmioDescend(m_hmmio,(LPMMCKINFO)&mmckinfoParent,NULL,MMIO_FINDRIFF))

{

//NOT WAVE FILE AND QUIT

}

//寻找 ’fmt’ 块

mmckinfoSubChunk.ckid =mmioFOURCC(’f’,’m’,’t’,’ ’)

if(mmioDescend(m_hmmio,&mmckinfoSubChunk,&mmckinfoParent,MMIO_FINDCHUNK))

{

//Can’t find ’fmt’ chunk

}

//获得 ’fmt ’块的大小,申请内存

dwFmtSize=mmckinfoSubChunk.cksize

m_hFormat=LocalAlloc(LMEM_MOVEABLE,LOWORD(dwFmtSize))

if(!m_hFormat)

{

//failed alloc memory

}

lpFormat=(WAVEFORMATEX*)LocalLock(m_hFormat)

if(!lpFormat)

{

//failed to lock the memory

}

if((unsigned long)mmioRead(m_hmmio,(HPSTR)lpFormat,dwFmtSize)!=dwFmtSize)

{

//failed to read format chunk

}

//离开 fmt 块

mmioAscend(m_hmmio,&mmckinfoSubChunk,0)

//寻找 ’data’ 块

mmckinfoSubChunk.ckid=mmioFOURCC(’d’,’a’,’t’,’a’)

if(mmioDescend(m_hmmio,&mmckinfoSubChunk,&mmckinfoParent,MMIO_FINDCHUNK))

{

//Can’t find ’data’ chunk

}

//获得 ’data’块的大小

m_dwDataSize=mmckinfoSubChunk.cksize

m_dwDataOffset =mmckinfoSubChunk.dwDataOffset

if(m_dwDataSize==0L)

{

//no data in the ’data’ chunk

}

//为音频数据分配内存

lpData=new char[m_dwDataSize]

if(!lpData)

{

//faile

}

if(mmioSeek(m_hmmio,SoundOffset,SEEK_SET)<0)

{

//Failed to read the data chunk

}

m_WaveLong=mmioRead(m_hmmio,lpData,SoundLong)

if(m_WaveLong<0)

{

//Failed to read the data chunk

}

//检查音频设备,返回音频输出设备的性能

if(waveOutGetDeVCaps(WAVE_MAPPER,&pwoc,sizeof(WAVEOUTCAPS))!=0)

{

//Unable to allocate or lock memory

}

//检查音频输出设备是否能播放指定的音频文件

if(waveOutOpen(&hWaveOut,DevsNum,lpFormat,NULL,NULL,CALLBACK_NULL)!=0)

{

//Failed to OPEN the wave out devices

}

//准备待播放的数据

pWaveOutHdr.lpData =(HPSTR)lpData

pWaveOutHdr.dwBufferLength =m_WaveLong

pWaveOutHdr.dwFlags =0

if(waveOutPrepareHeader(hWaveOut,&pWaveOutHdr,sizeof(WAVEHDR))!=0)

{

//Failed to prepare the wave data buffer

}

//播放音频数据文件

if(waveOutWrite(hWaveOut,&pWaveOutHdr,sizeof(WAVEHDR))!=0)

{

//Failed to write the wave data buffer

}

//关闭音频输出设备,释放内存

waveOutReset(hWaveOut)

waveOutClose(hWaveOut)

LocalUnlock(m_hFormat)

LocalFree(m_hFormat)

delete [] lpData

说明:1)以上使用的音频设备和声音文件 *** 作函数的声明包含在mmsystem.h头文件中,因此在程序中必须用#include "mmsystem.h"语句加入头文件。同时在编译时要加入动态连接导入库winmm.lib,具体实现方法是从Developer Studio的Project菜单中选择Settings,然后在Link选项卡上的Object/Library Modules控制中加入winmm.lib。2)在pWaveOutHdr.lpData中指定不同的数据,可以播放音频数据文件中任意指定位置的声音。3) 以上程序均在VC++6.0中调试通过,在文中省略了对错误及异常情况的处理,在实际应用中必须加入。

四.结论

在VC++中可以根据应用需要采用不同的方法播放声音文件。简单应用可以直接调用声音播放函数。第二种方法可以把声音作为资源加入可执行文件中。如果在播放之前要对声音数据进行处理,可用第三种方法。

所谓封装格式就是将已经编码压缩好的视频轨和音频轨按照一定的格式放到一个文件中.

1. TS封装 vs AVI封装

MPEG2视频无法封装在AVI里,WMV视频无法封装在TS里。

MKV倒是可以封装WMV和MPEG2, 不过高清机又橡旅不支持

AVI封装:要求文件必须有完整的索引才可以播放,当文件出现缺损的话,播放立刻停止

TS封装:要求文件必须有时间索引,并不要求一定的完整,就算文件有缺损,也可以跳过该部分播放

AVI最多可以封装两条音轨,TS没这限制

众所周知: BD就是TS封装, hddvd是PS封装,用TS封装可以无损的支持所有全部HDDVD和BD所带的视频和音频编码.

Video codecs

MPEG-2

MPEG-4 AVC

SMPTE VC-1

Audio codecs

Linear PCM

Dolby Digital

Dolby Digital Plus

Dolby TrueHD

DTS Digital Surround

DTS-HD

而avi封装目前仅仅能支持

Video codecs:

MPEG-4 AVC

对SMPTE VC-1支持不够好

Audio codecs

DTS

AC3

随着BD, HDD Remux的出现,到底哪个好,这也是这段时间争论最多的了。

那么就来看看AVI和TS到底是什么吧。

2. AVI容器-成熟的老技术

AVI是微软1992年推出用于对抗苹果Quicktime的技术,尽管国际学术界公

认AVI已经属于被淘汰的技术,但是由于windows的通用性,和简单易懂的

开发API,还在被广泛使用。

如图1所试, AVI的文件结构、分为头部, 主体和索引三部分. 主烂掘体中图像数据

和声音数据是交互存放的。从尾部的索引可以索引跳到自己想放的位置。

AVI本身只是提供了这么一个框架,内部的图像数据和声音顺据格式可以是任

意的编码形式。但是由于索引放在了文件尾部,所以在播放internet流媒体时

已属力不从心。

3. AVI容器-画质无损,音质呢?

要说到AVI的弱点,最大的问题就是对高质量VBR音轨的支持了。

VBR全称是Variable BitRate,就是动态比特率。和传统的CBR静态比特率不同,CBR约定死了

音质的采样率为固定值。因为声音是有高潮起伏的,显然,同样文件大小的情况下,VBR最大

限度的提高了音质。所以最新推出的高音质格式通常是VBR格式的。

随之问题也就来了,因为容器里的图像和声音是分开的,所以播放时需要一个图像和声音的

同步过程,如果CBR音轨的话因为码率是定值,同步不成为问题,可是VBR音轨是不断的在\

变换,而AVI没有时间戳去让VBR音轨和图像同步,这样就会产生图像声音不同步的问题。估

计实际动过手的兄弟应该深有体会吧。

那么,AVI是不是就不能支持VBR了呢。VirtualDub提供了一个变通的办法,有兴趣的可以区梁历凳

Doom9找找看参考资料。以前公认为这属于破解,但是最近也慢慢被学术界承认,成为了对

AVI技术扩充的一种手段。简单说来,通过冗余的数据变换包装来把VBR分成等量的块,达到

模拟CBR的效果。但是这个方法也有局限性,只有一部分VBR声音压缩方式可以使用,而且必

须要详细分析声音音频数据,针对每一种压缩格式制定算出最大冗余量,如果音质码率高的

话编码效率会很差,也很难实现。更何况还有不少是完全不支持的(TrueHD, DTS-HD Master

audio)。所以经常看到说AVI什么都好,音质问题只是因为某些音轨数据量太大,播放器放不

了的说法是不正确的。

附论证的技术资料,对细节不感兴趣的可跳过:

CBR音轨用以下这个记述可实现同步

WAVEFORMATEX::nBlockAlign 1Block大小(Byte)

AVIStreamHeader::dwRate / AVIStreamHeader::dwScale 1秒内包含的Block个数

nBlockAlign=1,dwScale=1,dwRate=nAvgBytesPerSec nAvgBytesPerSec是常量

VirtualDub的变通法

AVIStreamHeader::dwLength = 变换包的总数

WAVEFORMATEX::nBlockAlign = 变换包最大容量

4.TS

近年来,TS封装是随着MPEG2的流行而占据了主流的地位。全称则是Transport Stream

而电视节目是你任何时候打开电视机都能解码(收看)的,所以,MPEG2-TS格式的特点

就是要求从视频流的任一片段开始都是可以独立解码的。

从结构上来说,TS是由头文件和主体所组成的,扩充过的TS流还包括时间戳。这样不管

是什么格式的VBR音轨,都很容易通过时间戳来同步图像。

补充 这里对一些细节过于一笔带过了,详细请参考o版下面对TS流本身包的时间标记的解释

当然,对新的声音格式来说,需要新的分离器,解码器来实现解码。

目前在不断改进开发中。

TS不像AVI,从诞生那天起,就考虑到了网络播放,所以很快成为了世界标准并广泛应用

于电视台数字播放,手机等各个领域

结论,

新的BD和HDDVD的带来新的规格音频视频标准,要是想体验原汁原味的BD/HDVD音视频,

那么就下载原始BD'HDDVD文件或者TS REMUX版,但是如果现有设备不属于高配置,对一

些技术标准的差异并不在乎,又对近期可能产生的播放问题想避免的话,AVI也是一个很好

的选择。毕竟看片子是为了享受,大家各取所需吧。


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/tougao/12243587.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-22
下一篇 2023-05-22

发表评论

登录后才能评论

评论列表(0条)

保存