2012-04-29 23:27:12 +00:00
|
|
|
/* ScummVM - Graphic Adventure Engine
|
|
|
|
*
|
|
|
|
* ScummVM is the legal property of its developers, whose names
|
|
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
|
|
* file distributed with this source distribution.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
*/
|
2012-05-14 05:43:50 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This code is based on original Tony Tough source code
|
|
|
|
*
|
|
|
|
* Copyright (c) 1997-2003 Nayma Software
|
|
|
|
*/
|
2012-04-29 23:27:12 +00:00
|
|
|
|
2012-06-11 01:23:28 +00:00
|
|
|
#include "audio/audiostream.h"
|
|
|
|
#include "audio/decoders/adpcm.h"
|
|
|
|
#include "audio/decoders/raw.h"
|
|
|
|
#include "audio/decoders/wave.h"
|
2012-05-03 11:43:00 +00:00
|
|
|
#include "common/textconsole.h"
|
2012-04-29 23:27:12 +00:00
|
|
|
#include "tony/game.h"
|
2012-05-03 11:43:00 +00:00
|
|
|
#include "tony/tony.h"
|
2012-04-29 23:27:12 +00:00
|
|
|
|
|
|
|
namespace Tony {
|
|
|
|
|
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
#define RELEASE(x) {if ((x) != NULL) { (x)->release(); x = NULL; }}
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
|
|
|
|
/****************************************************************************\
|
|
|
|
*****************************************************************************
|
|
|
|
* class CODECRAW
|
|
|
|
* --------------
|
|
|
|
* Description: CODEC to play hard from pure samples
|
|
|
|
*****************************************************************************
|
|
|
|
\****************************************************************************/
|
|
|
|
|
|
|
|
class CODECRAW : public CODEC {
|
|
|
|
public:
|
|
|
|
CODECRAW(bool _bLoop = true);
|
|
|
|
|
|
|
|
virtual ~CODECRAW();
|
|
|
|
virtual uint32 decompress(Common::SeekableReadStream *stream, void *lpBuf, uint32 dwSize);
|
|
|
|
virtual void loopReset();
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************\
|
|
|
|
*****************************************************************************
|
|
|
|
* class CODECADPCM
|
|
|
|
* ----------------
|
|
|
|
* Description: Play ADPCM compressed data
|
|
|
|
*****************************************************************************
|
|
|
|
\****************************************************************************/
|
|
|
|
|
|
|
|
class CODECADPCM : public CODECRAW {
|
|
|
|
protected:
|
|
|
|
byte *lpTemp;
|
|
|
|
static const int indexTable[16];
|
|
|
|
static const int stepSizeTable[89];
|
|
|
|
|
|
|
|
public:
|
|
|
|
CODECADPCM(bool _bLoop = true, byte *lpTempBuffer = NULL);
|
|
|
|
virtual ~CODECADPCM();
|
|
|
|
virtual uint32 decompress(Common::SeekableReadStream *stream, void *lpBuf, uint32 dwSize) = 0;
|
|
|
|
virtual void loopReset() = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
class CODECADPCMSTEREO : public CODECADPCM {
|
|
|
|
protected:
|
|
|
|
int valpred[2], index[2];
|
|
|
|
|
|
|
|
public:
|
|
|
|
CODECADPCMSTEREO(bool _bLoop=true, byte *lpTempBuffer = NULL);
|
|
|
|
virtual ~CODECADPCMSTEREO();
|
|
|
|
virtual uint32 decompress(Common::SeekableReadStream *stream, void *lpBuf, uint32 dwSize);
|
|
|
|
virtual void loopReset();
|
|
|
|
};
|
|
|
|
|
|
|
|
class CODECADPCMMONO : public CODECADPCM {
|
|
|
|
protected:
|
|
|
|
int valpred, index;
|
|
|
|
|
|
|
|
public:
|
|
|
|
CODECADPCMMONO(bool _bLoop = true, byte *lpTempBuffer = NULL);
|
|
|
|
virtual ~CODECADPCMMONO();
|
|
|
|
virtual uint32 decompress(Common::SeekableReadStream *stream, void *lpBuf, uint32 dwSize);
|
|
|
|
virtual void loopReset();
|
|
|
|
};
|
|
|
|
|
|
|
|
/****************************************************************************\
|
|
|
|
* CODEC Methods
|
|
|
|
\****************************************************************************/
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
2012-06-19 10:50:48 +00:00
|
|
|
* Standard cosntructor. It's possible to specify whether you want to
|
|
|
|
* enable or disable the loop (which by default, and 'active).
|
|
|
|
*
|
|
|
|
* @param loop True if you want to loop, false to disable
|
|
|
|
*/
|
|
|
|
CODEC::CODEC(bool loop) {
|
|
|
|
_bLoop = loop;
|
|
|
|
_bEndReached = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CODEC::~CODEC() {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tell whether we have reached the end of the stream
|
2012-06-17 16:09:52 +00:00
|
|
|
*
|
2012-06-19 10:50:48 +00:00
|
|
|
* @return True if we're done, false otherwise.
|
|
|
|
*/
|
|
|
|
bool CODEC::endOfStream() {
|
|
|
|
return _bEndReached;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************\
|
|
|
|
* CODECRAW Methods
|
|
|
|
\****************************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Standard cosntructor. Simply calls the inherited constructor
|
|
|
|
*/
|
|
|
|
CODECRAW::CODECRAW(bool loop) : CODEC(loop) {
|
|
|
|
}
|
|
|
|
|
|
|
|
CODECRAW::~CODECRAW() {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reset the stream to the beginning of the file. In the case of RAW files, does nothing
|
|
|
|
*/
|
|
|
|
void CODECRAW::loopReset() {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Manage the RAW format. Simply copies the file's stream buffer
|
|
|
|
*
|
|
|
|
* @return Indicates the position of the file for the end of the loop
|
|
|
|
*/
|
|
|
|
uint32 CODECRAW::decompress(Common::SeekableReadStream *stream, void *buf, uint32 dwSize) {
|
|
|
|
byte *lpBuf = (byte *)buf;
|
|
|
|
uint32 dwRead;
|
|
|
|
uint32 dwEOF;
|
|
|
|
|
|
|
|
_bEndReached = false;
|
|
|
|
dwEOF = 0;
|
|
|
|
dwRead = stream->read(lpBuf, dwSize);
|
|
|
|
|
|
|
|
if (dwRead < dwSize) {
|
|
|
|
dwEOF = dwRead;
|
|
|
|
_bEndReached = true;
|
|
|
|
|
|
|
|
if (!_bLoop) {
|
|
|
|
Common::fill(lpBuf + dwRead, lpBuf + dwRead + (dwSize - dwRead), 0);
|
|
|
|
} else {
|
|
|
|
stream->seek(0);
|
|
|
|
dwRead = stream->read(lpBuf + dwRead, dwSize - dwRead);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return dwEOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************\
|
|
|
|
* CODECADPCM Methods
|
|
|
|
\****************************************************************************/
|
|
|
|
|
|
|
|
const int CODECADPCM::indexTable[16] = {
|
|
|
|
-1, -1, -1, -1, 2, 4, 6, 8,
|
|
|
|
-1, -1, -1, -1, 2, 4, 6, 8,
|
|
|
|
};
|
|
|
|
|
|
|
|
const int CODECADPCM::stepSizeTable[89] = {
|
|
|
|
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
|
|
|
|
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
|
|
|
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
|
|
|
|
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
|
|
|
|
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
|
|
|
|
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
|
|
|
|
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
|
|
|
|
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
|
|
|
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
|
|
|
|
};
|
|
|
|
|
|
|
|
#define MAXDECODESIZE (44100 * 2 * 2)
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Standard constructor. Initialises and allocates temporary memory tables
|
|
|
|
*/
|
|
|
|
CODECADPCM::CODECADPCM(bool loop, byte *lpTempBuffer) : CODECRAW(loop) {
|
|
|
|
// Alloca la memoria temporanea
|
|
|
|
if (lpTempBuffer != NULL) {
|
|
|
|
lpTemp = lpTempBuffer;
|
|
|
|
} else {
|
|
|
|
lpTemp = (byte *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, MAXDECODESIZE);
|
|
|
|
|
|
|
|
if (lpTemp == NULL) {
|
|
|
|
error("Insufficient memory!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CODECADPCMMONO::CODECADPCMMONO(bool loop, byte *lpTempBuffer) : CODECADPCM(loop,lpTempBuffer) {
|
|
|
|
// Inizializza per il playing
|
|
|
|
loopReset();
|
|
|
|
}
|
|
|
|
|
|
|
|
CODECADPCMMONO::~CODECADPCMMONO() {
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CODECADPCMSTEREO::CODECADPCMSTEREO(bool loop, byte *lpTempBuffer) : CODECADPCM(loop, lpTempBuffer) {
|
|
|
|
// Initialise for playing
|
|
|
|
loopReset();
|
|
|
|
}
|
|
|
|
|
|
|
|
CODECADPCMSTEREO::~CODECADPCMSTEREO() {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destructor. Free the buffer
|
|
|
|
*/
|
|
|
|
CODECADPCM::~CODECADPCM() {
|
|
|
|
globalDestroy(lpTemp);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reset the player before each play or loop
|
|
|
|
*/
|
|
|
|
void CODECADPCMSTEREO::loopReset() {
|
|
|
|
valpred[0] = 0;
|
|
|
|
valpred[1] = 0;
|
|
|
|
index[0] = 0;
|
|
|
|
index[1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CODECADPCMMONO::loopReset() {
|
|
|
|
valpred = 0;
|
|
|
|
index = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Manages decompressing the ADPCM 16:4 format.
|
2012-06-17 16:09:52 +00:00
|
|
|
*/
|
2012-06-19 10:50:48 +00:00
|
|
|
uint32 CODECADPCMMONO::decompress(Common::SeekableReadStream *stream, void *buf, uint32 dwSize) {
|
|
|
|
uint16 *lpBuf = (uint16 *)buf;
|
|
|
|
byte *inp;
|
|
|
|
int bufferstep;
|
2012-06-19 11:19:17 +00:00
|
|
|
int cache = 0;
|
2012-06-19 10:50:48 +00:00
|
|
|
int delta;
|
|
|
|
int sign;
|
|
|
|
int vpdiff;
|
|
|
|
uint32 eof, i;
|
|
|
|
int step;
|
|
|
|
|
|
|
|
bufferstep = 1;
|
|
|
|
step = stepSizeTable[index];
|
|
|
|
|
|
|
|
// Invokes the raw codec to read the stream from disk to loop.
|
|
|
|
eof = CODECRAW::decompress(stream, lpTemp, dwSize / 4);
|
|
|
|
inp = lpTemp;
|
|
|
|
|
|
|
|
eof *= 2;
|
|
|
|
|
|
|
|
// If you must do an immediate loop
|
|
|
|
if (endOfStream() && eof == 0) {
|
|
|
|
loopReset();
|
|
|
|
bufferstep = 1;
|
|
|
|
step = stepSizeTable[index];
|
|
|
|
} else if (!endOfStream())
|
|
|
|
eof = 0;
|
|
|
|
|
|
|
|
dwSize /= 2;
|
|
|
|
for (i = 0; i < dwSize; i++) {
|
|
|
|
// Check if we are at the end of the file, and are looping
|
|
|
|
if (eof != 0 && i == eof) {
|
|
|
|
loopReset();
|
|
|
|
bufferstep=1;
|
|
|
|
step = stepSizeTable[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the delta (4 bits)
|
|
|
|
if (bufferstep) {
|
|
|
|
cache = *inp++;
|
|
|
|
delta = (cache >> 4) & 0xF;
|
|
|
|
} else
|
|
|
|
delta = cache & 0xF;
|
|
|
|
|
|
|
|
// Find the new index
|
|
|
|
index += indexTable[delta];
|
|
|
|
if (index < 0) index = 0;
|
|
|
|
if (index > 88) index = 88;
|
|
|
|
|
|
|
|
// Reads the sign and separates it
|
|
|
|
sign = delta & 8;
|
|
|
|
delta = delta & 7;
|
|
|
|
|
|
|
|
// Find the difference from the previous value
|
|
|
|
vpdiff = step >> 3;
|
|
|
|
if (delta & 4) vpdiff += step;
|
|
|
|
if (delta & 2) vpdiff += step >> 1;
|
|
|
|
if (delta & 1) vpdiff += step >> 2;
|
|
|
|
|
|
|
|
if (sign)
|
|
|
|
valpred -= vpdiff;
|
|
|
|
else
|
|
|
|
valpred += vpdiff;
|
|
|
|
|
|
|
|
// Check the limits of the found value
|
|
|
|
if (valpred > 32767)
|
|
|
|
valpred = 32767;
|
|
|
|
else if (valpred < -32768)
|
|
|
|
valpred = -32768;
|
|
|
|
|
|
|
|
// Update the step
|
|
|
|
step = stepSizeTable[index];
|
|
|
|
|
|
|
|
// Write the value found
|
|
|
|
*lpBuf++ = (uint16)valpred;
|
|
|
|
|
|
|
|
bufferstep = !bufferstep;
|
|
|
|
}
|
|
|
|
|
|
|
|
return eof / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 CODECADPCMSTEREO::decompress(Common::SeekableReadStream *stream, void *buf, uint32 dwSize) {
|
|
|
|
uint16 *lpBuf=(uint16 *)buf;
|
|
|
|
byte *inp;
|
|
|
|
int bufferstep;
|
2012-06-19 11:19:17 +00:00
|
|
|
int cache = 0;
|
2012-06-19 10:50:48 +00:00
|
|
|
int delta;
|
|
|
|
int sign;
|
|
|
|
int vpdiff;
|
|
|
|
uint32 eof, i;
|
|
|
|
int step[2];
|
|
|
|
|
|
|
|
bufferstep = 1;
|
|
|
|
step[0] = stepSizeTable[index[0]];
|
|
|
|
step[1] = stepSizeTable[index[1]];
|
|
|
|
|
|
|
|
// Invokes the RAW codec to read the stream from disk.
|
|
|
|
eof = CODECRAW::decompress(stream, lpTemp, dwSize / 4);
|
|
|
|
inp = lpTemp;
|
|
|
|
|
|
|
|
eof *= 2;
|
|
|
|
|
|
|
|
// If you must do an immediate loop
|
|
|
|
if (endOfStream() && eof == 0) {
|
|
|
|
loopReset();
|
|
|
|
bufferstep = 1;
|
|
|
|
step[0] = stepSizeTable[index[0]];
|
|
|
|
step[1] = stepSizeTable[index[1]];
|
|
|
|
|
|
|
|
} else if (!endOfStream())
|
|
|
|
eof = 0;
|
|
|
|
|
|
|
|
dwSize /= 2;
|
|
|
|
|
|
|
|
for (i = 0;i < dwSize; i++) {
|
|
|
|
// If you must do an immediate loop
|
|
|
|
if (eof != 0 && i == eof) {
|
|
|
|
loopReset();
|
|
|
|
bufferstep = 1;
|
|
|
|
step[0] = stepSizeTable[index[0]];
|
|
|
|
step[1] = stepSizeTable[index[1]];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reads the delta (4 bits)
|
|
|
|
if (bufferstep) {
|
|
|
|
cache = *inp++;
|
|
|
|
delta = cache & 0xF;
|
|
|
|
} else
|
|
|
|
delta = (cache >> 4) & 0xF;
|
|
|
|
|
|
|
|
// Find the new index
|
|
|
|
index[bufferstep] += indexTable[delta];
|
|
|
|
if (index[bufferstep] < 0) index[bufferstep] = 0;
|
|
|
|
if (index[bufferstep] > 88) index[bufferstep] = 88;
|
|
|
|
|
|
|
|
// Reads the sign and separates it
|
|
|
|
sign = delta & 8;
|
|
|
|
delta = delta & 7;
|
|
|
|
|
|
|
|
// Find the difference from the previous value
|
|
|
|
vpdiff = step[bufferstep] >> 3;
|
|
|
|
if (delta & 4) vpdiff += step[bufferstep];
|
|
|
|
if (delta & 2) vpdiff += step[bufferstep] >> 1;
|
|
|
|
if (delta & 1) vpdiff += step[bufferstep] >> 2;
|
|
|
|
|
|
|
|
if (sign)
|
|
|
|
valpred[bufferstep] -= vpdiff;
|
|
|
|
else
|
|
|
|
valpred[bufferstep] += vpdiff;
|
|
|
|
|
|
|
|
// Check the limits of the value
|
|
|
|
if (valpred[bufferstep] > 32767)
|
|
|
|
valpred[bufferstep] = 32767;
|
|
|
|
else if (valpred[bufferstep] < -32768)
|
|
|
|
valpred[bufferstep] =- 32768;
|
|
|
|
|
|
|
|
// Update the step
|
|
|
|
step[bufferstep] = stepSizeTable[index[bufferstep]];
|
|
|
|
|
|
|
|
// Write the found value
|
|
|
|
*lpBuf++ = (uint16)valpred[bufferstep];
|
|
|
|
|
|
|
|
bufferstep = !bufferstep;
|
|
|
|
}
|
|
|
|
|
|
|
|
return eof / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************\
|
|
|
|
* FPSOUND Methods
|
|
|
|
\****************************************************************************/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
/**
|
|
|
|
* Default constructor. Initializes the attributes.
|
|
|
|
*
|
|
|
|
*/
|
2012-06-16 09:32:50 +00:00
|
|
|
FPSound::FPSound() {
|
2012-06-16 17:04:19 +00:00
|
|
|
_bSoundSupported = false;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Initializes the object, and prepare everything you need to create streams and sound effects.
|
|
|
|
*
|
|
|
|
* @returns True is everything is OK, False otherwise
|
|
|
|
*/
|
2012-06-16 09:32:50 +00:00
|
|
|
bool FPSound::init() {
|
2012-06-16 17:04:19 +00:00
|
|
|
_bSoundSupported = g_system->getMixer()->isReady();
|
|
|
|
return _bSoundSupported;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Destroy the object and free the memory
|
|
|
|
*
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
FPSound::~FPSound() {
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Allocates an object of type FPStream, and return its pointer
|
|
|
|
*
|
|
|
|
* @param lplpStream Will contain a pointer to the object you just created.
|
|
|
|
*
|
|
|
|
* @returns True is everything is OK, False otherwise
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
bool FPSound::createStream(FPStream **lplpStream) {
|
2012-06-16 17:04:19 +00:00
|
|
|
(*lplpStream) = new FPStream(_bSoundSupported);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
|
|
|
return (*lplpStream != NULL);
|
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Allocates an object of type FpSfx, and return its pointer
|
|
|
|
*
|
|
|
|
* @param lplpSfx Will contain a pointer to the object you just created.
|
|
|
|
*
|
|
|
|
* @returns True is everything is OK, False otherwise
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
bool FPSound::createSfx(FPSfx **lplpSfx) {
|
2012-06-16 17:04:19 +00:00
|
|
|
(*lplpSfx) = new FPSfx(_bSoundSupported);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
|
|
|
return (*lplpSfx != NULL);
|
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Set the general volume
|
|
|
|
*
|
|
|
|
* @param dwVolume Volume to set (0-63)
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
void FPSound::setMasterVolume(int dwVolume) {
|
2012-06-16 17:04:19 +00:00
|
|
|
if (!_bSoundSupported)
|
2012-05-03 11:43:00 +00:00
|
|
|
return;
|
|
|
|
|
2012-06-09 22:35:08 +00:00
|
|
|
g_system->getMixer()->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, CLIP<int>(dwVolume, 0, 63) * Audio::Mixer::kMaxChannelVolume / 63);
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Get the general volume
|
|
|
|
*
|
|
|
|
* @param lpdwVolume Variable that will contain the volume (0-63)
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
void FPSound::getMasterVolume(int *lpdwVolume) {
|
2012-06-16 17:04:19 +00:00
|
|
|
if (!_bSoundSupported)
|
2012-05-03 11:43:00 +00:00
|
|
|
return;
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-09 22:35:08 +00:00
|
|
|
*lpdwVolume = g_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) * 63 / Audio::Mixer::kMaxChannelVolume;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Default constructor.
|
|
|
|
*
|
|
|
|
* @remarks Do *NOT* declare an object directly, but rather
|
|
|
|
* create it using FPSound::CreateSfx()
|
|
|
|
*
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
FPSfx::FPSfx(bool bSoundOn) {
|
2012-06-16 17:04:19 +00:00
|
|
|
_bSoundSupported = bSoundOn;
|
|
|
|
_bFileLoaded = false;
|
|
|
|
_lastVolume = 63;
|
|
|
|
_hEndOfBuffer = CORO_INVALID_PID_VALUE;
|
|
|
|
_bIsVoice = false;
|
2012-06-12 15:35:27 +00:00
|
|
|
_loopStream = 0;
|
2012-06-11 02:03:52 +00:00
|
|
|
_rewindableStream = 0;
|
2012-06-16 17:04:19 +00:00
|
|
|
_bPaused = false;
|
2012-06-11 02:20:12 +00:00
|
|
|
|
|
|
|
_vm->_activeSfx.push_back(this);
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Default Destructor.
|
|
|
|
*
|
|
|
|
* @remarks It is also stops the sound effect that may be
|
|
|
|
* currently played, and free the memory it uses.
|
|
|
|
*
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
FPSfx::~FPSfx() {
|
2012-06-16 17:04:19 +00:00
|
|
|
if (!_bSoundSupported)
|
2012-05-03 11:43:00 +00:00
|
|
|
return;
|
|
|
|
|
2012-06-11 01:23:28 +00:00
|
|
|
g_system->getMixer()->stopHandle(_handle);
|
2012-06-11 02:20:12 +00:00
|
|
|
_vm->_activeSfx.remove(this);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-12 15:35:27 +00:00
|
|
|
if (_loopStream)
|
|
|
|
delete _loopStream; // _rewindableStream is deleted by deleting _loopStream
|
|
|
|
else
|
|
|
|
delete _rewindableStream;
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-11 01:23:28 +00:00
|
|
|
// FIXME
|
|
|
|
//if (hEndOfBuffer != CORO_INVALID_PID_VALUE)
|
|
|
|
// CloseHandle(hEndOfBuffer);
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Releases the memory used by the object.
|
|
|
|
*
|
|
|
|
* @remarks Must be called when the object is no longer used and
|
|
|
|
* **ONLY** if the object was created by
|
|
|
|
* FPSound::CreateStream().
|
|
|
|
* Object pointers are no longer valid after this call.
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
void FPSfx::release() {
|
2012-05-03 11:43:00 +00:00
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
bool FPSfx::loadWave(Common::SeekableReadStream *stream) {
|
2012-06-11 01:23:28 +00:00
|
|
|
if (!stream)
|
2012-05-03 11:43:00 +00:00
|
|
|
return false;
|
|
|
|
|
2012-06-11 02:03:52 +00:00
|
|
|
_rewindableStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-11 02:03:52 +00:00
|
|
|
if (!_rewindableStream)
|
2012-05-03 11:43:00 +00:00
|
|
|
return false;
|
|
|
|
|
2012-06-16 17:04:19 +00:00
|
|
|
_bFileLoaded = true;
|
|
|
|
setVolume(_lastVolume);
|
2012-05-03 11:43:00 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
bool FPSfx::loadVoiceFromVDB(Common::File &vdbFP) {
|
2012-06-16 17:04:19 +00:00
|
|
|
if (!_bSoundSupported)
|
2012-05-03 11:43:00 +00:00
|
|
|
return true;
|
|
|
|
|
2012-06-11 01:23:28 +00:00
|
|
|
uint32 size = vdbFP.readUint32LE();
|
|
|
|
uint32 rate = vdbFP.readUint32LE();
|
2012-06-16 17:04:19 +00:00
|
|
|
_bIsVoice = true;
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-11 02:03:52 +00:00
|
|
|
_rewindableStream = Audio::makeADPCMStream(vdbFP.readStream(size), DisposeAfterUse::YES, 0, Audio::kADPCMDVI, rate, 1);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 17:04:19 +00:00
|
|
|
_bFileLoaded = true;
|
2012-06-16 09:32:50 +00:00
|
|
|
setVolume(62);
|
2012-05-03 11:43:00 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Opens a file and loads a sound effect.
|
|
|
|
*
|
|
|
|
* @param lpszFileName Sfx filename
|
|
|
|
* @param dwCodec CODEC used to uncompress the samples
|
|
|
|
*
|
|
|
|
* @returns True is everything is OK, False otherwise
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
bool FPSfx::loadFile(const char *lpszFileName, uint32 dwCodec) {
|
2012-06-16 17:04:19 +00:00
|
|
|
if (!_bSoundSupported)
|
2012-05-03 11:43:00 +00:00
|
|
|
return true;
|
|
|
|
|
2012-06-11 01:23:28 +00:00
|
|
|
Common::File file;
|
2012-05-03 11:43:00 +00:00
|
|
|
if (!file.open(lpszFileName)) {
|
2012-06-16 09:32:50 +00:00
|
|
|
warning("FPSfx::LoadFile(): Cannot open sfx file!");
|
2012-05-03 11:43:00 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-06-11 01:23:28 +00:00
|
|
|
if (file.readUint32BE() != MKTAG('A', 'D', 'P', 0x10)) {
|
2012-06-16 09:32:50 +00:00
|
|
|
warning("FPSfx::LoadFile(): Invalid ADP header!");
|
2012-05-03 11:43:00 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-06-11 01:23:28 +00:00
|
|
|
uint32 rate = file.readUint32LE();
|
|
|
|
uint32 channels = file.readUint32LE();
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-11 01:23:28 +00:00
|
|
|
Common::SeekableReadStream *buffer = file.readStream(file.size() - file.pos());
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-11 01:23:28 +00:00
|
|
|
if (dwCodec == FPCODEC_ADPCM) {
|
2012-06-11 02:03:52 +00:00
|
|
|
_rewindableStream = Audio::makeADPCMStream(buffer, DisposeAfterUse::YES, 0, Audio::kADPCMDVI, rate, channels);
|
2012-06-11 01:23:28 +00:00
|
|
|
} else {
|
|
|
|
byte flags = Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN;
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-11 01:23:28 +00:00
|
|
|
if (channels == 2)
|
|
|
|
flags |= Audio::FLAG_STEREO;
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-11 02:03:52 +00:00
|
|
|
_rewindableStream = Audio::makeRawStream(buffer, rate, flags, DisposeAfterUse::YES);
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-16 17:04:19 +00:00
|
|
|
_bFileLoaded = true;
|
2012-05-03 11:43:00 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Play the Sfx in memory.
|
|
|
|
*
|
|
|
|
* @returns True is everything is OK, False otherwise
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
bool FPSfx::play() {
|
|
|
|
stop(); // sanity check
|
2012-06-11 01:23:28 +00:00
|
|
|
|
2012-06-16 17:04:19 +00:00
|
|
|
if (_bFileLoaded) {
|
2012-06-11 01:23:28 +00:00
|
|
|
// FIXME
|
|
|
|
//if (hEndOfBuffer != CORO_INVALID_PID_VALUE)
|
|
|
|
// ResetEvent(hEndOfBuffer);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-11 02:03:52 +00:00
|
|
|
_rewindableStream->rewind();
|
|
|
|
|
2012-06-12 15:35:27 +00:00
|
|
|
Audio::AudioStream *stream = _rewindableStream;
|
|
|
|
|
2012-06-16 17:04:19 +00:00
|
|
|
if (_bLoop) {
|
2012-06-12 15:35:27 +00:00
|
|
|
if (!_loopStream)
|
|
|
|
_loopStream = Audio::makeLoopingAudioStream(_rewindableStream, 0);
|
|
|
|
|
|
|
|
stream = _loopStream;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, stream, -1,
|
2012-06-11 01:23:28 +00:00
|
|
|
Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 17:04:19 +00:00
|
|
|
setVolume(_lastVolume);
|
2012-06-11 01:23:28 +00:00
|
|
|
|
2012-06-16 17:04:19 +00:00
|
|
|
if (_bPaused)
|
2012-06-11 01:23:28 +00:00
|
|
|
g_system->getMixer()->pauseHandle(_handle, true);
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
2012-06-11 01:23:28 +00:00
|
|
|
|
2012-05-03 11:43:00 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Stops a Sfx.
|
|
|
|
*
|
|
|
|
* @returns True is everything is OK, False otherwise
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
bool FPSfx::stop() {
|
2012-06-16 17:04:19 +00:00
|
|
|
if (_bFileLoaded) {
|
2012-06-11 01:23:28 +00:00
|
|
|
g_system->getMixer()->stopHandle(_handle);
|
2012-06-16 17:04:19 +00:00
|
|
|
_bPaused = false;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
2012-06-11 01:23:28 +00:00
|
|
|
|
2012-05-03 11:43:00 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Enables or disables the Sfx loop.
|
|
|
|
*
|
2012-06-19 10:50:48 +00:00
|
|
|
* @param _bLoop True to enable the loop, False to disable
|
2012-06-17 16:09:52 +00:00
|
|
|
*
|
|
|
|
* @remarks The loop must be activated BEFORE the sfx starts
|
|
|
|
* playing. Any changes made during the play will have
|
|
|
|
* no effect until the sfx is stopped then played again.
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
void FPSfx::setLoop(bool bLop) {
|
2012-06-16 17:04:19 +00:00
|
|
|
_bLoop = bLop;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Pauses a Sfx.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
void FPSfx::pause(bool bPause) {
|
2012-06-16 17:04:19 +00:00
|
|
|
if (_bFileLoaded) {
|
|
|
|
if (g_system->getMixer()->isSoundHandleActive(_handle) && (bPause ^ _bPaused))
|
2012-06-11 01:23:28 +00:00
|
|
|
g_system->getMixer()->pauseHandle(_handle, bPause);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 17:04:19 +00:00
|
|
|
_bPaused = bPause;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Change the volume of Sfx
|
|
|
|
*
|
|
|
|
* @param dwVolume Volume to be set (0-63)
|
|
|
|
*
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
void FPSfx::setVolume(int dwVolume) {
|
|
|
|
if (dwVolume > 63)
|
|
|
|
dwVolume = 63;
|
|
|
|
if (dwVolume < 0)
|
|
|
|
dwVolume = 0;
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 17:04:19 +00:00
|
|
|
_lastVolume = dwVolume;
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 17:04:19 +00:00
|
|
|
if (_bIsVoice) {
|
2012-06-16 09:32:50 +00:00
|
|
|
if (!GLOBALS._bCfgDubbing)
|
|
|
|
dwVolume = 0;
|
2012-05-03 11:43:00 +00:00
|
|
|
else {
|
2012-06-08 22:52:38 +00:00
|
|
|
dwVolume -= (10 - GLOBALS._nCfgDubbingVolume) * 2;
|
2012-05-21 21:53:13 +00:00
|
|
|
if (dwVolume < 0) dwVolume = 0;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
} else {
|
2012-06-16 09:32:50 +00:00
|
|
|
if (!GLOBALS._bCfgSFX)
|
|
|
|
dwVolume = 0;
|
2012-05-03 11:43:00 +00:00
|
|
|
else {
|
2012-06-08 22:52:38 +00:00
|
|
|
dwVolume -= (10 - GLOBALS._nCfgSFXVolume) * 2;
|
2012-06-16 09:32:50 +00:00
|
|
|
if (dwVolume < 0)
|
|
|
|
dwVolume = 0;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
}
|
2012-06-11 01:23:28 +00:00
|
|
|
|
|
|
|
if (g_system->getMixer()->isSoundHandleActive(_handle))
|
|
|
|
g_system->getMixer()->setChannelVolume(_handle, dwVolume * Audio::Mixer::kMaxChannelVolume / 63);
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Gets the Sfx volume
|
|
|
|
*
|
|
|
|
* @param lpdwVolume Will contain the current Sfx volume
|
|
|
|
*
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
void FPSfx::getVolume(int *lpdwVolume) {
|
2012-06-11 01:23:28 +00:00
|
|
|
if (g_system->getMixer()->isSoundHandleActive(_handle))
|
|
|
|
*lpdwVolume = g_system->getMixer()->getChannelVolume(_handle) * 63 / Audio::Mixer::kMaxChannelVolume;
|
|
|
|
else
|
|
|
|
*lpdwVolume = 0;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-11 02:20:12 +00:00
|
|
|
/**
|
|
|
|
* Returns true if the underlying sound has ended
|
|
|
|
*/
|
2012-06-17 16:09:52 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
bool FPSfx::endOfBuffer() const {
|
2012-06-12 15:35:27 +00:00
|
|
|
return !g_system->getMixer()->isSoundHandleActive(_handle) && (!_rewindableStream || _rewindableStream->endOfData());
|
2012-06-11 02:20:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Continually checks to see if active sounds have finished playing
|
|
|
|
* Sets the event signalling the sound has ended
|
|
|
|
*/
|
2012-06-16 09:32:50 +00:00
|
|
|
void FPSfx::soundCheckProcess(CORO_PARAM, const void *param) {
|
2012-06-11 02:20:12 +00:00
|
|
|
CORO_BEGIN_CONTEXT;
|
2012-06-16 09:32:50 +00:00
|
|
|
Common::List<FPSfx *>::iterator i;
|
2012-06-11 02:20:12 +00:00
|
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
// Check each active sound
|
|
|
|
for (_ctx->i = _vm->_activeSfx.begin(); _ctx->i != _vm->_activeSfx.end(); ++_ctx->i) {
|
2012-06-16 09:32:50 +00:00
|
|
|
FPSfx *sfx = *_ctx->i;
|
2012-06-11 02:20:12 +00:00
|
|
|
if (sfx->endOfBuffer())
|
2012-06-16 17:04:19 +00:00
|
|
|
CoroScheduler.setEvent(sfx->_hEndOfBuffer);
|
2012-06-11 02:20:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Delay until the next check is done
|
|
|
|
CORO_INVOKE_1(CoroScheduler.sleep, 50);
|
|
|
|
}
|
|
|
|
|
|
|
|
CORO_END_CODE;
|
|
|
|
}
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Default constructor.
|
|
|
|
*
|
|
|
|
* @remarks Do *NOT* declare an object directly, but rather
|
|
|
|
* create it using FPSound::CreateStream()
|
|
|
|
*/
|
2012-06-16 09:32:50 +00:00
|
|
|
FPStream::FPStream(bool bSoundOn) {
|
2012-05-03 12:49:30 +00:00
|
|
|
#ifdef REFACTOR_ME
|
2012-06-19 10:50:48 +00:00
|
|
|
hwnd=hWnd;
|
2012-05-21 21:53:13 +00:00
|
|
|
lpDS = LPDS;
|
2012-06-19 10:50:48 +00:00
|
|
|
_lpDSBuffer = NULL;
|
|
|
|
_lpDSNotify = NULL;
|
2012-05-03 12:49:30 +00:00
|
|
|
#endif
|
2012-06-19 10:50:48 +00:00
|
|
|
_bSoundSupported = bSoundOn;
|
|
|
|
_bFileLoaded = false;
|
|
|
|
_bIsPlaying = false;
|
|
|
|
_bPaused = false;
|
|
|
|
_bSyncExit = false;
|
|
|
|
_hHot1 = _hHot2 = _hHot3 = _hPlayThreadPlayFast = _hPlayThreadPlayNormal = CORO_INVALID_PID_VALUE;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
bool FPStream::createBuffer(int nBufSize) {
|
2012-05-03 11:43:00 +00:00
|
|
|
#ifdef REFACTOR_ME
|
|
|
|
static PCMWAVEFORMAT pcmwf;
|
|
|
|
static DSBUFFERDESC dsbdesc;
|
|
|
|
static HRESULT err;
|
|
|
|
static char errbuf[128];
|
|
|
|
|
|
|
|
if (bSoundSupported == false)
|
|
|
|
return true;
|
|
|
|
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Set the required structures for the creation of a secondary buffer for the stream containing exactly 1 second of music.
|
|
|
|
Also activate the volume control, in order to lower and raise the volume of the music regardless of the general volume.
|
|
|
|
Obviously it is a buffer in RAM */
|
|
|
|
|
2012-05-03 11:43:00 +00:00
|
|
|
pcmwf.wBitsPerSample = 16;
|
2012-05-21 21:53:13 +00:00
|
|
|
pcmwf.wf.wFormatTag = WAVE_FORMAT_PCM;
|
2012-05-03 11:43:00 +00:00
|
|
|
pcmwf.wf.nChannels = 2;
|
|
|
|
pcmwf.wf.nSamplesPerSec = 44100;
|
|
|
|
pcmwf.wf.nBlockAlign = (pcmwf.wBitsPerSample / 8) * pcmwf.wf.nChannels;
|
|
|
|
pcmwf.wf.nAvgBytesPerSec = (uint32)pcmwf.wf.nBlockAlign * (uint32)pcmwf.wf.nSamplesPerSec;
|
|
|
|
|
|
|
|
dsbdesc.dwSize = sizeof(dsbdesc);
|
|
|
|
dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY;
|
2012-05-21 21:53:13 +00:00
|
|
|
dwBufferSize = dsbdesc.dwBufferBytes = (((uint32)(pcmwf.wf.nAvgBytesPerSec * nBufSize) / 1000 + 31) / 32) * 32;
|
2012-05-03 11:43:00 +00:00
|
|
|
dsbdesc.lpwfxFormat = (LPWAVEFORMATEX) & pcmwf;
|
|
|
|
|
|
|
|
if ((err = lpDS->CreateSoundBuffer(&dsbdesc, &lpDSBuffer, NULL)) != DS_OK) {
|
2012-05-21 21:53:13 +00:00
|
|
|
wsprintf(errbuf, "Error creating the secondary buffer (%lx)", err);
|
2012-06-16 09:32:50 +00:00
|
|
|
MessageBox(hwnd, errbuf, "FPStream::FPStream()", MB_OK);
|
2012-05-03 11:43:00 +00:00
|
|
|
bSoundSupported = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
SetVolume(63);
|
|
|
|
|
|
|
|
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Create an alert when key positions are reached in the stream.
|
|
|
|
Key positions are located, respectively, immediately after
|
|
|
|
the start and immediately after the middle of the buffer */
|
2012-05-21 21:53:13 +00:00
|
|
|
err = lpDSBuffer->QueryInterface(IID_IDirectSoundNotify, (void **)&lpDSNotify);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
|
|
|
if (FAILED(err)) {
|
2012-05-21 21:53:13 +00:00
|
|
|
wsprintf(errbuf, "Error creating notify object! (%lx)", err);
|
2012-06-16 09:32:50 +00:00
|
|
|
MessageBox(hwnd, errbuf, "FPStream::FPStream()", MB_OK);
|
2012-05-03 11:43:00 +00:00
|
|
|
bSoundSupported = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-05-21 21:53:13 +00:00
|
|
|
hHot1 = CreateEvent(NULL, false, false, NULL);
|
|
|
|
hHot2 = CreateEvent(NULL, false, false, NULL);
|
|
|
|
hHot3 = CreateEvent(NULL, false, false, NULL);
|
2012-05-03 11:43:00 +00:00
|
|
|
hPlayThread_PlayFast = CreateEvent(NULL, false, false, NULL);
|
|
|
|
|
|
|
|
dspnHot[0].dwOffset = 32;
|
|
|
|
dspnHot[0].hEventNotify = hHot1;
|
|
|
|
|
|
|
|
dspnHot[1].dwOffset = dwBufferSize / 2 + 32;
|
|
|
|
dspnHot[1].hEventNotify = hHot2;
|
|
|
|
|
|
|
|
dspnHot[2].dwOffset = dwBufferSize - 32; //DSBPN_OFFSETSTOP;
|
|
|
|
dspnHot[2].hEventNotify = hHot3;
|
|
|
|
|
2012-05-21 21:53:13 +00:00
|
|
|
lpDSNotify->SetNotificationPositions(3, dspnHot);
|
2012-05-03 11:43:00 +00:00
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Default destructor.
|
|
|
|
*
|
|
|
|
* @remarks It calls CloseFile() if needed.
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
FPStream::~FPStream() {
|
2012-06-19 10:50:48 +00:00
|
|
|
if (!_bSoundSupported)
|
2012-05-03 11:43:00 +00:00
|
|
|
return;
|
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
if (_bIsPlaying)
|
|
|
|
stop();
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
if (_bFileLoaded)
|
|
|
|
unloadFile();
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
if (_hHot1) {
|
|
|
|
CoroScheduler.closeEvent(_hHot1);
|
|
|
|
_hHot1 = CORO_INVALID_PID_VALUE;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
2012-06-19 10:50:48 +00:00
|
|
|
if (_hHot2) {
|
|
|
|
CoroScheduler.closeEvent(_hHot2);
|
|
|
|
_hHot2 = CORO_INVALID_PID_VALUE;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
2012-06-19 10:50:48 +00:00
|
|
|
if (_hHot3) {
|
|
|
|
CoroScheduler.closeEvent(_hHot3);
|
|
|
|
_hHot3 = CORO_INVALID_PID_VALUE;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
2012-06-19 10:50:48 +00:00
|
|
|
if (_hPlayThreadPlayFast != CORO_INVALID_PID_VALUE) {
|
|
|
|
CoroScheduler.closeEvent(_hPlayThreadPlayFast);
|
|
|
|
_hPlayThreadPlayFast = CORO_INVALID_PID_VALUE;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
2012-06-19 10:50:48 +00:00
|
|
|
if (_hPlayThreadPlayNormal != CORO_INVALID_PID_VALUE) {
|
|
|
|
CoroScheduler.closeEvent(_hPlayThreadPlayNormal);
|
|
|
|
_hPlayThreadPlayNormal = CORO_INVALID_PID_VALUE;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
_syncToPlay = NULL;
|
|
|
|
#ifdef REFACTOR_ME
|
2012-05-03 11:43:00 +00:00
|
|
|
RELEASE(lpDSNotify);
|
|
|
|
RELEASE(lpDSBuffer);
|
2012-05-03 12:49:30 +00:00
|
|
|
#endif
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Releases the memory object.
|
|
|
|
*
|
|
|
|
* @remarks Must be called when the object is no longer used
|
|
|
|
* and **ONLY** if the object was created by
|
|
|
|
* FPSound::CreateStream().
|
|
|
|
* Object pointers are no longer valid after this call.
|
|
|
|
*/
|
2012-06-16 09:32:50 +00:00
|
|
|
void FPStream::release() {
|
2012-05-03 11:43:00 +00:00
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Opens a file stream
|
|
|
|
*
|
2012-06-19 10:50:48 +00:00
|
|
|
* @param fileName Filename to be opened
|
2012-06-17 16:09:52 +00:00
|
|
|
* @param dwCodec CODEC to be used to uncompress samples
|
|
|
|
*
|
|
|
|
* @returns True is everything is OK, False otherwise
|
|
|
|
*/
|
2012-06-19 10:50:48 +00:00
|
|
|
bool FPStream::loadFile(const Common::String &fileName, uint32 dwCodType, int nBufSize) {
|
|
|
|
if (!_bSoundSupported)
|
2012-05-03 11:43:00 +00:00
|
|
|
return true;
|
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
// Save the codec type
|
|
|
|
_dwCodec = dwCodType;
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
// Create the buffer
|
|
|
|
if (!createBuffer(nBufSize))
|
2012-05-03 11:43:00 +00:00
|
|
|
return true;
|
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
// Open the file stream for reading
|
|
|
|
if (!_file.open(fileName))
|
2012-05-03 11:43:00 +00:00
|
|
|
return false;
|
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
// Save the size of the stream
|
|
|
|
_dwSize = _file.size();
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
// All done
|
|
|
|
_bFileLoaded = true;
|
|
|
|
_bIsPlaying = false;
|
|
|
|
_bPaused = false;
|
|
|
|
|
|
|
|
return true;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Closes a file stream (opened or not).
|
|
|
|
*
|
|
|
|
* @returns For safety, the destructor calls unloadFile() if it has not
|
|
|
|
* been mentioned explicitly.
|
|
|
|
*
|
|
|
|
* @remarks It is necessary to call this function to free the
|
|
|
|
* memory used by the stream.
|
|
|
|
*/
|
2012-06-16 09:32:50 +00:00
|
|
|
bool FPStream::unloadFile() {
|
2012-06-19 10:50:48 +00:00
|
|
|
if (!_bSoundSupported || !_bFileLoaded)
|
2012-05-03 11:43:00 +00:00
|
|
|
return true;
|
|
|
|
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Closes the file handle stream */
|
2012-05-03 11:43:00 +00:00
|
|
|
_file.close();
|
2012-06-19 10:50:48 +00:00
|
|
|
#ifdef REFACTOR_ME
|
2012-05-03 11:43:00 +00:00
|
|
|
RELEASE(lpDSNotify);
|
|
|
|
RELEASE(lpDSBuffer);
|
2012-05-03 12:49:30 +00:00
|
|
|
#endif
|
2012-06-19 10:50:48 +00:00
|
|
|
|
|
|
|
// Flag that the file is no longer in memory
|
|
|
|
_bFileLoaded = false;
|
|
|
|
|
2012-05-03 11:43:00 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-06-18 06:24:33 +00:00
|
|
|
void FPStream::prefetch() {
|
2012-06-19 11:19:17 +00:00
|
|
|
void *lpBuf = NULL;
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
if (!_bSoundSupported || !_bFileLoaded)
|
2012-05-03 11:43:00 +00:00
|
|
|
return;
|
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
// Allocates a temporary buffer
|
|
|
|
_lpTempBuffer = (byte *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, _dwBufferSize / 2);
|
|
|
|
if (_lpTempBuffer == NULL)
|
2012-05-03 11:43:00 +00:00
|
|
|
return;
|
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
if (_dwCodec == FPCODEC_RAW) {
|
|
|
|
_codec = new CODECRAW(_bLoop);
|
|
|
|
} else if (_dwCodec == FPCODEC_ADPCM) {
|
|
|
|
_codec = new CODECADPCMSTEREO(_bLoop);
|
|
|
|
} else {
|
2012-05-03 11:43:00 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
// reset the file position
|
2012-05-03 11:43:00 +00:00
|
|
|
_file.seek(0);
|
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
#ifdef REFACTOR_ME
|
|
|
|
// Fills the buffer for the data already ready
|
2012-05-03 11:43:00 +00:00
|
|
|
if ((err = lpDSBuffer->Lock(0, dwBufferSize / 2, &lpBuf, (uint32 *)&dwHi, NULL, NULL, 0)) != DS_OK) {
|
2012-06-19 10:50:48 +00:00
|
|
|
_vm->GUIError("Cannot lock stream buffer!", "soundLoadStream()");
|
2012-05-03 11:43:00 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-06-19 11:19:17 +00:00
|
|
|
#else
|
|
|
|
error("lpBuf isn't set");
|
2012-06-19 10:50:48 +00:00
|
|
|
#endif
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
// Uncompress the data from the stream directly into the locked buffer
|
|
|
|
_codec->decompress(_file.readStream(_file.size()), lpBuf, _dwBufferSize / 2);
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
// Unlock the buffer
|
|
|
|
#ifdef REFACTOR_ME
|
|
|
|
lpDSBuffer->unlock(lpBuf, _dwBufferSize / 2, NULL, NULL);
|
|
|
|
#endif
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
// Create a thread to play the stream
|
|
|
|
_hThreadEnd = CoroScheduler.createEvent(false, false);
|
|
|
|
_hPlayThread = CoroScheduler.createProcess(playThread, this, sizeof(FPStream *));
|
|
|
|
|
|
|
|
// Start to play the buffer
|
|
|
|
#ifdef REFACTOR_ME
|
|
|
|
lpDSBuffer->setCurrentPosition(0);
|
|
|
|
#endif
|
|
|
|
_bIsPlaying = true;
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
#ifdef REFACTOR_ME
|
|
|
|
_dspnHot[0].dwOffset = 32;
|
|
|
|
_dspnHot[0].hEventNotify = _hHot1;
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
_dspnHot[1].dwOffset = dwBufferSize / 2 + 32;
|
|
|
|
_dspnHot[1].hEventNotify = _hHot2;
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
_dspnHot[2].dwOffset = dwBufferSize - 32; //DSBPN_OFFSETSTOP;
|
|
|
|
_dspnHot[2].hEventNotify = _hHot3;
|
2012-05-21 21:53:13 +00:00
|
|
|
|
|
|
|
if (FAILED(lpDSNotify->SetNotificationPositions(3, dspnHot))) {
|
|
|
|
int a = 1;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-06-18 06:24:33 +00:00
|
|
|
void FPStream::playFast() {
|
2012-05-03 11:43:00 +00:00
|
|
|
#ifdef REFACTOR_ME
|
|
|
|
dspnHot[0].dwOffset = 32;
|
|
|
|
dspnHot[0].hEventNotify = hHot1;
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-05-03 11:43:00 +00:00
|
|
|
dspnHot[1].dwOffset = dwBufferSize / 2 + 32;
|
|
|
|
dspnHot[1].hEventNotify = hHot2;
|
2012-05-21 21:53:13 +00:00
|
|
|
|
|
|
|
dspnHot[2].dwOffset = dwBufferSize - 32; //DSBPN_OFFSETSTOP;
|
2012-05-03 11:43:00 +00:00
|
|
|
dspnHot[2].hEventNotify = hHot3;
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-05-03 11:43:00 +00:00
|
|
|
lpDSBuffer->Stop();
|
|
|
|
|
2012-05-21 21:53:13 +00:00
|
|
|
if (FAILED(lpDSNotify->SetNotificationPositions(3, dspnHot))) {
|
2012-05-03 11:43:00 +00:00
|
|
|
warning("PlayFast SNP failed!");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FAILED(lpDSBuffer->Play(0, 0, DSBPLAY_LOOPING))) {
|
|
|
|
warning("PlayFast failed!\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Play the stream.
|
|
|
|
*
|
|
|
|
* @returns True is everything is OK, False otherwise
|
|
|
|
*/
|
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
bool FPStream::play() {
|
2012-05-03 11:43:00 +00:00
|
|
|
#ifdef REFACTOR_ME
|
|
|
|
uint32 dwId;
|
|
|
|
void *lpBuf;
|
|
|
|
uint32 dwHi;
|
|
|
|
HRESULT err;
|
|
|
|
|
|
|
|
if (!bSoundSupported || !bFileLoaded)
|
|
|
|
return false;
|
|
|
|
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Allocate a temporary buffer */
|
2012-05-03 11:43:00 +00:00
|
|
|
lpTempBuffer = (byte *)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, dwBufferSize / 2);
|
|
|
|
if (lpTempBuffer == NULL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (dwCodec) {
|
|
|
|
case FPCODEC_RAW:
|
2012-06-19 10:50:48 +00:00
|
|
|
_codec = new CODECRAW(_bLoop);
|
2012-05-03 11:43:00 +00:00
|
|
|
break;
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-05-03 11:43:00 +00:00
|
|
|
case FPCODEC_ADPCM:
|
2012-06-19 10:50:48 +00:00
|
|
|
_codec = new CODECADPCMSTEREO(_bLoop);
|
2012-05-03 11:43:00 +00:00
|
|
|
break;
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-05-03 11:43:00 +00:00
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Reset the file position */
|
2012-05-03 11:43:00 +00:00
|
|
|
_file.seek(0);
|
|
|
|
lpDSBuffer->Stop();
|
|
|
|
lpDSBuffer->SetCurrentPosition(0);
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Fills the buffer for the data already ready */
|
2012-05-21 21:53:13 +00:00
|
|
|
if ((err = lpDSBuffer->Lock(0, dwBufferSize / 2, &lpBuf, (uint32 *)&dwHi, NULL, NULL, 0)) != DS_OK) {
|
|
|
|
error("Cannot lock stream buffer!", "soundLoadStream()");
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Uncompress the data from the stream directly into the locked buffer */
|
2012-06-19 10:50:48 +00:00
|
|
|
_codec->Decompress(hFile, lpBuf, dwBufferSize / 2);
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Unlock the buffer */
|
2012-05-03 11:43:00 +00:00
|
|
|
lpDSBuffer->Unlock(lpBuf, dwBufferSize / 2, NULL, NULL);
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Create a thread to play the stream */
|
2012-05-03 11:43:00 +00:00
|
|
|
hThreadEnd = CreateEvent(NULL, false, false, NULL);
|
|
|
|
hPlayThread = CreateThread(NULL, 10240, (LPTHREAD_START_ROUTINE)PlayThread, (void *)this, 0, &dwId);
|
|
|
|
SetThreadPriority(hPlayThread, THREAD_PRIORITY_HIGHEST);
|
|
|
|
|
|
|
|
SetEvent(hPlayThread_PlayFast);
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-05-03 11:43:00 +00:00
|
|
|
#if 0
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Start to play the buffer */
|
2012-05-21 21:53:13 +00:00
|
|
|
lpDSBuffer->SetCurrentPosition(0);
|
|
|
|
|
|
|
|
dspnHot[0].dwOffset = 32;
|
|
|
|
dspnHot[0].hEventNotify = hHot1;
|
|
|
|
|
|
|
|
dspnHot[1].dwOffset = dwBufferSize / 2 + 32;
|
|
|
|
dspnHot[1].hEventNotify = hHot2;
|
|
|
|
|
|
|
|
dspnHot[2].dwOffset = dwBufferSize - 32; //DSBPN_OFFSETSTOP;
|
|
|
|
dspnHot[2].hEventNotify = hHot3;
|
|
|
|
|
|
|
|
if (FAILED(lpDSNotify->SetNotificationPositions(3, dspnHot))) {
|
|
|
|
int a = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
lpDSBuffer->Play(0, 0, DSBPLAY_LOOPING);
|
2012-05-03 11:43:00 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
bIsPlaying = true;
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Closes the stream.
|
|
|
|
*
|
|
|
|
* @returns True is everything is OK, False otherwise
|
|
|
|
*
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
bool FPStream::stop(bool bSync) {
|
2012-05-03 12:49:30 +00:00
|
|
|
#ifdef REFACTOR_ME
|
|
|
|
|
2012-05-03 11:43:00 +00:00
|
|
|
if (!bSoundSupported)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!bFileLoaded)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!bIsPlaying)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (bSync) {
|
|
|
|
// bSyncExit = true;
|
|
|
|
// lpDSBuffer->Stop();
|
|
|
|
// lpDSBuffer->Play(0, 0, 0);
|
|
|
|
return true;
|
|
|
|
} else {
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Close the DirectSound buffer */
|
2012-05-03 11:43:00 +00:00
|
|
|
lpDSBuffer->Stop();
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Notify the thread is should stop */
|
2012-05-03 11:43:00 +00:00
|
|
|
SetEvent(hThreadEnd);
|
2012-05-11 13:15:59 +00:00
|
|
|
WaitForSingleObject(hPlayThread, CORO_INFINITE);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Closes the handle used by the stream and free its memory */
|
2012-05-03 11:43:00 +00:00
|
|
|
CloseHandle(hPlayThread);
|
|
|
|
CloseHandle(hThreadEnd);
|
|
|
|
GlobalFree(lpTempBuffer);
|
|
|
|
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Close and free the CODEC */
|
2012-06-19 10:50:48 +00:00
|
|
|
delete _codec;
|
2012-05-03 11:43:00 +00:00
|
|
|
|
|
|
|
bIsPlaying = false;
|
|
|
|
bPaused = false;
|
|
|
|
}
|
2012-05-03 12:49:30 +00:00
|
|
|
#endif
|
2012-05-03 11:43:00 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
void FPStream::waitForSync(FPStream *toplay) {
|
2012-05-03 11:43:00 +00:00
|
|
|
#ifdef REFACTOR_ME
|
|
|
|
if (!bSoundSupported)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!bFileLoaded)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!bIsPlaying)
|
|
|
|
return;
|
|
|
|
|
|
|
|
SyncToPlay = toplay;
|
|
|
|
bSyncExit = true;
|
|
|
|
|
|
|
|
char buf[1024];
|
2012-05-21 21:53:13 +00:00
|
|
|
sprintf(buf, "Wait for sync: %x (SyncToPlay: [%x]=%x, SyncExit: [%x]=%d) MyThread: 0x%x\n",
|
|
|
|
this->lpDSBuffer, &this->SyncToPlay, SyncToPlay, &bSyncExit, bSyncExit, GetCurrentThreadId());
|
2012-05-03 11:43:00 +00:00
|
|
|
warning(buf);
|
|
|
|
|
2012-05-11 13:15:59 +00:00
|
|
|
WaitForSingleObject(hPlayThread, CORO_INFINITE);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Closes the handle used by the stream and free its memory */
|
2012-05-03 11:43:00 +00:00
|
|
|
CloseHandle(hPlayThread);
|
|
|
|
CloseHandle(hThreadEnd);
|
|
|
|
GlobalFree(lpTempBuffer);
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-16 21:48:16 +00:00
|
|
|
/* Close and free the CODEC */
|
2012-06-19 10:50:48 +00:00
|
|
|
delete _codec;
|
2012-05-03 11:43:00 +00:00
|
|
|
#endif
|
2012-06-16 17:04:19 +00:00
|
|
|
_bIsPlaying = false;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Thread playing the stream
|
|
|
|
*
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
void FPStream::playThread(CORO_PARAM, const void *param) {
|
|
|
|
CORO_BEGIN_CONTEXT;
|
|
|
|
byte *lpLockBuf;
|
|
|
|
uint32 dwResult;
|
|
|
|
byte *lpLockBuf2;
|
|
|
|
uint32 dwResult2;
|
|
|
|
bool cicla;
|
|
|
|
uint32 countEnd;
|
|
|
|
bool bPrecache;
|
|
|
|
char buf[1024];
|
|
|
|
uint32 hList[5];
|
|
|
|
CORO_END_CONTEXT(_ctx);
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
// FPStream *This = *(FPStream **)param;
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
#ifdef REFACTOR_ME
|
|
|
|
// Events that signal when you need to do something
|
|
|
|
_ctx->hList[0] = This->_hThreadEnd;
|
|
|
|
_ctx->hList[1] = This->_hHot1;
|
|
|
|
_ctx->hList[2] = This->_hHot2;
|
|
|
|
_ctx->hList[3] = This->_hHot3;
|
|
|
|
_ctx->hList[4] = This->_hPlayThreadPlayFast;
|
|
|
|
|
|
|
|
_ctx->cicla = true;
|
|
|
|
_ctx->bPrecache = true;
|
|
|
|
_ctx->countEnd = 0;
|
|
|
|
while (_ctx->cicla) {
|
|
|
|
if (This->_codec->endOfStream() && This->_codec->_bLoop == false) {
|
|
|
|
_ctx->countEnd++;
|
|
|
|
if (_ctx->countEnd == 3)
|
2012-05-03 11:43:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
// Uncompresses the data being written into the temporary buffer
|
|
|
|
if (This->_lastVolume == 0)
|
|
|
|
ZeroMemory(This->_lpTempBuffer, This->_dwBufferSize / 2);
|
|
|
|
else if (_ctx->bPrecache)
|
|
|
|
This->_codec->decompress(This->_file.readStream(This->_file.size()), This->_lpTempBuffer, This->_dwBufferSize / 2);
|
|
|
|
|
|
|
|
_ctx->bPrecache = false;
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
// Waits for an event. Since they are all in automatic reset, there is no need to reset it after
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
uint32 dwBufStatus;
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
CORO_INVOKE_4(CoroScheduler.waitForMultipleObjects, 5, _ctx->hList, false, CORO_INFINITE, &_ctx->dwResult);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
// Check to determine which event has been set
|
|
|
|
if (CoroScheduler.getEvent(This->_hThreadEnd)->signalled) {
|
|
|
|
/* Must leave the thread */
|
|
|
|
_ctx->cicla = false;
|
|
|
|
|
|
|
|
} else if (CoroScheduler.getEvent(This->_hHot1)->signalled) {
|
|
|
|
// Must fill the second half of the buffer
|
|
|
|
This->lpDSBuffer->Lock(This->_dwBufferSize / 2, This->_dwBufferSize / 2, (void **)&_ctx->lpLockBuf, &_ctx->dwResult, (void **)&_ctx->lpLockBuf2, &_ctx->dwResult2, 0);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
copyMemory(_ctx->lpLockBuf, This->_lpTempBuffer, This->_dwBufferSize / 2);
|
|
|
|
This->lpDSBuffer->Unlock(_ctx->lpLockBuf, This->_dwBufferSize / 2, _ctx->lpLockBuf2, 0);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
_ctx->bPrecache = true;
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
} else if (CoroScheduler.getEvent(This->_hHot2)->signalled) {
|
|
|
|
This->lpDSBuffer->Lock(0, This->_dwBufferSize / 2, (void **)&_ctx->lpLockBuf, &_ctx->dwResult, NULL, NULL, 0);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
copyMemory(_ctx->lpLockBuf, This->_lpTempBuffer, This->_dwBufferSize / 2);
|
|
|
|
This->lpDSBuffer->Unlock(_ctx->lpLockBuf, This->_dwBufferSize / 2, NULL, NULL);
|
|
|
|
|
|
|
|
_ctx->bPrecache = true;
|
|
|
|
|
|
|
|
} else if (CoroScheduler.getEvent(This->_hHot3)->signalled) {
|
|
|
|
|
|
|
|
if (This->_bSyncExit) {
|
|
|
|
CoroScheduler.setEvent(This->_syncToPlay->_hPlayThreadPlayFast);
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-05-21 21:53:13 +00:00
|
|
|
// Transfer immediatly control to the other threads
|
2012-06-19 10:50:48 +00:00
|
|
|
CORO_SLEEP(1);
|
|
|
|
|
|
|
|
This->_bSyncExit = false;
|
|
|
|
_ctx->cicla = false;
|
2012-05-21 21:53:13 +00:00
|
|
|
break;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
2012-06-19 10:50:48 +00:00
|
|
|
} else if (CoroScheduler.getEvent(This->_hPlayThreadPlayFast)->signalled) {
|
|
|
|
This->playFast();
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
// Close the DirectSound buffer
|
2012-05-03 11:43:00 +00:00
|
|
|
This->lpDSBuffer->Stop();
|
2012-05-08 14:42:27 +00:00
|
|
|
#endif
|
2012-06-19 10:50:48 +00:00
|
|
|
|
|
|
|
CORO_END_CODE;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Unables or disables stream loop.
|
|
|
|
*
|
2012-06-19 10:50:48 +00:00
|
|
|
* @param _bLoop True enable loop, False disables it
|
2012-06-17 16:09:52 +00:00
|
|
|
*
|
|
|
|
* @remarks The loop must be activated BEFORE the stream starts
|
|
|
|
* playing. Any changes made during the play will have no
|
|
|
|
* effect until the stream is stopped then played again.
|
|
|
|
*/
|
2012-06-16 09:32:50 +00:00
|
|
|
void FPStream::setLoop(bool loop) {
|
2012-06-16 17:04:19 +00:00
|
|
|
_bLoop = loop;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Pause sound effect
|
|
|
|
*
|
|
|
|
* @param bPause True enables pause, False disables it
|
|
|
|
*/
|
2012-06-16 09:32:50 +00:00
|
|
|
void FPStream::pause(bool bPause) {
|
2012-06-19 10:50:48 +00:00
|
|
|
if (_bFileLoaded) {
|
|
|
|
if (bPause && _bIsPlaying) {
|
2012-05-03 12:49:30 +00:00
|
|
|
#ifdef REFACTOR_ME
|
2012-06-19 10:50:48 +00:00
|
|
|
_lpDSBuffer->Stop();
|
|
|
|
#endif
|
|
|
|
_bIsPlaying = false;
|
|
|
|
_bPaused = true;
|
|
|
|
} else if (!bPause && _bPaused) {
|
|
|
|
#ifdef REFACTOR_ME
|
|
|
|
_dspnHot[0].dwOffset = 32;
|
|
|
|
_dspnHot[0].hEventNotify = hHot1;
|
2012-05-03 12:49:30 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
_dspnHot[1].dwOffset = dwBufferSize / 2 + 32;
|
|
|
|
_dspnHot[1].hEventNotify = hHot2;
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
_dspnHot[2].dwOffset = dwBufferSize - 32; //DSBPN_OFFSETSTOP;
|
|
|
|
_dspnHot[2].hEventNotify = hHot3;
|
2012-05-21 21:53:13 +00:00
|
|
|
|
|
|
|
if (FAILED(lpDSNotify->SetNotificationPositions(3, dspnHot))) {
|
2012-05-03 11:43:00 +00:00
|
|
|
int a = 1;
|
|
|
|
}
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-06-19 10:50:48 +00:00
|
|
|
lpDSBuffer->Play(0, 0, _bLoop);
|
|
|
|
#endif
|
|
|
|
_bIsPlaying = true;
|
|
|
|
_bPaused = false;
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 21:48:16 +00:00
|
|
|
// Trick to reset the volume after a possible new sound configuration
|
2012-06-19 10:50:48 +00:00
|
|
|
setVolume(_lastVolume);
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Change the volume of the stream
|
|
|
|
*
|
|
|
|
* @param dwVolume Volume to be set (0-63)
|
|
|
|
*
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
void FPStream::setVolume(int dwVolume) {
|
2012-05-03 11:43:00 +00:00
|
|
|
#ifdef REFACTOR_ME
|
|
|
|
if (dwVolume > 63) dwVolume = 63;
|
|
|
|
if (dwVolume < 0) dwVolume = 0;
|
2012-05-21 21:53:13 +00:00
|
|
|
|
2012-05-03 11:43:00 +00:00
|
|
|
lastVolume = dwVolume;
|
|
|
|
|
2012-05-13 12:34:40 +00:00
|
|
|
if (!GLOBALS.bCfgMusic) dwVolume = 0;
|
2012-05-03 11:43:00 +00:00
|
|
|
else {
|
2012-05-13 12:34:40 +00:00
|
|
|
dwVolume -= (10 - GLOBALS.nCfgMusicVolume) * 2;
|
2012-05-21 21:53:13 +00:00
|
|
|
if (dwVolume < 0) dwVolume = 0;
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (lpDSBuffer)
|
|
|
|
lpDSBuffer->SetVolume(dwVolume * (DSBVOLUME_MAX - DSBVOLUME_MIN) / 64 + DSBVOLUME_MIN);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-06-17 16:09:52 +00:00
|
|
|
/**
|
|
|
|
* Gets the vgolume of the stream
|
|
|
|
*
|
|
|
|
* @param lpdwVolume Variable that will contain the current volume
|
|
|
|
*
|
|
|
|
*/
|
2012-05-03 11:43:00 +00:00
|
|
|
|
2012-06-16 09:32:50 +00:00
|
|
|
void FPStream::getVolume(int *lpdwVolume) {
|
2012-05-03 11:43:00 +00:00
|
|
|
#ifdef REFACTOR_ME
|
|
|
|
if (lpDSBuffer)
|
|
|
|
lpDSBuffer->GetVolume((uint32 *)lpdwVolume);
|
|
|
|
*lpdwVolume -= (DSBVOLUME_MIN);
|
|
|
|
*lpdwVolume *= 64;
|
|
|
|
*lpdwVolume /= (DSBVOLUME_MAX - DSBVOLUME_MIN);
|
2012-05-03 12:49:30 +00:00
|
|
|
#endif
|
2012-05-03 11:43:00 +00:00
|
|
|
}
|
|
|
|
|
2012-04-29 23:27:12 +00:00
|
|
|
} // End of namespace Tony
|