65.9K
CodeProject 正在变化。 阅读更多。
Home

MIDI 包装类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.43/5 (15投票s)

2005年11月3日

CPOL

4分钟阅读

viewsIcon

63357

downloadIcon

1474

一篇介绍用于播放 MIDI 文件的 C++ 类的文章。

Sample Image

引言

MS DirectX 包含几个组件,它们基本上提供了多媒体编程的接口。DirectX 的音频部分由两个组件组成——DirectSound 和 DirectMusic。DirectSound 用于数字音乐,DirectMusic 用于合成音乐。这两种媒体在技术表示上有所不同,但在性能方面却非常相似。事实上,数字音乐的性能要更准确一些,可以说比合成音乐更“专业”。但在大多数情况下,这并不重要……但从编程的角度来看呢?DirectMusic 是一项庞大的技术,包含许多复杂的概念和技术。与使用 DirectMusic 相比,使用 DirectSound 要容易得多。学习 DirectMusic 编程不需要花上一个星期。

本文旨在介绍如何学习 DirectMusic 编程。特别是,我将重点介绍如何播放合成音乐文件,即 MIDI 文件。MIDI(Musical Instrument Device Interface,乐器数字接口)用于创建(编排)和播放合成音乐文件——.mid 文件。虽然编排与播放有很大不同,但我将向您介绍后者。编排非常复杂,需要一整本书才能完全讨论(甚至可能还不够)。

在这里,我的目的是介绍一些 C++ 类,它们将封装使用 DirectMusic 播放 .mid 文件所需的工作。我不会展示如何调用 CoCreateInstance 来创建 DirectMusic 组件对象以及如何使用它。相反,我将展示如何使用我准备好的类供您(以及我自己)使用。但您仍然可以查看包装类的代码,了解其中的工作原理。

背景

我一直喜欢创建 C++ 类,尤其是包装类。我一直喜欢将“坏”的东西变成“好”的东西。为了创造另一个好东西,我创建了四个类:

  • CDMusicPerf
  • CDMusicMIDI
  • CDMException
  • CDMusicMIDIHelper

我认为您可以通过查看这些类的代码来学习播放 MIDI 文件的底层代码。我在这些类的各种方法中添加了大量的注释来支持我的操作。现在,让我们看看这些类是什么。它们分布在四个文件中:

  • DirectMusic.h / DirectMusic.cpp – 包含 CDMusicPerfCDMusicMIDICDMusicMIDIHelper 类。
  • DMEcxept.h / DMExcept.cpp – 包含 CDMException 类。

我首先想谈谈 CDMException。这个类代表由 CDMusicPerfCDMusicMIDI 类引发的异常;CDMusicMIDIHelper 不使用这个类。CDMException 派生自 MFC 类 CException 并重写了其方法。但您不必担心 CDMException 中的内容;您只需像这样使用 try/catch 块即可:

try
{
    // some code
}

catch (CDMException *e)
{
    e->ReportError(); // you may omit this
    e->Delete(); // do not omit this
}

接下来是 CDMusicPerf。这个类代表 DirectMusic 接口 IDirectMusicPerformance8。该接口充当管理器,为 DirectMusic 的其他接口提供基本服务。它用于添加和删除端口,将 performance channels 映射到端口,播放段,调度消息并将它们通过工具路由,设置和检索音乐参数等等。CDMusicPerf 的声明(简化)如下:

class CDMusicPerf
{
    CComPtr<IDirectMusicPerformance8> m_pDMusicPerf;
public:
    CDMusicPerf();
    ~CDMusicPerf();
    BOOL Create(…);
    void Destroy();
    BOOL IsInitialized() const;
    void StopAll();
    void CollectGarbage();
    operator IDirectMusicPerformance8 *() {
        return m_pDMusicPerf;
    }
};

请注意最后一个方法。事实上,这是一个类型转换运算符,它将 CDMusicPerf 对象转换为 IDirectMusicPerformance8 接口指针。这个特性稍后将被 CDMusicMIDI::Create 方法使用。我认为其他方法的名称足以说明它们的作用。因此,我将向您介绍另一个类……

接下来是 CDMusicMIDI。严格来说,这个类代表 DirectMusic 接口 IDirectMusicSegment。该接口代表一个段,一段可以播放的音乐。CDMusicMIDI 类提供了从磁盘驱动器加载 MIDI 文件、播放/停止、设置/获取速度等方法。它的一些方法如下:

class CDMusicMIDI
{
public:
    CDMusicMIDI();
    ~CDMusicMIDI();
    
    BOOL Create(IDirectMusicPerformance8 *pPerf);
    void Destroy();
    BOOL LoadMIDI(LPCTSTR lpszFileName);
    BOOL IsInitialized() cons;
    BOOL Play();
    BOOL Stop();
    BOOL IsPlaying() const;
    void SetRepeats(DWORD dwRepeats);
    BOOL SetTempo(double dTempo);
    double GetTempo() const;
};

这两个类足以提供 MIDI 播放功能。但是,有时我需要加载十多个 MIDI 文件。这是为一个游戏准备的,该游戏使用 .mid 文件进行音乐播放,并在某个播放文件结束时,另一个文件必须开始播放。创建十个 CDMusicMIDI 对象并单独管理它们很不方便。我觉得我需要一个数组来存储这些对象。我的需求促使我创建了一个类似数组的类,它能够以调用其方法的方式轻松访问这些对象。于是就诞生了 CDMusicMIDIHelper。它的接口(简化)如下:

class CDMusicMIDIHelper
{
    CDMusicMIDIHelper();
    ~CDMusicMIDIHelper();

    UINT Add(…);
    UINT Insert(…);
    BOOL Remove(…);
    void RemoveAll();
    int GetSize() const;
    UINT GetIDByFileName(…) const;
    BOOL SetActiveID(…);
    UINT GetActiveID() const;
    CDMusicMIDI* GetMIDI(…);
    CDMusicMIDI& GetActiveMIDI();
    BOOL Play(…);
    BOOL Stop(…);
    BOOL IsPlaying(…) const ;
};

示例应用程序大量使用了这个类。查看其代码以了解更多信息。

使用代码

现在该看看这些类是如何使用的了。这并不难。您只需声明一个 CDMusicPerf 实例和一个或多个 CDMusicMIDI 实例:

#include "DirectMusic.h"
#include "DMExcept.h"
using namespace DirectMusic;

class CMyDialog : public CDialog
{
    CDMusicPerf m_perf;
    CDMusicMIDI m_midi;
    
    // ...
};

您需要创建这些对象。首先,在 OnInitDialog 中创建 m_perf

m_perf.Create(GetSafeHwnd());

然后,您可以创建 CDMusicMIDI 对象。在这种情况下:

m_midi.Create(m_perf);

之后,加载一个 MIDI 文件:

m_midi.LoadMIDI( strFilePath );

现在,您可以播放该文件:

m_midi.Play(2); // play this song for two times

就是这样。我创建了一个演示应用程序,展示了这些类提供的许多功能。尽情享受吧!

© . All rights reserved.