324 lines
12 KiB
C
324 lines
12 KiB
C
|
/*
|
||
|
sound.c
|
||
|
|
||
|
Level 1 kitchen sink DLL sound driver functions
|
||
|
|
||
|
Copyright (c) Microsoft Corporation 1990. All rights reserved
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include "mmsystem.h"
|
||
|
#include "mmddk.h"
|
||
|
#include "mmsysi.h"
|
||
|
#include "playwav.h"
|
||
|
|
||
|
BOOL WINAPI IsTaskLocked(void); // In Kernel
|
||
|
|
||
|
//
|
||
|
// place sndPlaySound in the _TEXT segment so the entire wave segment
|
||
|
// does not come in if no wave devices are loaded.
|
||
|
//
|
||
|
|
||
|
#pragma alloc_text(_TEXT, sndPlaySound)
|
||
|
|
||
|
static SZCODE szNull[] = "";
|
||
|
static SZCODE szSoundSection[] = "sounds"; // WIN.INI section for sounds
|
||
|
SZCODE szSystemDefault[] = "SystemDefault"; // Name of the default sound
|
||
|
|
||
|
#define SOUNDNAMELEN 128
|
||
|
static HGLOBAL hCurrentSound; // handle to current sound.
|
||
|
|
||
|
extern LPWAVEHDR lpWavHdr; // current playing sound PLAYWAV.C
|
||
|
|
||
|
/****************************************************************************/
|
||
|
|
||
|
static void PASCAL NEAR GetSoundName(
|
||
|
LPCSTR lszSoundName,
|
||
|
LPSTR lszBuffer)
|
||
|
{
|
||
|
OFSTRUCT of;
|
||
|
int i;
|
||
|
|
||
|
//
|
||
|
// if the sound is defined in the [sounds] section of WIN.INI
|
||
|
// get it and remove the description, otherwise assume it is a
|
||
|
// file and qualify it.
|
||
|
//
|
||
|
GetProfileString(szSoundSection, lszSoundName, lszSoundName, lszBuffer, SOUNDNAMELEN);
|
||
|
|
||
|
// remove any trailing text first
|
||
|
|
||
|
for (i = 0; lszBuffer[i] && (lszBuffer[i] != ' ') && (lszBuffer[i] != '\t') && (lszBuffer[i] != ','); i++)
|
||
|
;
|
||
|
lszBuffer[i] = (char)0;
|
||
|
|
||
|
if (OpenFile(lszBuffer, &of, OF_EXIST | OF_READ | OF_SHARE_DENY_NONE) != HFILE_ERROR)
|
||
|
OemToAnsi(of.szPathName, lszBuffer);
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* @doc EXTERNAL
|
||
|
*
|
||
|
* @api BOOL | sndPlaySound | This function plays a waveform
|
||
|
* sound specified by a filename or by an entry in the [sounds] section
|
||
|
* of WIN.INI. If the sound can't be found, it plays the
|
||
|
* default sound specified by the SystemDefault entry in the
|
||
|
* [sounds] section of WIN.INI. If there is no SystemDefault
|
||
|
* entry or if the default sound can't be found, the function
|
||
|
* makes no sound and returns FALSE.
|
||
|
*
|
||
|
* @parm LPCSTR | lpszSoundName | Specifies the name of the sound to play.
|
||
|
* The function searches the [sounds] section of WIN.INI for an entry
|
||
|
* with this name and plays the associated waveform file.
|
||
|
* If no entry by this name exists, then it assumes the name is
|
||
|
* the name of a waveform file. If this parameter is NULL, any
|
||
|
* currently playing sound is stopped.
|
||
|
*
|
||
|
* @parm UINT | wFlags | Specifies options for playing the sound using one
|
||
|
* or more of the following flags:
|
||
|
*
|
||
|
* @flag SND_SYNC | The sound is played synchronously and the
|
||
|
* function does not return until the sound ends.
|
||
|
* @flag SND_ASYNC | The sound is played asynchronously and the
|
||
|
* function returns immediately after beginning the sound. To terminate
|
||
|
* an asynchronously-played sound, call <f sndPlaySound> with
|
||
|
* <p lpszSoundName> set to NULL.
|
||
|
* @flag SND_NODEFAULT | If the sound can't be found, the function
|
||
|
* returns silently without playing the default sound.
|
||
|
* @flag SND_MEMORY | The parameter specified by <p lpszSoundName>
|
||
|
* points to an in-memory image of a waveform sound.
|
||
|
* @flag SND_LOOP | The sound will continue to play repeatedly
|
||
|
* until <f sndPlaySound> is called again with the
|
||
|
* <p lpszSoundName> parameter set to NULL. You must also specify the
|
||
|
* SND_ASYNC flag to loop sounds.
|
||
|
* @flag SND_NOSTOP | If a sound is currently playing, the
|
||
|
* function will immediately return FALSE without playing the requested
|
||
|
* sound.
|
||
|
*
|
||
|
* @rdesc Returns TRUE if the sound is played, otherwise
|
||
|
* returns FALSE.
|
||
|
*
|
||
|
* @comm The sound must fit in available physical memory and be playable
|
||
|
* by an installed waveform audio device driver. The directories
|
||
|
* searched for sound files are, in order: the current directory;
|
||
|
* the Windows directory; the Windows system directory; the directories
|
||
|
* listed in the PATH environment variable; the list of directories
|
||
|
* mapped in a network. See the Windows <f OpenFile> function for
|
||
|
* more information about the directory search order.
|
||
|
*
|
||
|
* If you specify the SND_MEMORY flag, <p lpszSoundName> must point
|
||
|
* to an in-memory image of a waveform sound. If the sound is stored
|
||
|
* as a resource, use <f LoadResource> and <f LockResource> to load
|
||
|
* and lock the resource and get a pointer to it. If the sound is not
|
||
|
* a resource, you must use <f GlobalAlloc> with the GMEM_MOVEABLE and
|
||
|
* GMEM_SHARE flags set and then <f GlobalLock> to allocate and lock
|
||
|
* memory for the sound.
|
||
|
*
|
||
|
* @xref MessageBeep
|
||
|
****************************************************************************/
|
||
|
|
||
|
BOOL WINAPI sndPlaySound(LPCSTR szSoundName, UINT wFlags)
|
||
|
{
|
||
|
//
|
||
|
// !!! quick exit for no wave devices !!!
|
||
|
//
|
||
|
static UINT wTotalWaveOutDevs = (UINT)-1;
|
||
|
|
||
|
if (wTotalWaveOutDevs == -1 ) {
|
||
|
wTotalWaveOutDevs = waveOutGetNumDevs();
|
||
|
}
|
||
|
|
||
|
if (wTotalWaveOutDevs)
|
||
|
return sndPlaySoundI(szSoundName, wFlags);
|
||
|
else
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************/
|
||
|
/*
|
||
|
@doc INTERNAL
|
||
|
|
||
|
@func BOOL | sndPlaySoundI | Internal version of <f>sndPlaySound<d> which
|
||
|
resides in the WAVE segment instead.
|
||
|
|
||
|
If the SND_NOSTOP flag is specifed and a wave file is currently
|
||
|
playing, or if for some reason no mmsystem window is present, the
|
||
|
function returns failure immediately. The first condition ensures
|
||
|
that a current sound is not interrupted if the flag is set. The
|
||
|
second condition is only in case of some start up error in which
|
||
|
the notification window was not created, or mmsystem was not
|
||
|
specified in the [drivers] line, and therefore never loaded.
|
||
|
|
||
|
Next, if the <p>lszSoundName<d> parameter does not represent a memory
|
||
|
file, and it is non-NULL, then it must represent a string. Therefore
|
||
|
the string must be parsed before sending the sound message to the
|
||
|
mmsystem window. This is because the mmsystem window may reside in a
|
||
|
a different task than the task which is calling the function, and
|
||
|
would most likely have a different current directory.
|
||
|
|
||
|
In this case, the parameter is first checked to determine if it
|
||
|
actually contains anything. For some reason a zero length string
|
||
|
was determined to be able to return TRUE from this function, so that
|
||
|
is checked.
|
||
|
|
||
|
Next the string is checked against INI entries, then parsed.
|
||
|
|
||
|
After parsing the sound name, ensure that a task switch only occurs if
|
||
|
the sound is asyncronous (SND_ASYNC), and a previous sound does not
|
||
|
need to be discarded.
|
||
|
|
||
|
If a task switch is needed, first ensure that intertask messages can
|
||
|
be sent by checking to see that this task is not locked, or that the
|
||
|
notification window is in the current task.
|
||
|
|
||
|
@parm LPCSTR | lszSoundName | Specifies the name of the sound to play.
|
||
|
|
||
|
@parm UINT | wFlags | Specifies options for playing the sound.
|
||
|
|
||
|
@rdesc Returns TRUE if the function was successful, else FALSE if an error
|
||
|
occurred.
|
||
|
*/
|
||
|
BOOL FAR PASCAL sndPlaySoundI(LPCSTR lszSoundName, UINT wFlags)
|
||
|
{
|
||
|
BOOL fPlayReturn;
|
||
|
PSTR szSoundName;
|
||
|
|
||
|
V_FLAGS(wFlags, SND_VALID, sndPlaySound, NULL);
|
||
|
|
||
|
if ((wFlags & SND_LOOP) && !(wFlags & SND_ASYNC)) {
|
||
|
LogParamError(ERR_BAD_FLAGS, (FARPROC)sndPlaySound, (LPVOID)(DWORD)wFlags);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (!(wFlags & SND_MEMORY) && lszSoundName)
|
||
|
V_STRING(lszSoundName, 128, FALSE);
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
if (wFlags & SND_MEMORY) {
|
||
|
DPRINTF1("MMSYSTEM: sndPlaySound(%lx)\r\n", lszSoundName);
|
||
|
}
|
||
|
else if (lszSoundName) {
|
||
|
if (wFlags & SND_ASYNC) {
|
||
|
if (wFlags & SND_LOOP) {
|
||
|
DPRINTF1("MMSYSTEM: sndPlaySound(%ls, SND_ASYNC|SND_LOOP)\r\n", lszSoundName);
|
||
|
}
|
||
|
else {
|
||
|
DPRINTF1("MMSYSTEM: sndPlaySound(%ls, SND_ASYNC)\r\n", lszSoundName);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
DPRINTF1("MMSYSTEM: sndPlaySound(%ls, SND_SYNC)\r\n", lszSoundName);
|
||
|
}
|
||
|
else
|
||
|
DOUT("MMSYSTEM: sndPlaySound(NULL)\r\n");
|
||
|
|
||
|
#endif //ifdef DEBUG
|
||
|
|
||
|
if (((wFlags & SND_NOSTOP) && lpWavHdr) || !hwndNotify)
|
||
|
return FALSE;
|
||
|
|
||
|
if (!(wFlags & SND_MEMORY) && lszSoundName) {
|
||
|
if (!*lszSoundName)
|
||
|
return TRUE;
|
||
|
if (!(szSoundName = (PSTR)LocalAlloc(LMEM_FIXED, SOUNDNAMELEN)))
|
||
|
return FALSE;
|
||
|
GetSoundName(lszSoundName, szSoundName);
|
||
|
lszSoundName = (LPCSTR)szSoundName;
|
||
|
} else
|
||
|
szSoundName = NULL;
|
||
|
|
||
|
if (!(wFlags & SND_ASYNC) && !lpWavHdr)
|
||
|
fPlayReturn = sndMessage((LPSTR)lszSoundName, wFlags);
|
||
|
else {
|
||
|
if (!IsTaskLocked() || (GetWindowTask(hwndNotify) == GetCurrentTask())) {
|
||
|
fPlayReturn = (BOOL)(LONG)SendMessage(hwndNotify, MM_SND_PLAY, (WPARAM)wFlags, (LPARAM)lszSoundName);
|
||
|
} else
|
||
|
fPlayReturn = FALSE;
|
||
|
}
|
||
|
if (szSoundName)
|
||
|
LocalFree((HLOCAL)szSoundName);
|
||
|
|
||
|
return fPlayReturn;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************/
|
||
|
static BOOL PASCAL NEAR SetCurrentSound(
|
||
|
LPCSTR lszSoundName)
|
||
|
{
|
||
|
HGLOBAL hSound;
|
||
|
BOOL f;
|
||
|
LPSTR lp;
|
||
|
|
||
|
if (hCurrentSound && (lp = GlobalLock(hCurrentSound))) {
|
||
|
f = lstrcmpi(lszSoundName, lp + sizeof(WAVEHDR)) == 0;
|
||
|
GlobalUnlock(hCurrentSound);
|
||
|
if (f)
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
DPRINTF(("MMSYSTEM: soundLoadFile(%ls)\r\n",lszSoundName));
|
||
|
|
||
|
if (hSound = soundLoadFile(lszSoundName)) {
|
||
|
soundFree(hCurrentSound);
|
||
|
hCurrentSound = hSound;
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
/****************************************************************************/
|
||
|
/*
|
||
|
@doc INTERNAL
|
||
|
|
||
|
@func BOOL | sndMessage | This function is called in response to an
|
||
|
MM_SND_PLAY message sent to the mmsystem window, and attempts to
|
||
|
play the specified file, or dump current sound caching.
|
||
|
|
||
|
If <p>lszSoundName<d> is NULL, any currently cached sound is
|
||
|
discarded, and the function returns success.
|
||
|
|
||
|
If the SND_MEMORY flag is set, then <p>lszSoundName<d> actually
|
||
|
points to a buffer containing a RIFF format WAVE memory file, and
|
||
|
the function attempts to play it. The load function performs
|
||
|
validation on this memory file. Unlike playing sound names,
|
||
|
memory files are not cached for future use.
|
||
|
|
||
|
Otherwise the <p>lszSoundName<d> parameter is actually an INI entry
|
||
|
or file name. The function initially attempts to load that sound,
|
||
|
and if it fails, attempts to load the system default sound. Note of
|
||
|
course that the SND_NODEFAULT flag is first checked to determine if
|
||
|
the default sound is to be played when the original name cannot be
|
||
|
located. If no default is wanted, or the default cannot be located,
|
||
|
the function returns failure. Note that in calling <f>GetSoundName<d>,
|
||
|
the <p>lszSoundName<d> parameter is modified. This function assumes
|
||
|
that the parameter passed has been previously allocated if a string is
|
||
|
passed to this function, and is not the actual user's parameter passed
|
||
|
to <f>sndPlaySound<d>.
|
||
|
|
||
|
@parm LPSTR | lszSoundName | Specifies the name of the sound to play.
|
||
|
|
||
|
@parm UINT | wFlags | Specifies options for playing the sound.
|
||
|
|
||
|
@rdesc Returns TRUE if the function was successful, else FALSE if an error
|
||
|
occurred.
|
||
|
*/
|
||
|
BOOL FAR PASCAL sndMessage(LPSTR lszSoundName, UINT wFlags)
|
||
|
{
|
||
|
if (!lszSoundName) {
|
||
|
soundFree(hCurrentSound);
|
||
|
hCurrentSound = NULL;
|
||
|
return TRUE;
|
||
|
}
|
||
|
if (wFlags & SND_MEMORY) {
|
||
|
soundFree(hCurrentSound);
|
||
|
hCurrentSound = soundLoadMemory(lszSoundName);
|
||
|
} else if (!SetCurrentSound(lszSoundName)) {
|
||
|
if (wFlags & SND_NODEFAULT)
|
||
|
return FALSE;
|
||
|
GetSoundName(szSystemDefault, lszSoundName);
|
||
|
if (!SetCurrentSound(lszSoundName))
|
||
|
return FALSE;
|
||
|
}
|
||
|
return soundPlay(hCurrentSound, wFlags);
|
||
|
}
|