mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-05 00:02:37 +00:00
[OS/2] Bug 509330 - rewrite nsSound, r=pweilbacher
This commit is contained in:
parent
a7cca2c611
commit
4872835c4b
369
widget/src/os2/MozSounds.cmd
Normal file
369
widget/src/os2/MozSounds.cmd
Normal file
@ -0,0 +1,369 @@
|
||||
/* mozillasounds.cmd */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is a supplemental OS/2 script.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Rich Walsh <dragtext@e-vertise.com>
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
/* MozSounds.cmd enables the user to associate sounds with Mozilla events */
|
||||
/* using the WPS Sound object in the System Setup folder. It does not set */
|
||||
/* the sounds itself - it simply adds entries to the Sound object's list of */
|
||||
/* events. This script only needs to be run once or twice: the first time */
|
||||
/* to enable selected sounds, and the second time to disable most of them */
|
||||
/* because they're so annoying. */
|
||||
/* */
|
||||
/* This script's design is coordinated with code in widget\os2\nsSound.cpp. */
|
||||
/* Please don't make significant changes to it (e.g. changing the names of */
|
||||
/* ini-file entries) without first examining nsSound.cpp. */
|
||||
/* */
|
||||
/* Note to Translators: everything that needs to be translated has been */
|
||||
/* grouped together and placed toward the end of the file (a heading */
|
||||
/* identifies where to start). Please preserve the formatting and don't */
|
||||
/* change any of the numeric values. Thanks... */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
|
||||
call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
|
||||
call SysLoadFuncs
|
||||
call InitVariables
|
||||
call Main
|
||||
exit
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
Main:
|
||||
|
||||
/* Get the location of the MMOS2 directory. */
|
||||
path = value('MMBASE',,'OS2ENVIRONMENT')
|
||||
if path = '' then do
|
||||
call SysCls
|
||||
call EnvError
|
||||
return
|
||||
end
|
||||
path = strip(path, B, ';')
|
||||
|
||||
/* Confirm that mmpm.ini can be found where we expect it to be. */
|
||||
iniFile = path'\MMPM.INI'
|
||||
call SysFileTree iniFile, 'stem', 'FO'
|
||||
if stem.0 <> 1 then do
|
||||
call SysCls
|
||||
call IniError
|
||||
return
|
||||
end
|
||||
|
||||
/* Make a backup of mmpm.ini if one doesn't already exist. */
|
||||
call SysFileTree iniFile'.BAK', 'stem', 'FO'
|
||||
if stem.0 = 0 then
|
||||
'@xcopy' iniFile path'\*.*.BAK /T > NUL'
|
||||
|
||||
/* Events are identified by number - MMOS2 uses 0-12. Mozilla events */
|
||||
/* start at 800. If this conflicts with another app, the base index */
|
||||
/* can be changed. The new value is stored in mmpm.ini where it can */
|
||||
/* be accessed by the Mozilla apps and this script. */
|
||||
baseIndex = SysIni(iniFile, 'MOZILLA_Events', 'BaseIndex')
|
||||
baseIndex = strip(baseIndex, T, X2C('0'))
|
||||
if baseIndex = 'ERROR:' | baseIndex <= '12' then
|
||||
baseIndex = defaultIndex
|
||||
|
||||
/* The main loop: display current status & respond to commands */
|
||||
do FOREVER
|
||||
call SysCls
|
||||
call GetStatus
|
||||
call Display
|
||||
pull cmd parms
|
||||
|
||||
select
|
||||
when cmd = soundCmd then do
|
||||
rc = SysOpenObject('<WP_SOUND>', 2, 'TRUE')
|
||||
leave
|
||||
end
|
||||
|
||||
when cmd = exitCmd then do
|
||||
leave
|
||||
end
|
||||
|
||||
when cmd = disableCmd then do
|
||||
call Disable
|
||||
end
|
||||
|
||||
when cmd = enableCmd then do
|
||||
call Enable
|
||||
end
|
||||
|
||||
/* this command is "undocumented" & should NOT be translated */
|
||||
when cmd = 'BASEINDEX' then do
|
||||
call ChangeBaseIndex
|
||||
end
|
||||
|
||||
otherwise
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Generate a listing of Mozilla events and their status: */
|
||||
/* 'enabled' if there's an entry, 'disabled' if not. */
|
||||
|
||||
GetStatus:
|
||||
|
||||
ctr = 1
|
||||
show = 0
|
||||
|
||||
do nbr = 1 to events.0
|
||||
rc = SysIni(iniFile, 'MMPM2_AlarmSounds', baseIndex + events.nbr.ndx)
|
||||
if rc = 'ERROR:' then
|
||||
status = disabledWord
|
||||
else
|
||||
status = enabledWord
|
||||
|
||||
if show = 0 then do
|
||||
out = " "nbr". " left(events.nbr.name, 15) status
|
||||
show = 1
|
||||
end
|
||||
else do
|
||||
line.ctr = left(out, 35) " " nbr". " left(events.nbr.name, 15) status
|
||||
ctr = ctr + 1
|
||||
show = 0
|
||||
end
|
||||
end
|
||||
|
||||
if show = 1 then
|
||||
line.ctr = left(out, 35)
|
||||
|
||||
return
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Disable an event sound by deleting its entry. */
|
||||
|
||||
Disable:
|
||||
|
||||
parse var parms nbr parms
|
||||
|
||||
if nbr = allWord then do
|
||||
nbr = '1'
|
||||
parms = '2 3 4 5 6 7'
|
||||
end
|
||||
|
||||
do while nbr <> ''
|
||||
if nbr >= '1' & nbr <= '7' then do
|
||||
key = baseIndex + events.nbr.ndx
|
||||
rc = SysIni(iniFile, 'MMPM2_AlarmSounds', key, 'DELETE:')
|
||||
end
|
||||
parse var parms nbr parms
|
||||
end
|
||||
|
||||
return
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Enable an event sound by adding an entry whose format is: */
|
||||
/* 'fq_soundfile#event_name#volume'. */
|
||||
/* Since this script isn't intended to set the actual soundfile, */
|
||||
/* it uses the same dummy value as MMOS2: 'x:\MMOS2\SOUNDS\' */
|
||||
|
||||
Enable:
|
||||
|
||||
parse var parms nbr parms
|
||||
|
||||
if nbr = allWord then do
|
||||
nbr = '1'
|
||||
parms = '2 3 4 5 6 7'
|
||||
end
|
||||
|
||||
do while nbr <> ''
|
||||
if nbr >= '1' & nbr <= '7' then do
|
||||
key = baseIndex + events.nbr.ndx
|
||||
|
||||
/* if there's an existing entry, preserve the filename */
|
||||
sndFile = SysIni(iniFile, 'MMPM2_AlarmSounds', key)
|
||||
if sndFile = 'ERROR:' | left(sndFile, 1) = '#' then
|
||||
sndFile = path'\'soundsDir'\'
|
||||
else
|
||||
parse var sndFile sndFile '#' .
|
||||
value = sndFile'#'events.nbr.name' (Mozilla)#80'X2C('0')
|
||||
|
||||
rc = SysIni(iniFile, 'MMPM2_AlarmSounds', key, value)
|
||||
end
|
||||
parse var parms nbr parms
|
||||
end
|
||||
|
||||
return
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* An "undocumented" function to change & restore the base index. */
|
||||
/* It renumbers existing entries using the new base & adds or deletes */
|
||||
/* a 'MOZILLA_Events\BaseIndex' entry depending on the new value. */
|
||||
|
||||
ChangeBaseIndex:
|
||||
|
||||
parse var parms newIndex parms
|
||||
|
||||
/* Ignore invalid values. */
|
||||
if newIndex = '' | newIndex <= '12' then
|
||||
return
|
||||
|
||||
/* Do NOT translate this. */
|
||||
if newIndex = 'DEFAULT' then
|
||||
newIndex = defaultIndex
|
||||
|
||||
/* If there's no change, exit after deleting the entry if it's the default */
|
||||
if newIndex = baseIndex then do
|
||||
if baseIndex = defaultIndex then
|
||||
rc SysIni(iniFile, 'MOZILLA_Events', 'BaseIndex', 'DELETE:')
|
||||
return
|
||||
end
|
||||
|
||||
/* Move existing entries from the old index to the new index. */
|
||||
do nbr = 1 to events.0
|
||||
value = SysIni(iniFile, 'MMPM2_AlarmSounds', baseIndex + events.nbr.ndx)
|
||||
if value = 'ERROR:' then
|
||||
iterate
|
||||
|
||||
rc = SysIni(iniFile, 'MMPM2_AlarmSounds', baseIndex + events.nbr.ndx, 'DELETE:')
|
||||
rc = SysIni(iniFile, 'MMPM2_AlarmSounds', newIndex + events.nbr.ndx, value)
|
||||
end
|
||||
|
||||
/* If the new index is the default, delete the ini entry; */
|
||||
/* otherwise, add or update the entry with the new value. */
|
||||
if newIndex = defaultIndex then
|
||||
rc = SysIni(iniFile, 'MOZILLA_Events', 'BaseIndex', 'DELETE:')
|
||||
else
|
||||
rc = SysIni(iniFile, 'MOZILLA_Events', 'BaseIndex', newIndex||X2C('0'))
|
||||
|
||||
baseIndex = newIndex
|
||||
|
||||
return
|
||||
|
||||
/*****************************************************************************/
|
||||
/* All strings that need to be translated appear below */
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Just like it says, init variables. */
|
||||
|
||||
InitVariables:
|
||||
|
||||
events.0 = 7
|
||||
events.1.ndx = 0
|
||||
events.1.name = 'New Mail'
|
||||
events.2.ndx = 1
|
||||
events.2.name = 'Alert Dialog'
|
||||
events.3.ndx = 2
|
||||
events.3.name = 'Confirm Dialog'
|
||||
events.4.ndx = 3
|
||||
events.4.name = 'Prompt Dialog'
|
||||
events.5.ndx = 4
|
||||
events.5.name = 'Select Dialog'
|
||||
events.6.ndx = 5
|
||||
events.6.name = 'Menu Execute'
|
||||
events.7.ndx = 6
|
||||
events.7.name = 'Menu Popup'
|
||||
|
||||
enabledWord = 'enabled'
|
||||
disabledWord = 'disabled'
|
||||
allWord = 'ALL'
|
||||
soundsDir = 'SOUNDS'
|
||||
|
||||
enableCmd = 'E'
|
||||
disableCmd = 'D'
|
||||
soundCmd = 'S'
|
||||
exitCmd = 'X'
|
||||
|
||||
defaultIndex = '800'
|
||||
|
||||
return
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Display event status & command info. */
|
||||
|
||||
Display:
|
||||
|
||||
say
|
||||
say " MozSounds.cmd lets you use the WPS Sound object in your System Setup"
|
||||
say " folder to assign sounds to the Mozilla events listed below. It does"
|
||||
say " NOT set the sound itself - it just adds and deletes entries in Sound."
|
||||
say " New or changed sounds take effect after the Mozilla app is restarted."
|
||||
say
|
||||
say " Event Status Event Status"
|
||||
say " ----------- -------- ----------- --------"
|
||||
say line.1
|
||||
say line.2
|
||||
say line.3
|
||||
say line.4
|
||||
say
|
||||
say " Commands:"
|
||||
say " E - enable event sound(s) example: 'E 1' or 'E 3 5 7' or 'E All'"
|
||||
say " D - disable event sound(s) example: 'D 2' or 'D 1 4 6' or 'D All'"
|
||||
say " S - open the WPS Sound object"
|
||||
say " X - exit this script"
|
||||
say
|
||||
call charout ," Enter a command > "
|
||||
|
||||
return
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Display an error message */
|
||||
|
||||
EnvError:
|
||||
|
||||
say ""
|
||||
say "ERROR: the 'MMBASE' environment variable is missing or invalid!"
|
||||
say " Your Mozilla app won't be able to play system sounds without it."
|
||||
say " Please add a line to config.sys like the following, then reboot."
|
||||
say " SET MMBASE=x:\MMOS2 (where 'x' is the correct drive)"
|
||||
|
||||
return
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Display an error message */
|
||||
|
||||
IniError:
|
||||
|
||||
say ""
|
||||
say "ERROR: file '"iniFile"' is missing or invalid!"
|
||||
say " Your Mozilla app won't be able to play system sounds without it."
|
||||
say " Please confirm that the 'SET MMBASE=' line in config.sys points"
|
||||
say " at your MMOS2 directory and that it contains 'MMPM.INI'."
|
||||
|
||||
return
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -27,6 +27,7 @@
|
||||
* IBM Corp.
|
||||
* Peter Weilbacher <mozilla@Weilbacher.org>
|
||||
* Lars Erdmann
|
||||
* Rich Walsh <dragtext@e-vertise.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -42,369 +43,114 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nscore.h"
|
||||
#include "plstr.h"
|
||||
/*****************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define INCL_DOS
|
||||
#define INCL_DOSERRORS
|
||||
#define INCL_WINSHELLDATA
|
||||
#define INCL_MMIOOS2
|
||||
#include <os2.h>
|
||||
#include <mmioos2.h>
|
||||
#include <mcios2.h>
|
||||
#define MCI_ERROR_LENGTH 128
|
||||
|
||||
#include "nsSound.h"
|
||||
#include "nsIURL.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
|
||||
#include "nsNativeCharsetUtils.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS2(nsSound, nsISound, nsIStreamLoaderObserver)
|
||||
|
||||
static int sInitialized = 0;
|
||||
static PRBool sMMPMInstalled = PR_FALSE;
|
||||
static HMODULE sHModMMIO = NULLHANDLE;
|
||||
|
||||
// function pointer definitions, include underscore (work around redef. warning)
|
||||
HMMIO (*APIENTRY _mmioOpen)(PSZ, PMMIOINFO, ULONG);
|
||||
USHORT (*APIENTRY _mmioClose)(HMMIO, USHORT);
|
||||
ULONG (*APIENTRY _mmioGetFormats)(PMMFORMATINFO, LONG, PVOID, PLONG, ULONG, ULONG);
|
||||
ULONG (*APIENTRY _mciSendCommand)(USHORT, USHORT, ULONG, PVOID, USHORT);
|
||||
#ifdef DEBUG
|
||||
ULONG (*APIENTRY _mmioGetLastError)(HMMIO);
|
||||
ULONG (*APIENTRY _mmioQueryFormatCount)(PMMFORMATINFO, PLONG, ULONG, ULONG);
|
||||
ULONG (*APIENTRY _mmioGetFormatName)(PMMFORMATINFO, PSZ, PLONG, ULONG, ULONG);
|
||||
ULONG (*APIENTRY _mciGetErrorString)(ULONG, PSZ, USHORT);
|
||||
#endif
|
||||
ULONG (*APIENTRY _mmioIniFileHandler)(PMMINIFILEINFO, ULONG);
|
||||
/*****************************************************************************/
|
||||
|
||||
// argument structure to pass to the background thread
|
||||
typedef struct _ARGBUFFER
|
||||
{
|
||||
HEV hev;
|
||||
PRUint32 bufLen;
|
||||
const char *buffer;
|
||||
PSZ pszFilename;
|
||||
PRUint32 bufLen;
|
||||
char buffer[1];
|
||||
} ARGBUFFER;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void InitGlobals(void)
|
||||
{
|
||||
ULONG ulrc = 0;
|
||||
char LoadError[CCHMAXPATH];
|
||||
HMODULE hModMDM = NULLHANDLE;
|
||||
|
||||
ulrc = DosLoadModule(LoadError, CCHMAXPATH, "MMIO", &sHModMMIO);
|
||||
ulrc += DosLoadModule(LoadError, CCHMAXPATH, "MDM", &hModMDM);
|
||||
if (ulrc == NO_ERROR) {
|
||||
#ifdef DEBUG
|
||||
printf("InitGlobals: MMOS2 is installed, both DLLs loaded\n");
|
||||
#endif
|
||||
sMMPMInstalled = PR_TRUE;
|
||||
// MMOS2 is installed, so we can query the necessary functions
|
||||
// mmio functions are in MMIO.DLL
|
||||
ulrc = DosQueryProcAddr(sHModMMIO, 0L, "mmioOpen", (PFN *)&_mmioOpen);
|
||||
ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioClose", (PFN *)&_mmioClose);
|
||||
ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioGetFormats", (PFN *)&_mmioGetFormats);
|
||||
// mci functions are in MDM.DLL
|
||||
ulrc += DosQueryProcAddr(hModMDM, 0L, "mciSendCommand", (PFN *)&_mciSendCommand);
|
||||
#ifdef DEBUG
|
||||
ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioGetLastError", (PFN *)&_mmioGetLastError);
|
||||
ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioQueryFormatCount", (PFN *)&_mmioQueryFormatCount);
|
||||
ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioGetFormatName", (PFN *)&_mmioGetFormatName);
|
||||
ulrc += DosQueryProcAddr(hModMDM, 0L, "mciGetErrorString", (PFN *)&_mciGetErrorString);
|
||||
#define DBG_MSG(x) fprintf(stderr, x "\n")
|
||||
#else
|
||||
#define DBG_MSG(x)
|
||||
#endif
|
||||
|
||||
ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioIniFileHandler", (PFN *)&_mmioIniFileHandler);
|
||||
// the number of defined Mozilla events (see nsISound.idl)
|
||||
#define EVENT_CNT 7
|
||||
|
||||
// if one of these failed, we have some kind of non-functional MMOS2 installation
|
||||
if (ulrc != NO_ERROR) {
|
||||
NS_WARNING("MMOS2 is installed, but seems to have corrupt DLLs");
|
||||
sMMPMInstalled = PR_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*****************************************************************************/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// static variables
|
||||
static PRBool sDllError = FALSE; // set if the MMOS2 dlls fail to load
|
||||
static char * sSoundFiles[EVENT_CNT] = {0}; // an array of sound file names
|
||||
|
||||
// Tries to determine the data format in the buffer using file "magic"
|
||||
// and a loop through MMOS2 audio codecs.
|
||||
// Returns the FourCC handle for the format, or 0 when failing to find format
|
||||
// and codec.
|
||||
FOURCC determineFourCC(PRUint32 aDataLen, const char *aData)
|
||||
{
|
||||
FOURCC fcc = 0;
|
||||
// function pointer definitions (underscore works around redef. warning)
|
||||
static HMMIO (*APIENTRY _mmioOpen)(PSZ, PMMIOINFO, ULONG);
|
||||
static USHORT (*APIENTRY _mmioClose)(HMMIO, USHORT);
|
||||
static ULONG (*APIENTRY _mciSendCommand)(USHORT, USHORT, ULONG, PVOID, USHORT);
|
||||
|
||||
// Start to compare the first bytes of the data with magic to determine the
|
||||
// most likely format upfront.
|
||||
if (memcmp(aData, "RIFF", 4) == 0) { // WAV
|
||||
fcc = mmioFOURCC('W', 'A', 'V', 'E');
|
||||
} else if (memcmp(aData, "ID3", 3) == 0 || // likely MP3 with ID3 header
|
||||
((aData[0] & 0xFF) == 0xFF && // various versions of MPEG layer 3
|
||||
((aData[1] & 0xFE) == 0xFA || // v1
|
||||
(aData[1] & 0xFE) == 0xF2 || // v2
|
||||
(aData[1] & 0xFE) == 0xE2))) // v2.5
|
||||
{
|
||||
fcc = mmioFOURCC('M','P','3',' ');
|
||||
} else if (memcmp(aData, "OggS", 4) == 0) { // OGG
|
||||
fcc = mmioFOURCC('O','G','G','S');
|
||||
} else if (memcmp(aData, "fLaC", 4) == 0) { // FLAC
|
||||
fcc = mmioFOURCC('f','L','a','C');
|
||||
}
|
||||
// helper functions
|
||||
static void initSounds(void);
|
||||
static PRBool initDlls(void);
|
||||
static void playSound(void *aArgs);
|
||||
static FOURCC determineFourCC(PRUint32 aDataLen, const char *aData);
|
||||
|
||||
// The following is too flakey because several OS/2 IOProc don't behave as
|
||||
// they should and would cause us to crash. So just skip this for now...
|
||||
#if 0
|
||||
if (fcc) // already found one
|
||||
return fcc;
|
||||
/*****************************************************************************/
|
||||
/* nsSound implementation */
|
||||
/*****************************************************************************/
|
||||
|
||||
// None of the popular formats found, so use the list of MMOS2 audio codecs to
|
||||
// find one that can open the file.
|
||||
MMFORMATINFO mmfi;
|
||||
LONG lNum;
|
||||
memset(&mmfi, '\0', sizeof(mmfi));
|
||||
mmfi.ulStructLen = sizeof(mmfi);
|
||||
mmfi.ulMediaType |= MMIO_MEDIATYPE_AUDIO;
|
||||
ULONG ulrc = _mmioQueryFormatCount(&mmfi, &lNum, 0L, 0L);
|
||||
|
||||
PMMFORMATINFO mmflist = (PMMFORMATINFO)calloc(lNum, sizeof(MMFORMATINFO));
|
||||
LONG lFormats;
|
||||
ulrc = _mmioGetFormats(&mmfi, lNum, mmflist, &lFormats, 0L, 0L);
|
||||
|
||||
MMIOINFO mi;
|
||||
memset(&mi, '\0', sizeof(mi));
|
||||
mi.fccChildIOProc = FOURCC_MEM;
|
||||
unsigned char szBuffer[sizeof(FOURCC) + CCHMAXPATH + 4];
|
||||
for (int i = lFormats-1; i >= 0; i--) {
|
||||
// Loop through formats. Do it backwards to find at least WAV before the
|
||||
// faulty VORBIS/FLAC/MP3 IOProcs that will open any format.
|
||||
MMFORMATINFO mmfi = mmflist[i];
|
||||
#ifdef DEBUG
|
||||
LONG lBytesRead;
|
||||
_mmioGetFormatName(&mmfi, (char *)szBuffer, &lBytesRead, 0L, 0L);
|
||||
printf("determineFour Codec %d: name=%s media=0x%lx ext=%s fcc=%c%c%c%c/%ld/%p\n",
|
||||
i, szBuffer, mmfi.ulMediaType, mmfi.szDefaultFormatExt,
|
||||
(char)(mmfi.fccIOProc), (char)(mmfi.fccIOProc >> 8),
|
||||
(char)(mmfi.fccIOProc >> 16), (char)(mmfi.fccIOProc >> 24),
|
||||
mmfi.fccIOProc, (void *)mmfi.fccIOProc);
|
||||
#endif
|
||||
|
||||
// this codec likely crashes the program when the buffer is not of the
|
||||
// expected format
|
||||
if (mmfi.fccIOProc == mmioFOURCC('A','V','C','A')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mi.fccIOProc = mmfi.fccIOProc;
|
||||
HMMIO hmmio= _mmioOpen(NULL, &mi, MMIO_READ);
|
||||
if (hmmio) {
|
||||
fcc = mmfi.fccIOProc;
|
||||
_mmioClose(hmmio, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(mmflist);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("determineFourCC: Codec fcc is 0x%lx or --%c%c%c%c--\n", fcc,
|
||||
(char)(fcc), (char)(fcc >> 8), (char)(fcc >> 16), (char)(fcc >> 24));
|
||||
#endif
|
||||
|
||||
return fcc;
|
||||
}
|
||||
|
||||
// Play the sound that was set up in the argument structure. If an error occurs,
|
||||
// beep at least. To be used as function for a new background thread.
|
||||
static void playSound(void *aArgs)
|
||||
{
|
||||
ULONG ulrc = NO_ERROR;
|
||||
ARGBUFFER args;
|
||||
memcpy(&args, aArgs, sizeof(args));
|
||||
|
||||
MMIOINFO mi;
|
||||
memset(&mi, '\0', sizeof(mi));
|
||||
HMMIO hmmio = NULLHANDLE;
|
||||
|
||||
do { // inner block (break in case of error)
|
||||
if (args.pszFilename) {
|
||||
// determine size of file that we want to read
|
||||
FILESTATUS3 fs3;
|
||||
memset(&fs3, '\0', sizeof(fs3));
|
||||
ulrc = DosQueryPathInfo(args.pszFilename, FIL_STANDARD, &fs3, sizeof(fs3));
|
||||
mi.cchBuffer = fs3.cbFile;
|
||||
} else {
|
||||
// use size of the existing buffer
|
||||
mi.cchBuffer = args.bufLen;
|
||||
}
|
||||
|
||||
// Read or copy the sound into a local memory buffer for easy playback.
|
||||
// (If we got the sound in a buffer originally, that buffer could
|
||||
// "disappear" while we are still playing it.)
|
||||
ulrc = DosAllocMem((PPVOID)&mi.pchBuffer, mi.cchBuffer,
|
||||
#ifdef OS2_HIGH_MEMORY /* only if compiled with high-memory */
|
||||
OBJ_ANY | /* support, we can allocate anywhere! */
|
||||
#endif
|
||||
PAG_READ | PAG_WRITE | PAG_COMMIT);
|
||||
if (ulrc != NO_ERROR) {
|
||||
#ifdef DEBUG
|
||||
printf("playSound: Could not allocate the sound buffer, ulrc=%ld\n", ulrc);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
if (args.pszFilename) {
|
||||
// read the sound from file into memory
|
||||
HFILE hf = NULLHANDLE;
|
||||
ULONG ulAction = 0;
|
||||
ulrc = DosOpen(args.pszFilename, &hf, &ulAction, 0, FILE_NORMAL,
|
||||
OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
|
||||
OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE,
|
||||
NULL);
|
||||
if (ulrc != NO_ERROR) {
|
||||
#ifdef DEBUG
|
||||
printf("playSound: could not open the sound file \"%s\" (%ld)\n",
|
||||
args.pszFilename, ulrc);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
ULONG ulRead = 0;
|
||||
ulrc = DosRead(hf, mi.pchBuffer, mi.cchBuffer, &ulRead);
|
||||
DosClose(hf);
|
||||
if (ulrc != NO_ERROR) {
|
||||
#ifdef DEBUG
|
||||
printf("playSound: read %ld of %ld bytes from the sound file \"%s\" (%ld)\n",
|
||||
ulRead, mi.cchBuffer, args.pszFilename, ulrc);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// copy the passed sound buffer into local memory
|
||||
memcpy(mi.pchBuffer, args.buffer, args.bufLen);
|
||||
}
|
||||
|
||||
DosPostEventSem(args.hev); // calling thread can continue
|
||||
|
||||
// Now the sound is loaded into memory in any case, play it from there.
|
||||
mi.fccChildIOProc = FOURCC_MEM;
|
||||
mi.fccIOProc = determineFourCC(mi.cchBuffer, mi.pchBuffer);
|
||||
if (!mi.fccIOProc) {
|
||||
NS_WARNING("playSound: unknown sound format in memory buffer");
|
||||
break;
|
||||
}
|
||||
mi.ulTranslate = MMIO_TRANSLATEDATA | MMIO_TRANSLATEHEADER;
|
||||
hmmio = _mmioOpen(NULL, &mi, MMIO_READ | MMIO_DENYWRITE);
|
||||
|
||||
if (!hmmio) {
|
||||
#ifdef DEBUG
|
||||
ULONG ulrc = _mmioGetLastError(hmmio);
|
||||
if (args.pszFilename) {
|
||||
printf("playSound: mmioOpen failed, cannot play sound from \"%s\" (%ld)\n",
|
||||
args.pszFilename, ulrc);
|
||||
} else {
|
||||
printf("playSound: mmioOpen failed, cannot play sound buffer (%ld)\n",
|
||||
ulrc);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
// open the sound device
|
||||
MCI_OPEN_PARMS mop;
|
||||
memset(&mop, '\0', sizeof(mop));
|
||||
mop.pszElementName = (PSZ)hmmio;
|
||||
mop.pszDeviceType = (PSZ)MAKEULONG(MCI_DEVTYPE_WAVEFORM_AUDIO, 0);
|
||||
ulrc = _mciSendCommand(0, MCI_OPEN,
|
||||
MCI_OPEN_MMIO | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE | MCI_WAIT,
|
||||
(PVOID)&mop, 0);
|
||||
if (ulrc != MCIERR_SUCCESS) {
|
||||
#ifdef DEBUG
|
||||
CHAR errorBuffer[MCI_ERROR_LENGTH];
|
||||
_mciGetErrorString(ulrc, errorBuffer, MCI_ERROR_LENGTH);
|
||||
printf("playSound: mciSendCommand with MCI_OPEN_MMIO returned %ld: %s\n",
|
||||
ulrc, errorBuffer);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
// play the sound
|
||||
MCI_PLAY_PARMS mpp;
|
||||
memset(&mpp, '\0', sizeof(mpp));
|
||||
ulrc = _mciSendCommand(mop.usDeviceID, MCI_PLAY, MCI_WAIT, &mpp, 0);
|
||||
#ifdef DEBUG
|
||||
// just ignore further failures in non-debug mode
|
||||
if (ulrc != MCIERR_SUCCESS) {
|
||||
CHAR errorBuffer[MCI_ERROR_LENGTH];
|
||||
_mciGetErrorString(ulrc, errorBuffer, MCI_ERROR_LENGTH);
|
||||
printf("playSound: mciSendCommand with MCI_PLAY returned %ld: %s\n",
|
||||
ulrc, errorBuffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
// end playing
|
||||
ulrc = _mciSendCommand(mop.usDeviceID, MCI_STOP, MCI_WAIT, &mpp, 0); // be nice
|
||||
ulrc = _mciSendCommand(mop.usDeviceID, MCI_CLOSE, MCI_WAIT, &mpp, 0);
|
||||
#ifdef DEBUG
|
||||
if (ulrc != MCIERR_SUCCESS) {
|
||||
CHAR errorBuffer[MCI_ERROR_LENGTH];
|
||||
_mciGetErrorString(ulrc, errorBuffer, MCI_ERROR_LENGTH);
|
||||
printf("playSound: mciSendCommand with MCI_CLOSE returned %ld: %s\n",
|
||||
ulrc, errorBuffer);
|
||||
}
|
||||
#endif
|
||||
_mmioClose(hmmio, 0);
|
||||
DosFreeMem(mi.pchBuffer);
|
||||
_endthread();
|
||||
} while(0); // end of inner block
|
||||
|
||||
// cleanup after an error
|
||||
WinAlarm(HWND_DESKTOP, WA_WARNING); // Beep()
|
||||
if (hmmio)
|
||||
_mmioClose(hmmio, 0);
|
||||
if (mi.pchBuffer)
|
||||
DosFreeMem(mi.pchBuffer);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Get the list of sound files associated with mozilla events the first time
|
||||
// this class is instantiated. However, defer initialization of the MMOS2
|
||||
// dlls until they're actually needed (which may be never).
|
||||
|
||||
nsSound::nsSound()
|
||||
{
|
||||
if (!sInitialized) {
|
||||
InitGlobals();
|
||||
}
|
||||
sInitialized++;
|
||||
#ifdef DEBUG
|
||||
printf("nsSound::nsSound: sInitialized=%d\n", sInitialized);
|
||||
#endif
|
||||
initSounds();
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
nsSound::~nsSound()
|
||||
{
|
||||
sInitialized--;
|
||||
#ifdef DEBUG
|
||||
printf("nsSound::~nsSound: sInitialized=%d\n", sInitialized);
|
||||
#endif
|
||||
// (try to) unload modules after last user ended
|
||||
if (!sInitialized) {
|
||||
#ifdef DEBUG
|
||||
printf("nsSound::~nsSound: Trying to free modules...\n");
|
||||
#endif
|
||||
ULONG ulrc;
|
||||
ulrc = DosFreeModule(sHModMMIO);
|
||||
// do not free MDM.DLL because it doesn't like to be unloaded repeatedly
|
||||
if (ulrc != NO_ERROR) {
|
||||
NS_WARNING("DosFreeModule did not work");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
NS_IMETHODIMP nsSound::Init()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
NS_IMETHODIMP nsSound::Beep()
|
||||
{
|
||||
WinAlarm(HWND_DESKTOP, WA_WARNING);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
// All attempts to play a sound file should be routed through this method.
|
||||
|
||||
NS_IMETHODIMP nsSound::Play(nsIURL *aURL)
|
||||
{
|
||||
if (sDllError) {
|
||||
DBG_MSG("nsSound::Play: MMOS2 initialization failed");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIStreamLoader> loader;
|
||||
return NS_NewStreamLoader(getter_AddRefs(loader), aURL, this);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
// After a sound has been loaded, start a new thread to play it.
|
||||
|
||||
NS_IMETHODIMP nsSound::OnStreamComplete(nsIStreamLoader *aLoader,
|
||||
nsISupports *context,
|
||||
nsresult aStatus,
|
||||
@ -420,12 +166,13 @@ NS_IMETHODIMP nsSound::OnStreamComplete(nsIStreamLoader *aLoader,
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
|
||||
if (channel) {
|
||||
channel->GetURI(getter_AddRefs(uri));
|
||||
if (uri) {
|
||||
nsCAutoString uriSpec;
|
||||
uri->GetSpec(uriSpec);
|
||||
printf("Failed to load %s\n", uriSpec.get());
|
||||
}
|
||||
channel->GetURI(getter_AddRefs(uri));
|
||||
if (uri) {
|
||||
nsCAutoString uriSpec;
|
||||
uri->GetSpec(uriSpec);
|
||||
fprintf(stderr, "nsSound::OnStreamComplete: failed to load %s\n",
|
||||
uriSpec.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -433,107 +180,376 @@ NS_IMETHODIMP nsSound::OnStreamComplete(nsIStreamLoader *aLoader,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!sMMPMInstalled) {
|
||||
NS_WARNING("Sound output only works with MMOS2 installed");
|
||||
Beep();
|
||||
return NS_OK;
|
||||
// allocate a buffer to hold the ARGBUFFER struct and sound data;
|
||||
// try using high-memory - if that fails, try low-memory
|
||||
ARGBUFFER * arg;
|
||||
if (DosAllocMem((PPVOID)&arg, sizeof(ARGBUFFER) + dataLen,
|
||||
OBJ_ANY | PAG_READ | PAG_WRITE | PAG_COMMIT)) {
|
||||
if (DosAllocMem((PPVOID)&arg, sizeof(ARGBUFFER) + dataLen,
|
||||
PAG_READ | PAG_WRITE | PAG_COMMIT)) {
|
||||
DBG_MSG("nsSound::OnStreamComplete: DosAllocMem failed");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
ARGBUFFER arg;
|
||||
memset(&arg, '\0', sizeof(arg));
|
||||
APIRET rc = DosCreateEventSem(NULL, &(arg.hev), 0UL, 0UL);
|
||||
// copy the sound data
|
||||
arg->bufLen = dataLen;
|
||||
memcpy(arg->buffer, data, dataLen);
|
||||
|
||||
// Play the sound on a new thread using MMOS2, in this case pass
|
||||
// the memory buffer in the argument structure.
|
||||
arg.bufLen = dataLen;
|
||||
arg.buffer = (char *)data;
|
||||
_beginthread(playSound, NULL, 32768, (void *)&arg);
|
||||
|
||||
// Wait until the buffer was copied, but not indefinitely to not block the
|
||||
// UI in case a really large sound file is copied.
|
||||
rc = DosWaitEventSem(arg.hev, 100);
|
||||
rc = DosCloseEventSem(arg.hev);
|
||||
// Play the sound on a new thread using MMOS2
|
||||
if (_beginthread(playSound, NULL, 32768, (void*)arg) < 0) {
|
||||
DosFreeMem((void*)arg);
|
||||
DBG_MSG("nsSound::OnStreamComplete: _beginthread failed");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsSound::Beep()
|
||||
{
|
||||
WinAlarm(HWND_DESKTOP, WA_WARNING);
|
||||
/*****************************************************************************/
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsSound::Play(nsIURL *aURL)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIStreamLoader> loader;
|
||||
rv = NS_NewStreamLoader(getter_AddRefs(loader), aURL, this);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsSound::Init()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
// This is obsolete and shouldn't get called (in theory).
|
||||
|
||||
NS_IMETHODIMP nsSound::PlaySystemSound(const nsAString &aSoundAlias)
|
||||
{
|
||||
if (!sMMPMInstalled) {
|
||||
return Beep();
|
||||
}
|
||||
if (aSoundAlias.IsEmpty())
|
||||
return NS_OK;
|
||||
|
||||
if (NS_IsMozAliasSound(aSoundAlias)) {
|
||||
NS_WARNING("nsISound::playSystemSound is called with \"_moz_\" events, they are obsolete, use nsISound::playEventSound instead");
|
||||
DBG_MSG("nsISound::playSystemSound was called with \"_moz_\" events, "
|
||||
"they are obsolete, use nsISound::playEventSound instead");
|
||||
|
||||
PRUint32 eventId;
|
||||
if (aSoundAlias.Equals(NS_SYSSOUND_ALERT_DIALOG))
|
||||
eventId = EVENT_ALERT_DIALOG_OPEN;
|
||||
else if (aSoundAlias.Equals(NS_SYSSOUND_CONFIRM_DIALOG))
|
||||
eventId = EVENT_CONFIRM_DIALOG_OPEN;
|
||||
else if (aSoundAlias.Equals(NS_SYSSOUND_MAIL_BEEP))
|
||||
eventId = EVENT_NEW_MAIL_RECEIVED;
|
||||
else
|
||||
return NS_OK;
|
||||
if (aSoundAlias.Equals(NS_SYSSOUND_MAIL_BEEP)) {
|
||||
eventId = EVENT_NEW_MAIL_RECEIVED;
|
||||
} else if (aSoundAlias.Equals(NS_SYSSOUND_ALERT_DIALOG)) {
|
||||
eventId = EVENT_ALERT_DIALOG_OPEN;
|
||||
} else if (aSoundAlias.Equals(NS_SYSSOUND_CONFIRM_DIALOG)) {
|
||||
eventId = EVENT_CONFIRM_DIALOG_OPEN;
|
||||
} else if (aSoundAlias.Equals(NS_SYSSOUND_PROMPT_DIALOG)) {
|
||||
eventId = EVENT_PROMPT_DIALOG_OPEN;
|
||||
} else if (aSoundAlias.Equals(NS_SYSSOUND_SELECT_DIALOG)) {
|
||||
eventId = EVENT_SELECT_DIALOG_OPEN;
|
||||
} else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_EXECUTE)) {
|
||||
eventId = EVENT_MENU_EXECUTE;
|
||||
} else if (aSoundAlias.Equals(NS_SYSSOUND_MENU_POPUP)) {
|
||||
eventId = EVENT_MENU_POPUP;
|
||||
} else {
|
||||
return NS_OK;
|
||||
}
|
||||
return PlayEventSound(eventId);
|
||||
}
|
||||
nsCAutoString nativeSoundAlias;
|
||||
NS_CopyUnicodeToNative(aSoundAlias, nativeSoundAlias);
|
||||
|
||||
ARGBUFFER arg;
|
||||
memset(&arg, '\0', sizeof(arg));
|
||||
APIRET rc = DosCreateEventSem(NULL, &(arg.hev), 0UL, 0UL);
|
||||
|
||||
// Play the sound on a new thread using MMOS2, in this case pass
|
||||
// the filename in the argument structure.
|
||||
arg.pszFilename = (PSZ)nativeSoundAlias.get();
|
||||
_beginthread(playSound, NULL, 32768, (void *)&arg);
|
||||
|
||||
// Try to wait a while until the file is loaded, but not too long...
|
||||
rc = DosWaitEventSem(arg.hev, 100);
|
||||
rc = DosCloseEventSem(arg.hev);
|
||||
|
||||
return NS_OK;
|
||||
// assume aSoundAlias is a file name
|
||||
return PlaySoundFile(aSoundAlias);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
// Attempt to play whatever event sounds the user has enabled.
|
||||
// If the attempt fails or a sound isn't set, fall back to a
|
||||
// beep for selected events.
|
||||
|
||||
NS_IMETHODIMP nsSound::PlayEventSound(PRUint32 aEventId)
|
||||
{
|
||||
// Prompt dialog and select dialog sounds do not correspond to OS/2
|
||||
// system sounds, ignore them. Ignore the menu sounds, too. Try to handle
|
||||
// the rest. Skip the beeps on systems without MMPM, too many of them are
|
||||
// confusing and annoying.
|
||||
if (!sDllError &&
|
||||
aEventId < EVENT_CNT &&
|
||||
sSoundFiles[aEventId] &&
|
||||
NS_SUCCEEDED(PlaySoundFile(
|
||||
nsDependentCString(sSoundFiles[aEventId])))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
switch(aEventId) {
|
||||
case EVENT_NEW_MAIL_RECEIVED:
|
||||
// We don't have a default mail sound on OS/2, so just "beep"
|
||||
return Beep(); // this corresponds to the "Warning" sound
|
||||
case EVENT_ALERT_DIALOG_OPEN:
|
||||
WinAlarm(HWND_DESKTOP, WA_ERROR); // play "Error" sound
|
||||
break;
|
||||
case EVENT_CONFIRM_DIALOG_OPEN:
|
||||
WinAlarm(HWND_DESKTOP, WA_NOTE); // play "Information" sound
|
||||
break;
|
||||
case EVENT_NEW_MAIL_RECEIVED:
|
||||
return Beep(); // play "Warning" sound
|
||||
case EVENT_ALERT_DIALOG_OPEN:
|
||||
WinAlarm(HWND_DESKTOP, WA_ERROR); // play "Error" sound
|
||||
break;
|
||||
case EVENT_CONFIRM_DIALOG_OPEN:
|
||||
WinAlarm(HWND_DESKTOP, WA_NOTE); // play "Information" sound
|
||||
break;
|
||||
case EVENT_PROMPT_DIALOG_OPEN:
|
||||
case EVENT_SELECT_DIALOG_OPEN:
|
||||
case EVENT_MENU_EXECUTE:
|
||||
case EVENT_MENU_POPUP:
|
||||
break;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
// Convert a UCS file path to native charset, then play it.
|
||||
|
||||
nsresult nsSound::PlaySoundFile(const nsAString &aSoundFile)
|
||||
{
|
||||
nsCAutoString buf;
|
||||
nsresult rv = NS_CopyUnicodeToNative(aSoundFile, buf);
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
return PlaySoundFile(buf);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
// Take a native charset path, convert it to a file URL, then play the file.
|
||||
|
||||
nsresult nsSound::PlaySoundFile(const nsACString &aSoundFile)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr <nsILocalFile> soundFile;
|
||||
rv = NS_NewNativeLocalFile(aSoundFile, PR_FALSE,
|
||||
getter_AddRefs(soundFile));
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
nsCOMPtr <nsIURI> fileURI;
|
||||
rv = NS_NewFileURI(getter_AddRefs(fileURI), soundFile);
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(fileURI,&rv);
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
return Play(fileURL);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* static helper functions */
|
||||
/*****************************************************************************/
|
||||
|
||||
// This function loads the names of sound files associated with Mozilla
|
||||
// events from mmpm.ini. Because of the overhead, it only makes one
|
||||
// attempt per session and caches the results.
|
||||
//
|
||||
// Mozilla event sounds can be added to mmpm.ini via a REXX script,
|
||||
// and can be edited using the 'Sound' object in the System Setup folder.
|
||||
// mmpm.ini entries have this format: [fq_path#event_name#volume]
|
||||
// 'fq_path' identifies the file; 'event_name' is the description that
|
||||
// appears in the Sound object's listbox; 'volume' is a numeric string
|
||||
// used by the WPS. Here's the REXX command to add a "New Mail" sound:
|
||||
// result = SysIni('C:\MMOS2\MMPM.INI', 'MMPM2_AlarmSounds', '800',
|
||||
// 'C:\MMOS2\SOUNDS\NOTES.WAV#New Mail#80');
|
||||
// Note that the key value is a numeric string. The base index for
|
||||
// Mozilla event keys is an arbitrary number that defaults to 800 if
|
||||
// an entry for "MOZILLA_Events\BaseIndex" can't be found in mmpm.ini.
|
||||
|
||||
static void initSounds(void)
|
||||
{
|
||||
static PRBool sSoundInit = FALSE;
|
||||
|
||||
if (sSoundInit) {
|
||||
return;
|
||||
}
|
||||
sSoundInit = TRUE;
|
||||
|
||||
// Confirm mmpm.ini exists where it's expected to prevent
|
||||
// PrfOpenProfile() from creating a new empty file there.
|
||||
FILESTATUS3 fs3;
|
||||
char buffer[CCHMAXPATH];
|
||||
char * ptr;
|
||||
ULONG rc = DosScanEnv("MMBASE", const_cast<const char **>(&ptr));
|
||||
if (!rc) {
|
||||
strcpy(buffer, ptr);
|
||||
ptr = strchr(buffer, ';');
|
||||
if (!ptr) {
|
||||
ptr = strchr(buffer, 0);
|
||||
}
|
||||
strcpy(ptr, "\\MMPM.INI");
|
||||
rc = DosQueryPathInfo(buffer, FIL_STANDARD, &fs3, sizeof(fs3));
|
||||
}
|
||||
if (rc) {
|
||||
ULONG ulBootDrive = 0;
|
||||
strcpy(buffer, "x:\\MMOS2\\MMPM.INI");
|
||||
DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
|
||||
&ulBootDrive, sizeof ulBootDrive);
|
||||
buffer[0] = 0x40 + ulBootDrive;
|
||||
rc = DosQueryPathInfo(buffer, FIL_STANDARD, &fs3, sizeof(fs3));
|
||||
}
|
||||
if (rc) {
|
||||
DBG_MSG("initSounds: unable to locate mmpm.ini");
|
||||
return;
|
||||
}
|
||||
|
||||
HINI hini = PrfOpenProfile(0, buffer);
|
||||
if (!hini) {
|
||||
DBG_MSG("initSounds: unable to open mmpm.ini");
|
||||
return;
|
||||
}
|
||||
|
||||
// If a base index has been set, use it, provided it doesn't
|
||||
// collide with the system-defined indices (0 - 12)
|
||||
LONG baseNdx = PrfQueryProfileInt(hini, "MOZILLA_Events",
|
||||
"BaseIndex", 800);
|
||||
if (baseNdx <= 12) {
|
||||
baseNdx = 800;
|
||||
}
|
||||
|
||||
// For each event, see if there's an entry in mmpm.ini.
|
||||
// If so, extract the file path, confirm it's valid,
|
||||
// then duplicate the string & save it for later use.
|
||||
for (LONG i = 0; i < EVENT_CNT; i++) {
|
||||
char key[16];
|
||||
ultoa(i + baseNdx, key, 10);
|
||||
if (!PrfQueryProfileString(hini, "MMPM2_AlarmSounds", key,
|
||||
0, buffer, sizeof(buffer))) {
|
||||
continue;
|
||||
}
|
||||
ptr = strchr(buffer, '#');
|
||||
if (!ptr || ptr == buffer) {
|
||||
continue;
|
||||
}
|
||||
*ptr = 0;
|
||||
if (DosQueryPathInfo(buffer, FIL_STANDARD, &fs3, sizeof(fs3)) ||
|
||||
(fs3.attrFile & FILE_DIRECTORY) || !fs3.cbFile) {
|
||||
continue;
|
||||
}
|
||||
sSoundFiles[i] = strdup(buffer);
|
||||
}
|
||||
|
||||
PrfCloseProfile(hini);
|
||||
return;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
// Only one attempt is made per session to initialize MMOS2. Once
|
||||
// the dlls are loaded, they remain loaded to avoid stability issues.
|
||||
|
||||
static PRBool initDlls(void)
|
||||
{
|
||||
static PRBool sDllInit = FALSE;
|
||||
|
||||
if (sDllInit) {
|
||||
return TRUE;
|
||||
}
|
||||
sDllInit = TRUE;
|
||||
|
||||
HMODULE hmodMMIO = 0;
|
||||
HMODULE hmodMDM = 0;
|
||||
char szError[32];
|
||||
if (DosLoadModule(szError, sizeof(szError), "MMIO", &hmodMMIO) ||
|
||||
DosLoadModule(szError, sizeof(szError), "MDM", &hmodMDM)) {
|
||||
DBG_MSG("initDlls: DosLoadModule failed");
|
||||
sDllError = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (DosQueryProcAddr(hmodMMIO, 0L, "mmioOpen", (PFN *)&_mmioOpen) ||
|
||||
DosQueryProcAddr(hmodMMIO, 0L, "mmioClose", (PFN *)&_mmioClose) ||
|
||||
DosQueryProcAddr(hmodMDM, 0L, "mciSendCommand", (PFN *)&_mciSendCommand)) {
|
||||
DBG_MSG("initDlls: DosQueryProcAddr failed");
|
||||
sDllError = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
// Background thread proc to play the sound that was set up in
|
||||
// the argument structure. If an error occurs, beep at least.
|
||||
// aArgs is allocated from system memory & must be freed.
|
||||
|
||||
static void playSound(void * aArgs)
|
||||
{
|
||||
BOOL fOK = FALSE;
|
||||
HMMIO hmmio = 0;
|
||||
MMIOINFO mi;
|
||||
|
||||
do {
|
||||
if (!initDlls())
|
||||
break;
|
||||
|
||||
memset(&mi, 0, sizeof(MMIOINFO));
|
||||
mi.cchBuffer = ((ARGBUFFER*)aArgs)->bufLen;
|
||||
mi.pchBuffer = ((ARGBUFFER*)aArgs)->buffer;
|
||||
mi.ulTranslate = MMIO_TRANSLATEDATA | MMIO_TRANSLATEHEADER;
|
||||
mi.fccChildIOProc = FOURCC_MEM;
|
||||
mi.fccIOProc = determineFourCC(mi.cchBuffer, mi.pchBuffer);
|
||||
if (!mi.fccIOProc) {
|
||||
DBG_MSG("playSound: unknown sound format");
|
||||
break;
|
||||
}
|
||||
|
||||
hmmio = _mmioOpen(NULL, &mi, MMIO_READ | MMIO_DENYWRITE);
|
||||
if (!hmmio) {
|
||||
DBG_MSG("playSound: _mmioOpen failed");
|
||||
break;
|
||||
}
|
||||
|
||||
// open the sound device
|
||||
MCI_OPEN_PARMS mop;
|
||||
memset(&mop, 0, sizeof(mop));
|
||||
mop.pszElementName = (PSZ)hmmio;
|
||||
mop.pszDeviceType = (PSZ)MAKEULONG(MCI_DEVTYPE_WAVEFORM_AUDIO, 0);
|
||||
if (_mciSendCommand(0, MCI_OPEN,
|
||||
MCI_OPEN_MMIO | MCI_OPEN_TYPE_ID |
|
||||
MCI_OPEN_SHAREABLE | MCI_WAIT,
|
||||
(PVOID)&mop, 0)) {
|
||||
DBG_MSG("playSound: MCI_OPEN failed");
|
||||
break;
|
||||
}
|
||||
fOK = TRUE;
|
||||
|
||||
// play the sound
|
||||
MCI_PLAY_PARMS mpp;
|
||||
memset(&mpp, 0, sizeof(mpp));
|
||||
if (_mciSendCommand(mop.usDeviceID, MCI_PLAY, MCI_WAIT, &mpp, 0)) {
|
||||
DBG_MSG("playSound: MCI_PLAY failed");
|
||||
}
|
||||
|
||||
// stop & close the device
|
||||
_mciSendCommand(mop.usDeviceID, MCI_STOP, MCI_WAIT, &mpp, 0);
|
||||
if (_mciSendCommand(mop.usDeviceID, MCI_CLOSE, MCI_WAIT, &mpp, 0)) {
|
||||
DBG_MSG("playSound: MCI_CLOSE failed");
|
||||
}
|
||||
|
||||
} while (0);
|
||||
|
||||
if (!fOK)
|
||||
WinAlarm(HWND_DESKTOP, WA_WARNING);
|
||||
|
||||
if (hmmio)
|
||||
_mmioClose(hmmio, 0);
|
||||
DosFreeMem(aArgs);
|
||||
|
||||
_endthread();
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
// Try to determine the data format in the buffer using file "magic".
|
||||
// Returns the FourCC handle for the format, or 0 when failing to
|
||||
// find format and codec.
|
||||
|
||||
static FOURCC determineFourCC(PRUint32 aDataLen, const char *aData)
|
||||
{
|
||||
FOURCC fcc = 0;
|
||||
|
||||
// Compare the first bytes of the data with magic to determine
|
||||
// the most likely format (other possible formats are ignored
|
||||
// because the mmio procs for them are too unreliable)
|
||||
if (memcmp(aData, "RIFF", 4) == 0) // WAV
|
||||
fcc = mmioFOURCC('W', 'A', 'V', 'E');
|
||||
else
|
||||
if (memcmp(aData, "ID3", 3) == 0) // likely MP3 with ID3 header
|
||||
fcc = mmioFOURCC('M','P','3',' ');
|
||||
else
|
||||
if ((aData[0] & 0xFF) == 0xFF && // various versions of MPEG layer 3
|
||||
((aData[1] & 0xFE) == 0xFA || // v1
|
||||
(aData[1] & 0xFE) == 0xF2 || // v2
|
||||
(aData[1] & 0xFE) == 0xE2)) // v2.5
|
||||
fcc = mmioFOURCC('M','P','3',' ');
|
||||
else
|
||||
if (memcmp(aData, "OggS", 4) == 0) // OGG
|
||||
fcc = mmioFOURCC('O','G','G','S');
|
||||
else
|
||||
if (memcmp(aData, "fLaC", 4) == 0) // FLAC
|
||||
fcc = mmioFOURCC('f','L','a','C');
|
||||
|
||||
return fcc;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
@ -54,6 +54,10 @@ public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISOUND
|
||||
NS_DECL_NSISTREAMLOADEROBSERVER
|
||||
|
||||
protected:
|
||||
nsresult PlaySoundFile(const nsAString &aSoundFile);
|
||||
nsresult PlaySoundFile(const nsACString &aSoundFile);
|
||||
};
|
||||
|
||||
#endif /* __nsSound_h__ */
|
||||
|
Loading…
x
Reference in New Issue
Block a user