Introduction
cSound
is a really tiny and very easy to use C++ class to play *.WAV, *.MID and *.RMI files. You can add it to your project without changes.
To play a Wave, Midi or Riff-Midi file you need only one function call!
This class uses MCI, it does NOT require DirectX. It will run under Windows 95 /
98 / ME / NT / 2000 / XP.
It will play Midi via the Midi Mapper. Which Midi device is used by the mapper depends on the settings
in your Control Panel --> MultiMedia. Try the different devices, they sound
extremely different !!! I added some specific Midi files to the download to check.
The Main Demo project is written in MFC but the cSound
class is completely free of MFC.
Midi without DirectX
If you search the internet for source code to play Midi you will find a lot
which require DirectX to be installed on the users computer.
Advantages of using DirectX to play Midi
-
If you select the DirectX Microsoft Synthesizer (default device) it will
be possible that multiple applications can play Midi at once.
-
If you have a very CHEAP sound card, the Microsoft Synthesizer will sound better
than the driver of your soundcard.
Disadvantages of using DirectX to play Midi
-
Every user of your software needs to have DirectX installed. On Windows 95 and NT he will always have to update Direct X.
Users of Windows 98 and 2000 will also need to update DirectX if your
application requires DirectX 7 or 8. The download of DirectX is unacceptable for modem users (> 12 Megabyte)
-
The documentation of DirectX in the MSDN is poor, poor, poor ! And if you even want to write for an older DirectX version (like 5 or 6)
to allow users of Windows 98 and 2000 to use your application without updating
DirectX you will find NOTHING about that in the actual MSDN ! Microsoft completely removed the older documentations !
-
When your application initializes DirectX a dozen of DLLs is loaded
which consume about 4 Megabytes of RAM.
On a Pentium 100 this loading of DLLs takes 3 seconds.
-
After playing a Midi file DirectX does NOT automatically free the used
Midi device. This means that other applications (e.g. WinAmp) can NOT access it.
Your software has to take care to remove the port after playing the file.
The problem is that the sound is played asynchronously and you don't know
when DirectX will finish to play. To find out in advance the length of a Midi sound
is very complicated because the tempo can change multiple times in a Midi file.
So you could check with a timer in certain intervals if the sound is still playing
(
IDirectMusicPerformance::IsPlaying()
) and than remove the port - but this is awkward !
-
If you have a middle-class or a good soundcard (like Soundblaster live)
you will find that the Microsoft Synthesizer (default device) sounds awful !
You have to insert extra code (
IDirectMusic::EnumPort()
), a combo box,
code to store the user settings etc. to allow the user to choose a Midi device,
which sounds better. My cSound
class does not need that because it uses
the device, which the user has selected in the Control Panel.
Using the code
Calling cSound cannot be easier. It's only one function call:
cSound::PlaySoundFile(Path);
The cMidiDemoDlg.h file
private:
cSound i_Sound;
The cMidiDemoDlg.cpp file
void CMidiDemoDlg::PlayMidiOrWav(char *p_s8PathToSoundFile)
{
char s8_Buf[300];
DWORD u32_Err;
if (u32_Err=i_Sound.PlaySoundFile(p_s8PathToSoundFile))
{
if (u32_Err == ERR_INVALID_FILETYPE)
strcpy(s8_Buf, "This filetype is not supported !");
else if (u32_Err == ERR_PLAY_WAV)
strcpy(s8_Buf, "Windows could not play the Wav file !");
else if (u32_Err == MCIERR_SEQ_NOMIDIPRESENT)
strcpy(s8_Buf, "There is no Midi device installed or"
" it is used by another application!");
else
{
if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0,
u32_Err, 0, s8_Buf, sizeof(s8_Buf), 0))
{
if (!mciGetErrorString(u32_Err, s8_Buf, sizeof(s8_Buf)))
{
sprintf(s8_Buf, "Error %d", u32_Err);
}
}
}
MessageBox(s8_Buf, "Error", MB_ICONSTOP);
}
}
The cSound class
#include "Mmsystem.h"
#define ERR_INVALID_FILETYPE 50123
#define ERR_PLAY_WAV 50124
class cSound
{
public:
cSound();
virtual ~cSound();
DWORD PlaySoundFile(char *p_s8File);
void StopSoundFile();
};
The cSound.cpp file
DWORD cSound::PlaySoundFile(char *p_s8File)
{
DWORD u32_Attr = ::GetFileAttributes(p_s8File);
if (u32_Attr == 0xFFFFFFFF || (u32_Attr &
FILE_ATTRIBUTE_DIRECTORY))
return ERROR_FILE_NOT_FOUND;
char *p_s8Ext = strrchr(p_s8File, '.');
if (!p_s8Ext)
return ERR_INVALID_FILETYPE;
if (stricmp(p_s8Ext, ".wav") == 0)
{
StopSoundFile();
if (!PlaySound(p_s8File, 0, SND_FILENAME | SND_ASYNC))
return ERR_PLAY_WAV;
return 0;
}
DWORD u32_Err;
if (!stricmp(p_s8Ext, ".mid") || !stricmp(p_s8Ext, ".midi")
|| !stricmp(p_s8Ext, ".rmi"))
{
StopSoundFile();
static char s8_LastFile[MAX_PATH] = "";
if (strcmp(s8_LastFile, p_s8File) != 0)
{
strcpy(s8_LastFile, p_s8File);
mciSendString("close all", 0, 0, 0);
char s8_Buf[300];
sprintf(s8_Buf,
"open \"%s\" type sequencer alias MidiDemo",
p_s8File);
if (u32_Err=mciSendString(s8_Buf, 0, 0, 0))
return u32_Err;
}
if (u32_Err=mciSendString("play MidiDemo from 0", 0, 0, 0))
{
if (u32_Err == 2) u32_Err = MCIERR_SEQ_NOMIDIPRESENT;
return u32_Err;
}
return 0;
}
return ERR_INVALID_FILETYPE;
}
void cSound::StopSoundFile()
{
PlaySound(0, 0, SND_PURGE);
mciSendString("stop MidiDemo", 0, 0, 0);
}
Known bugs in Windows NT, 2000 and XP
-
On Windows NT, 2000 and XP the first note of a Midi song is omitted in playing
if the song does not begin with a rest of at least a quarter note !!
(The Midi sample-files added to the download all begin with a rest)
-
On Windows NT, 2000 and XP the
mciSendString("open...")
command is extremely slow
-
Only on Windows XP
mciSendString("open...")
gives a stupid error message
if the Midi device is occupied by another application.
(MCIERR_SEQ_NOMIDIPRESENT
instead of MCIERR_SEQ_PORT_INUSE
)
mciGetErrorString()
translates this to
"No Midi hardware available or no drivers installed" !!
Another Midi Player using the MMSystem Interface
In the MSDN you find a sample code
which also plays Midi files without DirectX. (search for "MidiPlyr") It uses the
midiOutxxx()
and midiStreamxxx()
Interface of WinMM.Dll (This interface is also used by WinAmp).
The bugs in Windows 2000 and XP described above do not affect MidiPlyr.
It works perfectly on all platforms. But this sample code is EXTREMELY complex. (and written in pure C)
Midi class using DirectX 8
If you still want a DirectX Midi Player
(which also supports 3D-Sound, special effects etc) download cMidiMusic (C++) from
http://www.codeproject.com
(It requires DirectX 8)