mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
473 lines
14 KiB
473 lines
14 KiB
//*********************************************************************
|
|
//* Microsoft Windows **
|
|
//* Copyright(c) Microsoft Corp., 1995 **
|
|
//*********************************************************************
|
|
|
|
//
|
|
// MIDI.C - Code to play MIDI files
|
|
//
|
|
|
|
// HISTORY:
|
|
//
|
|
// 7/21/95 jeremys Created.
|
|
//
|
|
|
|
#include "all.h"
|
|
#include "midi.h"
|
|
#include "blob.h"
|
|
|
|
const CHAR c_szSequencer[] = "sequencer";
|
|
const CHAR c_szWaveAudio[] = "waveaudio";
|
|
|
|
DWORD PlayAudio_w(struct Mwin * tw,LPSTR lpszFileName,BOOL fMIDI);
|
|
|
|
DWORD PlayMIDIFile(struct Mwin * tw,LPSTR lpszMIDIFileName)
|
|
{
|
|
return PlayAudio_w(tw,lpszMIDIFileName,TRUE);
|
|
|
|
}
|
|
|
|
DWORD PlayWaveFile(struct Mwin * tw,LPSTR lpszWaveFileName)
|
|
{
|
|
return PlayAudio_w(tw,lpszWaveFileName,FALSE);
|
|
}
|
|
|
|
VOID HandleBGSoundRequest(struct Mwin * tw,PLAYSOUNDREQ * pPlaySoundReq)
|
|
{
|
|
if (pPlaySoundReq->dwFileType == BA_TYPE_MIDI) {
|
|
// stop any MIDI playing we are currently doing
|
|
StopBackgroundAudio(tw,SBA_STOP_MIDI);
|
|
if (tw->w3doc && tw->w3doc->pBGSoundInfo) {
|
|
// free memory in tw for filename of previous MIDI file, if any
|
|
if (tw->w3doc->pBGSoundInfo->pszMidiFileName)
|
|
GTR_FREE(tw->w3doc->pBGSoundInfo->pszMidiFileName);
|
|
|
|
// remember the filename of the current MIDI file
|
|
tw->w3doc->pBGSoundInfo->pszMidiFileName =
|
|
pPlaySoundReq->pszFileName;
|
|
tw->w3doc->pBGSoundInfo->nMidiFileLoopsRemaining =
|
|
pPlaySoundReq->nLoops;
|
|
}
|
|
PlayMIDIFile(tw,pPlaySoundReq->pszFileName);
|
|
} else {
|
|
// stop any waveform audio playing we are currently doing
|
|
StopBackgroundAudio(tw,SBA_STOP_WAVEFORM);
|
|
if (tw->w3doc && tw->w3doc->pBGSoundInfo) {
|
|
// free memory in tw for filename of previous sound file, if any
|
|
if (tw->w3doc->pBGSoundInfo->pszSoundFileName)
|
|
GTR_FREE(tw->w3doc->pBGSoundInfo->pszSoundFileName);
|
|
|
|
// remember the filename of the current sound file
|
|
tw->w3doc->pBGSoundInfo->pszSoundFileName =
|
|
pPlaySoundReq->pszFileName;
|
|
tw->w3doc->pBGSoundInfo->nSoundFileLoopsRemaining =
|
|
pPlaySoundReq->nLoops;
|
|
tw->w3doc->pBGSoundInfo->dwWaveType = pPlaySoundReq->dwFileType;
|
|
}
|
|
|
|
PlaySoundFile(tw,pPlaySoundReq->pszFileName,pPlaySoundReq->dwFileType);
|
|
}
|
|
|
|
// free the sound req structure. Note that we don't have to free
|
|
// pPlaySoundReq->pszFileName... that buffer is now pointed to by tw
|
|
// and will get freed when tw is freed.
|
|
GTR_FREE(pPlaySoundReq);
|
|
}
|
|
|
|
DWORD PlayAudio_w(struct Mwin * tw,LPSTR lpszFileName,BOOL fMIDI)
|
|
{
|
|
UINT wDeviceID;
|
|
DWORD dwReturn;
|
|
MCI_OPEN_PARMS mciOpenParms;
|
|
MCI_PLAY_PARMS mciPlayParms;
|
|
|
|
/*
|
|
* Open the device by specifying the
|
|
* device name and device element.
|
|
* MCI will attempt to choose the
|
|
* MIDI Mapper as the output port.
|
|
*/
|
|
mciOpenParms.lpstrDeviceType = (fMIDI ? c_szSequencer : c_szWaveAudio);
|
|
mciOpenParms.lpstrElementName = lpszFileName;
|
|
if (dwReturn = (DWORD) mciSendCommand((MCIDEVICEID) NULL, MCI_OPEN,
|
|
MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
|
|
(DWORD)(LPVOID) &mciOpenParms)) {
|
|
/*
|
|
* Failed to open device;
|
|
* don't close it, just return error.
|
|
*/
|
|
return dwReturn;
|
|
}
|
|
|
|
/* Device opened successfully. Get the device ID. */
|
|
wDeviceID = mciOpenParms.wDeviceID;
|
|
|
|
/*
|
|
* Begin playback. The window procedure function
|
|
* for the parent window is notified with an
|
|
* MM_MCINOTIFY message when playback is complete.
|
|
* The window procedure then closes the device.
|
|
*/
|
|
mciPlayParms.dwCallback = (DWORD) tw->hWndFrame;
|
|
if (dwReturn = mciSendCommand(wDeviceID, MCI_PLAY,
|
|
MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms)) {
|
|
mciSendCommand(wDeviceID, MCI_CLOSE, 0, 0);
|
|
return dwReturn;
|
|
}
|
|
|
|
// remember the device ID so we can close it later if need be
|
|
// (if we change the page or exit the app while still playing)
|
|
if (fMIDI) {
|
|
tw->w3doc->pBGSoundInfo->dwMidiDeviceID = wDeviceID;
|
|
} else {
|
|
tw->w3doc->pBGSoundInfo->dwWaveDeviceID = wDeviceID;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
VOID HandleMciNotify(struct Mwin * tw,DWORD dwFlags,DWORD dwDeviceID)
|
|
{
|
|
|
|
// if this is "success" notification, that means a wave or
|
|
// MIDI file has finished playing. We need to close the device
|
|
// in this case.
|
|
if (dwFlags & MCI_NOTIFY_SUCCESSFUL) {
|
|
BGSOUNDINFO * pBGSoundInfo;
|
|
BOOL fPlayAgain=FALSE;
|
|
|
|
if (tw && tw->w3doc && tw->w3doc->pBGSoundInfo) {
|
|
|
|
pBGSoundInfo = tw->w3doc->pBGSoundInfo;
|
|
|
|
// figure out if this was a wave or MIDI device, update the
|
|
// status in our structure.
|
|
if (dwDeviceID == pBGSoundInfo->dwMidiDeviceID) {
|
|
// if loop counter is > 1, decrement loop counter and
|
|
// repeat. If loop counter is -1 (infinite), repeat
|
|
// without decrementing loop counter. Otherwise
|
|
// don't repeat the sound.
|
|
|
|
if (pBGSoundInfo->nMidiFileLoopsRemaining > 1) {
|
|
pBGSoundInfo->nMidiFileLoopsRemaining --;
|
|
fPlayAgain = TRUE;
|
|
} else if (pBGSoundInfo->nMidiFileLoopsRemaining == -1) {
|
|
fPlayAgain = TRUE;
|
|
} else {
|
|
pBGSoundInfo->dwMidiDeviceID = 0;
|
|
}
|
|
} else if (dwDeviceID == pBGSoundInfo->dwWaveDeviceID) {
|
|
// if loop counter is > 1, decrement loop counter and
|
|
// repeat. If loop counter is -1 (infinite), repeat
|
|
// without decrementing loop counter. Otherwise
|
|
// don't repeat the sound.
|
|
|
|
if (pBGSoundInfo->nSoundFileLoopsRemaining > 1) {
|
|
pBGSoundInfo->nSoundFileLoopsRemaining --;
|
|
fPlayAgain = TRUE;
|
|
} else if (pBGSoundInfo->nSoundFileLoopsRemaining == -1) {
|
|
fPlayAgain = TRUE;
|
|
} else {
|
|
pBGSoundInfo->dwWaveDeviceID = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fPlayAgain) {
|
|
// play the sound/midi file again from the beginning
|
|
MCI_PLAY_PARMS mciPlayParms;
|
|
DWORD dwReturn;
|
|
|
|
mciPlayParms.dwCallback = (DWORD) tw->hWndFrame;
|
|
mciPlayParms.dwFrom = 0;
|
|
if (dwReturn = mciSendCommand(dwDeviceID, MCI_PLAY,
|
|
MCI_FROM | MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms)) {
|
|
mciSendCommand(dwDeviceID, MCI_CLOSE, 0, 0);
|
|
}
|
|
|
|
} else {
|
|
// close the device (stop playing)
|
|
mciSendCommand(dwDeviceID, MCI_CLOSE, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: StopBackgroundAudio
|
|
|
|
SYNOPSIS: Stops any playing background audio of the specified type
|
|
|
|
ENTRY: tw - browser window
|
|
dwFlags - a combination of SBA_* flags specifying that
|
|
wave audio, MIDI playback, or both should be
|
|
stopped.
|
|
|
|
********************************************************************/
|
|
VOID StopBackgroundAudio(struct Mwin * tw,DWORD dwFlags)
|
|
{
|
|
BGSOUNDINFO * pBGSoundInfo;
|
|
|
|
if (!tw || !tw->w3doc || !tw->w3doc->pBGSoundInfo)
|
|
return;
|
|
|
|
pBGSoundInfo = tw->w3doc->pBGSoundInfo;
|
|
|
|
// if "stop MIDI" flags specified and we are playing MIDI, stop now.
|
|
// Do this by closing the open MCI device.
|
|
if ((dwFlags & SBA_STOP_MIDI) && pBGSoundInfo->dwMidiDeviceID) {
|
|
mciSendCommand(pBGSoundInfo->dwMidiDeviceID,MCI_CLOSE,0,0);
|
|
pBGSoundInfo->dwMidiDeviceID = 0;
|
|
}
|
|
|
|
// if "stop wave audio" flags specified and we are playing a
|
|
// sound file, stop now.
|
|
if (dwFlags & SBA_STOP_WAVEFORM) {
|
|
|
|
// if we are playing .WAV file, an MCI device will be open, close it
|
|
if (pBGSoundInfo->dwWaveDeviceID) {
|
|
mciSendCommand(pBGSoundInfo->dwWaveDeviceID,MCI_CLOSE,0,0);
|
|
pBGSoundInfo->dwWaveDeviceID = 0;
|
|
}
|
|
|
|
// if we are playing .AU or .AIFF, send message to window to stop it
|
|
if (pBGSoundInfo->hwndAuAiffPlayer) {
|
|
if (IsWindow(pBGSoundInfo->hwndAuAiffPlayer)) {
|
|
SendMessage(pBGSoundInfo->hwndAuAiffPlayer, WM_COMMAND,
|
|
MAKEWPARAM(IDCANCEL,BN_CLICKED), (LPARAM) 0 );
|
|
}
|
|
pBGSoundInfo->hwndAuAiffPlayer = NULL;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: RestartBackgroundAudio
|
|
|
|
SYNOPSIS: Called when revisiting a page. Background sounds
|
|
that were played on that page are started again.
|
|
|
|
NOTE: Note that all sounds (waveform and MIDI) are restarted
|
|
from the beginning, not where they may have left off.
|
|
|
|
********************************************************************/
|
|
VOID RestartBackgroundAudio(struct Mwin * tw)
|
|
{
|
|
if (!tw || !tw->w3doc || !tw->w3doc->pBGSoundInfo) {
|
|
// nothing to do... bogus page or no background audio for this
|
|
// page.
|
|
return;
|
|
}
|
|
|
|
// restart MIDI sequence if there is one for page
|
|
if (tw->w3doc->pBGSoundInfo->pszMidiFileName) {
|
|
PlayMIDIFile(tw,tw->w3doc->pBGSoundInfo->pszMidiFileName);
|
|
}
|
|
|
|
// restart sound file if there is one for page
|
|
if (tw->w3doc->pBGSoundInfo->pszSoundFileName) {
|
|
PlaySoundFile(tw,tw->w3doc->pBGSoundInfo->pszSoundFileName,
|
|
tw->w3doc->pBGSoundInfo->dwWaveType);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: PlaySoundFile
|
|
|
|
SYNOPSIS: Plays a .wav, .au, or .aiff sound file in the background
|
|
with no UI.
|
|
|
|
ENTRY: tw - browser window
|
|
lpszFileName - local file name of file to play
|
|
dwSoundFileType - a BA_* define indicating the type of file
|
|
|
|
NOTE: .wav is processed differently from .au and .aiff, this
|
|
function abstracts that.
|
|
|
|
********************************************************************/
|
|
VOID PlaySoundFile(struct Mwin * tw,LPSTR lpszFileName,DWORD dwSoundFileType)
|
|
{
|
|
struct SoundInfo *si;
|
|
|
|
// .WAV files are handled by the brief .WAV playing code above. .AU
|
|
// and .AIFF files are more complicated and will be handled by our
|
|
// normal internal player with the UI hidden.
|
|
|
|
// for .WAV files, just call our .WAV playing code and get out
|
|
if (dwSoundFileType == BA_TYPE_WAV) {
|
|
PlayWaveFile(tw,lpszFileName);
|
|
return;
|
|
}
|
|
|
|
// for .AU and .AIFF files, we need to set up a structure to pass to the
|
|
// processing code.
|
|
|
|
if (!(si = GTR_MALLOC(sizeof(struct SoundInfo))))
|
|
goto LError;
|
|
memset(si, 0, sizeof(struct SoundInfo));
|
|
|
|
si->tw_refer = tw;
|
|
si->bNoDeleteFile = TRUE;
|
|
|
|
// hide the player dialog
|
|
si->fHidden = TRUE;
|
|
|
|
// set the number of loops
|
|
if (tw->w3doc && tw->w3doc->pBGSoundInfo) {
|
|
si->nLoopsRemaining = tw->w3doc->pBGSoundInfo->nSoundFileLoopsRemaining;
|
|
}
|
|
|
|
if (!(si->fsOrig = GTR_MALLOC(_MAX_PATH + 1)))
|
|
goto LError;
|
|
strcpy(si->fsOrig,lpszFileName);
|
|
|
|
// call the appropriate procedure to process and play the sound file
|
|
switch (dwSoundFileType) {
|
|
|
|
case BA_TYPE_AU:
|
|
si->type = SOUND_AU;
|
|
AuProcess(si,"");
|
|
break;
|
|
|
|
case BA_TYPE_AIFF:
|
|
si->type = SOUND_AIFF;
|
|
AiffProcess(si,"");
|
|
break;
|
|
}
|
|
|
|
if (tw->w3doc && tw->w3doc->pBGSoundInfo) {
|
|
// remember the window handle of sound player so we can kill it
|
|
// later if necessary
|
|
tw->w3doc->pBGSoundInfo->hwndAuAiffPlayer = si->hwnd;
|
|
}
|
|
|
|
// note that si struct will be freed by sound player once the sound
|
|
// completes. (We are, however, guaranteed that si will be valid when we
|
|
// deference it above.)
|
|
return;
|
|
|
|
LError:
|
|
// free the structure we allocated to set up AuProcess/AiffProcess
|
|
// parameters
|
|
if (si) {
|
|
if (si->fsOrig)
|
|
GTR_FREE(si->fsOrig);
|
|
|
|
GTR_FREE(si);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: HandleBG_AUSoundComplete
|
|
|
|
SYNOPSIS: Called when AU/AIFF sound player window (which is hidden)
|
|
sends message to main window to indicate it's done
|
|
|
|
NOTE: We keep window handle of the player tucked away so we
|
|
can terminate it if user changes pages or such, now
|
|
we need to know it stoped so we don't try to notify a
|
|
non-existent (or totally incorrect!) window to stop later.
|
|
|
|
********************************************************************/
|
|
VOID HandleBGSound_AUComplete(struct Mwin * tw,HWND hwndPlayer)
|
|
{
|
|
// look in our sound info structure for the window handle of sound
|
|
// player
|
|
if (tw && tw->w3doc && tw->w3doc->pBGSoundInfo) {
|
|
|
|
// sound player passes in its window handle with the message.
|
|
// sanity-check that the window handle is the one we expect...
|
|
|
|
if (tw->w3doc->pBGSoundInfo->hwndAuAiffPlayer ==
|
|
hwndPlayer) {
|
|
|
|
// yes, all is kosher. Set our copy of the window handle
|
|
// to NULL so we know there's no background sound playing now.
|
|
tw->w3doc->pBGSoundInfo->hwndAuAiffPlayer = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: BackgroundSoundFile_Callback
|
|
|
|
SYNOPSIS: Called when we are done downloading a file as a result
|
|
of a background sound tag
|
|
|
|
NOTES: param is a pointer to a BGBLOBPARAMS struct we previously
|
|
allocated and must be freed.
|
|
|
|
********************************************************************/
|
|
/*Evil Tables that should be replaced with mime stuff-----------------------*/
|
|
static STI rgSoundExtensions[] = {
|
|
{"WAV", BA_TYPE_WAV},
|
|
{"AU", BA_TYPE_AU},
|
|
{"AIF", BA_TYPE_AIFF},
|
|
{"AIFF", BA_TYPE_AIFF},
|
|
{"MID", BA_TYPE_MIDI}
|
|
};
|
|
#define nSoundExtensions (sizeof(rgSoundExtensions)/sizeof(STI))
|
|
|
|
|
|
DWORD DwValidSoundFile(PCSTR pcszFileName)
|
|
{
|
|
char * pszExt;
|
|
|
|
pszExt = strrchr(pcszFileName,'.');
|
|
if (pszExt)
|
|
{
|
|
pszExt++; // point after the '.'
|
|
return StringTableToIndex(pszExt, rgSoundExtensions, nSoundExtensions, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void BackgroundSoundFile_Callback(struct Mwin* tw, ELEMENT* pel)
|
|
{
|
|
DWORD dwSoundFileType;
|
|
PLAYSOUNDREQ * pPlaySoundReq;
|
|
|
|
// allocate a struct to keep info on player state and name of
|
|
// file, if not already allocated
|
|
if (!tw->w3doc->pBGSoundInfo)
|
|
{
|
|
tw->w3doc->pBGSoundInfo = (BGSOUNDINFO *) GTR_MALLOC(sizeof(BGSOUNDINFO));
|
|
if (!tw->w3doc->pBGSoundInfo)
|
|
return;
|
|
memset(tw->w3doc->pBGSoundInfo,0,sizeof(BGSOUNDINFO));
|
|
}
|
|
|
|
// find the file extension
|
|
if(dwSoundFileType = DwValidSoundFile(pel->pblob->szFileName))
|
|
{
|
|
// we can't start playing right this second, because
|
|
// playing through MCI takes a second or two to start up
|
|
// before the audio starts. We can't block that long in a light-
|
|
// weight thread, so send a message to the main window proc
|
|
// to start sound in the context of a real thread
|
|
|
|
// set up a PLAYSOUNDREQ struct to tell the main thread
|
|
// the file name, file type, etc.
|
|
pPlaySoundReq = GTR_MALLOC(sizeof(*pPlaySoundReq));
|
|
memset(pPlaySoundReq,0,sizeof(*pPlaySoundReq));
|
|
if (!pPlaySoundReq)
|
|
return;
|
|
|
|
pPlaySoundReq->pszFileName = GTR_strdup(pel->pblob->szFileName);
|
|
if (!pPlaySoundReq->pszFileName)
|
|
return;
|
|
|
|
pPlaySoundReq->dwFileType=dwSoundFileType;
|
|
pPlaySoundReq->nLoops = (DWORD) pel->pblob->vp;
|
|
|
|
PostMessage(tw->hWndFrame,WM_START_BGSOUND,0,(LPARAM) pPlaySoundReq);
|
|
}
|
|
}
|
|
|