MCI Midi 类






3.33/5 (12投票s)
2003年7月15日
4分钟阅读

106428

3959
一个非常小巧且易于使用的 C++ 类,用于播放 *.WAV、*.MID 和 *.RMI 文件
- 下载演示项目 - 21.7 Kb (包含 Midi 示例)
- 下载源代码 - 19.3 Kb
引言
cSound
是一个非常小巧且易于使用的 C++ 类,用于播放 *.WAV、*.MID 和 *.RMI 文件。您可以直接将它添加到您的项目中。要播放 Wave、Midi 或 Riff-Midi 文件,只需一个函数调用!该类使用 MCI,**不**需要 DirectX。它可以在 Windows 95 / 98 / ME / NT / 2000 / XP 下运行。
它将通过 Midi 映射器播放 Midi。映射器使用的 Midi 设备取决于您在“控制面板”-->“多媒体”中的设置。请尝试不同的设备,它们的音效差异很大!我已将一些特定的 Midi 文件添加到下载中供您测试。主演示项目是用 MFC 编写的,但 cSound
类完全不依赖 MFC。
无需 DirectX 的 Midi
如果您在互联网上搜索播放 Midi 的源代码,会发现很多都需要在用户的计算机上安装 DirectX。
使用 DirectX 播放 Midi 的优点
- 如果您选择 Microsoft DirectX 合成器(默认设备),则可能允许多个应用程序同时播放 Midi。
- 如果您有一个非常便宜的声卡,Microsoft 合成器的音质会比您声卡的驱动程序好。
使用 DirectX 播放 Midi 的缺点
- 您的软件的每个用户都需要安装 DirectX。在 Windows 95 和 NT 上,他们总是需要更新 DirectX。如果您的应用程序需要 DirectX 7 或 8,Windows 98 和 2000 的用户也需要更新 DirectX。对于使用调制解调器的用户来说,下载 DirectX 是不可接受的(> 12 兆字节)。
- MSDN 中 DirectX 的文档太糟糕了!甚至如果您想为旧版本的 DirectX(如 5 或 6)进行开发,以允许 Windows 98 和 2000 的用户在不更新 DirectX 的情况下使用您的应用程序,您在当前的 MSDN 中找不到任何相关信息!Microsoft 已完全删除了旧文档!
- 当您的应用程序初始化 DirectX 时,会加载十几到二十几个 DLL,消耗大约 4 兆字节的 RAM。在 Pentium 100 电脑上,加载这些 DLL 需要 3 秒钟。
- 播放 Midi 文件后,DirectX **不会**自动释放使用的 Midi 设备。这意味着其他应用程序(例如 WinAmp)无法访问它。您的软件必须负责在播放文件后移除端口。问题在于声音是异步播放的,您不知道 DirectX 何时完成播放。要提前确定 Midi 声音的长度非常复杂,因为 Midi 文件中的速度可能会多次改变。所以您可能需要用计时器以一定的间隔检查声音是否仍在播放(
IDirectMusicPerformance::IsPlaying()
),然后再移除端口 - 但这很麻烦! - 如果您拥有一家中等或高档声卡(如 Soundblaster live),您会发现 Microsoft 合成器(默认设备)听起来非常糟糕!您需要插入额外的代码(
IDirectMusic::EnumPort()
)、一个组合框、用于存储用户设置的代码等,以允许用户选择听起来更好的 Midi 设备。我的cSound
类不需要这些,因为它使用的是用户在控制面板中选择的设备。
使用代码
调用 cSound 简直不能再简单了。只有一个函数调用
cSound::PlaySoundFile(Path);
cMidiDemoDlg.h
文件
private:
cSound i_Sound;
cMidiDemoDlg.cpp
文件
void CMidiDemoDlg::PlayMidiOrWav(char *p_s8PathToSoundFile) { char s8_Buf[300]; DWORD u32_Err; if (u32_Err=i_Sound.PlaySoundFile(p_s8PathToSoundFile)) { // errors defined in cSound.h 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 { // translate errors from WinError.h if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, u32_Err, 0, s8_Buf, sizeof(s8_Buf), 0)) { // translate errors from MMsystem.h if (!mciGetErrorString(u32_Err, s8_Buf, sizeof(s8_Buf))) { sprintf(s8_Buf, "Error %d", u32_Err); } } } MessageBox(s8_Buf, "Error", MB_ICONSTOP); } }
cSound 类
#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(); };
cSound.cpp
文件
/**************************************** Plays Wav or (Riff) Midi file ****************************************/ DWORD cSound::PlaySoundFile(char *p_s8File) { // It is important to check if the file exists ! // On Windows NT PlaySound() returns TRUE // even if the file does not exist ! // Then PlaySound() makes the PC speaker beep !!! // mciSendString("open...") also gives an absolutely // stupid error message // if the file does not exist ! DWORD u32_Attr = ::GetFileAttributes(p_s8File); if (u32_Attr == 0xFFFFFFFF || (u32_Attr & FILE_ATTRIBUTE_DIRECTORY)) return ERROR_FILE_NOT_FOUND; // Get file extension char *p_s8Ext = strrchr(p_s8File, '.'); if (!p_s8Ext) return ERR_INVALID_FILETYPE; if (stricmp(p_s8Ext, ".wav") == 0) { StopSoundFile(); // PlaySound() is very primitive: no Error Code available 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] = ""; // the mciSendString("open...") command is slow // (on Windows NT, 2000 and XP) // so we call it only if necessary 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)) { // replace stupid error messages if (u32_Err == 2) u32_Err = MCIERR_SEQ_NOMIDIPRESENT; return u32_Err; } return 0; } return ERR_INVALID_FILETYPE; } /************************************************** Stops the currently playing Wav and Midi ***************************************************/ void cSound::StopSoundFile() { PlaySound(0, 0, SND_PURGE); // Stop Wav mciSendString("stop MidiDemo", 0, 0, 0); // Stop Midi }
Windows NT、2000 和 XP 中的已知错误
- 在 Windows NT、2000 和 XP 上,如果 Midi 歌曲不以至少四分音符的休止符开头,则会省略歌曲的第一个音符!(下载中添加的所有 Midi 示例文件都以休止符开头)
- 在 Windows NT、2000 和 XP 上,
mciSendString("open...")
命令非常缓慢。 - 仅在 Windows XP 上,如果 Midi 设备被另一个应用程序占用,
mciSendString("open...")
会给出愚蠢的错误消息。(MCIERR_SEQ_NOMIDIPRESENT
而不是MCIERR_SEQ_PORT_INUSE
)mciGetErrorString()
将其翻译为“没有 Midi 硬件可用或没有安装驱动程序”!!
另一个使用 MMSystem 接口的 Midi 播放器
在 MSDN 中,您可以找到一个也无需 DirectX 即可播放 Midi 文件的示例代码。(搜索“MidiPlyr”)它使用了 WinMM.Dll 的 midiOutxxx()
和 midiStreamxxx()
接口(WinAmp 也使用此接口)。Windows 2000 和 XP 中的上述错误不会影响 MidiPlyr。它可以在所有平台上完美运行。但这个示例代码**极其**复杂。(且是用纯 C 编写的)
使用 DirectX 8 的 Midi 类
如果您仍然想要一个 DirectX Midi 播放器(还支持 3D 音效、特殊效果等),请从 https://codeproject.org.cn 下载 cMidiMusic (C++) (需要 DirectX 8)