Click here to Skip to main content
Click here to Skip to main content

Playing Midi Files with DirectMusic

By , 30 Dec 2010
 

Introduction

This article focuses on the use of CMidiMusic which allows an easy access to DirectX DirectMusic API. This class allows loading and playing general midi sequence files(.mid). The class is designed to perform midi playback in one segment and offers additional features like using any midi port installed in your system, 3D sound environment, sound effects, etc.

Direct Audio Specifications

The DirectX 8.0 Audio part offers improved integration between DirectSound and DirectMusic, including a great set of new features. Some of these are listed hereafter:

  • The last version of Direct Audio allows using hardware acceleration for sound synthesis.
  • With the new AudioPath model, the sound of a port does not go directly to a directsound buffer, instead, it goes to an audiopath which controls data flow from a performance to the final output. The audiopath allows controlling the 3D position of each sound and adding other effects.
  • The segments are modified independently and you can apply effects like pan, volume, individually.
  • It allows using DLS2 (Downloadable sound level 2 standard) which provides a great sound quality and unlimited use of instruments with the software synthesizer.
  • FX (Reverb, Chorus...) if it is available in the software synthesizer.
  • It overcomes the 16 midi channels limit, allowing the use of as many midi channels as the software is able to handle.
  • The playback can be controlled accurately in run time by selecting different sets of musical variations and changes in the chords progress.
  • 3D Positioning.

Main Interfaces of DirectMusic

  • IDirectMusic8: This interface allows managing buffers and ports. There should only exist one instance of this interface per application.
  • IDirectMusicPerformance8: This is the most important interface in playback management. It is used to add and remove ports, play segments, notify event reception, control music parameters and obtain timing information.
  • IDirectMusicPort8: This interface provides access to DirectMusicPorts objects like MPU-401 or the software synthesizer.
  • IDirectMusicSegment8: This interface represents a segment, musical piece made up of multiple tracks. It can contain a midi file, a wave, a segment.
  • IDirectMusicLoader8: Its main function is to find and load the different objects. These objects are to be stored in a segment.
  • IDirectMusicSegmentState8: The (playback) engine creates a SegmentState object which allows analyzing the state of the segment currently playing.
  • IDirectMusicAudioPath8: The IDirectMusicAudioPath8 interface represents the stages of data flow from the data file to the primary buffer.

DirectMusic Architecture

Once a resource has been loaded in a segment, the performance dispatches the messages defined by a tool of an application, such tools are grouped in toolgraphs which process specific segment messages. A tool can modify a message and pass it on, delete it, or send a new message.

Finally, the messages are delivered to the output tool, which converts the data to MIDI format before passing it to the synthesizer. Channel-specific MIDI messages are directed to the appropriate channel group on the synthesizer. The synthesizer creates sound waves and streams them to a device called a sink, which manages the distribution of data through buses to DirectSound buffers.

There are three kinds of buffers:

  • Sink-in buffers are DirectSound secondary buffers into which the sink streams data. Here are applied many effects like 3D, pan, volume, etc...The resulting waveform is passed either directly to the primary buffer or to one or more mix-in buffers.
  • Mix-in buffers receive data from other buffers, apply effects, and mix the resulting wave forms. These buffers can be used to apply global effects.
  • The primary buffer performs the final mixing on all data and passes it to the rendering device.

The following diagram shows the flow of data from files to the speakers:

Using CMidiMusic

In first sight, it is necessary to add in Visual C++ IDE this library: Go to Project -> Settings -> Object Library Modules and add dxguid.lib of DirectX8 SDK.

After this, it will be necessary to include in the project the header "dmusic.h" and "dmusic.cpp" file and finally instance an object of CMidiMusic class type as shown below:

void CPlayerDlg::OnButton_Start()     
{
    DWORD dwcount; // Counter variable to enumerate the midi ports 
    INFOPORT Info; // INFOPORT structure to store port information 
    BOOL bSelected;

    CMidiMusic *pMidi;     // Pointer to CMidiMusic object type
    pMidi=new CMidiMusic;     // Allocate it      
    pMidi->Initialize(FALSE);// Initialize without 3D positioning
    
    dwcount=0;
    bSelected=FALSE;
    
     // Port enumeration  phase 
     // It is necessary to supply a port counter 
    while (pMidi->PortEnumeration(dwcount,&Info)==S_OK)
    {
        // Ensure it is an output hardware device
        if (Info.dwClass==DMUS_PC_OUTPUTCLASS) 
        {
            if (!((Info.dwFlags & DMUS_PC_SOFTWARESYNTH) || bSelected))
            {
                // Select the enumerated port 
                pMidi->SelectPort(&Info);
                bSelected=TRUE;
            }
        }
    
    dwcount++;      
   }

     // Read the MIDI file 
    pMidi->LoadMidiFromFile("c:\\music\\song_004.mid");
     // Play the file
    pMidi->Play();
    AfxMessageBox("Playing...");
     // Stop it
    pMidi->Stop();

    //Important!: Delete the pointer to the object in order to call the 
    //destructor 
    //which call the DirectMusic releases interfaces
    delete pMidi;
}

CMidiMusic Capabilities

Synthesizer 3D Effects (Reverb,Chorus)
Microsoft Software Yes Only in not 3D mode
Hardware No No
External No No

More Information

For more information about the CMidiMusic class, read the attached file readme.txt included in the sources. You will be able to find more information in the online help of http://www.microsoft.com/directx and DirectX 8 SDK technical documentation.

Overview of the Demo Project

As you can see, this is not WinAmp, wish it was. ;) Nevertheless, all of CMidiMusic class features are made available.

History

  • 31 Jan 2002 - Updated source files
  • 12 May 2003 - Updated source files
  • 28 Dec 2010 - Updated article and source files

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)

About the Author

Carlos Jiménez de Parga
Software Developer
Spain Spain
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionMay I convert and redistribute your project?member_flix01_11 Jan '07 - 5:58 
Hello,
 
I'm starting to convert your project from MFC to SmartWin++.
 
I would like to know if I'm allowed to include it in a future version of the my IDE (http://sallyide.sourceforge.net[^])
as an example project or if there are some restrictions.
 
P.S. At the present time, I'm at a good point (http://sallyide.sourceforge.net/screenMidiPlayer.jpg[^]),
even if I think I won't implement the master volume (I can't understand how it's done) and
my "Reverb" button does not seem to work on my system (maybe it's just a question of "Synthetizer").
 

 
Regards,
Flix.
AnswerRe: May I convert and redistribute your project?memberCarlos Jiménez de Parga11 Jan '07 - 6:37 
Of course, you are completely allowed to include it (I'm aware you are refering to CMidiMusic class) in your IDE related project, even the DirectMIDI library Smile | :) .
 
You don't have to pay anything in return, however, some acreditation in your project would be grateful (I don't eat air Big Grin | :-D ).
 
Good luck with your great project! Maybe someday I make use of it. Who knows!
Questionnewb needs tutorialsmemberlaserbeak433 May '06 - 0:27 
hi
i just read the cMidiDemo article and needed something with more "performance" features so i decided to read this stuff about directx.
 
i'm a newb so i dont really need much info on performance at the moment. but what i do need is some sort of strp by step guide on how to set these projects up in msvc++ and run them. basicaly i need lots of help on starting up apps so i can get comfortable with an IDE. can someone please help?
 
any links to midi related tutorials with such features?
GeneralMusic timesussAnonymous26 Apr '05 - 2:38 
Your code is very nice. I use it to my program but I want to ask you somthing. I want to know can I convert miditime that i get it like "Tick value" to real time(how long this song play). I just to show how long of the song like in winam or media player. Thank you!!!!!!!!!1
GeneralRe: Music timememberCarlos Jiménez de Parga26 Apr '05 - 5:40 
Thank you for appreciating this old piece of code.
 
First off, if you want to get the length of a music segment you will have to call IDirectMusicPerformance8::MusicToReferenceTime(MUSIC_TIME mt,REFERENCE_TIME *rtSegTime) after the call to CMidiMusic::GetLength(MUSIC_TIME *mt) that will give you the length of the sequence in ticks. Once you have it all, you must proceed to convert the rtSegTime variable which is a 64-bit value returned by the master clock and is incremented every 100 ns to common time. So, you must start making calculations to solve the duration in hours, minutes and seconds:
These formulas may help you:
// Divide by 10.000.000 to get seconds</code>

REFERENCE_TIME rtTime = rtSegTime / 10000000;
 
unsigned int hours = (unsigned int)(rtTime / 3600);    
 
rtTime %= 3600;
 
unsigned int minutes = (unsigned int)(rtTime / 60);      
 
rtTime %= 60;
 
unsigned int seconds = (unsigned int)rtTime;
 

GeneralRe: Music timemembertongbj@hotmail.com29 Apr '05 - 2:01 
Thank you for your reply. I just find a little bug in your program and i need your help again. When use pause button some note was skipped. Could you plese help me to fix this problem. I found that note skipped is relative with tempo.
GeneralEditing the Midi filememberUhli23 Feb '05 - 23:41 
Hello Carlos,
 
the CMidiMusic class is great. I have written a simple Midi file editor using CMidiMusic to playback a Midi file. When the Midi file gets changed and the user wants to listen to the changes, I tried loadind the Midi file again. This didn't work because DirectMusic seems to have a lock on the old Midi file. When I destroy my instance of CMidiMusic and create a new instance it works fine. Since I neither know the internals of CMidiMusic nor the internals of DirectX. I wanted to know if this is a safe way to use CMidiMusic.
 
Michael J. Mueller
GeneralRe: Editing the Midi filememberCarlos Jiménez de Parga24 Feb '05 - 5:04 
Of course. There is not any other alternative at this abstraction level because the IDirectMusicLoader8::LoadObjectFromFile locks the file completely and doesn't allow any other shared access to the resource. Therefore, your solution could be valid at object oriented level, though it would be better to use the audio part of the DirectMidi library to get it, since it allows to migrate from the CDirectMusic object level to a more accuracy DirectX/DirectMusic API level using CSegments wrapped objects. Unfortunately, in DirectMusic is hard to access to the IDirectMusicSegment internals in order to modify MIDI commands and music data, otherwise, your problem could be solved in a smart way.
You could also work with allocated memory, making all the modifications in a memory space dedicated to store modified MIDI data and, finally, saving it to disk. This approach could be, more or less, -use files only when needed-, not like an intuitive operation.
 

GeneralTime position problem using MCImemberwanders1 Feb '05 - 7:55 
I'm using MCI to playback MIDI for a game. Having all my music parts in the same midi file has worked great till Win XP, where suddenly MCI_OPEN delays proportional to the file size, in my case 20 seconds to load.
 
Resorting to DirectMusic to solve the problem but would do fine with the low level API found in WINMM.DLL which works perfectly without any delays. But here's the problem. When setting a start position the music doesn't start where it should. Having checked the exact time position in a several midi editors, setting the start pos doesn't work, even if adjusting the position +- a few seconds. This worked fine in earlier versions of MCI, Win 95/98.
 
Any help appreciated. Confused | :confused:
 
wanders
GeneralRe: Time position problem using MCImemberCarlos Jiménez de Parga1 Feb '05 - 8:41 
I'm also a little confused about your questioned problem. You said you are using MCI for playing MIDI music for a game. Then you said that the MCI_OPEN command suddenly delays the amazing amount of 20 seconds to load the MIDI sequence. Why is that?
Other related questions to find out the cause:
Are you using DirectMusic via DirectMIDI? otherwise, which interface method are you invoking?
 
Please, I request you to be as explicit as possible when descibing the problem. Thank you.
 


General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 30 Dec 2010
Article Copyright 2001 by Carlos Jiménez de Parga
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid