Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

MIDI Wrapper Classes

4.43/5 (16 votes)
3 Nov 2005CPOL4 min read 1   1.5K  
An article that presents some C++ classes to play MIDI files.

Sample Image

Introduction

MS DirectX has several components that are basically provide interfaces for multimedia programming. DirectX’s audio section consists of two components – DirectSound and DirectMusic. DirectSound is for digital music, and DirectMusic is for synthesized music. These two types of media are rather different in terms of their technical representation and much similar in sense of their performance. To the point, the performance of digital music is a bit more accurate, one may say, more qualified than that of synthesized music. But this is not significant in most cases… But what from the programming point of view? DirectMusic is a huge technology with many complicated concepts and techniques. Using DirectSound is rather easy in comparison with using DirectMusic. Not a week you will have to spend to learn DirectMusic programming.

This article is aimed at showing how to get to know DirectMusic programming. Especially, I will concentrate on how to play synthesized files, that is, MIDI files. Musical Instrument Device Interface (shortly MIDI) is used to create (compose) and play synthesized musical files - .mid files. Though composing is rather different than playing, I am going to introduce you to the latter. Composition is so complex that it needs a whole book to be discussed fully (and maybe not fully).

Here, my purpose is to represent some C++ classes that will wrap the work with DirectMusic necessary to play .mid files. I will not show how to call CoCreateInstance to create a DirectMusic component object and how to use it. Instead, I will show how to use the classes that I have prepared to be used by you (and me too). But still you can look into the wrapper classes’ code and learn how the stuff there works.

Background

I’ve always liked creating C++ classes, especially when these classes are wrappers. I’ve always liked to turn “bad” things into “good” ones. To make another good thing, I have created four classes:

  • CDMusicPerf
  • CDMusicMIDI
  • CDMException
  • CDMusicMIDIHelper

I think you can learn the low-level code of playing MIDI files by looking into the code of these classes. I have put lots of comments to support my actions within those classes’ methods. Now, let’s see what the classes are. They are placed in four files:

  • DirectMusic.h / DirectMusic.cpp – contain the CDMusicPerf, the CDMusicMIDI and the CDMusicMIDIHelper classes.
  • DMEcxept.h / DMExcept.cpp – contain the CDMException class.

I’d like first to speak about CDMException. This class represents exceptions provoked from the CDMusicPerf and the CDMusicMIDI classes; CDMusicMIDIHelper doesn’t use this class. CDMException is derived from the MFC class CException and overrides its methods. But you may not worry about the stuff in CDMException; you simply can use try/catch blocks like this:

try
{
    // some code
}

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

Next, is CDMusicPerf’s turn. This class represents the DirectMusic interface IDirectMusicPerformance8. This interface plays the role of a manager by providing basic services to other interfaces of DirectMusic. It is used for adding and removing ports, mapping performance channels to ports, playing segments, dispatching messages and routing them through tools, setting and retrieving music parameters and so on. CDMusicPerf has the following declaration (simplified):

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

Pay your attention to the last method. To the point, that is a casting operator that casts the CDMusicPerf object into an IDirectMusicPerformance8 interface pointer. This feature will be used later by the CDMusicMIDI::Create method. I think the other methods’ names describe themselves. So, I will introduce you to another class…

Next comes the CDMusicMIDI. Not to say strictly, this class represents the DirectMusic IDirectMusicSegment interface. This interface represents a segment, a piece of music that can be played. The CDMusicMIDI class provides methods to load MIDI files from disk drives and play/stop, set/get tempo and so on. Some of its methods are the following:

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

These two classes are quite enough to provide MIDI playback. But, sometimes, I needed to load more than ten MIDI files. That was for a game that used .mid files to perform music playback and as soon as a playing file ended, another one had to start its playback. It was not convenient to create ten CDMusicMIDI objects and manipulate them separately. I felt that I needed some array to store the objects. My needs led me to create an array-similar class that would represent easy access to those objects in the sense of calling their methods. So was born CDMusicMIDIHelper. It has the following interface (simplified):

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

The sample application uses this class intensively. See its code to learn more.

Using the code

It’s time to see how these classes are used. Not a difficult thing. You simply declare one instance of CDMusicPerf and one or more instances of CDMusicMIDI:

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

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

You need to create those objects. First, create m_perf in OnInitDialog:

m_perf.Create(GetSafeHwnd());

Then, you may create CDMusicMIDI object(s). In this case:

m_midi.Create(m_perf);

Afterwards, load a MIDI file:

m_midi.LoadMIDI( strFilePath );

Now, you may play the file:

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

That’s all. I have created a demo application that shows many features that are provided by these classes. Enjoy it!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)