mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-12 03:56:20 +00:00
fe45300891
Fixes Trac#6618, Trac#6638, Trac#7011.
531 lines
15 KiB
C++
531 lines
15 KiB
C++
/* 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.
|
|
*
|
|
*/
|
|
|
|
#ifndef TSAGE_SOUND_H
|
|
#define TSAGE_SOUND_H
|
|
|
|
#include "common/scummsys.h"
|
|
#include "common/mutex.h"
|
|
#include "common/queue.h"
|
|
#include "audio/mixer.h"
|
|
#include "common/list.h"
|
|
#include "tsage/saveload.h"
|
|
#include "tsage/core.h"
|
|
|
|
namespace Audio {
|
|
class QueuingAudioStream;
|
|
}
|
|
|
|
namespace OPL {
|
|
class OPL;
|
|
}
|
|
|
|
namespace TsAGE {
|
|
|
|
class Sound;
|
|
|
|
#define SOUND_ARR_SIZE 16
|
|
#define ROLAND_DRIVER_NUM 2
|
|
#define ADLIB_DRIVER_NUM 3
|
|
#define SBLASTER_DRIVER_NUM 4
|
|
#define CALLBACKS_PER_SECOND 60
|
|
|
|
struct trackInfoStruct {
|
|
int _numTracks;
|
|
int _chunks[SOUND_ARR_SIZE];
|
|
int _voiceTypes[SOUND_ARR_SIZE];
|
|
};
|
|
|
|
enum SoundDriverStatus {SNDSTATUS_FAILED = 0, SNDSTATUS_DETECTED = 1, SNDSTATUS_SKIPPED = 2};
|
|
enum VoiceType {VOICETYPE_0 = 0, VOICETYPE_1 = 1};
|
|
|
|
class SoundDriverEntry {
|
|
public:
|
|
int _driverNum;
|
|
SoundDriverStatus _status;
|
|
int _field2, _field6;
|
|
Common::String _shortDescription;
|
|
Common::String _longDescription;
|
|
};
|
|
|
|
struct GroupData {
|
|
uint32 _groupMask;
|
|
const byte *_pData;
|
|
};
|
|
|
|
struct RegisterValue {
|
|
uint8 _regNum;
|
|
uint8 _value;
|
|
|
|
RegisterValue(int regNum, int value) {
|
|
_regNum = regNum; _value = value;
|
|
}
|
|
};
|
|
|
|
class SoundDriver {
|
|
public:
|
|
Common::String _shortDescription, _longDescription;
|
|
int _minVersion, _maxVersion;
|
|
// The following fields were originally held in separate arrays in the SoundManager class
|
|
uint32 _groupMask;
|
|
const GroupData *_groupOffset;
|
|
int _driverResID;
|
|
public:
|
|
SoundDriver();
|
|
virtual ~SoundDriver() {}
|
|
|
|
const Common::String &getShortDriverDescription() { return _shortDescription; }
|
|
const Common::String &getLongDriverDescription() { return _longDescription; }
|
|
|
|
virtual bool open() { return true; } // Method #0
|
|
virtual void close() {} // Method #1
|
|
virtual bool reset() { return true; } // Method #2
|
|
virtual const GroupData *getGroupData() { return NULL; } // Method #3
|
|
virtual void installPatch(const byte *data, int size) {} // Method #4
|
|
virtual void poll() {} // Method #5
|
|
virtual void method6() {} // Method #6
|
|
virtual int setMasterVolume(int volume) { return 0; } // Method #7
|
|
virtual void proc16() {} // Method #8
|
|
virtual void proc18(int al, VoiceType voiceType) {} // Method #9
|
|
virtual void proc20(int al, VoiceType voiceType) {} // Method #10
|
|
virtual void proc22(int al, VoiceType voiceType, int v3) {} // Method #11
|
|
virtual void proc24(int channel, int voiceIndex, Sound *sound, int v1, int v2) {}
|
|
virtual void setProgram(int channel, int program) {} // Method #13
|
|
virtual void setVolume1(int channel, int v2, int v3, int volume) {}
|
|
virtual void setPitchBlend(int channel, int pitchBlend) {} // Method #15
|
|
virtual void playSound(const byte *channelData, int dataOffset, int program, int channel, int v0, int v1) {}// Method #16
|
|
virtual void updateVoice(int channel) {} // Method #17
|
|
virtual void proc36() {} // Method #18
|
|
virtual void proc38(int channel, int cmd, int value) {} // Method #19
|
|
virtual void setPitch(int channel, int pitchBlend) {} // Method #20
|
|
virtual void proc42(int channel, int cmd, int value, int *v1, int *v2) {} // Method #21
|
|
};
|
|
|
|
struct VoiceStructEntryType0 {
|
|
Sound *_sound;
|
|
int _channelNum;
|
|
int _priority;
|
|
bool _fieldA;
|
|
Sound *_sound2;
|
|
int _channelNum2;
|
|
int _priority2;
|
|
bool _field12;
|
|
Sound *_sound3;
|
|
int _channelNum3;
|
|
int _priority3;
|
|
};
|
|
|
|
struct VoiceStructEntryType1 {
|
|
int _field4;
|
|
int _field5;
|
|
int _field6;
|
|
Sound *_sound;
|
|
int _channelNum;
|
|
int _priority;
|
|
Sound *_sound2;
|
|
int _channelNum2;
|
|
int _priority2;
|
|
Sound *_sound3;
|
|
int _channelNum3;
|
|
int _priority3;
|
|
};
|
|
|
|
struct VoiceStructEntry {
|
|
int _voiceNum;
|
|
int _field1;
|
|
SoundDriver *_driver;
|
|
|
|
VoiceStructEntryType0 _type0;
|
|
VoiceStructEntryType1 _type1;
|
|
};
|
|
|
|
class VoiceTypeStruct {
|
|
public:
|
|
VoiceType _voiceType;
|
|
int _total;
|
|
int _numVoices;
|
|
int _field3;
|
|
|
|
Common::Array<VoiceStructEntry> _entries;
|
|
};
|
|
|
|
class SoundManager : public SaveListener {
|
|
private:
|
|
SoundDriver *instantiateDriver(int driverNum);
|
|
public:
|
|
bool _sndmgrReady;
|
|
int _ourSndResVersion, _ourDrvResVersion;
|
|
SynchronizedList<Sound *> _playList;
|
|
Common::List<SoundDriver *> _installedDrivers;
|
|
VoiceTypeStruct *_voiceTypeStructPtrs[SOUND_ARR_SIZE];
|
|
uint32 _groupsAvail;
|
|
int _masterVol;
|
|
int _newVolume;
|
|
Common::Mutex _serverDisabledMutex;
|
|
Common::Mutex _serverSuspendedMutex;
|
|
bool _driversDetected;
|
|
SynchronizedList<Sound *> _soundList;
|
|
Common::List<SoundDriverEntry> _availableDrivers;
|
|
bool _needToRethink;
|
|
// Misc flags
|
|
bool _soTimeIndexFlag;
|
|
public:
|
|
SoundManager();
|
|
~SoundManager();
|
|
|
|
void dispatch();
|
|
virtual void listenerSynchronize(Serializer &s);
|
|
virtual void postInit();
|
|
void syncSounds();
|
|
|
|
static void saveNotifier(bool postFlag);
|
|
void saveNotifierProc(bool postFlag);
|
|
static void loadNotifier(bool postFlag);
|
|
void loadNotifierProc(bool postFlag);
|
|
|
|
void installConfigDrivers();
|
|
Common::List<SoundDriverEntry> &buildDriverList(bool detectFlag);
|
|
Common::List<SoundDriverEntry> &getDriverList(bool detectFlag);
|
|
void dumpDriverList();
|
|
void installDriver(int driverNum);
|
|
bool isInstalled(int driverNum) const;
|
|
void unInstallDriver(int driverNum);
|
|
void checkResVersion(const byte *soundData);
|
|
int determineGroup(const byte *soundData);
|
|
int extractPriority(const byte *soundData);
|
|
int extractLoop(const byte *soundData);
|
|
bool isOnPlayList(Sound *sound);
|
|
void extractTrackInfo(trackInfoStruct *trackInfo, const byte *soundData, int groupNum);
|
|
void addToSoundList(Sound *sound);
|
|
void removeFromSoundList(Sound *sound);
|
|
void addToPlayList(Sound *sound);
|
|
void removeFromPlayList(Sound *sound);
|
|
void rethinkVoiceTypes();
|
|
void updateSoundVol(Sound *sound);
|
|
void updateSoundPri(Sound *sound);
|
|
void updateSoundLoop(Sound *sound);
|
|
void setMasterVol(int volume);
|
|
int getMasterVol() const;
|
|
void loadSound(int soundNum, bool showErrors);
|
|
void unloadSound(int soundNum);
|
|
bool isFading();
|
|
|
|
// _sf methods
|
|
static SoundManager &sfManager();
|
|
static void sfTerminate();
|
|
static int sfDetermineGroup(const byte *soundData);
|
|
static void sfAddToPlayList(Sound *sound);
|
|
static void sfRemoveFromPlayList(Sound *sound);
|
|
static bool sfIsOnPlayList(Sound *sound);
|
|
static void sfRethinkSoundDrivers();
|
|
static void sfRethinkVoiceTypes();
|
|
static void sfUpdateVolume(Sound *sound);
|
|
static void sfDereferenceAll();
|
|
static void sfUpdatePriority(Sound *sound);
|
|
static void sfUpdateLoop(Sound *sound);
|
|
static void sfSetMasterVol(int volume);
|
|
static void sfExtractTrackInfo(trackInfoStruct *trackInfo, const byte *soundData, int groupNum);
|
|
static void sfExtractGroupMask();
|
|
static bool sfInstallDriver(SoundDriver *driver);
|
|
static void sfUnInstallDriver(SoundDriver *driver);
|
|
static void sfInstallPatchBank(SoundDriver *driver, const byte *bankData);
|
|
static void sfDoAddToPlayList(Sound *sound);
|
|
static bool sfDoRemoveFromPlayList(Sound *sound);
|
|
static void sfDoUpdateVolume(Sound *sound);
|
|
static void sfSoundServer(void *);
|
|
static void sfProcessFading();
|
|
static void sfUpdateVoiceStructs();
|
|
static void sfUpdateVoiceStructs2();
|
|
};
|
|
|
|
class Sound: public EventHandler {
|
|
private:
|
|
void _prime(int soundResID, bool dontQueue);
|
|
void _primeBuffer(const byte *soundData);
|
|
void _unPrime();
|
|
public:
|
|
bool _stoppedAsynchronously;
|
|
int _soundResID;
|
|
int _group;
|
|
int _sndResPriority;
|
|
int _fixedPriority;
|
|
int _sndResLoop;
|
|
int _fixedLoop;
|
|
int _priority;
|
|
int _volume;
|
|
int _loop;
|
|
int _pausedCount;
|
|
int _mutedCount;
|
|
int _hold;
|
|
int _cueValue;
|
|
int _fadeDest;
|
|
int _fadeSteps;
|
|
int _fadeTicks;
|
|
int _fadeCounter;
|
|
bool _stopAfterFadeFlag;
|
|
uint32 _timer;
|
|
uint32 _newTimeIndex;
|
|
int _loopTimer;
|
|
int _chProgram[SOUND_ARR_SIZE];
|
|
int _chModulation[SOUND_ARR_SIZE];
|
|
int _chVolume[SOUND_ARR_SIZE];
|
|
int _chPan[SOUND_ARR_SIZE];
|
|
int _chDamper[SOUND_ARR_SIZE];
|
|
int _chPitchBlend[SOUND_ARR_SIZE];
|
|
int _chVoiceType[SOUND_ARR_SIZE];
|
|
int _chNumVoices[SOUND_ARR_SIZE];
|
|
int _chSubPriority[SOUND_ARR_SIZE];
|
|
int _chFlags[SOUND_ARR_SIZE];
|
|
bool _chWork[SOUND_ARR_SIZE];
|
|
trackInfoStruct _trackInfo;
|
|
byte *_channelData[SOUND_ARR_SIZE];
|
|
int _trkChannel[SOUND_ARR_SIZE];
|
|
int _trkState[SOUND_ARR_SIZE];
|
|
int _trkLoopState[SOUND_ARR_SIZE];
|
|
int _trkIndex[SOUND_ARR_SIZE];
|
|
int _trkLoopIndex[SOUND_ARR_SIZE];
|
|
int _trkRest[SOUND_ARR_SIZE];
|
|
int _trkLoopRest[SOUND_ARR_SIZE];
|
|
|
|
bool _primed;
|
|
bool _isEmpty;
|
|
byte *_remoteReceiver;
|
|
public:
|
|
Sound();
|
|
~Sound();
|
|
|
|
void synchronize(Serializer &s);
|
|
void orientAfterRestore();
|
|
|
|
void play(int soundResID);
|
|
void stop();
|
|
void prime(int soundResID);
|
|
void unPrime();
|
|
void go();
|
|
void halt(void);
|
|
bool isPlaying();
|
|
int getSoundNum() const;
|
|
bool isPrimed() const;
|
|
bool isPaused() const;
|
|
bool isMuted() const;
|
|
void pause(bool flag);
|
|
void mute(bool flag);
|
|
void fade(int fadeDest, int fadeSteps, int fadeTicks, bool stopAfterFadeFlag);
|
|
void setTimeIndex(uint32 timeIndex);
|
|
uint32 getTimeIndex() const;
|
|
int getCueValue() const;
|
|
void setCueValue(int cueValue);
|
|
void setVol(int volume);
|
|
int getVol() const;
|
|
void setPri(int priority);
|
|
void setLoop(int flag);
|
|
int getPri() const;
|
|
int getLoop();
|
|
void holdAt(int amount);
|
|
void release();
|
|
void orientAfterDriverChange();
|
|
|
|
// _so methods
|
|
void soPrimeSound(bool dontQueue);
|
|
void soSetTimeIndex(uint timeIndex);
|
|
bool soServiceTracks();
|
|
void soPrimeChannelData();
|
|
void soRemoteReceive();
|
|
void soServiceTrackType0(int trackIndex, const byte *channelData);
|
|
void soUpdateDamper(VoiceTypeStruct *voiceType, int channelNum, VoiceType mode, int v0);
|
|
void soPlaySound(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0, int v1);
|
|
void soPlaySound2(VoiceTypeStruct *vtStruct, const byte *channelData, int channelNum, VoiceType voiceType, int v0);
|
|
void soProc38(VoiceTypeStruct *vtStruct, int channelNum, VoiceType voiceType, int cmd, int value);
|
|
void soProc40(VoiceTypeStruct *vtStruct, int channelNum, int pitchBlend);
|
|
void soDoTrackCommand(int channelNum, int command, int value);
|
|
bool soDoUpdateTracks(int command, int value);
|
|
void soSetTrackPos(int trackIndex, int trackPos, int cueValue);
|
|
|
|
void soServiceTrackType1(int trackIndex, const byte *channelData);
|
|
int soFindSound(VoiceTypeStruct *vtStruct, int channelNum);
|
|
};
|
|
|
|
class ASound: public EventHandler {
|
|
public:
|
|
Sound _sound;
|
|
EventHandler *_endAction;
|
|
int _cueValue;
|
|
|
|
ASound();
|
|
~ASound();
|
|
virtual void synchronize(Serializer &s);
|
|
virtual void dispatch();
|
|
|
|
void play(int soundNum, EventHandler *endAction = NULL, int volume = 127);
|
|
void stop();
|
|
void prime(int soundNum, Action *action = NULL);
|
|
void unPrime();
|
|
void go() { _sound.go(); }
|
|
void hault(void) { _sound.halt(); }
|
|
bool isPlaying() { return _sound.isPlaying(); }
|
|
int getSoundNum() const { return _sound.getSoundNum(); }
|
|
bool isPaused() const { return _sound.isPaused(); }
|
|
bool isMuted() const { return _sound.isMuted(); }
|
|
void pause(bool flag) { _sound.pause(flag); }
|
|
void mute(bool flag) { _sound.mute(flag); }
|
|
void fade(int fadeDest, int fadeSteps, int fadeTicks, bool stopAfterFadeFlag, EventHandler *endAction);
|
|
void fadeIn() { fade(127, 5, 10, false, NULL); }
|
|
void fadeOut(Action *action) { fade(0, 5, 10, true, action); }
|
|
void setTimeIndex(uint32 timeIndex) { _sound.setTimeIndex(timeIndex); }
|
|
uint32 getTimeIndex() const { return _sound.getTimeIndex(); }
|
|
void setPri(int v) { _sound.setPri(v); }
|
|
void setLoop(int total) { _sound.setLoop(total); }
|
|
int getPri() const { return _sound.getPri(); }
|
|
int getLoop() { return _sound.getLoop(); }
|
|
void setVol(int volume) { _sound.setVol(volume); }
|
|
int getVol() const { return _sound.getVol(); }
|
|
void holdAt(int v) { _sound.holdAt(v); }
|
|
void release() { _sound.release(); }
|
|
void fadeSound(int soundNum);
|
|
};
|
|
|
|
class ASoundExt: public ASound {
|
|
public:
|
|
int _soundNum;
|
|
|
|
ASoundExt();
|
|
void fadeOut2(EventHandler *endAction);
|
|
void changeSound(int soundNum);
|
|
|
|
virtual Common::String getClassName() { return "ASoundExt"; }
|
|
virtual void synchronize(Serializer &s);
|
|
virtual void signal();
|
|
};
|
|
|
|
class PlayStream: public EventHandler {
|
|
class ResFileData {
|
|
public:
|
|
int _fileChunkSize;
|
|
uint _indexSize;
|
|
uint _chunkSize;
|
|
|
|
void load(Common::SeekableReadStream &stream);
|
|
};
|
|
private:
|
|
Common::File _file;
|
|
ResFileData _resData;
|
|
Audio::QueuingAudioStream *_audioStream;
|
|
Audio::SoundHandle _soundHandle;
|
|
uint16 *_index;
|
|
EventHandler *_endAction;
|
|
int _voiceNum;
|
|
|
|
static uint32 getFileOffset(const uint16 *data, int count, int voiceNum);
|
|
public:
|
|
PlayStream();
|
|
virtual ~PlayStream();
|
|
|
|
bool setFile(const Common::String &filename);
|
|
bool play(int voiceNum, EventHandler *endAction);
|
|
void stop();
|
|
bool isPlaying() const;
|
|
|
|
virtual void remove();
|
|
virtual void dispatch();
|
|
};
|
|
|
|
#define ADLIB_CHANNEL_COUNT 9
|
|
|
|
class AdlibSoundDriver: public SoundDriver {
|
|
private:
|
|
GroupData _groupData;
|
|
Audio::Mixer *_mixer;
|
|
OPL::OPL *_opl;
|
|
byte _portContents[256];
|
|
const byte *_patchData;
|
|
int _masterVolume;
|
|
Common::Mutex _queueMutex;
|
|
Common::Queue<RegisterValue> _queue;
|
|
|
|
bool _channelVoiced[ADLIB_CHANNEL_COUNT];
|
|
int _channelVolume[ADLIB_CHANNEL_COUNT];
|
|
int _v4405E[ADLIB_CHANNEL_COUNT];
|
|
int _v44067[ADLIB_CHANNEL_COUNT];
|
|
int _v44070[ADLIB_CHANNEL_COUNT];
|
|
int _v44079[ADLIB_CHANNEL_COUNT];
|
|
int _v44082[ADLIB_CHANNEL_COUNT + 1];
|
|
int _pitchBlend[ADLIB_CHANNEL_COUNT];
|
|
int _v4409E[ADLIB_CHANNEL_COUNT];
|
|
|
|
|
|
void write(byte reg, byte value);
|
|
void flush();
|
|
void updateChannelVolume(int channel);
|
|
void setVoice(int channel);
|
|
void clearVoice(int channel);
|
|
void updateChannel(int channel);
|
|
void setFrequency(int channel);
|
|
public:
|
|
AdlibSoundDriver();
|
|
virtual ~AdlibSoundDriver();
|
|
|
|
virtual bool open();
|
|
virtual void close();
|
|
virtual bool reset();
|
|
virtual const GroupData *getGroupData();
|
|
virtual void installPatch(const byte *data, int size);
|
|
virtual int setMasterVolume(int volume);
|
|
virtual void playSound(const byte *channelData, int dataOffset, int program, int channel, int v0, int v1);
|
|
virtual void updateVoice(int channel);
|
|
virtual void proc38(int channel, int cmd, int value);
|
|
virtual void setPitch(int channel, int pitchBlend);
|
|
|
|
private:
|
|
void onTimer();
|
|
};
|
|
|
|
class SoundBlasterDriver: public SoundDriver {
|
|
private:
|
|
GroupData _groupData;
|
|
Audio::Mixer *_mixer;
|
|
Audio::SoundHandle _soundHandle;
|
|
Audio::QueuingAudioStream *_audioStream;
|
|
int _sampleRate;
|
|
|
|
byte _masterVolume;
|
|
byte _channelVolume;
|
|
const byte *_channelData;
|
|
public:
|
|
SoundBlasterDriver();
|
|
virtual ~SoundBlasterDriver();
|
|
|
|
virtual bool open();
|
|
virtual void close();
|
|
virtual bool reset();
|
|
virtual const GroupData *getGroupData();
|
|
virtual int setMasterVolume(int volume);
|
|
virtual void playSound(const byte *channelData, int dataOffset, int program, int channel, int v0, int v1);
|
|
virtual void updateVoice(int channel);
|
|
virtual void proc38(int channel, int cmd, int value);
|
|
virtual void proc42(int channel, int cmd, int value, int *v1, int *v2);
|
|
};
|
|
|
|
|
|
} // End of namespace TsAGE
|
|
|
|
#endif
|