Qt 游戏开发必备!用 QtMultimedia 播放 Ogg 格式音乐

 

Qt游戏开发必备!用QtMultimedia播放Ogg格式音乐

 

QtMultimediaQt4.6提出来的一个音频和视频的新底层。目的是针对开发者提供更加完全的视频和音频控制,同时不损失平台无关性的优点。而Ogg是一个优秀的开源多媒体容器,可以容纳多种编码格式的内容,而大家最为熟知的是Vorbis这个编码格式。这回我将亲自尝试QtMultimedia下播放OggVorbis音乐,而在游戏中播放音乐必不可少。

源代码和演示程序下载地址:这里

我的开发环境:

Ubuntu+ gcc4.4 + QtSDK 4.8.1 + QtCreator2.6

Windows8+ minGW4.4 + QtSDK 4.8.3 + QtCreator2.6

由于OggVorbis开源的性质,导致我们必须亲自解码。网上关于Ogg解码的内容非常少,我也是结合《游戏音频程序设计-Beginning.Game.Audio.Programming》和Ogg的文档才对OggVorbis格式有着一些理解。首先需要下载“libogg-1.3.0”和“libvorbis-1.3.2”这两个软件开发包。可以在xiph.org上(http://www.xiph.org/)得到这两个软件开发包(也可以下载我的源代码,那里面附带了oggvorbis的源代码和项目文件)。然后解压,软件开发包中附带了VisualStudio的项目文件,而我使用的是QtCreator2.6,所以我在阅读了VisualStudio的项目文件之后自己写了一个.pro文件来生成OggVorbis的静态库。大家可以到我的资源中下载相关的项目文件。(博客授权于英特尔,原博客地址:http://blog.csdn.net/jiangcaiyang123)

构建后获得了libOgglibVorbislibVorbisFile三个静态库,我们就可以使用现有的库函数构建我们的实验了。

下面是我定义的QOggVorbis类的声明:

#ifndef _QOGGVORBIS_H_
#define _QOGGVORBIS_H_
#include <QObject>
#include <QString>
#include <QStringList>
#define DECLRARE_PROPERTY_WITH_GETTERS( aType, aProperty )
 private:\
 aType m_ ## aProperty; public:\
 aType aProperty( void ) { return m_ ## aProperty; }
 
struct OggVorbis_File;// 前向声明
 
class QOggVorbis: public QObject
{
 Q_OBJECT
 Q_PROPERTY( quint16 audioFormat READ audioFormat )
 Q_PROPERTY( quint16 channels READ channels )
 Q_PROPERTY( quint32 sampleRate READ sampleRate )
 Q_PROPERTY( quint32 byteRate READ byteRate )
 Q_PROPERTY( quint16 blockAlign READ blockAlign )
 Q_PROPERTY( quint16 bitsPerSample READ bitsPerSample )
 Q_PROPERTY( QStringList userComments READ userComments )
 Q_PROPERTY( QString vendor READ vendor )
 Q_PROPERTY( qint8* data READ data )
 Q_PROPERTY( quint32 dataSize READ dataSize )
public:
 QOggVorbis( void );
 QOggVorbis( const QString& fileName );
 ~QOggVorbis( void );
 bool load( const QString& fileName );
 void clear( void );
private:
 bool getComment( OggVorbis_File* vf );
 bool decode( OggVorbis_File* vf );
 DECLRARE_PROPERTY_WITH_GETTERS( quint16, audioFormat )
 DECLRARE_PROPERTY_WITH_GETTERS( quint16, channels )
 DECLRARE_PROPERTY_WITH_GETTERS( quint32, sampleRate )// 也就是频率Frequency
 DECLRARE_PROPERTY_WITH_GETTERS( quint32, byteRate )
 DECLRARE_PROPERTY_WITH_GETTERS( quint16, blockAlign )
 DECLRARE_PROPERTY_WITH_GETTERS( quint16, bitsPerSample )// 也就是SampleRate
 DECLRARE_PROPERTY_WITH_GETTERS( QStringList, userComments )
 DECLRARE_PROPERTY_WITH_GETTERS( QString, vendor )
 DECLRARE_PROPERTY_WITH_GETTERS( qint8*, data )
 DECLRARE_PROPERTY_WITH_GETTERS( quint32, dataSize )
};
#endif // _QOGGVORBIS_H_

这里使用Q_PROPERTY宏来对这个类进行moc,可以通过setProperty()函数和getProperty()函数来获得成员的值,而DECLRARE_PROPERTY_WITH_GETTERS是一个自定义的宏,用来定义一个数据成员和一个Getter。由于Qt中有些类(如QString)是隐式共享(implicitsharing)的,返回变量还是它的引用都没有关系。一些私有的成员函数由于传值需要用到OggVorbis_File结构,而又不想破坏它的封装性,只有先前向声明OggVorbis_File结构,再将结构的指针作为参数进行传递。

进行QtMultimedia的编程,需要使用QAudioFormatQAudioDeviceInfoQAudioOutput这三个类。首先用QAudioFormat设置音频的格式,然后用这种格式来匹配QaudioDeviceInfo,随后利用QAudioFormatQAudioDeviceInfo的信息来创建QAudioOutput的对象。最后利用QAudioOutput的对象(或对象指针)进行播放。下面是相关的代码:

// main.cpp 主函数所在的空间
// 2013 年 1 月21日 19:33:17 By jiangcaiyang
#include <QCoreApplication>
#include <QBuffer>
#include <QtMultimedia>
#include <QtDebug>
#include "QOggVorbis.h"
class TestAudio
{
public:
 TestAudio( void )
 {
 }
 ~TestAudio( void )
 {
 Release( );
 }
 bool LoadOggFile( const QString& fileName )
 {
 if ( !m_OggVorbis.load( fileName ) ) return false;
 quint32 sampleRate = m_OggVorbis.sampleRate( );
 quint16 channels = m_OggVorbis.channels( );
 quint16 sampleSize = m_OggVorbis.bitsPerSample( );
 QStringList comments = m_OggVorbis.userComments( );
 QListIterator<QString> iterCmts( comments );
 qDebug( ) << "Ogg file information: " <<
 "[sampleRate: " << sampleRate <<
 "][channels: " << channels <<
 "][sampleSize: " << sampleSize <<
 ']';
 // 显示 Ogg 文件额外信息
 qDebug( ) << "Ogg comments: ";
 while ( iterCmts.hasNext( ) )
 {
 qDebug( ) << iterCmts.next( );
 }
 // 设置音频格式
 m_Format.setSampleRate( sampleRate );
 m_Format.setChannelCount( channels );
 m_Format.setSampleSize( sampleSize );
 m_Format.setCodec( "audio/pcm" );
 m_Format.setByteOrder( QAudioFormat::LittleEndian );
 m_Format.setSampleType( QAudioFormat::SignedInt );
 // 初始化音频设备
 m_DeviceInfo = QAudioDeviceInfo::defaultOutputDevice( );
 if ( !m_DeviceInfo.isFormatSupported( m_Format ) )
 {
 qDebug( ) << "Cannot support this format, try a corresponding format.\n";
 m_Format = m_DeviceInfo.nearestFormat( m_Format );
 }
 m_Buffer.setData( (const char*)m_OggVorbis.data( ),
 m_OggVorbis.dataSize( ) );
 m_pOutput = new QAudioOutput( m_DeviceInfo, m_Format, 0 );
 return true;
 }
 void Play( void )
 {
 m_Buffer.open( QIODevice::ReadOnly );
 m_pOutput->start( &m_Buffer );
 }
 void Release( void )
 {
 m_Buffer.close( );
 delete m_pOutput;
 m_pOutput = 0;
 }
private:
 QAudioFormat m_Format;
 QAudioDeviceInfo m_DeviceInfo;
 QOggVorbis m_OggVorbis;
 QAudioOutput* m_pOutput;
 QBuffer m_Buffer;
};
int main(int argc, char* argv[] )
{
 QCoreApplication a( argc, argv );
 // 读取并且播放
 TestAudio testAudio;
 if ( !testAudio.LoadOggFile( "../TestSound.ogg" ) ) return 1;
 qDebug( ) << "Read test sound successful!\n";
 testAudio.Play( );
 qDebug( ) << "Now Playing audio!\n";
 return a.exec();
}
有关编译器优化的更完整信息,请参阅优化通知