为什么MIDI音序器无法在Windows 10上播放第二次

时间:2015-08-03 21:48:24

标签: c windows audio midi

多年来,我一直在使用Windows中的高级MIDI界面在我的游戏中播放MIDI文件作为背景音乐。现在我已经从已经升级到Windows 10的几个人那里听说背景音乐将在第一次播放(在启动程序之后),但是一旦完成,它就无法再次启动(或者开始播放下一个MIDI文件) 。我自己还没有安装Windows 10,但是我已经安装了几个调试程序,第二次程序试图启动一个MIDI文件播放,这个调用是在PlayMusic()中进行的:

mciSendCommand(MCIwDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms);

返回值343(MCIERR_SEQ_NOMIDIPRESENT)。然而,如果程序退出并再次启动,则可以使背景MIDI再次播放。

我有两个功能:PlayMusic(char * fname)和StopMusic(),用于启动和停止播放MIDI文件,以及跟踪音乐文件当前是否正在播放的全局变量MusicPlaying。然后,主窗口处理程序处理MM_MCINOTIFY消息,当收到MCI_NOTIFY_SUCCESSFUL时,它会向我的主代码排队通知音乐已完成,然后主代码最终会再次调用(如果有)PlayMusic()重新开始播放文件。这是代码块(MusicPlaying是一个知道MIDI文件是否处于活动状态的全局变量):

//*********************************************************
void StopMusic() {

    if( MusicPlaying ) {
        mciSendCommand(MCIwDeviceID, MCI_STOP, MCI_WAIT, 0);
        mciSendCommand(MCIwDeviceID, MCI_CLOSE, MCI_WAIT, 0);
        MusicPlaying = 0;
    }
}

//**********************************************************************
void PlayMusic(char *pMem) {

    MCIERROR        dwReturn;

    StopMusic();    // stop any previously playing music
    // BuildPath() just adds the appropriate folder info for the file
    BuildPath(pMem, DIR_MUSIC, FALSE);

// Open the device by specifying the device name and device element.
// MCI will attempt to choose the MIDI Mapper as the output port.
    mciOpenParms.dwCallback = 0;
    mciOpenParms.wDeviceID = 0;
    mciOpenParms.lpstrDeviceType = "sequencer";//NULL;
    mciOpenParms.lpstrElementName = (TCHAR *)TmpPath;
    mciOpenParms.lpstrAlias = NULL;
    dwReturn = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_WAIT | MCI_OPEN_ELEMENT, (DWORD)(LPVOID) &mciOpenParms);
    if( dwReturn ) return; // Failed to open device, bail out
// Begin playback. The window procedure function for the parent window
// will be notified with an MM_MCINOTIFY message when playback is
// complete. At that time, the window procedure closes the device.
    MCIwDeviceID = mciOpenParms.wDeviceID;
    mciPlayParms.dwCallback = (DWORD)(hWndMain);
    dwReturn = mciSendCommand(MCIwDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms);
    if( dwReturn ) {        // if error
        mciSendCommand(MCIwDeviceID, MCI_CLOSE, 0, 0);  // close MCI device
        return; // and bail out
    }
    MusicPlaying = 1;
}

并在主窗口消息处理器中,当MIDI文件完成播放时:

case MM_MCINOTIFY:
    //***** SEE "NOTE" BELOW FOR DEBUG CODE INSERTED HERE *****
    // various MIDI messages, we only care about termination
    switch( wParam ) {
     case MCI_NOTIFY_ABORTED:               // value of 4
     case MCI_NOTIFY_FAILURE:
     case MCI_NOTIFY_SUPERSEDED:
         break;
     case MCI_NOTIFY_SUCCESSFUL:            // value of 1
         mciSendCommand(MCIwDeviceID, MCI_CLOSE, 0, 0);
         MusicPlaying = 0;
         AddMsg(KHDR_MUSIC_DONE, 0, 0, 0);       // Queue msg that music finished
         break;
     default:
         break;
    }
    return 0;

正常的事件顺序是:

Main game code calls PlayMusic() to start a MIDI playing
    (Nothing in StopMusic() since nothing's playing first time)
    MCI_OPEN
    MCI_PLAY
    MusicPlaying = 1;
MainWnd MM_MCINOTIFY MCI_NOTIFY_SUCCESSFUL when MIDI finished
    MCI_CLOSE
    MusicPlaying = 0;
    queue KHDR_MUSIC_DONE msg to main game code
Main game code eventually sends another PlayMusic() command

如果某些事情导致主游戏代码过早地停止音乐,那么它会调用StopMusic(),这将会:

MCI_STOP
MCI_CLOSE
MusicPlaying = 0;

注意:(请参阅上面的MM_MCINOTIFY中的“**** SEE'NOTE'...”行)作为调试的一部分,我此时插入了一个MessageBox调用来显示正在接收的通知。使用Win 10的用户看到了与早期版本的Windows完全相同的消息:当发生SUCCESSFUL时,wParam = 1和lParam = 1,当发生ABORTED时,wParam = 4和lParam = 1。但这里是踢球者:那时MessageBox正在发生,然后当用户在MessageBox上点击OK时,MIDI文件重新启动就好了!我的第一个想法是,MessageBox出现并被点击的时间延迟使得MIDI系统有时间“重置”。但是进一步测试,插入代码延迟2秒将“MUSIC_DONE”消息排队到我的主代码对问题没有影响。所以看起来它可能与从我的主窗口到MessageBox窗口的上下文切换有关,而不是任何时间延迟。

我找到了一个网页,讨论了Windows 8上MIDI系统发生的变化,这可能与SOMETHING有关,除了我的MIDI播放代码似乎没有播放第一个文件的麻烦,它只是拒绝玩任何后续的。该页面位于:

http://coolsoft.altervista.org/en/blog/2013/03/what-happened-midi-mapper-windows-8

我还听到一个报告说另一个开发者的另一个游戏程序遇到了同样的问题:它会播放一次背景音乐,然后MIDI系统似乎会打嗝。

所以,大问题:有没有人知道需要做些什么才能让Windows 10顺利播放顺序MIDI文件?

1 个答案:

答案 0 :(得分:0)

首先,我强烈建议您使用Win 10系统进行调试,即使它位于VM中。由于Win 10是免费升级,这应该不是问题。

当您调用mciSendCommand(...,MCI_CLOSE,...)时,请在第三个参数中指定MCI_WAIT。 0在这里无效。也许在Win 10上,这会导致代码不要等到Close完成。