TITANIC: Added more sound manager methods, field renames in CProximity

This commit is contained in:
Paul Gilbert 2016-08-06 18:54:00 -04:00
parent 551048118c
commit 7b5b06f9d3
11 changed files with 301 additions and 43 deletions

View File

@ -634,11 +634,11 @@ void CGameObject::loadSound(const CString &name) {
}
}
int CGameObject::playSound(const CString &name, int val2, int val3, int val4) {
int CGameObject::playSound(const CString &name, uint volume, int val3, bool repeated) {
CProximity prox;
prox._field8 = val2;
prox._channelVolume = volume;
prox._fieldC = val3;
prox._field20 = val4;
prox._repeated = repeated;
return playSound(name, prox);
}

View File

@ -183,7 +183,7 @@ protected:
/**
* Plays a sound
*/
int playSound(const CString &name, int val2 = 100, int val3 = 0, int val4 = 0);
int playSound(const CString &name, uint volume = 100, int val3 = 0, bool repeated = false);
/**
* Plays a sound

View File

@ -25,12 +25,12 @@
namespace Titanic {
CProximity::CProximity() : _field4(0), _field8(100), _fieldC(0),
_speechHandle(-1), _field14(0), _field18(0), _field1C(1.875),
_field20(0), _field24(10), _field28(0), _field2C(0.0),
_field30(0.5), _field34(0), _posX(0.0), _posY(0.0), _posZ(0.0),
_field44(0), _field48(0), _field4C(0), _field50(0), _field54(0),
_field58(0), _field5C(0), _field60(0), _endTalkerFn(nullptr),
CProximity::CProximity() : _field4(0), _channelVolume(100), _fieldC(0),
_speechHandle(-1), _field14(0), _frequencyMultiplier(0.0), _field1C(1.875),
_repeated(false), _field24(10), _field28(0), _azimuth(0.0),
_range(0.5), _elevation(0), _posX(0.0), _posY(0.0), _posZ(0.0),
_hasVelocity(false), _velocityX(0), _velocityY(0), _velocityZ(0),
_field54(0), _field58(0), _field5C(0), _field60(0), _endTalkerFn(nullptr),
_talker(nullptr), _field6C(0) {
}

View File

@ -34,25 +34,25 @@ typedef void (*CEndTalkerFn)(TTtalker *talker);
class CProximity {
public:
int _field4;
int _field8;
int _channelVolume;
int _fieldC;
int _speechHandle;
int _field14;
int _field18;
int _field1C;
int _field20;
double _frequencyMultiplier;
double _field1C;
bool _repeated;
int _field24;
int _field28;
double _field2C;
double _field30;
int _field34;
double _azimuth;
double _range;
double _elevation;
double _posX;
double _posY;
double _posZ;
int _field44;
int _field48;
int _field4C;
int _field50;
bool _hasVelocity;
double _velocityX;
double _velocityY;
double _velocityZ;
int _field54;
int _field58;
int _field5C;

View File

@ -41,6 +41,11 @@ int QMixer::qsWaveMixOpenChannel(int iChannel, QMixFlag mode) {
return 0;
}
int QMixer::qsWaveMixEnableChannel(int iChannel, uint flags, bool enabled) {
// Not currently implemented in ScummVM
return 0;
}
void QMixer::qsWaveMixCloseSession() {
_mixer->stopAll();
}
@ -69,4 +74,29 @@ void QMixer::qsWaveMixSetPolarPosition(int iChannel, uint flags, const QSPOLAR &
// Not currently implemented in ScummVM
}
void QMixer::qsWaveMixSetListenerPosition(const QSVECTOR &position, uint flags) {
// Not currently implemented in ScummVM
}
void QMixer::qsWaveMixSetListenerOrientation(const QSVECTOR &direction, const QSVECTOR &up, uint flags) {
// Not currently implemented in ScummVM
}
void QMixer::qsWaveMixSetDistanceMapping(int iChannel, uint flags, const QMIX_DISTANCES &distances) {
// Not currently implemented in ScummVM
}
void QMixer::qsWaveMixSetFrequency(int iChannel, uint flags, uint frequency) {
// Not currently implemented in ScummVM
}
void QMixer::qsWaveMixSetSourceVelocity(int iChannel, uint flags, const QSVECTOR &velocity) {
// Not currently implemented in ScummVM
}
int QMixer::qsWaveMixPlayEx(int iChannel, uint flags, CWaveFile *mixWave, int loops, const QMIXPLAYPARAMS &params) {
// Not currently implemented in ScummVM
return 0;
}
} // End of namespace Titanic z

View File

@ -15,7 +15,7 @@
* 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
* aint with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
@ -24,6 +24,7 @@
#define TITANIC_QMIXER_H
#include "audio/mixer.h"
#include "titanic/sound/wave_file.h"
namespace Titanic {
@ -33,7 +34,51 @@ enum QMixFlag {
QMIX_OPENCOUNT = 2, // Open iChannel Channels (eg. if iChannel = 4 will create channels 0-3)
QMIX_OPENAVAILABLE = 3, // Open the first unopened channel, and return channel number
QMIX_USEONCE = 0x10 // settings are temporary
// Channel function flags
QMIX_ALL = 0x01, // apply to all channels
QMIX_NOREMIX = 0x02, // don't remix
QMIX_CONTROL_NOREMIX = 0x04, // don't remix
QMIX_USEONCE = 0x10, // settings are temporary
};
// qsWaveMixEnableChannel flags: if mode==0, use conventional, high-performance
// stereo mixer. Non-zero modes imply some form of additional processing.
enum QMixChannelFlag {
QMIX_CHANNEL_STEREO = 0x0000, // Perform stereo mixing
QMIX_CHANNEL_QSOUND = 0x0001, // Perform QSound localization (default)
QMIX_CHANNEL_DOPPLER = 0x0002, // Calculate velocity using position updates
QMIX_CHANNEL_RANGE = 0x0004, // Do range effects
QMIX_CHANNEL_ELEVATION = 0x0008, // Do elevation effects
QMIX_CHANNEL_NODOPPLERPITCH = 0x0010, // Disable Doppler pitch shift for this channel
QMIX_CHANNEL_PITCH_COPY = 0x0000, // Pitch shifting using copying (fastest)
QMIX_CHANNEL_PITCH_LINEAR = 0x0100, // Pitch shifting using linear interpolation (better but slower)
QMIX_CHANNEL_PITCH_SPLINE = 0x0200, // Pitch shifting using spline interpolation (better yet, but much slower)
QMIX_CHANNEL_PITCH_FILTER = 0x0300, // Pitch shifting using FIR filter (best, but slowest)
QMIX_CHANNEL_PITCH_MASK = 0x0700 // Bits reserved for pitch types
};
/**
* Options for dwFlags parameter in QSWaveMixPlayEx.
*
* Notes: The QMIX_USELRUCHANNEL flag has two roles. When QMIX_CLEARQUEUE is also set,
* the channel that has been playing the longest (least-recently-used) is cleared and
* the buffer played. When QMIX_QUEUEWAVE is set, the channel that will first finish
* playing will be selected and the buffer queued to play. Of course, if an unused
* channel is found, it will be selected instead.
* If QMIX_WAIT hasn't been specified, then the channel number will be returned
* in the iChannel field.
*/
enum QMixPlayFlag {
QMIX_QUEUEWAVE = 0x0000, // Queue on channel
QMIX_CLEARQUEUE = 0x0001, // Clear queue on channel
QMIX_USELRUCHANNEL = 0x0002, // See notes above
QMIX_HIGHPRIORITY = 0x0004,
QMIX_WAIT = 0x0008, // Queue to be played with other sounds
QMIX_IMMEDIATE = 0x0020, // Apply volume/pan changes without interpolation
QMIX_PLAY_SETEVENT = 0x0100, // Calls SetEvent in the original library when done
QMIX_PLAY_PULSEEVENT = 0x0200, // Calls PulseEvent in the original library when done
QMIX_PLAY_NOTIFYSTOP = 0x0400 // Do callback even when stopping or flushing sound
};
/**
@ -83,6 +128,35 @@ struct QSPOLAR {
azimuth(azimuth_), range(range_), elevation(elevation_) {}
};
struct QMIX_DISTANCES {
int cbSize; // Structure size
double minDistance; // sounds are at full volume if closer than this
double maxDistance; // sounds are muted if further away than this
double scale; // relative amount to adjust rolloff by
QMIX_DISTANCES() : cbSize(16), minDistance(0.0), maxDistance(0.0), scale(0.0) {}
QMIX_DISTANCES(double minDistance_, double maxDistance_, double scale_) :
cbSize(16), minDistance(minDistance_), maxDistance(maxDistance_), scale(scale_) {}
};
typedef void (*LPQMIXDONECALLBACK)(int iChannel, CWaveFile *lpWave, void *dwUser);
struct QMIXPLAYPARAMS {
uint dwSize; // Size of the play structure
void *lpImage; // Additional preprocessed audio for high performance
uint hwndNotify; // if set, WOM_OPEN and WOM_DONE messages sent to that window
LPQMIXDONECALLBACK callback; // Callback function
void *dwUser; // User data accompanying callback
int lStart;
int lStartLoop;
int lEndLoop;
int lEnd;
const void *lpChannelParams; // initialize with these parameters
QMIXPLAYPARAMS() : dwSize(36), lpImage(nullptr), hwndNotify(0), callback(nullptr),
dwUser(nullptr), lStart(0), lStartLoop(0), lEndLoop(0), lEnd(0), lpChannelParams(nullptr) {}
};
/**
* This class represents an interface to the QMixer library developed by
* QSound Labs, Inc. Which itself is apparently based on Microsoft's
@ -116,6 +190,11 @@ public:
*/
int qsWaveMixOpenChannel(int iChannel, QMixFlag mode);
/**
* Enables a given channel
*/
int qsWaveMixEnableChannel(int iChannel, uint flags, bool enabled);
/**
* Closes down the mixer
*/
@ -161,6 +240,41 @@ public:
* @param position Polar position for channel
*/
void qsWaveMixSetPolarPosition(int iChannel, uint flags, const QSPOLAR &position);
/**
* Sets the listener position
*/
void qsWaveMixSetListenerPosition(const QSVECTOR &position, uint flags = 0);
/**
* Sets the listener orientation
*/
void qsWaveMixSetListenerOrientation(const QSVECTOR &direction, const QSVECTOR &up, uint flags = 0);
/**
* Sets the mapping ditance range
*/
void qsWaveMixSetDistanceMapping(int iChannel, uint flags, const QMIX_DISTANCES &distances);
/**
*
*/
void qsWaveMixSetFrequency(int iChannel, uint flags, uint frequency);
/**
* Sets the velocity of the source (listener)
*/
void qsWaveMixSetSourceVelocity(int iChannel, uint flags, const QSVECTOR &velocity);
/**
* Plays sound
* @param iChannel The channel number to be played on
* @param flags Play flags
* @param mixWave Data for the sound to be played
* @param loops Number of loops to play (-1 for forever)
* @param params Playback parameter data
*/
int qsWaveMixPlayEx(int iChannel, uint flags, CWaveFile *waveFile, int loops, const QMIXPLAYPARAMS &params);
};
} // End of namespace Titanic

View File

@ -29,7 +29,7 @@ const uint LATENCY = 100;
const uint CHANNELS_COUNT = 16;
CSoundManager::CSoundManager() : _musicPercent(75.0), _speechPercent(75.0),
_masterPercent(75.0), _parrotPercent(75.0), _field14(1) {
_masterPercent(75.0), _parrotPercent(75.0), _handleCtr(1) {
}
/*------------------------------------------------------------------------*/
@ -52,7 +52,7 @@ void QSoundManagerSounds::flushChannel(int iChannel) {
}
}
void QSoundManagerSounds::flushChannel(int v1, int iChannel) {
void QSoundManagerSounds::flushChannel(CWaveFile *waveFile, int iChannel) {
for (iterator i = begin(); i != end(); ++i) {
QSoundManagerSound *item = *i;
if (item->_waveFile->isLoaded() && item->_iChannel == iChannel) {
@ -223,12 +223,75 @@ void QSoundManager::setParrotPercent(double percent) {
_parrotPercent = percent;
}
void QSoundManager::proc29() {
warning("TODO");
void QSoundManager::setListenerPosition(double posX, double posY, double posZ,
double directionX, double directionY, double directionZ, bool stopSounds) {
if (stopSounds) {
// Stop any running sounds
for (uint idx = 0; idx < _slots.size(); ++idx) {
if (_slots[idx]._val3)
stopSound(_slots[idx]._handle);
}
}
qsWaveMixSetListenerPosition(QSVECTOR(posX, posY, posZ));
qsWaveMixSetListenerOrientation(QSVECTOR(directionX, directionY, directionZ),
QSVECTOR(0.0, 0.0, -1.0));
}
void QSoundManager::proc30() {
warning("TODO");
int QSoundManager::playWave(CWaveFile *waveFile, int iChannel, uint flags, CProximity &prox) {
if (!waveFile || !waveFile->isLoaded())
return 0;
prox._channelVolume = CLIP(prox._channelVolume, 0, 100);
prox._fieldC = CLIP(prox._fieldC, -100, 100);
int slotIndex = findFreeSlot();
if (slotIndex == -1)
return -1;
switch (prox._field28) {
case 1:
qsWaveMixSetPolarPosition(iChannel, 8, QSPOLAR(prox._azimuth, prox._range, prox._elevation));
qsWaveMixEnableChannel(iChannel, QMIX_CHANNEL_ELEVATION, true);
qsWaveMixSetDistanceMapping(iChannel, 8, QMIX_DISTANCES(5.0, 3.0, 1.0));
break;
case 2:
qsWaveMixSetSourcePosition(iChannel, 8, QSVECTOR(prox._posX, prox._posY, prox._posZ));
qsWaveMixEnableChannel(iChannel, QMIX_CHANNEL_ELEVATION, true);
qsWaveMixSetDistanceMapping(iChannel, 8, QMIX_DISTANCES(5.0, 3.0, 1.0));
break;
default:
qsWaveMixEnableChannel(iChannel, QMIX_CHANNEL_ELEVATION, true);
qsWaveMixSetPolarPosition(iChannel, 8, QSPOLAR(0.0, 1.0, 0.0));
break;
}
if (prox._frequencyMultiplier || prox._field1C != 1.875) {
uint freq = (uint)(waveFile->getFrequency() * prox._frequencyMultiplier);
qsWaveMixSetFrequency(iChannel, 8, freq);
}
_sounds.add(waveFile, iChannel, prox._endTalkerFn, prox._talker);
QMIXPLAYPARAMS playParams;
playParams.callback = soundFinished;
playParams.dwUser = this;
if (!qsWaveMixPlayEx(iChannel, flags, waveFile, prox._repeated ? -1 : 0, playParams)) {
Slot &slot = _slots[slotIndex];
slot._handle = _handleCtr++;
slot._channel = iChannel;
slot._waveFile = waveFile;
slot._val3 = prox._field28;
return slot._handle;
} else {
_sounds.flushChannel(waveFile, iChannel);
if (prox._field60)
delete waveFile;
return 0;
}
}
void QSoundManager::soundFreed(Audio::SoundHandle &handle) {
@ -292,4 +355,23 @@ void QSoundManager::updateVolumes() {
updateVolume(idx, 250);
}
void QSoundManager::soundFinished(int iChannel, CWaveFile *waveFile, void *soundManager) {
static_cast<QSoundManager *>(soundManager)->_sounds.flushChannel(waveFile, iChannel);
}
int QSoundManager::findFreeSlot() {
for (uint idx = 0; idx < _slots.size(); ++idx) {
if (!_slots[idx]._waveFile)
return idx;
}
return -1;
}
void QSoundManager::setChannelVolume(int iChannel, uint volume, uint mode) {
_channelsVolume[iChannel] = volume;
_channelsMode[iChannel] = mode;
updateVolume(iChannel, 250);
}
} // End of namespace Titanic z

View File

@ -41,7 +41,7 @@ protected:
double _speechPercent;
double _masterPercent;
double _parrotPercent;
int _field14;
uint _handleCtr;
public:
CSoundManager();
virtual ~CSoundManager() {}
@ -211,7 +211,7 @@ public:
/**
* Flushes a wave file attached to the specified channel
*/
void flushChannel(int v1, int iChannel);
void flushChannel(CWaveFile *waveFile, int iChannel);
/**
* Returns true if the list contains the specified wave file
@ -225,12 +225,13 @@ public:
*/
class QSoundManager : public CSoundManager, public QMixer {
struct Slot {
uint _val1;
CWaveFile *_waveFile;
uint _val2;
uint _ticks;
int _channel;
uint _handle;
Slot() : _val1(0), _val2(0), _ticks(0), _channel(0), _handle(0) {}
uint _val3;
Slot() : _waveFile(0), _val2(0), _ticks(0), _channel(0), _handle(0), _val3(0) {}
};
private:
QSoundManagerSounds _sounds;
@ -254,6 +255,21 @@ private:
* Updates all the volumes
*/
void updateVolumes();
/**
* Called by the QMixer when a sound finishes playing
*/
static void soundFinished(int iChannel, CWaveFile *waveFile, void *soundManager);
/**
* Finds the first free slot
*/
int findFreeSlot();
/**
* Sets a channel volume
*/
void setChannelVolume(int iChannel, uint volume, uint mode);
public:
int _field18;
int _field1C;
@ -361,8 +377,16 @@ public:
*/
virtual void setParrotPercent(double percent);
virtual void proc29();
virtual void proc30();
/**
* Sets the position and orientation for the listener (player)
*/
virtual void setListenerPosition(double posX, double posY, double posZ,
double directionX, double directionY, double directionZ, bool stopSounds);
/**
* Starts a wave file playing
*/
virtual int playWave(CWaveFile *waveFile, int iChannel, uint flags, CProximity &prox);
/**
* Called when a wave file is freed

View File

@ -51,5 +51,8 @@ bool CWaveFile::loadSound(const CString &name) {
return true;
}
uint CWaveFile::getFrequency() const {
return _stream->getRate();
}
} // End of namespace Titanic z

View File

@ -52,6 +52,11 @@ public:
* Returns true if the wave file has data loaded
*/
bool isLoaded() const { return _stream != nullptr; }
/**
* Return the frequency of the loaded wave file
*/
uint getFrequency() const;
};
} // End of namespace Titanic

View File

@ -504,17 +504,17 @@ void CTrueTalkManager::playSpeech(TTtalker *talker, TTroomScript *roomScript, CV
}
if (milli > 0) {
p3._field8 = (index * 3) / 2;
p3._channelVolume = (index * 3) / 2;
p3._field28 = 1;
p3._field2C = -135.0;
p3._field30 = 1.0;
p3._field34 = 0;
p3._azimuth = -135.0;
p3._range = 1.0;
p3._elevation = 0;
p2._field8 = (index * 3) / 4;
p2._channelVolume = (index * 3) / 4;
p2._field28 = 0;
p2._field2C = 135.0;
p2._field30 = 1.0;
p2._field34 = 0;
p2._azimuth = 135.0;
p2._range = 1.0;
p2._elevation = 0;
}
_gameManager->_sound.managerProc8(p1._field24);