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

MCI Midi 类

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.33/5 (12投票s)

2003年7月15日

4分钟阅读

viewsIcon

106428

downloadIcon

3959

一个非常小巧且易于使用的 C++ 类,用于播放 *.WAV、*.MID 和 *.RMI 文件

引言

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 的优点

  1. 如果您选择 Microsoft DirectX 合成器(默认设备),则可能允许多个应用程序同时播放 Midi。
  2. 如果您有一个非常便宜的声卡,Microsoft 合成器的音质会比您声卡的驱动程序好。

使用 DirectX 播放 Midi 的缺点

  1. 您的软件的每个用户都需要安装 DirectX。在 Windows 95 和 NT 上,他们总是需要更新 DirectX。如果您的应用程序需要 DirectX 7 或 8,Windows 98 和 2000 的用户也需要更新 DirectX。对于使用调制解调器的用户来说,下载 DirectX 是不可接受的(> 12 兆字节)。
  2. MSDN 中 DirectX 的文档太糟糕了!甚至如果您想为旧版本的 DirectX(如 5 或 6)进行开发,以允许 Windows 98 和 2000 的用户在不更新 DirectX 的情况下使用您的应用程序,您在当前的 MSDN 中找不到任何相关信息!Microsoft 已完全删除了旧文档!
  3. 当您的应用程序初始化 DirectX 时,会加载十几到二十几个 DLL,消耗大约 4 兆字节的 RAM。在 Pentium 100 电脑上,加载这些 DLL 需要 3 秒钟。
  4. 播放 Midi 文件后,DirectX **不会**自动释放使用的 Midi 设备。这意味着其他应用程序(例如 WinAmp)无法访问它。您的软件必须负责在播放文件后移除端口。问题在于声音是异步播放的,您不知道 DirectX 何时完成播放。要提前确定 Midi 声音的长度非常复杂,因为 Midi 文件中的速度可能会多次改变。所以您可能需要用计时器以一定的间隔检查声音是否仍在播放(IDirectMusicPerformance::IsPlaying()),然后再移除端口 - 但这很麻烦!
  5. 如果您拥有一家中等或高档声卡(如 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 中的已知错误

  1. 在 Windows NT、2000 和 XP 上,如果 Midi 歌曲不以至少四分音符的休止符开头,则会省略歌曲的第一个音符!(下载中添加的所有 Midi 示例文件都以休止符开头)
  2. 在 Windows NT、2000 和 XP 上,mciSendString("open...") 命令非常缓慢。
  3. 仅在 Windows XP 上,如果 Midi 设备被另一个应用程序占用,mciSendString("open...") 会给出愚蠢的错误消息。(MCIERR_SEQ_NOMIDIPRESENT 而不是 MCIERR_SEQ_PORT_INUSEmciGetErrorString() 将其翻译为“没有 Midi 硬件可用或没有安装驱动程序”!!

另一个使用 MMSystem 接口的 Midi 播放器

在 MSDN 中,您可以找到一个也无需 DirectX 即可播放 Midi 文件的示例代码。(搜索“MidiPlyr”)它使用了 WinMM.DllmidiOutxxx()midiStreamxxx() 接口(WinAmp 也使用此接口)。Windows 2000 和 XP 中的上述错误不会影响 MidiPlyr。它可以在所有平台上完美运行。但这个示例代码**极其**复杂。(且是用纯 C 编写的)

使用 DirectX 8 的 Midi 类

如果您仍然想要一个 DirectX Midi 播放器(还支持 3D 音效、特殊效果等),请从 https://codeproject.org.cn 下载 cMidiMusic (C++) (需要 DirectX 8)

© . All rights reserved.