您的位置:首页 > 运维架构

开发游戏音频程序——MP3的播放

2011-09-09 21:27 465 查看
最近学了一些游戏开发必不可少的MP3文件播放知识。首先,我的目标机器是windows,所以这些是在windows平台下开发的。随后我学到,要直接解码MP3文件是非常的困难的。因为MP3文件的闭源,我们都没有知道怎样才能一个一个地解码。好在DirectShow帮我们解决了解码的工作,我们需要的只是加入头文件,并且手动链接库文件,使用它的函数就行了。

为了使用它的函数,我们必须加入头文件dshow.h。这个头文件是不容易找到的。以后的DirectX里面都没有了DirectShow的踪影。我只好下载2004年十二月的DirectX版本,这个版本里就有dshow.h。好了,我们加入这个头文件不外乎就是要加一些接口。这里我列举这一些接口的名字:

IGraphBuilder

IMediaControl

IMediaEventEx

 

为了能够解析IGraphBuilder这个符号,我们还要手动地链接Strmiids.lib库。这个库不是特别的显眼,但是没有它我们的程序就无法通过。

 

好了,准备工作做好了后,我们开始写一个类了。这个类很简单,叫CMP3。这个类里面嵌入了另外一个MP3标签类CMP3Tag。下面就是我这个类的代码。

 

class CMP3
{
public:
    CMP3( void )                                            // 默认构造函数
    {
        m_GraphBuilder                  = NULL;
        m_MediaControl                  = NULL;
        m_MediaEventEx                  = NULL;
        m_IsPlaying                     = false;
    }
    ~CMP3( void );                                          // 析构函数
    bool LoadMP3File( TString strFilename );                // 读取MP3文件
    bool Play( void );                                      // 播放MP3文件
    void ProcessNotification( void );                       // 处理通知
    CMP3Tag GetMP3Tag( void ) { return m_tag; }             // 获取MP3标签
    bool IsPlaying( void ) { return m_IsPlaying; }          // 判断是否正在播放
private:
    bool                                m_IsPlaying;
    IGraphBuilder*                      m_GraphBuilder;
    IMediaControl*                      m_MediaControl;
    IMediaEventEx*                      m_MediaEventEx;
    CMP3Tag                             m_tag;
};

这些函数都是能简单明了的。除了一个ProcessNotification。这个函数的作用是检测是否MP3已经播放完毕。如果你不使用多线程的话,这个函数必须在一个循环内调用。

 

而它的被调用者:CMP3Tag类,则是一个储存MP3标签的类:

class CMP3Tag// MP3标签类
{
public:
    bool ReadMP3Tag( TString strFilename );                 // 读取MP3标签
    TString GetGenre( void );                               // 获取流派
    TString GetComment( void ){ return m_Comment; }         // 获取评论
    TString GetYear( void ){ return m_Year; }               // 获取发行年
    TString GetAlbum( void ){ return m_Album; }             // 获取专辑
    TString GetArtist( void ){ return m_Artist; }           // 获取艺术家
    TString GetTitle( void ){ return m_Title; }             // 获取标题
    enum MP3_GENRE
    {
        GENRE_BLUES = 0,         GENRE_CLASSICROCK,      GENRE_COUNTRY,         GENRE_DANCE,
        GENRE_DISCO,             GENRE_FUNK,             GENRE_GRUNGE,          GENRE_HIPHOP,
        GENRE_JAZZ,              GENRE_METAL,            GENRE_NEWAGE,          GENRE_OLDIES,
        GENRE_OTHER,             GENRE_POP,              GENRE_RANDB,           GENRE_RAP,
        GENRE_REGGAE,            GENRE_ROCK,             GENRE_TECHNO,          GENRE_INDUSTRIAL,
        GENRE_ALTERNATIVE,       GENRE_SKA,              GENRE_DEATHMETAL,      GENRE_PRANKS,
        GENRE_SOUNDTRACK,        GENRE_EUROTECHNO,       GENRE_AMBIENT,         GENRE_TRIPHOP,
        GENRE_VOCAL,             GENRE_JAZZANDFUNK,      GENRE_FUSION,          GENRE_TRANCE,
        GENRE_CLASSICAL,         GENRE_INSTRUMENTAL,     GENRE_ACID,            GENRE_HOUSE,
        GENRE_GAME,              GENRE_SOUNDCLIP,        GENRE_GOSPEL,          GENRE_NOISE,
        GENRE_ALTERNROCK,        GENRE_BASS,             GENRE_SOUL,            GENRE_PUNK,
        GENRE_SPACE,             GENRE_MEDITATIVE,       GENRE_INSTRUMENTALPOP, GENRE_INSTRUMENTALROCK,
        GENRE_ETHNIC,            GENRE_GOTHIC,           GENRE_DARKWAVE,        GENRE_TECHNOINDUSTRIAL,
        GENRE_ELECTRONIC,        GENRE_POPFOLK,          GENRE_EURODANCE,       GENRE_DREAM,
        GENRE_SOUTHERNROCK,      GENRE_COMEDY,           GENRE_CULT,            GENRE_GANGSTA,
        GENRE_TOP40,             GENRE_CHRISTIANRAP,     GENRE_POPFUNK,         GENRE_JUNGLE,
        GENRE_NATIVAAMERICAN,    GENRE_CABARET,          GENRE_NEWWAVE,         GENRE_PSYCHADELIC,
        GENRE_RAVE,              GENRE_SHOWTUNES,        GENRE_TRAILER,         GENRE_LOFI,
        GENRE_TRIBAL,            GENRE_ACIDPUNK,         GENRE_ACIDJAZZ,        GENRE_POLKA,
        GENRE_RETRO,             GENRE_MUSICAL,          GENRE_ROCKANDROLL,     GENRE_HARDROCK,
        GENRE_FOLK,              GENRE_FOLKROCK,         GENRE_NATIONALFOLK,    GENRE_SWING,
        GENRE_FASTFUSION,        GENRE_BEBOP,            GENRE_LATIN,           GENRE_REVIVAL,
        GENRE_CELTIC,            GENRE_BLUEGRASS,        GENRE_AVANTGARDE,      GENRE_GOTHICROCK,
        GENRE_PROGRESSIVEROCK,   GENRE_PSYCHEDELICROCK,  GENRE_SYMPHONICROCK,   GENRE_SLOWROCK,
        GENRE_BIGBAND,           GENRE_CHORUS,           GENRE_EASYLISTENING,   GENRE_ACOUSTIC,
        GENRE_HUMOR,             GENRE_SPEECH,           GENRE_CHANSON,         GENRE_OPERA,
        GENRE_CHAMBERMUSIC,      GENRE_SONATA,           GENRE_SYMPHONY,        GENRE_BOOTYBRASS,
        GENRE_PRIMUS,            GENRE_PORNGROOVE,       GENRE_SATIRE,          GENRE_SLOWJAM,
        GENRE_CLUB,              GENRE_TANGO,            GENRE_SAMBA,           GENRE_FOLKLORE,
        GENRE_BALLAD,            GENRE_POWERBALLAD,      GENRE_RTHYMICSOUL,     GENRE_FREESTYLE,
        GENRE_DUET,              GENRE_PUNKROCK,         GENRE_DRUMSOLO,        GENRE_ACAPELA,
        GENRE_EUROHOUSE,         GENRE_DANCEHALL
    };
private:
    char m_Genre;
    TString m_Comment;
    TString m_Year;
    TString m_Album;
    TString m_Artist;
    TString m_Title;
};

看得眼花了吧。其实这些枚举是为了标志MP3的流派的。你看看,为了这样一个小的属性,我们做了不知多少的努力!

 

我们把CMP3以及它的标签类的实现写在一个cpp里面。这个cpp就是CMP3.cpp。下面就是这个源文件的实现:

/*---------------------------------------------------------------------------
蒋轶民制作E-mail:jiangcaiyang123@163.com
最后编辑:年月日:51:26
文件名:CMP3.cpp
作用:MP3类的实现文件
----------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
// 头文件
#include <fstream>
#include "CMP3.h"
/*--------------------------------------------------------------------------*/
std::wstring MultibyteToWstring( LPCSTR pszSrc )
{
    int nSize = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)pszSrc, (int)strlen( pszSrc ) + 1, 0, 0 );
    if ( nSize <= 0 ) return NULL;                              // 转换失败
    WCHAR* pwszDst = new WCHAR[nSize+1];
    if ( pwszDst == NULL ) return NULL;                         // 分配空间错误
    MultiByteToWideChar( CP_ACP, 0, (LPCSTR)pszSrc, (int)strlen( pszSrc ) + 1, pwszDst, nSize );
    pwszDst[nSize] = 0;
    if ( pwszDst[0] == 0xFEFF )                                 // 跳过标识符xFEFF
    {
        for ( int i = 0; i < nSize; i++ )
            pwszDst[i] = pwszDst[i+1];
    }
    std::wstring wCharString( pwszDst );
    delete pwszDst;
    return wCharString;
}
/*--------------------------------------------------------------------------*/
bool CMP3Tag::ReadMP3Tag( TString strFilename )                 // 读取MP3标签
{
    // 读取文件
    std::ifstream in( strFilename.c_str(), std::ios::in | std::ios::binary );
    if ( !in ) return false;
 
    in.seekg( -128, std::ios::end );                                    // 文件指针移到倒数的字节处
 
    char strTag[4] = { 0 };
    in.read( strTag, 3 );
    if ( strcmp( strTag, "TAG" ) != 0 ) return false;
 
    // 正确地读取到MP3信息标签,开始读取MP3信息
    char genre;
    char comment[31]            = { 0 };
    char year[5]                = { 0 };
    char album[31]              = { 0 };
    char artist[31]             = { 0 };
    char title[31]              = { 0 };
    in.read( title, 30 );
    in.read( artist, 30 );
    in.read( album, 30 );
    in.read( year, 4 );
    in.read( comment, 30 );
    in.read( &genre, 1 );
 
    in.close();                                 // 关闭文件
 
    m_Genre                     = genre;
    m_Comment                   = MultibyteToWstring( comment );
    m_Year                      = MultibyteToWstring( year );
    m_Album                     = MultibyteToWstring( album );
    m_Artist                    = MultibyteToWstring( artist );
    m_Title                     = MultibyteToWstring( title );
 
    return true;
}
/*--------------------------------------------------------------------------*/
TString CMP3Tag::GetGenre( void )                               // 获取流派
{
 
    switch ( m_Genre )
    {
    case GENRE_BLUES:             return TEXT( "Blues" );
    case GENRE_CLASSICROCK:       return TEXT( "Classic Rock" );
    case GENRE_COUNTRY:           return TEXT( "Country" );
    case GENRE_DANCE:             return TEXT( "Dance" );
    case GENRE_DISCO:             return TEXT( "Disco" );
    case GENRE_FUNK:              return TEXT( "Funk" );
    case GENRE_GRUNGE:            return TEXT( "Grunge" );
    case GENRE_HIPHOP:            return TEXT( "Hip-hop" );
    case GENRE_JAZZ:              return TEXT( "Jazz" );
    case GENRE_METAL:             return TEXT( "Metal" );
    case GENRE_NEWAGE:            return TEXT( "New Age" );
    case GENRE_OLDIES:            return TEXT( "Oldies" );
    case GENRE_OTHER:             return TEXT( "Other" );
    case GENRE_POP:               return TEXT( "Pop" );
    case GENRE_RANDB:             return TEXT( "R&B" );
    case GENRE_RAP:               return TEXT( "Rap" );
    case GENRE_REGGAE:            return TEXT( "Reggae" );
    case GENRE_ROCK:              return TEXT( "Rock" );
    case GENRE_TECHNO:            return TEXT( "Techno" );
    case GENRE_INDUSTRIAL:        return TEXT( "Industrial" );
    case GENRE_ALTERNATIVE:       return TEXT( "Alternative" );
    case GENRE_SKA:               return TEXT( "Ska" );
    case GENRE_DEATHMETAL:        return TEXT( "Death Metal" );
    case GENRE_PRANKS:            return TEXT( "Pranks" );
    case GENRE_SOUNDTRACK:        return TEXT( "Soundtrack" );
    case GENRE_EUROTECHNO:        return TEXT( "Eurotechno" );
    case GENRE_AMBIENT:           return TEXT( "Ambient" );
    case GENRE_TRIPHOP:           return TEXT( "Triphop" );
    case GENRE_VOCAL:             return TEXT( "Vocal" );
    case GENRE_JAZZANDFUNK:       return TEXT( "Jazz & Funk" );
    case GENRE_FUSION:            return TEXT( "Fusion" );
    case GENRE_TRANCE:            return TEXT( "Trance" );
    case GENRE_CLASSICAL:         return TEXT( "Classical" );
    case GENRE_INSTRUMENTAL:      return TEXT( "Instrumental" );
    case GENRE_ACID:              return TEXT( "Acid" );
    case GENRE_HOUSE:             return TEXT( "House" );
    case GENRE_GAME:              return TEXT( "Game" );
    case GENRE_SOUNDCLIP:         return TEXT( "Sound Clip" );
    case GENRE_GOSPEL:            return TEXT( "Gospel" );
    case GENRE_NOISE:             return TEXT( "Noise" );
    case GENRE_ALTERNROCK:        return TEXT( "Alternative Rock" );
    case GENRE_BASS:              return TEXT( "Bass" );
    case GENRE_SOUL:              return TEXT( "Soul" );
    case GENRE_PUNK:              return TEXT( "Punk" );
    case GENRE_SPACE:             return TEXT( "Space" );
    case GENRE_MEDITATIVE:        return TEXT( "Meditative" );
    case GENRE_INSTRUMENTALPOP:   return TEXT( "Instrumental Pop" );
    case GENRE_INSTRUMENTALROCK:  return TEXT( "Instrumental Rock" );
    case GENRE_ETHNIC:            return TEXT( "Ethnic" );
    case GENRE_GOTHIC:            return TEXT( "Gothic" );
    case GENRE_DARKWAVE:          return TEXT( "Darkwave" );
    case GENRE_TECHNOINDUSTRIAL:  return TEXT( "Techno-industrial" );
    case GENRE_ELECTRONIC:        return TEXT( "Electronic" );
    case GENRE_POPFOLK:           return TEXT( "Pop Folk" );
    case GENRE_EURODANCE:         return TEXT( "EuroDance" );
    case GENRE_DREAM:             return TEXT( "Dream" );
    case GENRE_SOUTHERNROCK:      return TEXT( "Southern Rock" );
    case GENRE_COMEDY:            return TEXT( "Comedy" );
    case GENRE_CULT:              return TEXT( "Cult" );
    case GENRE_GANGSTA:           return TEXT( "Gangsta" );
    case GENRE_TOP40:             return TEXT( "Top 40" );
    case GENRE_CHRISTIANRAP:      return TEXT( "Christian Rap" );
    case GENRE_POPFUNK:           return TEXT( "Pop Funk" );
    case GENRE_JUNGLE:            return TEXT( "Jungle" );
    case GENRE_NATIVAAMERICAN:    return TEXT( "Native American" );
    case GENRE_CABARET:           return TEXT( "Cabaret" );
    case GENRE_NEWWAVE:           return TEXT( "New Wave" );
    case GENRE_PSYCHADELIC:       return TEXT( "Psychadelic" );
    case GENRE_RAVE:              return TEXT( "Rave" );
    case GENRE_SHOWTUNES:         return TEXT( "Showtunes" );
    case GENRE_TRAILER:           return TEXT( "Trailer" );
    case GENRE_LOFI:              return TEXT( "Lo-fi" );
    case GENRE_TRIBAL:            return TEXT( "Tribal" );
    case GENRE_ACIDPUNK:          return TEXT( "Acid Punk" );
    case GENRE_ACIDJAZZ:          return TEXT( "Acid Jazz" );
    case GENRE_POLKA:             return TEXT( "Polka" );
    case GENRE_RETRO:             return TEXT( "Retro" );
    case GENRE_MUSICAL:           return TEXT( "Musical" );
    case GENRE_ROCKANDROLL:       return TEXT( "Rock & Roll" );
    case GENRE_HARDROCK:          return TEXT( "Hard Rock" );
    case GENRE_FOLK:              return TEXT( "Folk" );
    case GENRE_FOLKROCK:          return TEXT( "Folk Rock" );
    case GENRE_NATIONALFOLK:      return TEXT( "National Folk" );
    case GENRE_SWING:             return TEXT( "Swing" );
    case GENRE_FASTFUSION:        return TEXT( "Fast Fusion" );
    case GENRE_BEBOP:             return TEXT( "Bebop" );
    case GENRE_LATIN:             return TEXT( "Latin" );
    case GENRE_REVIVAL:           return TEXT( "Revival" );
    case GENRE_CELTIC:            return TEXT( "Celtic" );
    case GENRE_BLUEGRASS:         return TEXT( "Bluegrass" );
    case GENRE_AVANTGARDE:        return TEXT( "Avant-Garde" );
    case GENRE_GOTHICROCK:        return TEXT( "Gothic Rock" );
    case GENRE_PROGRESSIVEROCK:   return TEXT( "Progressive Rock" );
    case GENRE_PSYCHEDELICROCK:   return TEXT( "Psychedelic Rock" );
    case GENRE_SYMPHONICROCK:     return TEXT( "Symphonic Rock" );
    case GENRE_SLOWROCK:          return TEXT( "Slow Rock" );
    case GENRE_BIGBAND:           return TEXT( "Big Band" );
    case GENRE_CHORUS:            return TEXT( "Chorus" );
    case GENRE_EASYLISTENING:     return TEXT( "Easy Listening" );
    case GENRE_ACOUSTIC:          return TEXT( "Acoustic" );
    case GENRE_HUMOR:             return TEXT( "Humor" );
    case GENRE_SPEECH:            return TEXT( "Speech" );
    case GENRE_CHANSON:           return TEXT( "Chanson" );
    case GENRE_OPERA:             return TEXT( "Opera" );
    case GENRE_CHAMBERMUSIC:      return TEXT( "Chamber Music" );
    case GENRE_SONATA:            return TEXT( "Sonata" );
    case GENRE_SYMPHONY:          return TEXT( "Symphony" );
    case GENRE_BOOTYBRASS:        return TEXT( "Booty Brass" );
    case GENRE_PRIMUS:            return TEXT( "Primus" );
    case GENRE_PORNGROOVE:        return TEXT( "Porn Groove" );
    case GENRE_SATIRE:            return TEXT( "Satire" );
    case GENRE_SLOWJAM:           return TEXT( "Slow Jam" );
    case GENRE_CLUB:              return TEXT( "Club" );
    case GENRE_TANGO:             return TEXT( "Tango" );
    case GENRE_SAMBA:             return TEXT( "Samba" );
    case GENRE_FOLKLORE:          return TEXT( "Folklore" );
    case GENRE_BALLAD:            return TEXT( "Ballad" );
    case GENRE_POWERBALLAD:       return TEXT( "Power Ballad" );
    case GENRE_RTHYMICSOUL:       return TEXT( "Rthymic Soul" );
    case GENRE_FREESTYLE:         return TEXT( "Freestyle" );
    case GENRE_DUET:              return TEXT( "Duet" );
    case GENRE_PUNKROCK:          return TEXT( "Punk Rock" );
    case GENRE_DRUMSOLO:          return TEXT( "Drum Solo" );
    case GENRE_ACAPELA:           return TEXT( "A Capela" );
    case GENRE_EUROHOUSE:         return TEXT( "EuroHouse" );
    case GENRE_DANCEHALL:         return TEXT( "Dance Hall" );
    }
    return TEXT( "Unknown" );
}                              
/*--------------------------------------------------------------------------*/
bool CMP3::LoadMP3File( TString strFilename )               // 读取MP3文件
{
    m_tag.ReadMP3Tag( strFilename );                        // 读取文件标签
 
    HRESULT hr;                                             // 结果的句柄
 
    // 初始化COM
    hr = CoInitialize( NULL );
 
    // 创建接口
    hr = CoCreateInstance( CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,

        IID_IGraphBuilder, (void **)&m_GraphBuilder );
 
    m_GraphBuilder->QueryInterface( IID_IMediaControl,
        (void** )&m_MediaControl );
    m_GraphBuilder->QueryInterface( IID_IMediaEventEx,
        (void** )&m_MediaEventEx );
 
    // 渲染文件(读取文件)
    m_GraphBuilder->RenderFile( strFilename.c_str(), NULL );
 
    return true;
}
/*--------------------------------------------------------------------------*/
bool CMP3::Play( void )                                         // 播放MP3文件
{
    ThrowIfFailed( m_MediaControl->Run(), "播放(运行)失败。" );
    m_IsPlaying = true;
    return true;
}
/*--------------------------------------------------------------------------*/
CMP3::~CMP3( void )                                             // 析构函数
{
    // 释放成员的空间
    m_GraphBuilder->Release();
    m_MediaControl->Release();
    m_MediaEventEx->Release();
}
/*--------------------------------------------------------------------------*/
void CMP3::ProcessNotification( void )                          // 处理消息,用来判定是否播放结束
{
    long eventcode, param1, param2;
 
    while ( m_MediaEventEx->GetEvent( &eventcode, ¶m1, ¶m2, 0 ) == S_OK )
    {
        if ( eventcode == EC_COMPLETE )
        {
            m_IsPlaying = false;
        }
        m_MediaEventEx->FreeEventParams(eventcode, param1, param2);
    }
}

可以很简单地调用这个类:

int main( int, char** )                         // 主函数
{
    CMP3 mp3_1, mp3_2;
    mp3_1.LoadMP3File( TEXT( "09. えがいてあ_そ_ぼ!.mp3" ) );
    mp3_2.LoadMP3File( TEXT( "46.MOETAN CORNER.mp3" ) );
 
    cout << "就绪,按任意键播放音乐:\n";
    if ( getch() )
    {
        mp3_1.Play();
        cout << "第一个音乐:";
        cout << WsToMultibyte( mp3_1.GetMP3Tag().GetTitle() );
        cout << "播放。\n";
    }
    if ( getch() )
    {
        mp3_2.Play();
        cout << "第二个音乐:";
        cout << WsToMultibyte( mp3_2.GetMP3Tag().GetTitle() );
        cout << "播放。\n";
    }
    if ( getch() ) cout << "音乐播放结束,现在关闭程序。\n";
   
    return 0;
}

程序的运行结果是:

 

我这个工程完整的代码的下载地址是:

http://download.csdn.net/detail/jiangcaiyang123/3589169

 

怎么样?很简单吧。其有时间的话,我还会写更多的关于游戏开发的心得体会的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐