Qt 遊戲開發必備!用 QtMultimedia 播放 Ogg 格式音樂

pamxy發表於2013-06-13

轉自:http://software.intel.com/zh-cn/blogs/2013/03/15/qt-qtmultimedia-ogg/?utm_campaign=CSDN&utm_source=intel.csdn.net&utm_medium=Link&utm_content=others-%20QtMultimedia

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();
} 

 

相關文章