[Scummvm-git-logs] scummvm master -> 0239ed5e963da9ce6f824386b87ef1144d3c1d9d
athrxx
athrxx at scummvm.org
Sat Feb 1 20:12:23 UTC 2020
This automated email contains information about 4 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
46ef0109b5 KYRA: (EOB) - prepare for PC Speaker support
0820b0617d KYRA: (EOB) - add PC Speaker driver
2c91759a05 KYRA: (LOK) - add missing delay (bug #11330)
0239ed5e96 KYRA: (HOF) - fix bug #11331
Commit: 46ef0109b53e7ceb9a356c4483d318cabb3fcacc
https://github.com/scummvm/scummvm/commit/46ef0109b53e7ceb9a356c4483d318cabb3fcacc
Author: athrxx (athrxx at scummvm.org)
Date: 2020-02-01T21:09:49+01:00
Commit Message:
KYRA: (EOB) - prepare for PC Speaker support
This commit does not include the actual driver. It only provides the necessary refactoring and resource handling.
Changed paths:
A engines/kyra/sound/drivers/pc_base.h
A engines/kyra/sound/drivers/pcspeaker_v1.cpp
A engines/kyra/sound/drivers/pcspeaker_v2.cpp
A engines/kyra/sound/sound_pc_midi.cpp
A engines/kyra/sound/sound_pc_v1.cpp
A engines/kyra/sound/sound_pc_v1.h
R engines/kyra/sound/drivers/adlib.h
R engines/kyra/sound/drivers/pcspeaker.cpp
R engines/kyra/sound/sound_adlib.cpp
R engines/kyra/sound/sound_adlib.h
R engines/kyra/sound/sound_midi.cpp
devtools/create_kyradat/create_kyradat.cpp
devtools/create_kyradat/resources/eob1_dos.h
dists/engine-data/kyra.dat
engines/kyra/detection_tables.h
engines/kyra/engine/eobcommon.cpp
engines/kyra/engine/kyra_v1.cpp
engines/kyra/module.mk
engines/kyra/resource/staticres.cpp
engines/kyra/sound/drivers/adlib.cpp
engines/kyra/sound/sound_intern.h
diff --git a/devtools/create_kyradat/create_kyradat.cpp b/devtools/create_kyradat/create_kyradat.cpp
index a2ec311..0ae98ac 100644
--- a/devtools/create_kyradat/create_kyradat.cpp
+++ b/devtools/create_kyradat/create_kyradat.cpp
@@ -45,7 +45,7 @@
enum {
- kKyraDatVersion = 95
+ kKyraDatVersion = 96
};
const ExtractFilename extractFilenames[] = {
diff --git a/devtools/create_kyradat/resources/eob1_dos.h b/devtools/create_kyradat/resources/eob1_dos.h
index 61cf3af..d35fb74 100644
--- a/devtools/create_kyradat/resources/eob1_dos.h
+++ b/devtools/create_kyradat/resources/eob1_dos.h
@@ -1755,20 +1755,23 @@ static const EoBCharacter kEoB1NpcPresetsDOS[9] = {
static const EoBCharacterProvider kEoB1NpcPresetsDOSProvider = { ARRAYSIZE(kEoB1NpcPresetsDOS), kEoB1NpcPresetsDOS };
-static const char *const kEoB1SoundFilesIntroDOS[1] = {
- "SOUND"
+static const char *const kEoB1SoundFilesIntroDOS[2] = {
+ "SOUND",
+ "PCSOUND"
};
static const StringListProvider kEoB1SoundFilesIntroDOSProvider = { ARRAYSIZE(kEoB1SoundFilesIntroDOS), kEoB1SoundFilesIntroDOS };
-static const char *const kEoB1SoundFilesIngameDOS[1] = {
- "ADLIB"
+static const char *const kEoB1SoundFilesIngameDOS[2] = {
+ "ADLIB",
+ "PCGAMESN"
};
static const StringListProvider kEoB1SoundFilesIngameDOSProvider = { ARRAYSIZE(kEoB1SoundFilesIngameDOS), kEoB1SoundFilesIngameDOS };
-static const char *const kEoB1SoundFilesFinaleDOS[1] = {
- "ADLIB"
+static const char *const kEoB1SoundFilesFinaleDOS[2] = {
+ "ADLIB",
+ "PCGAMESN"
};
static const StringListProvider kEoB1SoundFilesFinaleDOSProvider = { ARRAYSIZE(kEoB1SoundFilesFinaleDOS), kEoB1SoundFilesFinaleDOS };
diff --git a/dists/engine-data/kyra.dat b/dists/engine-data/kyra.dat
index 7b037b8..568e7f1 100644
Binary files a/dists/engine-data/kyra.dat and b/dists/engine-data/kyra.dat differ
diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h
index 2f7f248..a81dc97 100644
--- a/engines/kyra/detection_tables.h
+++ b/engines/kyra/detection_tables.h
@@ -1654,7 +1654,7 @@ const KYRAGameDescription adGameDescs[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO8(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_EOB_HPGRAPHS, GAMEOPTION_EOB_MOUSESWAP)
+ GUIO9(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_MIDIPCJR, GUIO_RENDERVGA, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_EOB_HPGRAPHS, GAMEOPTION_EOB_MOUSESWAP)
},
EOB_FLAGS
},
@@ -1670,7 +1670,7 @@ const KYRAGameDescription adGameDescs[] = {
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO8(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_EOB_HPGRAPHS, GAMEOPTION_EOB_MOUSESWAP)
+ GUIO9(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_MIDIPCJR, GUIO_RENDERVGA, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_EOB_HPGRAPHS, GAMEOPTION_EOB_MOUSESWAP)
},
EOB_FLAGS
},
@@ -1686,7 +1686,7 @@ const KYRAGameDescription adGameDescs[] = {
Common::IT_ITA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO8(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_EOB_HPGRAPHS, GAMEOPTION_EOB_MOUSESWAP)
+ GUIO9(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_MIDIPCJR, GUIO_RENDERVGA, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_EOB_HPGRAPHS, GAMEOPTION_EOB_MOUSESWAP)
},
EOB_FLAGS
},
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index e812303..d51f132 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -25,7 +25,7 @@
#include "kyra/engine/kyra_rpg.h"
#include "kyra/resource/resource.h"
#include "kyra/sound/sound_intern.h"
-#include "kyra/sound/sound_adlib.h"
+#include "kyra/sound/sound_pc_v1.h"
#include "kyra/script/script_eob.h"
#include "kyra/engine/timer.h"
#include "kyra/gui/debugger.h"
@@ -411,11 +411,10 @@ Common::Error EoBCoreEngine::init() {
// don't really need one). We just disable the sound in the settings.
MidiDriver::DeviceHandle dev = 0;
if (_flags.platform == Common::kPlatformDOS) {
- dev = MidiDriver::detectDevice(/*MDT_PCSPK | */MDT_ADLIB);
- //if (MidiDriver::getMusicType(dev) == MT_ADLIB)
- _sound = new SoundAdLibPC(this, _mixer);
- //else
- // _sound = new SoundPCS(this, _mixer);
+ int flags = MDT_ADLIB | MDT_PCSPK;
+ dev = MidiDriver::detectDevice(_flags.gameID == GI_EOB1 ? flags | MDT_PCJR : flags);
+ MusicType type = MidiDriver::getMusicType(dev);
+ _sound = new SoundPC_v1(this, _mixer, type == MT_ADLIB ? Sound::kAdLib : type == MT_PCSPK ? Sound::kPCSpkr : Sound::kPCjr);
} else if (_flags.platform == Common::kPlatformFMTowns) {
dev = MidiDriver::detectDevice(MDT_TOWNS);
// SoundTowns_Darkmoon requires initialized _staticres
@@ -435,8 +434,8 @@ Common::Error EoBCoreEngine::init() {
assert(_sound);
_sound->init();
- // This if for EOB1 PC-98 only
- _sound->loadSfxFile("EFECT.OBJ");
+ if (_flags.platform == Common::kPlatformPC98)
+ _sound->loadSfxFile("EFECT.OBJ");
// Setup volume settings (and read in all ConfigManager settings)
_configNullSound = (MidiDriver::getMusicType(dev) == MT_NULL);
diff --git a/engines/kyra/engine/kyra_v1.cpp b/engines/kyra/engine/kyra_v1.cpp
index ec4f07e..b83aa3b 100644
--- a/engines/kyra/engine/kyra_v1.cpp
+++ b/engines/kyra/engine/kyra_v1.cpp
@@ -121,7 +121,7 @@ Common::Error KyraEngine_v1::init() {
// Kyra games.
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_PCSPK | MDT_MIDI | MDT_ADLIB | ((_flags.gameID == GI_KYRA2 || _flags.gameID == GI_LOL) ? MDT_PREFER_GM : MDT_PREFER_MT32));
if (MidiDriver::getMusicType(dev) == MT_ADLIB) {
- _sound = new SoundAdLibPC(this, _mixer);
+ _sound = new SoundPC_v1(this, _mixer, Sound::kAdLib);
} else {
Sound::kType type;
const MusicType midiType = MidiDriver::getMusicType(dev);
@@ -153,7 +153,7 @@ Common::Error KyraEngine_v1::init() {
// missing. It's just that at least at the time of writing they
// are decidedly inferior to the AdLib ones.
if (ConfMan.getBool("multi_midi")) {
- SoundAdLibPC *adlib = new SoundAdLibPC(this, _mixer);
+ SoundPC_v1 *adlib = new SoundPC_v1(this, _mixer, Sound::kAdLib);
assert(adlib);
_sound = new MixedSoundDriver(this, _mixer, soundMidiPc, adlib);
diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk
index 3f4bab6..84fffb7 100644
--- a/engines/kyra/module.mk
+++ b/engines/kyra/module.mk
@@ -60,10 +60,10 @@ MODULE_OBJS := \
sequence/sequences_v2.o \
sequence/sequences_hof.o \
sequence/sequences_mr.o \
- sound/sound_adlib.o \
sound/sound_amiga_lok.o \
sound/sound_digital_mr.o \
- sound/sound_midi.o \
+ sound/sound_pc_midi.o \
+ sound/sound_pc_v1.o \
sound/sound_pc98_lok.o \
sound/sound_pc98_v2.o \
sound/sound_towns_lok.o \
@@ -72,7 +72,7 @@ MODULE_OBJS := \
sound/drivers/adlib.o \
sound/drivers/audstream.o \
sound/drivers/midi.o \
- sound/drivers/pcspeaker.o \
+ sound/drivers/pcspeaker_v2.o \
text/text.o \
text/text_lok.o \
text/text_hof.o \
@@ -132,7 +132,8 @@ MODULE_OBJS += \
sound/sound_pc98_eob.o \
sound/sound_towns_darkmoon.o \
sound/drivers/audiomaster2.o \
- sound/drivers/mlalf98.o
+ sound/drivers/mlalf98.o \
+ sound/drivers/pcspeaker_v1.o
endif
# This module can be built as a plugin
diff --git a/engines/kyra/resource/staticres.cpp b/engines/kyra/resource/staticres.cpp
index 61adcd9..9f13b71 100644
--- a/engines/kyra/resource/staticres.cpp
+++ b/engines/kyra/resource/staticres.cpp
@@ -39,7 +39,7 @@
namespace Kyra {
-#define RESFILE_VERSION 95
+#define RESFILE_VERSION 96
namespace {
bool checkKyraDat(Common::SeekableReadStream *file) {
diff --git a/engines/kyra/sound/drivers/adlib.cpp b/engines/kyra/sound/drivers/adlib.cpp
index 0a1d277..a576eb2 100644
--- a/engines/kyra/sound/drivers/adlib.cpp
+++ b/engines/kyra/sound/drivers/adlib.cpp
@@ -36,16 +36,348 @@
*
*/
+ // Basic AdLib Programming:
+ // https://web.archive.org/web/20050322080425/http://www.gamedev.net/reference/articles/article446.asp
-#include "kyra/sound/drivers/adlib.h"
+#include "kyra/sound/drivers/pc_base.h"
#include "audio/fmopl.h"
-
+#include "common/mutex.h"
#define CALLBACKS_PER_SECOND 72
namespace Kyra {
-AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) {
+class AdLibDriver : public PCSoundDriver {
+public:
+ AdLibDriver(Audio::Mixer *mixer, int version);
+ virtual ~AdLibDriver();
+
+ virtual void initDriver() override;
+ virtual void setSoundData(uint8 *data, uint32 size) override;
+ virtual void queueTrack(int track, int volume) override;
+ virtual bool isChannelPlaying(int channel) const override;
+ virtual void stopAllChannels() override;
+ virtual int getSoundTrigger() const override { return _soundTrigger; }
+ virtual void resetSoundTrigger() override { _soundTrigger = 0; }
+
+ virtual void callback() override;
+
+ virtual void setSyncJumpMask(uint16 mask) override { _syncJumpMask = mask; }
+
+ virtual void setMusicVolume(uint8 volume) override;
+ virtual void setSfxVolume(uint8 volume) override;
+
+private:
+ // These variables have not yet been named, but some of them are partly
+ // known nevertheless:
+ //
+ // pitchBend - Sound-related. Possibly some sort of pitch bend.
+ // unk18 - Sound-effect. Used for secondaryEffect1()
+ // unk19 - Sound-effect. Used for secondaryEffect1()
+ // unk20 - Sound-effect. Used for secondaryEffect1()
+ // unk21 - Sound-effect. Used for secondaryEffect1()
+ // unk22 - Sound-effect. Used for secondaryEffect1()
+ // unk29 - Sound-effect. Used for primaryEffect1()
+ // unk30 - Sound-effect. Used for primaryEffect1()
+ // unk31 - Sound-effect. Used for primaryEffect1()
+ // unk32 - Sound-effect. Used for primaryEffect2()
+ // unk33 - Sound-effect. Used for primaryEffect2()
+ // unk34 - Sound-effect. Used for primaryEffect2()
+ // unk35 - Sound-effect. Used for primaryEffect2()
+ // unk36 - Sound-effect. Used for primaryEffect2()
+ // unk37 - Sound-effect. Used for primaryEffect2()
+ // unk38 - Sound-effect. Used for primaryEffect2()
+ // unk39 - Currently unused, except for updateCallback56()
+ // unk40 - Currently unused, except for updateCallback56()
+ // unk41 - Sound-effect. Used for primaryEffect2()
+
+ struct Channel {
+ bool lock; // New to ScummVM
+ uint8 opExtraLevel2;
+ const uint8 *dataptr;
+ uint8 duration;
+ uint8 repeatCounter;
+ int8 baseOctave;
+ uint8 priority;
+ uint8 dataptrStackPos;
+ const uint8 *dataptrStack[4];
+ int8 baseNote;
+ uint8 unk29;
+ uint8 unk31;
+ uint16 unk30;
+ uint16 unk37;
+ uint8 unk33;
+ uint8 unk34;
+ uint8 unk35;
+ uint8 unk36;
+ uint8 unk32;
+ uint8 unk41;
+ uint8 unk38;
+ uint8 opExtraLevel1;
+ uint8 spacing2;
+ uint8 baseFreq;
+ uint8 tempo;
+ uint8 position;
+ uint8 regAx;
+ uint8 regBx;
+ typedef void (AdLibDriver::*Callback)(Channel&);
+ Callback primaryEffect;
+ Callback secondaryEffect;
+ uint8 fractionalSpacing;
+ uint8 opLevel1;
+ uint8 opLevel2;
+ uint8 opExtraLevel3;
+ uint8 twoChan;
+ uint8 unk39;
+ uint8 unk40;
+ uint8 spacing1;
+ uint8 durationRandomness;
+ uint8 unk19;
+ uint8 unk18;
+ int8 unk20;
+ int8 unk21;
+ uint8 unk22;
+ uint16 offset;
+ uint8 tempoReset;
+ uint8 rawNote;
+ int8 pitchBend;
+ uint8 volumeModifier;
+ };
+
+ void primaryEffect1(Channel &channel);
+ void primaryEffect2(Channel &channel);
+ void secondaryEffect1(Channel &channel);
+
+ void resetAdLibState();
+ void writeOPL(byte reg, byte val);
+ void initChannel(Channel &channel);
+ void noteOff(Channel &channel);
+ void unkOutput2(uint8 num);
+
+ uint16 getRandomNr();
+ void setupDuration(uint8 duration, Channel &channel);
+
+ void setupNote(uint8 rawNote, Channel &channel, bool flag = false);
+ void setupInstrument(uint8 regOffset, const uint8 *dataptr, Channel &channel);
+ void noteOn(Channel &channel);
+
+ void adjustVolume(Channel &channel);
+
+ uint8 calculateOpLevel1(Channel &channel);
+ uint8 calculateOpLevel2(Channel &channel);
+
+ uint16 checkValue(int16 val) {
+ if (val < 0)
+ val = 0;
+ else if (val > 0x3F)
+ val = 0x3F;
+ return val;
+ }
+
+ // The sound data has at least two lookup tables:
+ //
+ // * One for programs, starting at offset 0.
+ // * One for instruments, starting at offset 500.
+
+ uint8 *getProgram(int progId) {
+ const uint16 offset = READ_LE_UINT16(_soundData + 2 * progId);
+
+ // In case an invalid offset is specified we return nullptr to
+ // indicate an error. 0xFFFF seems to indicate "this is not a valid
+ // program/instrument". However, 0 is also invalid because it points
+ // inside the offset table itself. We also ignore any offsets outside
+ // of the actual data size.
+ // The original does not contain any safety checks and will simply
+ // read outside of the valid sound data in case an invalid offset is
+ // encountered.
+ if (offset == 0 || offset >= _soundDataSize) {
+ return nullptr;
+ } else {
+ return _soundData + offset;
+ }
+ }
+
+ const uint8 *getInstrument(int instrumentId) {
+ return getProgram(_numPrograms + instrumentId);
+ }
+
+ void setupPrograms();
+ void executePrograms();
+
+ struct ParserOpcode {
+ typedef int (AdLibDriver::*POpcode)(const uint8 *&dataptr, Channel &channel, uint8 value);
+ POpcode function;
+ const char *name;
+ };
+
+ void setupParserOpcodeTable();
+ const ParserOpcode *_parserOpcodeTable;
+ int _parserOpcodeTableSize;
+
+ int update_setRepeat(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_checkRepeat(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupProgram(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_jump(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_jumpToSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_returnFromSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setBaseOctave(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_stopChannel(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_playRest(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_writeAdLib(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupNoteAndDuration(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setBaseNote(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_stopOtherChannel(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_waitForEndOfProgram(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupInstrument(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupPrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_removePrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setBaseFreq(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupPrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setPriority(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback23(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback24(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupDuration(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_playNote(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setFractionalNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_removeSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setExtraLevel3(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_changeExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setAMDepth(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setVibratoDepth(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_changeExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback38(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback39(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_removePrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_pitchBend(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_resetToGlobalTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_nop(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setDurationRandomness(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_changeChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback46(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setupRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_playRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_removeRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback51(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback52(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback53(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setSoundTrigger(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int update_setTempoReset(const uint8 *&dataptr, Channel &channel, uint8 value);
+ int updateCallback56(const uint8 *&dataptr, Channel &channel, uint8 value);
+private:
+ // These variables have not yet been named, but some of them are partly
+ // known nevertheless:
+ //
+ // _unkValue1 - Unknown. Used for updating _unkValue2
+ // _unkValue2 - Unknown. Used for updating _unkValue4
+ // _unkValue4 - Unknown. Used for updating _unkValue5
+ // _unkValue5 - Unknown. Used for controlling updateCallback24().
+ // _unkValue6 - Unknown. Rhythm section volume?
+ // _unkValue7 - Unknown. Rhythm section volume?
+ // _unkValue8 - Unknown. Rhythm section volume?
+ // _unkValue9 - Unknown. Rhythm section volume?
+ // _unkValue10 - Unknown. Rhythm section volume?
+ // _unkValue11 - Unknown. Rhythm section volume?
+ // _unkValue12 - Unknown. Rhythm section volume?
+ // _unkValue13 - Unknown. Rhythm section volume?
+ // _unkValue14 - Unknown. Rhythm section volume?
+ // _unkValue15 - Unknown. Rhythm section volume?
+ // _unkValue16 - Unknown. Rhythm section volume?
+ // _unkValue17 - Unknown. Rhythm section volume?
+ // _unkValue18 - Unknown. Rhythm section volume?
+ // _unkValue19 - Unknown. Rhythm section volume?
+ // _unkValue20 - Unknown. Rhythm section volume?
+ // _freqTable[] - Probably frequences for the 12-tone scale.
+ // _unkTable2[] - Unknown. Currently only used by updateCallback46()
+ // _unkTable2_1[] - One of the tables in _unkTable2[]
+ // _unkTable2_2[] - One of the tables in _unkTable2[]
+ // _unkTable2_3[] - One of the tables in _unkTable2[]
+
+ int _curChannel;
+ uint8 _soundTrigger;
+
+ uint16 _rnd;
+
+ uint8 _unkValue1;
+ uint8 _unkValue2;
+ uint8 _callbackTimer;
+ uint8 _unkValue4;
+ uint8 _unkValue5;
+ uint8 _unkValue6;
+ uint8 _unkValue7;
+ uint8 _unkValue8;
+ uint8 _unkValue9;
+ uint8 _unkValue10;
+ uint8 _unkValue11;
+ uint8 _unkValue12;
+ uint8 _unkValue13;
+ uint8 _unkValue14;
+ uint8 _unkValue15;
+ uint8 _unkValue16;
+ uint8 _unkValue17;
+ uint8 _unkValue18;
+ uint8 _unkValue19;
+ uint8 _unkValue20;
+
+ OPL::OPL *_adlib;
+
+ uint8 *_soundData;
+ uint32 _soundDataSize;
+
+ struct QueueEntry {
+ QueueEntry() : data(0), id(0), volume(0) {}
+ QueueEntry(uint8 *ptr, uint8 track, uint8 vol) : data(ptr), id(track), volume(vol) {}
+ uint8 *data;
+ uint8 id;
+ uint8 volume;
+ };
+
+ QueueEntry _programQueue[16];
+ int _programStartTimeout;
+ int _programQueueStart, _programQueueEnd;
+ bool _retrySounds;
+
+ void adjustSfxData(uint8 *data, int volume);
+ uint8 *_sfxPointer;
+ int _sfxPriority;
+ int _sfxVelocity;
+
+ Channel _channels[10];
+
+ uint8 _vibratoAndAMDepthBits;
+ uint8 _rhythmSectionBits;
+
+ uint8 _curRegOffset;
+ uint8 _tempo;
+
+ const uint8 *_tablePtr1;
+ const uint8 *_tablePtr2;
+
+ static const uint8 _regOffset[];
+ static const uint16 _freqTable[];
+ static const uint8 *const _unkTable2[];
+ static const uint8 _unkTable2_1[];
+ static const uint8 _unkTable2_2[];
+ static const uint8 _unkTable2_3[];
+ static const uint8 _pitchBendTables[][32];
+
+ uint16 _syncJumpMask;
+
+ Common::Mutex _mutex;
+ Audio::Mixer *_mixer;
+
+ uint8 _musicVolume, _sfxVolume;
+
+ int _numPrograms;
+ int _version;
+};
+
+AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) : PCSoundDriver() {
setupParserOpcodeTable();
_version = version;
@@ -1961,6 +2293,10 @@ const uint8 AdLibDriver::_pitchBendTables[][32] = {
0x36, 0x37, 0x39, 0x3B, 0x3E, 0x41, 0x44, 0x47 }
};
+PCSoundDriver *PCSoundDriver::createAdLib(Audio::Mixer *mixer, int version) {
+ return new AdLibDriver(mixer, version);
+}
+
} // End of namespace Kyra
#undef CALLBACKS_PER_SECOND
diff --git a/engines/kyra/sound/drivers/adlib.h b/engines/kyra/sound/drivers/adlib.h
deleted file mode 100644
index 2bdddb7..0000000
--- a/engines/kyra/sound/drivers/adlib.h
+++ /dev/null
@@ -1,392 +0,0 @@
-/* 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.
- *
- * LGPL License
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-
-#ifndef KYRA_SOUND_ADLIBDRIVER_H
-#define KYRA_SOUND_ADLIBDRIVER_H
-
-#include "kyra/resource/resource.h"
-#include "common/mutex.h"
-
-// Basic AdLib Programming:
-// https://web.archive.org/web/20050322080425/http://www.gamedev.net/reference/articles/article446.asp
-
-
-namespace Audio {
-class Mixer;
-}
-
-namespace OPL {
-class OPL;
-}
-
-namespace Kyra {
-
-class AdLibDriver {
-public:
- AdLibDriver(Audio::Mixer *mixer, int version);
- ~AdLibDriver();
-
- void initDriver();
- void setSoundData(uint8 *data, uint32 size);
- void queueTrack(int track, int volume);
- bool isChannelPlaying(int channel) const;
- void stopAllChannels();
- int getSoundTrigger() const { return _soundTrigger; }
- void resetSoundTrigger() { _soundTrigger = 0; }
-
- void callback();
-
- void setSyncJumpMask(uint16 mask) { _syncJumpMask = mask; }
-
- void setMusicVolume(uint8 volume);
- void setSfxVolume(uint8 volume);
-
-private:
- // These variables have not yet been named, but some of them are partly
- // known nevertheless:
- //
- // pitchBend - Sound-related. Possibly some sort of pitch bend.
- // unk18 - Sound-effect. Used for secondaryEffect1()
- // unk19 - Sound-effect. Used for secondaryEffect1()
- // unk20 - Sound-effect. Used for secondaryEffect1()
- // unk21 - Sound-effect. Used for secondaryEffect1()
- // unk22 - Sound-effect. Used for secondaryEffect1()
- // unk29 - Sound-effect. Used for primaryEffect1()
- // unk30 - Sound-effect. Used for primaryEffect1()
- // unk31 - Sound-effect. Used for primaryEffect1()
- // unk32 - Sound-effect. Used for primaryEffect2()
- // unk33 - Sound-effect. Used for primaryEffect2()
- // unk34 - Sound-effect. Used for primaryEffect2()
- // unk35 - Sound-effect. Used for primaryEffect2()
- // unk36 - Sound-effect. Used for primaryEffect2()
- // unk37 - Sound-effect. Used for primaryEffect2()
- // unk38 - Sound-effect. Used for primaryEffect2()
- // unk39 - Currently unused, except for updateCallback56()
- // unk40 - Currently unused, except for updateCallback56()
- // unk41 - Sound-effect. Used for primaryEffect2()
-
- struct Channel {
- bool lock; // New to ScummVM
- uint8 opExtraLevel2;
- const uint8 *dataptr;
- uint8 duration;
- uint8 repeatCounter;
- int8 baseOctave;
- uint8 priority;
- uint8 dataptrStackPos;
- const uint8 *dataptrStack[4];
- int8 baseNote;
- uint8 unk29;
- uint8 unk31;
- uint16 unk30;
- uint16 unk37;
- uint8 unk33;
- uint8 unk34;
- uint8 unk35;
- uint8 unk36;
- uint8 unk32;
- uint8 unk41;
- uint8 unk38;
- uint8 opExtraLevel1;
- uint8 spacing2;
- uint8 baseFreq;
- uint8 tempo;
- uint8 position;
- uint8 regAx;
- uint8 regBx;
- typedef void (AdLibDriver::*Callback)(Channel&);
- Callback primaryEffect;
- Callback secondaryEffect;
- uint8 fractionalSpacing;
- uint8 opLevel1;
- uint8 opLevel2;
- uint8 opExtraLevel3;
- uint8 twoChan;
- uint8 unk39;
- uint8 unk40;
- uint8 spacing1;
- uint8 durationRandomness;
- uint8 unk19;
- uint8 unk18;
- int8 unk20;
- int8 unk21;
- uint8 unk22;
- uint16 offset;
- uint8 tempoReset;
- uint8 rawNote;
- int8 pitchBend;
- uint8 volumeModifier;
- };
-
- void primaryEffect1(Channel &channel);
- void primaryEffect2(Channel &channel);
- void secondaryEffect1(Channel &channel);
-
- void resetAdLibState();
- void writeOPL(byte reg, byte val);
- void initChannel(Channel &channel);
- void noteOff(Channel &channel);
- void unkOutput2(uint8 num);
-
- uint16 getRandomNr();
- void setupDuration(uint8 duration, Channel &channel);
-
- void setupNote(uint8 rawNote, Channel &channel, bool flag = false);
- void setupInstrument(uint8 regOffset, const uint8 *dataptr, Channel &channel);
- void noteOn(Channel &channel);
-
- void adjustVolume(Channel &channel);
-
- uint8 calculateOpLevel1(Channel &channel);
- uint8 calculateOpLevel2(Channel &channel);
-
- uint16 checkValue(int16 val) {
- if (val < 0)
- val = 0;
- else if (val > 0x3F)
- val = 0x3F;
- return val;
- }
-
- // The sound data has at least two lookup tables:
- //
- // * One for programs, starting at offset 0.
- // * One for instruments, starting at offset 500.
-
- uint8 *getProgram(int progId) {
- const uint16 offset = READ_LE_UINT16(_soundData + 2 * progId);
-
- // In case an invalid offset is specified we return nullptr to
- // indicate an error. 0xFFFF seems to indicate "this is not a valid
- // program/instrument". However, 0 is also invalid because it points
- // inside the offset table itself. We also ignore any offsets outside
- // of the actual data size.
- // The original does not contain any safety checks and will simply
- // read outside of the valid sound data in case an invalid offset is
- // encountered.
- if (offset == 0 || offset >= _soundDataSize) {
- return nullptr;
- } else {
- return _soundData + offset;
- }
- }
-
- const uint8 *getInstrument(int instrumentId) {
- return getProgram(_numPrograms + instrumentId);
- }
-
- void setupPrograms();
- void executePrograms();
-
- struct ParserOpcode {
- typedef int (AdLibDriver::*POpcode)(const uint8 *&dataptr, Channel &channel, uint8 value);
- POpcode function;
- const char *name;
- };
-
- void setupParserOpcodeTable();
- const ParserOpcode *_parserOpcodeTable;
- int _parserOpcodeTableSize;
-
- int update_setRepeat(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_checkRepeat(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setupProgram(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_jump(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_jumpToSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_returnFromSubroutine(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setBaseOctave(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_stopChannel(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_playRest(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_writeAdLib(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setupNoteAndDuration(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setBaseNote(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setupSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_stopOtherChannel(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_waitForEndOfProgram(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setupInstrument(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setupPrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_removePrimaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setBaseFreq(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setupPrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setPriority(const uint8 *&dataptr, Channel &channel, uint8 value);
- int updateCallback23(const uint8 *&dataptr, Channel &channel, uint8 value);
- int updateCallback24(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setupDuration(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_playNote(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setFractionalNoteSpacing(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_removeSecondaryEffect1(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setExtraLevel3(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_changeExtraLevel2(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setAMDepth(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setVibratoDepth(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_changeExtraLevel1(const uint8 *&dataptr, Channel &channel, uint8 value);
- int updateCallback38(const uint8 *&dataptr, Channel &channel, uint8 value);
- int updateCallback39(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_removePrimaryEffect2(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_pitchBend(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_resetToGlobalTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_nop(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setDurationRandomness(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_changeChannelTempo(const uint8 *&dataptr, Channel &channel, uint8 value);
- int updateCallback46(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setupRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_playRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_removeRhythmSection(const uint8 *&dataptr, Channel &channel, uint8 value);
- int updateCallback51(const uint8 *&dataptr, Channel &channel, uint8 value);
- int updateCallback52(const uint8 *&dataptr, Channel &channel, uint8 value);
- int updateCallback53(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setSoundTrigger(const uint8 *&dataptr, Channel &channel, uint8 value);
- int update_setTempoReset(const uint8 *&dataptr, Channel &channel, uint8 value);
- int updateCallback56(const uint8 *&dataptr, Channel &channel, uint8 value);
-private:
- // These variables have not yet been named, but some of them are partly
- // known nevertheless:
- //
- // _unkValue1 - Unknown. Used for updating _unkValue2
- // _unkValue2 - Unknown. Used for updating _unkValue4
- // _unkValue4 - Unknown. Used for updating _unkValue5
- // _unkValue5 - Unknown. Used for controlling updateCallback24().
- // _unkValue6 - Unknown. Rhythm section volume?
- // _unkValue7 - Unknown. Rhythm section volume?
- // _unkValue8 - Unknown. Rhythm section volume?
- // _unkValue9 - Unknown. Rhythm section volume?
- // _unkValue10 - Unknown. Rhythm section volume?
- // _unkValue11 - Unknown. Rhythm section volume?
- // _unkValue12 - Unknown. Rhythm section volume?
- // _unkValue13 - Unknown. Rhythm section volume?
- // _unkValue14 - Unknown. Rhythm section volume?
- // _unkValue15 - Unknown. Rhythm section volume?
- // _unkValue16 - Unknown. Rhythm section volume?
- // _unkValue17 - Unknown. Rhythm section volume?
- // _unkValue18 - Unknown. Rhythm section volume?
- // _unkValue19 - Unknown. Rhythm section volume?
- // _unkValue20 - Unknown. Rhythm section volume?
- // _freqTable[] - Probably frequences for the 12-tone scale.
- // _unkTable2[] - Unknown. Currently only used by updateCallback46()
- // _unkTable2_1[] - One of the tables in _unkTable2[]
- // _unkTable2_2[] - One of the tables in _unkTable2[]
- // _unkTable2_3[] - One of the tables in _unkTable2[]
-
- int _curChannel;
- uint8 _soundTrigger;
-
- uint16 _rnd;
-
- uint8 _unkValue1;
- uint8 _unkValue2;
- uint8 _callbackTimer;
- uint8 _unkValue4;
- uint8 _unkValue5;
- uint8 _unkValue6;
- uint8 _unkValue7;
- uint8 _unkValue8;
- uint8 _unkValue9;
- uint8 _unkValue10;
- uint8 _unkValue11;
- uint8 _unkValue12;
- uint8 _unkValue13;
- uint8 _unkValue14;
- uint8 _unkValue15;
- uint8 _unkValue16;
- uint8 _unkValue17;
- uint8 _unkValue18;
- uint8 _unkValue19;
- uint8 _unkValue20;
-
- OPL::OPL *_adlib;
-
- uint8 *_soundData;
- uint32 _soundDataSize;
-
- struct QueueEntry {
- QueueEntry() : data(0), id(0), volume(0) {}
- QueueEntry(uint8 *ptr, uint8 track, uint8 vol) : data(ptr), id(track), volume(vol) {}
- uint8 *data;
- uint8 id;
- uint8 volume;
- };
-
- QueueEntry _programQueue[16];
- int _programStartTimeout;
- int _programQueueStart, _programQueueEnd;
- bool _retrySounds;
-
- void adjustSfxData(uint8 *data, int volume);
- uint8 *_sfxPointer;
- int _sfxPriority;
- int _sfxVelocity;
-
- Channel _channels[10];
-
- uint8 _vibratoAndAMDepthBits;
- uint8 _rhythmSectionBits;
-
- uint8 _curRegOffset;
- uint8 _tempo;
-
- const uint8 *_tablePtr1;
- const uint8 *_tablePtr2;
-
- static const uint8 _regOffset[];
- static const uint16 _freqTable[];
- static const uint8 *const _unkTable2[];
- static const uint8 _unkTable2_1[];
- static const uint8 _unkTable2_2[];
- static const uint8 _unkTable2_3[];
- static const uint8 _pitchBendTables[][32];
-
- uint16 _syncJumpMask;
-
- Common::Mutex _mutex;
- Audio::Mixer *_mixer;
-
- uint8 _musicVolume, _sfxVolume;
-
- int _numPrograms;
- int _version;
-};
-
-} // End of namespace Kyra
-
-#endif
diff --git a/engines/kyra/sound/drivers/pc_base.h b/engines/kyra/sound/drivers/pc_base.h
new file mode 100644
index 0000000..b13b4a2
--- /dev/null
+++ b/engines/kyra/sound/drivers/pc_base.h
@@ -0,0 +1,64 @@
+/* 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 KYRA_SOUND_PCDRIVER_H
+#define KYRA_SOUND_PCDRIVER_H
+
+#include "kyra/resource/resource.h"
+
+namespace Audio {
+ class Mixer;
+}
+
+namespace Kyra {
+
+class PCSoundDriver {
+public:
+ PCSoundDriver() {}
+ virtual ~PCSoundDriver() {}
+
+ virtual void initDriver() = 0;
+ virtual void setSoundData(uint8 *data, uint32 size) = 0;
+ virtual void queueTrack(int track, int volume) = 0;
+ virtual bool isChannelPlaying(int channel) const = 0;
+ virtual void stopAllChannels() = 0;
+ virtual int getSoundTrigger() const = 0;
+ virtual void resetSoundTrigger() = 0;
+
+ virtual void callback() = 0;
+
+ // AdLiB specific
+ virtual void setSyncJumpMask(uint16) {}
+
+ virtual void setMusicVolume(uint8 volume) = 0;
+ virtual void setSfxVolume(uint8 volume) = 0;
+
+ static PCSoundDriver *createAdLib(Audio::Mixer *mixer, int version);
+#ifdef ENABLE_EOB
+ static PCSoundDriver *createPCSpk(Audio::Mixer *mixer);
+#endif
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/sound/drivers/pcspeaker.cpp b/engines/kyra/sound/drivers/pcspeaker.cpp
deleted file mode 100644
index 110adde..0000000
--- a/engines/kyra/sound/drivers/pcspeaker.cpp
+++ /dev/null
@@ -1,366 +0,0 @@
-/* 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.
- *
- */
-
-#include "kyra/sound/sound_intern.h"
-
-#include "audio/mixer.h"
-#include "audio/softsynth/pcspk.h"
-
-namespace Kyra {
-
-MidiDriver_PCSpeaker::MidiDriver_PCSpeaker(Audio::Mixer *mixer)
- : MidiDriver_Emulated(mixer), _rate(mixer->getOutputRate()) {
- _timerValue = 0;
- memset(_channel, 0, sizeof(_channel));
- memset(_note, 0, sizeof(_note));
-
- for (int i = 0; i < 2; ++i)
- _note[i].hardwareChannel = 0xFF;
-
- _speaker = new Audio::PCSpeaker(_rate);
- assert(_speaker);
- _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
-
- _countdown = 0xFFFF;
- _hardwareChannel[0] = 0xFF;
- _modulationFlag = false;
-}
-
-MidiDriver_PCSpeaker::~MidiDriver_PCSpeaker() {
- _mixer->stopHandle(_mixerSoundHandle);
- delete _speaker;
- _speaker = 0;
-}
-
-void MidiDriver_PCSpeaker::send(uint32 data) {
- Common::StackLock lock(_mutex);
-
- uint8 channel = data & 0x0F;
- uint8 param1 = (data >> 8) & 0xFF;
- uint8 param2 = (data >> 16) & 0xFF;
-
- uint8 flags = 0x00;
-
- if (channel > 1)
- return;
-
- switch (data & 0xF0) {
- case 0x80: // note off
- noteOff(channel, param1);
- return;
-
- case 0x90: // note on
- if (channel > 1)
- return;
-
- if (param2)
- noteOn(channel, param1);
- else
- noteOff(channel, param1);
- return;
-
- case 0xB0: // controller
- switch (param1) {
- case 0x01: // modulation
- _channel[channel].modulation = param2;
- break;
-
- case 0x40: // hold
- _channel[channel].hold = param2;
- if (param2 < 0x40)
- resetController(channel);
- return;
-
- case 0x70: // voice protect
- _channel[channel].voiceProtect = param2;
- return;
-
- case 0x79: // all notes off
- _channel[channel].hold = 0;
- resetController(channel);
- _channel[channel].modulation = 0;
- _channel[channel].pitchBendLow = 0;
- _channel[channel].pitchBendHigh = 0x40;
- flags = 0x01;
- break;
-
- default:
- return;
- }
- break;
-
- case 0xE0: // pitch bend
- flags = 0x01;
- _channel[channel].pitchBendLow = param1;
- _channel[channel].pitchBendHigh = param2;
- break;
-
- default:
- return;
- }
-
- for (int i = 0; i < 2; ++i) {
- if (_note[i].enabled && _note[i].midiChannel == channel) {
- _note[i].flags |= flags;
- setupTone(i);
- }
- }
-}
-
-void MidiDriver_PCSpeaker::resetController(int channel) {
- for (int i = 0; i < 2; ++i) {
- if (_note[i].enabled && _note[i].midiChannel == channel && _note[i].processHold)
- noteOff(channel, _note[i].note);
- }
-}
-
-void MidiDriver_PCSpeaker::noteOn(int channel, int note) {
- int n = 0;
-
- while (n < 2 && _note[n].enabled)
- ++n;
-
- if (n >= 2)
- return;
-
- _note[n].midiChannel = channel;
- _note[n].note = note;
- _note[n].enabled = true;
- _note[n].processHold = false;
- _note[n].hardwareFlags = 0x20;
- _note[n].priority = 0x7FFF;
- _note[n].flags = 0x01;
-
- turnNoteOn(n);
-}
-
-void MidiDriver_PCSpeaker::turnNoteOn(int note) {
- if (_hardwareChannel[0] == 0xFF) {
- _note[note].hardwareChannel = 0;
- ++_channel[_note[note].midiChannel].noteCount;
- _hardwareChannel[0] = _note[note].midiChannel;
- _note[note].flags = 0x01;
-
- setupTone(note);
- } else {
- overwriteNote(note);
- }
-}
-
-void MidiDriver_PCSpeaker::overwriteNote(int note) {
- int totalNotes = 0;
- for (int i = 0; i < 2; ++i) {
- if (_note[i].enabled) {
- ++totalNotes;
- const int channel = _note[i].midiChannel;
-
- uint16 priority = 0xFFFF;
- if (_channel[channel].voiceProtect < 0x40)
- priority = _note[i].priority;
-
- if (_channel[channel].noteCount > priority)
- priority = 0;
- else
- priority -= _channel[channel].noteCount;
-
- _note[i].precedence = priority;
- }
- }
-
- if (totalNotes <= 1)
- return;
-
- do {
- uint16 maxValue = 0;
- uint16 minValue = 0xFFFF;
- int newNote = 0;
-
- for (int i = 0; i < 2; ++i) {
- if (_note[i].enabled) {
- if (_note[i].hardwareChannel == 0xFF) {
- if (_note[i].precedence >= maxValue) {
- maxValue = _note[i].precedence;
- newNote = i;
- }
- } else {
- if (_note[i].precedence <= minValue) {
- minValue = _note[i].precedence;
- note = i;
- }
- }
- }
- }
-
- if (maxValue < minValue)
- return;
-
- turnNoteOff(_note[note].hardwareChannel);
- _note[note].enabled = false;
-
- _note[newNote].hardwareChannel = _note[note].hardwareChannel;
- ++_channel[_note[newNote].midiChannel].noteCount;
- _hardwareChannel[_note[note].hardwareChannel] = _note[newNote].midiChannel;
- _note[newNote].flags = 0x01;
-
- setupTone(newNote);
- } while (--totalNotes);
-}
-
-void MidiDriver_PCSpeaker::noteOff(int channel, int note) {
- for (int i = 0; i < 2; ++i) {
- if (_note[i].enabled && _note[i].note == note && _note[i].midiChannel == channel) {
- if (_channel[i].hold < 0x40) {
- turnNoteOff(i);
- _note[i].enabled = false;
- } else {
- _note[i].processHold = true;
- }
- }
- }
-}
-
-void MidiDriver_PCSpeaker::turnNoteOff(int note) {
- if (_note[note].hardwareChannel != 0xFF) {
- _note[note].hardwareFlags &= 0xDF;
- _note[note].flags |= 1;
-
- setupTone(note);
-
- --_channel[_note[note].midiChannel].noteCount;
-
- _hardwareChannel[_note[note].hardwareChannel] = 0xFF;
- _note[note].hardwareChannel = 0xFF;
- }
-}
-
-void MidiDriver_PCSpeaker::setupTone(int note) {
- if (_note[note].hardwareChannel == 0xFF)
- return;
-
- if (!(_note[note].flags & 0x01))
- return;
-
- if (!(_note[note].hardwareFlags & 0x20)) {
- _speaker->stop();
- } else {
- const int midiChannel = _note[note].midiChannel;
- uint16 pitchBend = (_channel[midiChannel].pitchBendHigh << 7) | _channel[midiChannel].pitchBendLow;
-
- int noteValue = _note[note].note;
-
- noteValue -= 24;
- do {
- noteValue += 12;
- } while (noteValue < 0);
-
- noteValue += 12;
- do {
- noteValue -= 12;
- } while (noteValue > 95);
-
- int16 modulation = _note[note].modulation;
-
- int tableIndex = MAX(noteValue - 12, 0);
- uint16 note1 = (_noteTable2[tableIndex] << 8) | _noteTable1[tableIndex];
- tableIndex = MIN(noteValue + 12, 95);
- uint16 note2 = (_noteTable2[tableIndex] << 8) | _noteTable1[tableIndex];
- uint16 note3 = (_noteTable2[noteValue] << 8) | _noteTable1[noteValue];
-
- int32 countdown = pitchBend - 0x2000;
- countdown += modulation;
-
- if (countdown >= 0)
- countdown *= (note2 - note3);
- else
- countdown *= (note3 - note1);
-
- countdown /= 0x2000;
- countdown += note3;
-
- countdown = uint16(countdown & 0xFFFF);
- if (countdown != _countdown)
- _countdown = countdown;
-
- _speaker->play(Audio::PCSpeaker::kWaveFormSquare, 1193180 / _countdown, -1);
- }
-
- _note[note].flags &= 0xFE;
-}
-
-void MidiDriver_PCSpeaker::generateSamples(int16 *buffer, int numSamples) {
- Common::StackLock lock(_mutex);
- _speaker->readBuffer(buffer, numSamples);
-}
-
-void MidiDriver_PCSpeaker::onTimer() {
- /*Common::StackLock lock(_mutex);
-
- _timerValue += 20;
- if (_timerValue < 120)
- return;
- _timerValue -= 120;
-
- _modulationFlag = !_modulationFlag;
- for (int i = 0; i < 2; ++i) {
- if (_note[i].enabled) {
- uint16 modValue = 5 * _channel[_note[i].midiChannel].modulation;
- if (_modulationFlag)
- modValue = -modValue;
- _note[i].modulation = modValue;
- _note[i].flags |= 1;
-
- setupTone(i);
- }
- }*/
-}
-
-const uint8 MidiDriver_PCSpeaker::_noteTable1[] = {
- 0x88, 0xB5, 0x4E, 0x40, 0x41, 0xCD, 0xC4, 0x3D,
- 0x43, 0x7C, 0x2A, 0xD6, 0x88, 0xB5, 0xFF, 0xD1,
- 0x20, 0xA7, 0xE2, 0x1E, 0xCE, 0xBE, 0xF2, 0x8A,
- 0x44, 0x41, 0x7F, 0xE8, 0x90, 0x63, 0x63, 0x8F,
- 0xE7, 0x5F, 0x01, 0xBD, 0xA2, 0xA0, 0xBF, 0xF4,
- 0x48, 0xB1, 0x31, 0xC7, 0x70, 0x2F, 0xFE, 0xE0,
- 0xD1, 0xD0, 0xDE, 0xFB, 0x24, 0x58, 0x98, 0xE3,
- 0x39, 0x97, 0xFF, 0x6F, 0xE8, 0x68, 0xEF, 0x7D,
- 0x11, 0xAC, 0x4C, 0xF1, 0x9C, 0x4B, 0xFF, 0xB7,
- 0x74, 0x34, 0xF7, 0xBE, 0x88, 0x56, 0x26, 0xF8,
- 0xCE, 0xA5, 0x7F, 0x5B, 0x3A, 0x1A, 0xFB, 0xDF,
- 0xC4, 0xAB, 0x93, 0x7C, 0x67, 0x52, 0x3F, 0x2D
-};
-
-const uint8 MidiDriver_PCSpeaker::_noteTable2[] = {
- 0x8E, 0x86, 0xFD, 0xF0, 0xE2, 0xD5, 0xC9, 0xBE,
- 0xB3, 0xA9, 0xA0, 0x96, 0x8E, 0x86, 0x7E, 0x77,
- 0x71, 0x6A, 0x64, 0x5F, 0x59, 0x54, 0x4F, 0x4B,
- 0x47, 0x43, 0x3F, 0x3B, 0x38, 0x35, 0x32, 0x2F,
- 0x2C, 0x2A, 0x28, 0x25, 0x23, 0x21, 0x1F, 0x1D,
- 0x1C, 0x1A, 0x19, 0x17, 0x16, 0x15, 0x13, 0x12,
- 0x11, 0x10, 0x0F, 0x0E, 0x0E, 0x0D, 0x0C, 0x0B,
- 0x0B, 0x0A, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07,
- 0x07, 0x06, 0x06, 0x05, 0x05, 0x05, 0x04, 0x04,
- 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02,
- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
-};
-
-} // End of namespace Kyra
diff --git a/engines/kyra/sound/drivers/pcspeaker_v1.cpp b/engines/kyra/sound/drivers/pcspeaker_v1.cpp
new file mode 100644
index 0000000..01dcbab
--- /dev/null
+++ b/engines/kyra/sound/drivers/pcspeaker_v1.cpp
@@ -0,0 +1,99 @@
+/* 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.
+ *
+ */
+
+#ifdef ENABLE_EOB
+
+#include "kyra/sound/drivers/pc_base.h"
+#include "audio/mixer.h"
+#include "audio/softsynth/pcspk.h"
+
+namespace Kyra {
+
+class PCSpeakerDriver : public PCSoundDriver {
+public:
+ PCSpeakerDriver(Audio::Mixer *mixer);
+ virtual ~PCSpeakerDriver();
+
+ virtual void initDriver() override;
+ virtual void setSoundData(uint8 *data, uint32 size) override;
+ virtual void queueTrack(int track, int volume) override;
+ virtual bool isChannelPlaying(int channel) const override;
+ virtual void stopAllChannels() override;
+ virtual int getSoundTrigger() const override { return _soundTrigger; }
+ virtual void resetSoundTrigger() override { _soundTrigger = 0; }
+
+ virtual void callback() override;
+
+ virtual void setMusicVolume(uint8 volume) override;
+ virtual void setSfxVolume(uint8 volume) override;
+
+private:
+ int _soundTrigger;
+};
+
+PCSpeakerDriver::PCSpeakerDriver(Audio::Mixer *mixer) : PCSoundDriver() {
+
+}
+
+PCSpeakerDriver::~PCSpeakerDriver() {
+
+}
+
+void PCSpeakerDriver::initDriver() {
+
+}
+
+void PCSpeakerDriver::setSoundData(uint8 *data, uint32 size) {
+
+}
+
+void PCSpeakerDriver::queueTrack(int track, int volume) {
+
+}
+
+bool PCSpeakerDriver::isChannelPlaying(int channel) const {
+ return true;
+}
+
+void PCSpeakerDriver::stopAllChannels() {
+
+}
+
+void PCSpeakerDriver::callback() {
+
+}
+
+void PCSpeakerDriver::setMusicVolume(uint8 volume) {
+
+}
+
+void PCSpeakerDriver::setSfxVolume(uint8 volume) {
+
+}
+
+PCSoundDriver *PCSoundDriver::createPCSpk(Audio::Mixer *mixer) {
+ return new PCSpeakerDriver(mixer);
+}
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/sound/drivers/pcspeaker_v2.cpp b/engines/kyra/sound/drivers/pcspeaker_v2.cpp
new file mode 100644
index 0000000..110adde
--- /dev/null
+++ b/engines/kyra/sound/drivers/pcspeaker_v2.cpp
@@ -0,0 +1,366 @@
+/* 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.
+ *
+ */
+
+#include "kyra/sound/sound_intern.h"
+
+#include "audio/mixer.h"
+#include "audio/softsynth/pcspk.h"
+
+namespace Kyra {
+
+MidiDriver_PCSpeaker::MidiDriver_PCSpeaker(Audio::Mixer *mixer)
+ : MidiDriver_Emulated(mixer), _rate(mixer->getOutputRate()) {
+ _timerValue = 0;
+ memset(_channel, 0, sizeof(_channel));
+ memset(_note, 0, sizeof(_note));
+
+ for (int i = 0; i < 2; ++i)
+ _note[i].hardwareChannel = 0xFF;
+
+ _speaker = new Audio::PCSpeaker(_rate);
+ assert(_speaker);
+ _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+
+ _countdown = 0xFFFF;
+ _hardwareChannel[0] = 0xFF;
+ _modulationFlag = false;
+}
+
+MidiDriver_PCSpeaker::~MidiDriver_PCSpeaker() {
+ _mixer->stopHandle(_mixerSoundHandle);
+ delete _speaker;
+ _speaker = 0;
+}
+
+void MidiDriver_PCSpeaker::send(uint32 data) {
+ Common::StackLock lock(_mutex);
+
+ uint8 channel = data & 0x0F;
+ uint8 param1 = (data >> 8) & 0xFF;
+ uint8 param2 = (data >> 16) & 0xFF;
+
+ uint8 flags = 0x00;
+
+ if (channel > 1)
+ return;
+
+ switch (data & 0xF0) {
+ case 0x80: // note off
+ noteOff(channel, param1);
+ return;
+
+ case 0x90: // note on
+ if (channel > 1)
+ return;
+
+ if (param2)
+ noteOn(channel, param1);
+ else
+ noteOff(channel, param1);
+ return;
+
+ case 0xB0: // controller
+ switch (param1) {
+ case 0x01: // modulation
+ _channel[channel].modulation = param2;
+ break;
+
+ case 0x40: // hold
+ _channel[channel].hold = param2;
+ if (param2 < 0x40)
+ resetController(channel);
+ return;
+
+ case 0x70: // voice protect
+ _channel[channel].voiceProtect = param2;
+ return;
+
+ case 0x79: // all notes off
+ _channel[channel].hold = 0;
+ resetController(channel);
+ _channel[channel].modulation = 0;
+ _channel[channel].pitchBendLow = 0;
+ _channel[channel].pitchBendHigh = 0x40;
+ flags = 0x01;
+ break;
+
+ default:
+ return;
+ }
+ break;
+
+ case 0xE0: // pitch bend
+ flags = 0x01;
+ _channel[channel].pitchBendLow = param1;
+ _channel[channel].pitchBendHigh = param2;
+ break;
+
+ default:
+ return;
+ }
+
+ for (int i = 0; i < 2; ++i) {
+ if (_note[i].enabled && _note[i].midiChannel == channel) {
+ _note[i].flags |= flags;
+ setupTone(i);
+ }
+ }
+}
+
+void MidiDriver_PCSpeaker::resetController(int channel) {
+ for (int i = 0; i < 2; ++i) {
+ if (_note[i].enabled && _note[i].midiChannel == channel && _note[i].processHold)
+ noteOff(channel, _note[i].note);
+ }
+}
+
+void MidiDriver_PCSpeaker::noteOn(int channel, int note) {
+ int n = 0;
+
+ while (n < 2 && _note[n].enabled)
+ ++n;
+
+ if (n >= 2)
+ return;
+
+ _note[n].midiChannel = channel;
+ _note[n].note = note;
+ _note[n].enabled = true;
+ _note[n].processHold = false;
+ _note[n].hardwareFlags = 0x20;
+ _note[n].priority = 0x7FFF;
+ _note[n].flags = 0x01;
+
+ turnNoteOn(n);
+}
+
+void MidiDriver_PCSpeaker::turnNoteOn(int note) {
+ if (_hardwareChannel[0] == 0xFF) {
+ _note[note].hardwareChannel = 0;
+ ++_channel[_note[note].midiChannel].noteCount;
+ _hardwareChannel[0] = _note[note].midiChannel;
+ _note[note].flags = 0x01;
+
+ setupTone(note);
+ } else {
+ overwriteNote(note);
+ }
+}
+
+void MidiDriver_PCSpeaker::overwriteNote(int note) {
+ int totalNotes = 0;
+ for (int i = 0; i < 2; ++i) {
+ if (_note[i].enabled) {
+ ++totalNotes;
+ const int channel = _note[i].midiChannel;
+
+ uint16 priority = 0xFFFF;
+ if (_channel[channel].voiceProtect < 0x40)
+ priority = _note[i].priority;
+
+ if (_channel[channel].noteCount > priority)
+ priority = 0;
+ else
+ priority -= _channel[channel].noteCount;
+
+ _note[i].precedence = priority;
+ }
+ }
+
+ if (totalNotes <= 1)
+ return;
+
+ do {
+ uint16 maxValue = 0;
+ uint16 minValue = 0xFFFF;
+ int newNote = 0;
+
+ for (int i = 0; i < 2; ++i) {
+ if (_note[i].enabled) {
+ if (_note[i].hardwareChannel == 0xFF) {
+ if (_note[i].precedence >= maxValue) {
+ maxValue = _note[i].precedence;
+ newNote = i;
+ }
+ } else {
+ if (_note[i].precedence <= minValue) {
+ minValue = _note[i].precedence;
+ note = i;
+ }
+ }
+ }
+ }
+
+ if (maxValue < minValue)
+ return;
+
+ turnNoteOff(_note[note].hardwareChannel);
+ _note[note].enabled = false;
+
+ _note[newNote].hardwareChannel = _note[note].hardwareChannel;
+ ++_channel[_note[newNote].midiChannel].noteCount;
+ _hardwareChannel[_note[note].hardwareChannel] = _note[newNote].midiChannel;
+ _note[newNote].flags = 0x01;
+
+ setupTone(newNote);
+ } while (--totalNotes);
+}
+
+void MidiDriver_PCSpeaker::noteOff(int channel, int note) {
+ for (int i = 0; i < 2; ++i) {
+ if (_note[i].enabled && _note[i].note == note && _note[i].midiChannel == channel) {
+ if (_channel[i].hold < 0x40) {
+ turnNoteOff(i);
+ _note[i].enabled = false;
+ } else {
+ _note[i].processHold = true;
+ }
+ }
+ }
+}
+
+void MidiDriver_PCSpeaker::turnNoteOff(int note) {
+ if (_note[note].hardwareChannel != 0xFF) {
+ _note[note].hardwareFlags &= 0xDF;
+ _note[note].flags |= 1;
+
+ setupTone(note);
+
+ --_channel[_note[note].midiChannel].noteCount;
+
+ _hardwareChannel[_note[note].hardwareChannel] = 0xFF;
+ _note[note].hardwareChannel = 0xFF;
+ }
+}
+
+void MidiDriver_PCSpeaker::setupTone(int note) {
+ if (_note[note].hardwareChannel == 0xFF)
+ return;
+
+ if (!(_note[note].flags & 0x01))
+ return;
+
+ if (!(_note[note].hardwareFlags & 0x20)) {
+ _speaker->stop();
+ } else {
+ const int midiChannel = _note[note].midiChannel;
+ uint16 pitchBend = (_channel[midiChannel].pitchBendHigh << 7) | _channel[midiChannel].pitchBendLow;
+
+ int noteValue = _note[note].note;
+
+ noteValue -= 24;
+ do {
+ noteValue += 12;
+ } while (noteValue < 0);
+
+ noteValue += 12;
+ do {
+ noteValue -= 12;
+ } while (noteValue > 95);
+
+ int16 modulation = _note[note].modulation;
+
+ int tableIndex = MAX(noteValue - 12, 0);
+ uint16 note1 = (_noteTable2[tableIndex] << 8) | _noteTable1[tableIndex];
+ tableIndex = MIN(noteValue + 12, 95);
+ uint16 note2 = (_noteTable2[tableIndex] << 8) | _noteTable1[tableIndex];
+ uint16 note3 = (_noteTable2[noteValue] << 8) | _noteTable1[noteValue];
+
+ int32 countdown = pitchBend - 0x2000;
+ countdown += modulation;
+
+ if (countdown >= 0)
+ countdown *= (note2 - note3);
+ else
+ countdown *= (note3 - note1);
+
+ countdown /= 0x2000;
+ countdown += note3;
+
+ countdown = uint16(countdown & 0xFFFF);
+ if (countdown != _countdown)
+ _countdown = countdown;
+
+ _speaker->play(Audio::PCSpeaker::kWaveFormSquare, 1193180 / _countdown, -1);
+ }
+
+ _note[note].flags &= 0xFE;
+}
+
+void MidiDriver_PCSpeaker::generateSamples(int16 *buffer, int numSamples) {
+ Common::StackLock lock(_mutex);
+ _speaker->readBuffer(buffer, numSamples);
+}
+
+void MidiDriver_PCSpeaker::onTimer() {
+ /*Common::StackLock lock(_mutex);
+
+ _timerValue += 20;
+ if (_timerValue < 120)
+ return;
+ _timerValue -= 120;
+
+ _modulationFlag = !_modulationFlag;
+ for (int i = 0; i < 2; ++i) {
+ if (_note[i].enabled) {
+ uint16 modValue = 5 * _channel[_note[i].midiChannel].modulation;
+ if (_modulationFlag)
+ modValue = -modValue;
+ _note[i].modulation = modValue;
+ _note[i].flags |= 1;
+
+ setupTone(i);
+ }
+ }*/
+}
+
+const uint8 MidiDriver_PCSpeaker::_noteTable1[] = {
+ 0x88, 0xB5, 0x4E, 0x40, 0x41, 0xCD, 0xC4, 0x3D,
+ 0x43, 0x7C, 0x2A, 0xD6, 0x88, 0xB5, 0xFF, 0xD1,
+ 0x20, 0xA7, 0xE2, 0x1E, 0xCE, 0xBE, 0xF2, 0x8A,
+ 0x44, 0x41, 0x7F, 0xE8, 0x90, 0x63, 0x63, 0x8F,
+ 0xE7, 0x5F, 0x01, 0xBD, 0xA2, 0xA0, 0xBF, 0xF4,
+ 0x48, 0xB1, 0x31, 0xC7, 0x70, 0x2F, 0xFE, 0xE0,
+ 0xD1, 0xD0, 0xDE, 0xFB, 0x24, 0x58, 0x98, 0xE3,
+ 0x39, 0x97, 0xFF, 0x6F, 0xE8, 0x68, 0xEF, 0x7D,
+ 0x11, 0xAC, 0x4C, 0xF1, 0x9C, 0x4B, 0xFF, 0xB7,
+ 0x74, 0x34, 0xF7, 0xBE, 0x88, 0x56, 0x26, 0xF8,
+ 0xCE, 0xA5, 0x7F, 0x5B, 0x3A, 0x1A, 0xFB, 0xDF,
+ 0xC4, 0xAB, 0x93, 0x7C, 0x67, 0x52, 0x3F, 0x2D
+};
+
+const uint8 MidiDriver_PCSpeaker::_noteTable2[] = {
+ 0x8E, 0x86, 0xFD, 0xF0, 0xE2, 0xD5, 0xC9, 0xBE,
+ 0xB3, 0xA9, 0xA0, 0x96, 0x8E, 0x86, 0x7E, 0x77,
+ 0x71, 0x6A, 0x64, 0x5F, 0x59, 0x54, 0x4F, 0x4B,
+ 0x47, 0x43, 0x3F, 0x3B, 0x38, 0x35, 0x32, 0x2F,
+ 0x2C, 0x2A, 0x28, 0x25, 0x23, 0x21, 0x1F, 0x1D,
+ 0x1C, 0x1A, 0x19, 0x17, 0x16, 0x15, 0x13, 0x12,
+ 0x11, 0x10, 0x0F, 0x0E, 0x0E, 0x0D, 0x0C, 0x0B,
+ 0x0B, 0x0A, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07,
+ 0x07, 0x06, 0x06, 0x05, 0x05, 0x05, 0x04, 0x04,
+ 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
+};
+
+} // End of namespace Kyra
diff --git a/engines/kyra/sound/sound_adlib.cpp b/engines/kyra/sound/sound_adlib.cpp
deleted file mode 100644
index 2e72583..0000000
--- a/engines/kyra/sound/sound_adlib.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-/* 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.
- *
- */
-
-#include "kyra/sound/sound_intern.h"
-#include "kyra/sound/drivers/adlib.h"
-
-#include "common/system.h"
-#include "common/config-manager.h"
-
-
-namespace Kyra {
-
-// Kyra 1 sound triggers. Most noticeably, these are used towards the end of
-// the game, in the castle, to cycle between different songs. The same music is
-// used in other places throughout the game, but the player is less likely to
-// spend enough time there to notice.
-
-const int SoundAdLibPC::_kyra1SoundTriggers[] = {
- 0, 4, 5, 3
-};
-
-const int SoundAdLibPC::_kyra1NumSoundTriggers = ARRAYSIZE(SoundAdLibPC::_kyra1SoundTriggers);
-
-SoundAdLibPC::SoundAdLibPC(KyraEngine_v1 *vm, Audio::Mixer *mixer)
- : Sound(vm, mixer), _driver(0), _trackEntries(), _soundDataPtr(0) {
- memset(_trackEntries, 0, sizeof(_trackEntries));
-
- _soundTriggers = 0;
- _numSoundTriggers = 0;
- _sfxPlayingSound = -1;
- _soundFileLoaded.clear();
- _currentResourceSet = 0;
- memset(&_resInfo, 0, sizeof(_resInfo));
-
- switch (vm->game()) {
- case GI_LOL:
- _version = _vm->gameFlags().isDemo ? 3 : 4;
- break;
- case GI_KYRA2:
- _version = 4;
- break;
- case GI_KYRA1:
- _version = 3;
- _soundTriggers = _kyra1SoundTriggers;
- _numSoundTriggers = _kyra1NumSoundTriggers;
- break;
- case GI_EOB2:
- _version = 2;
- break;
- case GI_EOB1:
- _version = 1;
- break;
- default:
- break;
- }
-
- _driver = new AdLibDriver(mixer, _version);
- assert(_driver);
-}
-
-SoundAdLibPC::~SoundAdLibPC() {
- delete _driver;
- delete[] _soundDataPtr;
- for (int i = 0; i < 3; i++)
- initAudioResourceInfo(i, 0);
-}
-
-bool SoundAdLibPC::init() {
- _driver->initDriver();
- return true;
-}
-
-void SoundAdLibPC::process() {
- int trigger = _driver->getSoundTrigger();
-
- if (trigger < _numSoundTriggers) {
- int soundId = _soundTriggers[trigger];
-
- if (soundId)
- playTrack(soundId);
- } else {
- warning("Unknown sound trigger %d", trigger);
- // TODO: At this point, we really want to clear the trigger...
- }
-}
-
-void SoundAdLibPC::updateVolumeSettings() {
- bool mute = false;
- if (ConfMan.hasKey("mute"))
- mute = ConfMan.getBool("mute");
-
- int newMusicVolume = mute ? 0 : ConfMan.getInt("music_volume");
- //newMusicVolume = (newMusicVolume * 145) / Audio::Mixer::kMaxMixerVolume + 110;
- newMusicVolume = CLIP(newMusicVolume, 0, 255);
-
- int newSfxVolume = mute ? 0 : ConfMan.getInt("sfx_volume");
- //newSfxVolume = (newSfxVolume * 200) / Audio::Mixer::kMaxMixerVolume + 55;
- newSfxVolume = CLIP(newSfxVolume, 0, 255);
-
- _driver->setMusicVolume(newMusicVolume);
- _driver->setSfxVolume(newSfxVolume);
-}
-
-void SoundAdLibPC::playTrack(uint8 track) {
- if (_musicEnabled) {
- // WORKAROUND: There is a bug in the Kyra 1 "Pool of Sorrow"
- // music which causes the channels to get progressively out of
- // sync for each loop. To avoid that, we declare that all four
- // of the song channels have to jump "in sync".
-
- if (track == 4 && _soundFileLoaded.equalsIgnoreCase("KYRA1B.ADL"))
- _driver->setSyncJumpMask(0x000F);
- else
- _driver->setSyncJumpMask(0);
- play(track, 0xFF);
- }
-}
-
-void SoundAdLibPC::haltTrack() {
- play(0, 0);
- play(0, 0);
- //_vm->_system->delayMillis(3 * 60);
-}
-
-bool SoundAdLibPC::isPlaying() const {
- return _driver->isChannelPlaying(0);
-}
-
-void SoundAdLibPC::playSoundEffect(uint8 track, uint8 volume) {
- if (_sfxEnabled)
- play(track, volume);
-}
-
-void SoundAdLibPC::play(uint8 track, uint8 volume) {
- uint16 soundId = 0;
-
- if (_version == 4)
- soundId = READ_LE_UINT16(&_trackEntries[track<<1]);
- else
- soundId = _trackEntries[track];
-
- if ((soundId == 0xFFFF && _version == 4) || (soundId == 0xFF && _version < 4) || !_soundDataPtr)
- return;
-
- _driver->queueTrack(soundId, volume);
-}
-
-void SoundAdLibPC::beginFadeOut() {
- play(_version > 2 ? 1 : 15, 0xFF);
-}
-
-int SoundAdLibPC::checkTrigger() {
- return _driver->getSoundTrigger();
-}
-
-void SoundAdLibPC::resetTrigger() {
- _driver->resetSoundTrigger();
-}
-
-void SoundAdLibPC::initAudioResourceInfo(int set, void *info) {
- if (set >= kMusicIntro && set <= kMusicFinale) {
- delete _resInfo[set];
- _resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : 0;
- }
-}
-
-void SoundAdLibPC::selectAudioResourceSet(int set) {
- if (set >= kMusicIntro && set <= kMusicFinale) {
- if (_resInfo[set])
- _currentResourceSet = set;
- }
-}
-
-bool SoundAdLibPC::hasSoundFile(uint file) const {
- if (file < res()->fileListSize)
- return (res()->fileList[file] != 0);
- return false;
-}
-
-void SoundAdLibPC::loadSoundFile(uint file) {
- if (file < res()->fileListSize)
- internalLoadFile(res()->fileList[file]);
-}
-
-void SoundAdLibPC::loadSoundFile(Common::String file) {
- internalLoadFile(file);
-}
-
-void SoundAdLibPC::internalLoadFile(Common::String file) {
- file += ((_version == 1) ? ".DAT" : ".ADL");
- if (_soundFileLoaded == file)
- return;
-
- if (_soundDataPtr)
- haltTrack();
-
- uint8 *fileData = 0; uint32 fileSize = 0;
-
- fileData = _vm->resource()->fileData(file.c_str(), &fileSize);
- if (!fileData) {
- warning("Couldn't find music file: '%s'", file.c_str());
- return;
- }
-
- playSoundEffect(0);
- playSoundEffect(0);
-
- _driver->stopAllChannels();
- _soundDataPtr = 0;
-
- int soundDataSize = fileSize;
- uint8 *p = fileData;
-
- if (_version == 4) {
- memcpy(_trackEntries, p, 500);
- p += 500;
- soundDataSize -= 500;
- } else {
- memcpy(_trackEntries, p, 120);
- p += 120;
- soundDataSize -= 120;
- }
-
- _soundDataPtr = new uint8[soundDataSize];
- assert(_soundDataPtr);
-
- memcpy(_soundDataPtr, p, soundDataSize);
-
- delete[] fileData;
- fileData = p = 0;
- fileSize = 0;
-
- _driver->setSoundData(_soundDataPtr, soundDataSize);
-
- _soundFileLoaded = file;
-}
-
-} // End of namespace Kyra
diff --git a/engines/kyra/sound/sound_adlib.h b/engines/kyra/sound/sound_adlib.h
deleted file mode 100644
index 0baa00b..0000000
--- a/engines/kyra/sound/sound_adlib.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/* 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 KYRA_SOUND_ADLIB_H
-#define KYRA_SOUND_ADLIB_H
-
-#include "kyra/sound/sound.h"
-
-#include "common/mutex.h"
-
-namespace Kyra {
-class AdLibDriver;
-
-/**
- * AdLib implementation of the sound output device.
- *
- * It uses a special sound file format special to
- * Dune II, Kyrandia 1 and 2. While Dune II and
- * Kyrandia 1 are using exact the same format, the
- * one of Kyrandia 2 slightly differs.
- *
- * See AdLibDriver for more information.
- * @see AdLibDriver
- */
-class SoundAdLibPC : public Sound {
-public:
- SoundAdLibPC(KyraEngine_v1 *vm, Audio::Mixer *mixer);
- ~SoundAdLibPC();
-
- virtual kType getMusicType() const { return kAdLib; }
-
- virtual bool init();
- virtual void process();
-
- virtual void updateVolumeSettings();
-
- virtual void initAudioResourceInfo(int set, void *info);
- virtual void selectAudioResourceSet(int set);
- virtual bool hasSoundFile(uint file) const;
- virtual void loadSoundFile(uint file);
- virtual void loadSoundFile(Common::String file);
-
- virtual void playTrack(uint8 track);
- virtual void haltTrack();
- virtual bool isPlaying() const;
-
- virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF);
-
- virtual void beginFadeOut();
-
- virtual int checkTrigger();
- virtual void resetTrigger();
-private:
- void internalLoadFile(Common::String file);
-
- void play(uint8 track, uint8 volume);
-
- const SoundResourceInfo_PC *res() const {return _resInfo[_currentResourceSet]; }
- SoundResourceInfo_PC *_resInfo[3];
- int _currentResourceSet;
-
- AdLibDriver *_driver;
-
- int _version;
- uint8 _trackEntries[500];
- uint8 *_soundDataPtr;
- int _sfxPlayingSound;
-
- Common::String _soundFileLoaded;
-
- int _numSoundTriggers;
- const int *_soundTriggers;
-
- static const int _kyra1NumSoundTriggers;
- static const int _kyra1SoundTriggers[];
-};
-
-} // End of namespace Kyra
-
-#endif
diff --git a/engines/kyra/sound/sound_intern.h b/engines/kyra/sound/sound_intern.h
index fc8c5a7..628635e 100644
--- a/engines/kyra/sound/sound_intern.h
+++ b/engines/kyra/sound/sound_intern.h
@@ -25,7 +25,7 @@
#include "kyra/sound/sound.h"
-#include "kyra/sound/sound_adlib.h"
+#include "kyra/sound/sound_pc_v1.h"
#include "audio/midiparser.h"
#include "audio/softsynth/emumidi.h"
diff --git a/engines/kyra/sound/sound_midi.cpp b/engines/kyra/sound/sound_midi.cpp
deleted file mode 100644
index b1a681d..0000000
--- a/engines/kyra/sound/sound_midi.cpp
+++ /dev/null
@@ -1,414 +0,0 @@
-/* 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.
- *
- */
-
-#include "kyra/sound/drivers/midi.h"
-
-#include "kyra/resource/resource.h"
-
-#include "common/system.h"
-#include "common/config-manager.h"
-#include "common/translation.h"
-
-#include "gui/message.h"
-
-namespace Kyra {
-
-SoundMidiPC::SoundMidiPC(KyraEngine_v1 *vm, Audio::Mixer *mixer, MidiDriver *driver, kType type) : Sound(vm, mixer) {
- _driver = driver;
- _output = 0;
-
- _musicFile = _sfxFile = 0;
- _currentResourceSet = 0;
- memset(&_resInfo, 0, sizeof(_resInfo));
-
- _music = MidiParser::createParser_XMIDI();
- assert(_music);
- for (int i = 0; i < 3; ++i) {
- _sfx[i] = MidiParser::createParser_XMIDI();
- assert(_sfx[i]);
- }
-
- _musicVolume = _sfxVolume = 0;
- _fadeMusicOut = false;
-
- _type = type;
- assert(_type == kMidiMT32 || _type == kMidiGM || _type == kPCSpkr);
-
- // Only General MIDI isn't a Roland MT-32 MIDI implemenation,
- // even the PC Speaker driver is a Roland MT-32 based MIDI implementation.
- // Thus we set "_nativeMT32" for all types except Gerneral MIDI to true.
- _nativeMT32 = (_type != kMidiGM);
-
- // KYRA1 does not include any General MIDI tracks, thus we have
- // to overwrite the internal type with MT32 to get the correct
- // file extension.
- if (_vm->game() == GI_KYRA1 && _type == kMidiGM)
- _type = kMidiMT32;
-
- // Display a warning about possibly wrong sound when the user only has
- // a General MIDI device, but the game is setup to use Roland MT32 MIDI.
- // (This will only happen in The Legend of Kyrandia 1 though, all other
- // supported games include special General MIDI tracks).
- if (_type == kMidiMT32 && !_nativeMT32) {
- ::GUI::MessageDialog dialog(_("You appear to be using a General MIDI device,\n"
- "but your game only supports Roland MT32 MIDI.\n"
- "We try to map the Roland MT32 instruments to\n"
- "General MIDI ones. It is still possible that\n"
- "some tracks sound incorrect."));
- dialog.runModal();
- }
-}
-
-SoundMidiPC::~SoundMidiPC() {
- Common::StackLock lock(_mutex);
- _output->setTimerCallback(0, 0);
-
- delete _music;
- for (int i = 0; i < 3; ++i)
- delete _sfx[i];
-
- delete _output; // This automatically frees _driver (!)
-
- if (_musicFile != _sfxFile)
- delete[] _sfxFile;
-
- delete[] _musicFile;
-
- for (int i = 0; i < 3; i++)
- initAudioResourceInfo(i, 0);
-}
-
-bool SoundMidiPC::init() {
- _output = new MidiOutput(_vm->_system, _driver, _nativeMT32, (_type != kMidiGM));
- assert(_output);
-
- updateVolumeSettings();
-
- _music->setMidiDriver(_output);
- _music->setTempo(_output->getBaseTempo());
- _music->setTimerRate(_output->getBaseTempo());
-
- for (int i = 0; i < 3; ++i) {
- _sfx[i]->setMidiDriver(_output);
- _sfx[i]->setTempo(_output->getBaseTempo());
- _sfx[i]->setTimerRate(_output->getBaseTempo());
- }
-
- _output->setTimerCallback(this, SoundMidiPC::onTimer);
-
- if (_nativeMT32 && _type == kMidiMT32) {
- const char *midiFile = 0;
- const char *pakFile = 0;
- if (_vm->game() == GI_KYRA1) {
- midiFile = "INTRO";
- } else if (_vm->game() == GI_KYRA2) {
- midiFile = "HOF_SYX";
- pakFile = "AUDIO.PAK";
- } else if (_vm->game() == GI_LOL) {
- midiFile = "LOREINTR";
-
- if (_vm->gameFlags().isDemo) {
- if (_vm->gameFlags().useAltShapeHeader) {
- // Intro demo
- pakFile = "INTROVOC.PAK";
- } else {
- // Kyra2 SEQ player based demo
- pakFile = "GENERAL.PAK";
- midiFile = "LOLSYSEX";
- }
- } else {
- if (_vm->gameFlags().isTalkie)
- pakFile = "ENG/STARTUP.PAK";
- else
- pakFile = "INTROVOC.PAK";
- }
- }
-
- if (!midiFile)
- return true;
-
- if (pakFile)
- _vm->resource()->loadPakFile(pakFile);
-
- loadSoundFile(midiFile);
- playTrack(0);
-
- Common::Event event;
- while (isPlaying() && !_vm->shouldQuit()) {
- _vm->_system->updateScreen();
- _vm->_eventMan->pollEvent(event);
- _vm->_system->delayMillis(10);
- }
-
- if (pakFile)
- _vm->resource()->unloadPakFile(pakFile);
- }
-
- return true;
-}
-
-void SoundMidiPC::updateVolumeSettings() {
- Common::StackLock lock(_mutex);
-
- if (!_output)
- return;
-
- bool mute = false;
- if (ConfMan.hasKey("mute"))
- mute = ConfMan.getBool("mute");
-
- const int newMusVol = (mute ? 0 : ConfMan.getInt("music_volume"));
- _sfxVolume = (mute ? 0 : ConfMan.getInt("sfx_volume"));
-
- _output->setSourceVolume(0, newMusVol, newMusVol != _musicVolume);
- _musicVolume = newMusVol;
-
- for (int i = 1; i < 4; ++i)
- _output->setSourceVolume(i, _sfxVolume, false);
-}
-
-void SoundMidiPC::initAudioResourceInfo(int set, void *info) {
- if (set >= kMusicIntro && set <= kMusicFinale) {
- delete _resInfo[set];
- _resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : 0;
- }
-}
-
-void SoundMidiPC::selectAudioResourceSet(int set) {
- if (set >= kMusicIntro && set <= kMusicFinale) {
- if (_resInfo[set])
- _currentResourceSet = set;
- }
-}
-
-bool SoundMidiPC::hasSoundFile(uint file) const {
- if (file < res()->fileListSize)
- return (res()->fileList[file] != 0);
- return false;
-}
-
-void SoundMidiPC::loadSoundFile(uint file) {
- if (file < res()->fileListSize)
- loadSoundFile(res()->fileList[file]);
-}
-
-void SoundMidiPC::loadSoundFile(Common::String file) {
- Common::StackLock lock(_mutex);
- file = getFileName(file);
-
- if (_mFileName == file)
- return;
-
- if (!_vm->resource()->exists(file.c_str()))
- return;
-
- // When loading a new file we stop all notes
- // still running on our own, just to prevent
- // glitches
- for (int i = 0; i < 16; ++i)
- _output->stopNotesOnChannel(i);
-
- delete[] _musicFile;
- uint32 fileSize = 0;
- _musicFile = _vm->resource()->fileData(file.c_str(), &fileSize);
- _mFileName = file;
-
- _output->setSoundSource(0);
- _music->loadMusic(_musicFile, fileSize);
- _music->stopPlaying();
-
- // Since KYRA1 uses the same file for SFX and Music
- // we setup sfx to play from music file as well
- if (_vm->game() == GI_KYRA1) {
- for (int i = 0; i < 3; ++i) {
- _output->setSoundSource(i+1);
- _sfx[i]->loadMusic(_musicFile, fileSize);
- _sfx[i]->stopPlaying();
- }
- }
-}
-
-void SoundMidiPC::loadSfxFile(Common::String file) {
- Common::StackLock lock(_mutex);
-
- // Kyrandia 1 doesn't use a special sfx file
- if (_vm->game() == GI_KYRA1)
- return;
-
- file = getFileName(file);
-
- if (_sFileName == file)
- return;
-
- if (!_vm->resource()->exists(file.c_str()))
- return;
-
- delete[] _sfxFile;
-
- uint32 fileSize = 0;
- _sfxFile = _vm->resource()->fileData(file.c_str(), &fileSize);
- _sFileName = file;
-
- for (int i = 0; i < 3; ++i) {
- _output->setSoundSource(i+1);
- _sfx[i]->loadMusic(_sfxFile, fileSize);
- _sfx[i]->stopPlaying();
- }
-}
-
-void SoundMidiPC::playTrack(uint8 track) {
- if (!_musicEnabled)
- return;
-
- haltTrack();
-
- // The following two lines are meant as a fix for bug #6314.
- // It is on purpose that they are outside the mutex lock.
- _output->allSoundsOff();
- _vm->delay(250);
-
- Common::StackLock lock(_mutex);
- _fadeMusicOut = false;
-
- _output->setSourceVolume(0, _musicVolume, true);
-
- _output->initSource(0);
- _output->setSourceVolume(0, _musicVolume, true);
- _music->setTrack(track);
-}
-
-void SoundMidiPC::haltTrack() {
- Common::StackLock lock(_mutex);
-
- _output->setSoundSource(0);
- _music->stopPlaying();
- _output->deinitSource(0);
-}
-
-bool SoundMidiPC::isPlaying() const {
- Common::StackLock lock(_mutex);
-
- return _music->isPlaying();
-}
-
-void SoundMidiPC::playSoundEffect(uint8 track, uint8) {
- if (!_sfxEnabled)
- return;
-
- Common::StackLock lock(_mutex);
- for (int i = 0; i < 3; ++i) {
- if (!_sfx[i]->isPlaying()) {
- _output->initSource(i+1);
- _sfx[i]->setTrack(track);
- return;
- }
- }
-}
-
-void SoundMidiPC::stopAllSoundEffects() {
- Common::StackLock lock(_mutex);
-
- for (int i = 0; i < 3; ++i) {
- _output->setSoundSource(i+1);
- _sfx[i]->stopPlaying();
- _output->deinitSource(i+1);
- }
-}
-
-void SoundMidiPC::beginFadeOut() {
- Common::StackLock lock(_mutex);
-
- _fadeMusicOut = true;
- _fadeStartTime = _vm->_system->getMillis();
-}
-
-void SoundMidiPC::pause(bool paused) {
- Common::StackLock lock(_mutex);
-
- if (paused) {
- _music->setMidiDriver(0);
- for (int i = 0; i < 3; i++)
- _sfx[i]->setMidiDriver(0);
- for (int i = 0; i < 16; i++)
- _output->stopNotesOnChannel(i);
- } else {
- _music->setMidiDriver(_output);
- for (int i = 0; i < 3; ++i)
- _sfx[i]->setMidiDriver(_output);
- // Possible TODO (IMHO unnecessary): restore notes and/or update _fadeStartTime
- }
-}
-
-void SoundMidiPC::onTimer(void *data) {
- SoundMidiPC *midi = (SoundMidiPC *)data;
-
- Common::StackLock lock(midi->_mutex);
-
- if (midi->_fadeMusicOut) {
- static const uint32 musicFadeTime = 1 * 1000;
-
- if (midi->_fadeStartTime + musicFadeTime > midi->_vm->_system->getMillis()) {
- int volume = (byte)((musicFadeTime - (midi->_vm->_system->getMillis() - midi->_fadeStartTime)) * midi->_musicVolume / musicFadeTime);
- midi->_output->setSourceVolume(0, volume, true);
- } else {
- for (int i = 0; i < 16; ++i)
- midi->_output->stopNotesOnChannel(i);
- for (int i = 0; i < 4; ++i)
- midi->_output->deinitSource(i);
-
- midi->_output->setSoundSource(0);
- midi->_music->stopPlaying();
-
- for (int i = 0; i < 3; ++i) {
- midi->_output->setSoundSource(i+1);
- midi->_sfx[i]->stopPlaying();
- }
-
- midi->_fadeMusicOut = false;
- }
- }
-
- midi->_output->setSoundSource(0);
- midi->_music->onTimer();
-
- for (int i = 0; i < 3; ++i) {
- midi->_output->setSoundSource(i+1);
- midi->_sfx[i]->onTimer();
- }
-}
-
-Common::String SoundMidiPC::getFileName(const Common::String &str) {
- Common::String file = str;
- if (_type == kMidiMT32)
- file += ".XMI";
- else if (_type == kMidiGM)
- file += ".C55";
- else if (_type == kPCSpkr)
- file += ".PCS";
-
- if (_vm->resource()->exists(file.c_str()))
- return file;
-
- return str + ".XMI";
-}
-
-} // End of namespace Kyra
diff --git a/engines/kyra/sound/sound_pc_midi.cpp b/engines/kyra/sound/sound_pc_midi.cpp
new file mode 100644
index 0000000..d15315b
--- /dev/null
+++ b/engines/kyra/sound/sound_pc_midi.cpp
@@ -0,0 +1,414 @@
+/* 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.
+ *
+ */
+
+#include "kyra/sound/drivers/midi.h"
+
+#include "kyra/resource/resource.h"
+
+#include "common/system.h"
+#include "common/config-manager.h"
+#include "common/translation.h"
+
+#include "gui/message.h"
+
+namespace Kyra {
+
+SoundMidiPC::SoundMidiPC(KyraEngine_v1 *vm, Audio::Mixer *mixer, MidiDriver *driver, kType type) : Sound(vm, mixer) {
+ _driver = driver;
+ _output = 0;
+
+ _musicFile = _sfxFile = 0;
+ _currentResourceSet = 0;
+ memset(&_resInfo, 0, sizeof(_resInfo));
+
+ _music = MidiParser::createParser_XMIDI();
+ assert(_music);
+ for (int i = 0; i < 3; ++i) {
+ _sfx[i] = MidiParser::createParser_XMIDI();
+ assert(_sfx[i]);
+ }
+
+ _musicVolume = _sfxVolume = 0;
+ _fadeMusicOut = false;
+
+ _type = type;
+ assert(_type == kMidiMT32 || _type == kMidiGM || _type == kPCSpkr);
+
+ // Only General MIDI isn't a Roland MT-32 MIDI implemenation,
+ // even the PC Speaker driver is a Roland MT-32 based MIDI implementation.
+ // Thus we set "_nativeMT32" for all types except Gerneral MIDI to true.
+ _nativeMT32 = (_type != kMidiGM);
+
+ // KYRA1 does not include any General MIDI tracks, thus we have
+ // to overwrite the internal type with MT32 to get the correct
+ // file extension.
+ if (_vm->game() == GI_KYRA1 && _type == kMidiGM)
+ _type = kMidiMT32;
+
+ // Display a warning about possibly wrong sound when the user only has
+ // a General MIDI device, but the game is setup to use Roland MT32 MIDI.
+ // (This will only happen in The Legend of Kyrandia 1 though, all other
+ // supported games include special General MIDI tracks).
+ if (_type == kMidiMT32 && !_nativeMT32) {
+ ::GUI::MessageDialog dialog(_("You appear to be using a General MIDI device,\n"
+ "but your game only supports Roland MT32 MIDI.\n"
+ "We try to map the Roland MT32 instruments to\n"
+ "General MIDI ones. It is still possible that\n"
+ "some tracks sound incorrect."));
+ dialog.runModal();
+ }
+}
+
+SoundMidiPC::~SoundMidiPC() {
+ Common::StackLock lock(_mutex);
+ _output->setTimerCallback(0, 0);
+
+ delete _music;
+ for (int i = 0; i < 3; ++i)
+ delete _sfx[i];
+
+ delete _output; // This automatically frees _driver (!)
+
+ if (_musicFile != _sfxFile)
+ delete[] _sfxFile;
+
+ delete[] _musicFile;
+
+ for (int i = 0; i < 3; i++)
+ initAudioResourceInfo(i, 0);
+}
+
+bool SoundMidiPC::init() {
+ _output = new MidiOutput(_vm->_system, _driver, _nativeMT32, (_type != kMidiGM));
+ assert(_output);
+
+ updateVolumeSettings();
+
+ _music->setMidiDriver(_output);
+ _music->setTempo(_output->getBaseTempo());
+ _music->setTimerRate(_output->getBaseTempo());
+
+ for (int i = 0; i < 3; ++i) {
+ _sfx[i]->setMidiDriver(_output);
+ _sfx[i]->setTempo(_output->getBaseTempo());
+ _sfx[i]->setTimerRate(_output->getBaseTempo());
+ }
+
+ _output->setTimerCallback(this, SoundMidiPC::onTimer);
+
+ if (_nativeMT32 && _type == kMidiMT32) {
+ const char *midiFile = 0;
+ const char *pakFile = 0;
+ if (_vm->game() == GI_KYRA1) {
+ midiFile = "INTRO";
+ } else if (_vm->game() == GI_KYRA2) {
+ midiFile = "HOF_SYX";
+ pakFile = "AUDIO.PAK";
+ } else if (_vm->game() == GI_LOL) {
+ midiFile = "LOREINTR";
+
+ if (_vm->gameFlags().isDemo) {
+ if (_vm->gameFlags().useAltShapeHeader) {
+ // Intro demo
+ pakFile = "INTROVOC.PAK";
+ } else {
+ // Kyra2 SEQ player based demo
+ pakFile = "GENERAL.PAK";
+ midiFile = "LOLSYSEX";
+ }
+ } else {
+ if (_vm->gameFlags().isTalkie)
+ pakFile = "ENG/STARTUP.PAK";
+ else
+ pakFile = "INTROVOC.PAK";
+ }
+ }
+
+ if (!midiFile)
+ return true;
+
+ if (pakFile)
+ _vm->resource()->loadPakFile(pakFile);
+
+ loadSoundFile(midiFile);
+ playTrack(0);
+
+ Common::Event event;
+ while (isPlaying() && !_vm->shouldQuit()) {
+ _vm->_system->updateScreen();
+ _vm->_eventMan->pollEvent(event);
+ _vm->_system->delayMillis(10);
+ }
+
+ if (pakFile)
+ _vm->resource()->unloadPakFile(pakFile);
+ }
+
+ return true;
+}
+
+void SoundMidiPC::updateVolumeSettings() {
+ Common::StackLock lock(_mutex);
+
+ if (!_output)
+ return;
+
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
+
+ const int newMusVol = (mute ? 0 : ConfMan.getInt("music_volume"));
+ _sfxVolume = (mute ? 0 : ConfMan.getInt("sfx_volume"));
+
+ _output->setSourceVolume(0, newMusVol, newMusVol != _musicVolume);
+ _musicVolume = newMusVol;
+
+ for (int i = 1; i < 4; ++i)
+ _output->setSourceVolume(i, _sfxVolume, false);
+}
+
+void SoundMidiPC::initAudioResourceInfo(int set, void *info) {
+ if (set >= kMusicIntro && set <= kMusicFinale) {
+ delete _resInfo[set];
+ _resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : 0;
+ }
+}
+
+void SoundMidiPC::selectAudioResourceSet(int set) {
+ if (set >= kMusicIntro && set <= kMusicFinale) {
+ if (_resInfo[set])
+ _currentResourceSet = set;
+ }
+}
+
+bool SoundMidiPC::hasSoundFile(uint file) const {
+ if (file < res()->fileListSize)
+ return (res()->fileList[file] != 0);
+ return false;
+}
+
+void SoundMidiPC::loadSoundFile(uint file) {
+ if (file < res()->fileListSize)
+ loadSoundFile(res()->fileList[file]);
+}
+
+void SoundMidiPC::loadSoundFile(Common::String file) {
+ Common::StackLock lock(_mutex);
+ file = getFileName(file);
+
+ if (_mFileName == file)
+ return;
+
+ if (!_vm->resource()->exists(file.c_str()))
+ return;
+
+ // When loading a new file we stop all notes
+ // still running on our own, just to prevent
+ // glitches
+ for (int i = 0; i < 16; ++i)
+ _output->stopNotesOnChannel(i);
+
+ delete[] _musicFile;
+ uint32 fileSize = 0;
+ _musicFile = _vm->resource()->fileData(file.c_str(), &fileSize);
+ _mFileName = file;
+
+ _output->setSoundSource(0);
+ _music->loadMusic(_musicFile, fileSize);
+ _music->stopPlaying();
+
+ // Since KYRA1 uses the same file for SFX and Music
+ // we setup sfx to play from music file as well
+ if (_vm->game() == GI_KYRA1) {
+ for (int i = 0; i < 3; ++i) {
+ _output->setSoundSource(i+1);
+ _sfx[i]->loadMusic(_musicFile, fileSize);
+ _sfx[i]->stopPlaying();
+ }
+ }
+}
+
+void SoundMidiPC::loadSfxFile(Common::String file) {
+ Common::StackLock lock(_mutex);
+
+ // Kyrandia 1 doesn't use a special sfx file
+ if (_vm->game() == GI_KYRA1)
+ return;
+
+ file = getFileName(file);
+
+ if (_sFileName == file)
+ return;
+
+ if (!_vm->resource()->exists(file.c_str()))
+ return;
+
+ delete[] _sfxFile;
+
+ uint32 fileSize = 0;
+ _sfxFile = _vm->resource()->fileData(file.c_str(), &fileSize);
+ _sFileName = file;
+
+ for (int i = 0; i < 3; ++i) {
+ _output->setSoundSource(i+1);
+ _sfx[i]->loadMusic(_sfxFile, fileSize);
+ _sfx[i]->stopPlaying();
+ }
+}
+
+void SoundMidiPC::playTrack(uint8 track) {
+ if (!_musicEnabled)
+ return;
+
+ haltTrack();
+
+ // The following two lines are meant as a fix for bug #6314.
+ // It is on purpose that they are outside the mutex lock.
+ _output->allSoundsOff();
+ _vm->delay(250);
+
+ Common::StackLock lock(_mutex);
+ _fadeMusicOut = false;
+
+ _output->setSourceVolume(0, _musicVolume, true);
+
+ _output->initSource(0);
+ _output->setSourceVolume(0, _musicVolume, true);
+ _music->setTrack(track);
+}
+
+void SoundMidiPC::haltTrack() {
+ Common::StackLock lock(_mutex);
+
+ _output->setSoundSource(0);
+ _music->stopPlaying();
+ _output->deinitSource(0);
+}
+
+bool SoundMidiPC::isPlaying() const {
+ Common::StackLock lock(_mutex);
+
+ return _music->isPlaying();
+}
+
+void SoundMidiPC::playSoundEffect(uint8 track, uint8) {
+ if (!_sfxEnabled)
+ return;
+
+ Common::StackLock lock(_mutex);
+ for (int i = 0; i < 3; ++i) {
+ if (!_sfx[i]->isPlaying()) {
+ _output->initSource(i+1);
+ _sfx[i]->setTrack(track);
+ return;
+ }
+ }
+}
+
+void SoundMidiPC::stopAllSoundEffects() {
+ Common::StackLock lock(_mutex);
+
+ for (int i = 0; i < 3; ++i) {
+ _output->setSoundSource(i+1);
+ _sfx[i]->stopPlaying();
+ _output->deinitSource(i+1);
+ }
+}
+
+void SoundMidiPC::beginFadeOut() {
+ Common::StackLock lock(_mutex);
+
+ _fadeMusicOut = true;
+ _fadeStartTime = _vm->_system->getMillis();
+}
+
+void SoundMidiPC::pause(bool paused) {
+ Common::StackLock lock(_mutex);
+
+ if (paused) {
+ _music->setMidiDriver(0);
+ for (int i = 0; i < 3; i++)
+ _sfx[i]->setMidiDriver(0);
+ for (int i = 0; i < 16; i++)
+ _output->stopNotesOnChannel(i);
+ } else {
+ _music->setMidiDriver(_output);
+ for (int i = 0; i < 3; ++i)
+ _sfx[i]->setMidiDriver(_output);
+ // Possible TODO (IMHO unnecessary): restore notes and/or update _fadeStartTime
+ }
+}
+
+void SoundMidiPC::onTimer(void *data) {
+ SoundMidiPC *midi = (SoundMidiPC *)data;
+
+ Common::StackLock lock(midi->_mutex);
+
+ if (midi->_fadeMusicOut) {
+ static const uint32 musicFadeTime = 1 * 1000;
+
+ if (midi->_fadeStartTime + musicFadeTime > midi->_vm->_system->getMillis()) {
+ int volume = (byte)((musicFadeTime - (midi->_vm->_system->getMillis() - midi->_fadeStartTime)) * midi->_musicVolume / musicFadeTime);
+ midi->_output->setSourceVolume(0, volume, true);
+ } else {
+ for (int i = 0; i < 16; ++i)
+ midi->_output->stopNotesOnChannel(i);
+ for (int i = 0; i < 4; ++i)
+ midi->_output->deinitSource(i);
+
+ midi->_output->setSoundSource(0);
+ midi->_music->stopPlaying();
+
+ for (int i = 0; i < 3; ++i) {
+ midi->_output->setSoundSource(i+1);
+ midi->_sfx[i]->stopPlaying();
+ }
+
+ midi->_fadeMusicOut = false;
+ }
+ }
+
+ midi->_output->setSoundSource(0);
+ midi->_music->onTimer();
+
+ for (int i = 0; i < 3; ++i) {
+ midi->_output->setSoundSource(i+1);
+ midi->_sfx[i]->onTimer();
+ }
+}
+
+Common::String SoundMidiPC::getFileName(const Common::String &str) {
+ Common::String file = str;
+ if (_type == kMidiMT32)
+ file += ".XMI";
+ else if (_type == kMidiGM)
+ file += ".C55";
+ else if (_type == kPCSpkr)
+ file += ".SND";//".PCS";
+
+ if (_vm->resource()->exists(file.c_str()))
+ return file;
+
+ return str + ".XMI";
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/sound/sound_pc_v1.cpp b/engines/kyra/sound/sound_pc_v1.cpp
new file mode 100644
index 0000000..94b489b
--- /dev/null
+++ b/engines/kyra/sound/sound_pc_v1.cpp
@@ -0,0 +1,263 @@
+/* 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.
+ *
+ */
+
+#include "kyra/sound/sound_intern.h"
+#include "kyra/sound/drivers/pc_base.h"
+
+#include "common/system.h"
+#include "common/config-manager.h"
+
+
+namespace Kyra {
+
+// Kyra 1 sound triggers. Most noticeably, these are used towards the end of
+// the game, in the castle, to cycle between different songs. The same music is
+// used in other places throughout the game, but the player is less likely to
+// spend enough time there to notice.
+
+const int SoundPC_v1::_kyra1SoundTriggers[] = {
+ 0, 4, 5, 3
+};
+
+const int SoundPC_v1::_kyra1NumSoundTriggers = ARRAYSIZE(SoundPC_v1::_kyra1SoundTriggers);
+
+SoundPC_v1::SoundPC_v1(KyraEngine_v1 *vm, Audio::Mixer *mixer, kType type)
+ : Sound(vm, mixer), _driver(0), _trackEntries(), _soundDataPtr(0), _type(type) {
+ memset(_trackEntries, 0, sizeof(_trackEntries));
+
+ _soundTriggers = 0;
+ _numSoundTriggers = 0;
+ _sfxPlayingSound = -1;
+ _soundFileLoaded.clear();
+ _currentResourceSet = 0;
+ memset(&_resInfo, 0, sizeof(_resInfo));
+
+ switch (vm->game()) {
+ case GI_LOL:
+ _version = _vm->gameFlags().isDemo ? 3 : 4;
+ break;
+ case GI_KYRA2:
+ _version = 4;
+ break;
+ case GI_KYRA1:
+ _version = 3;
+ _soundTriggers = _kyra1SoundTriggers;
+ _numSoundTriggers = _kyra1NumSoundTriggers;
+ break;
+ case GI_EOB2:
+ _version = 2;
+ break;
+ case GI_EOB1:
+ _version = 1;
+ break;
+ default:
+ break;
+ }
+
+ // Correct the type to someting we support. NullSound is treated as a silent AdLib driver.
+ if (_type != kAdLib && _type != kPCSpkr)
+ _type = kAdLib;
+
+ _driver = (type == kAdLib) ? PCSoundDriver::createAdLib(mixer, _version) : PCSoundDriver::createPCSpk(mixer);
+ assert(_driver);
+}
+
+SoundPC_v1::~SoundPC_v1() {
+ delete _driver;
+ delete[] _soundDataPtr;
+ for (int i = 0; i < 3; i++)
+ initAudioResourceInfo(i, 0);
+}
+
+bool SoundPC_v1::init() {
+ _driver->initDriver();
+ return true;
+}
+
+void SoundPC_v1::process() {
+ int trigger = _driver->getSoundTrigger();
+
+ if (trigger < _numSoundTriggers) {
+ int soundId = _soundTriggers[trigger];
+
+ if (soundId)
+ playTrack(soundId);
+ } else {
+ warning("Unknown sound trigger %d", trigger);
+ // TODO: At this point, we really want to clear the trigger...
+ }
+}
+
+void SoundPC_v1::updateVolumeSettings() {
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
+
+ int newMusicVolume = mute ? 0 : ConfMan.getInt("music_volume");
+ //newMusicVolume = (newMusicVolume * 145) / Audio::Mixer::kMaxMixerVolume + 110;
+ newMusicVolume = CLIP(newMusicVolume, 0, 255);
+
+ int newSfxVolume = mute ? 0 : ConfMan.getInt("sfx_volume");
+ //newSfxVolume = (newSfxVolume * 200) / Audio::Mixer::kMaxMixerVolume + 55;
+ newSfxVolume = CLIP(newSfxVolume, 0, 255);
+
+ _driver->setMusicVolume(newMusicVolume);
+ _driver->setSfxVolume(newSfxVolume);
+}
+
+void SoundPC_v1::playTrack(uint8 track) {
+ if (_musicEnabled) {
+ // WORKAROUND: There is a bug in the Kyra 1 "Pool of Sorrow"
+ // music which causes the channels to get progressively out of
+ // sync for each loop. To avoid that, we declare that all four
+ // of the song channels have to jump "in sync".
+
+ if (track == 4 && _soundFileLoaded.equalsIgnoreCase("KYRA1B.ADL"))
+ _driver->setSyncJumpMask(0x000F);
+ else
+ _driver->setSyncJumpMask(0);
+ play(track, 0xFF);
+ }
+}
+
+void SoundPC_v1::haltTrack() {
+ play(0, 0);
+ play(0, 0);
+ //_vm->_system->delayMillis(3 * 60);
+}
+
+bool SoundPC_v1::isPlaying() const {
+ return _driver->isChannelPlaying(0);
+}
+
+void SoundPC_v1::playSoundEffect(uint8 track, uint8 volume) {
+ if (_sfxEnabled)
+ play(track, volume);
+}
+
+void SoundPC_v1::play(uint8 track, uint8 volume) {
+ uint16 soundId = 0;
+
+ if (_version == 4)
+ soundId = READ_LE_UINT16(&_trackEntries[track<<1]);
+ else
+ soundId = _trackEntries[track];
+
+ if ((soundId == 0xFFFF && _version == 4) || (soundId == 0xFF && _version < 4) || !_soundDataPtr)
+ return;
+
+ _driver->queueTrack(soundId, volume);
+}
+
+void SoundPC_v1::beginFadeOut() {
+ play(_version > 2 ? 1 : 15, 0xFF);
+}
+
+int SoundPC_v1::checkTrigger() {
+ return _driver->getSoundTrigger();
+}
+
+void SoundPC_v1::resetTrigger() {
+ _driver->resetSoundTrigger();
+}
+
+void SoundPC_v1::initAudioResourceInfo(int set, void *info) {
+ if (set >= kMusicIntro && set <= kMusicFinale) {
+ delete _resInfo[set];
+ _resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : 0;
+ }
+}
+
+void SoundPC_v1::selectAudioResourceSet(int set) {
+ if (set >= kMusicIntro && set <= kMusicFinale) {
+ if (_resInfo[set])
+ _currentResourceSet = set;
+ }
+}
+
+bool SoundPC_v1::hasSoundFile(uint file) const {
+ if (file < res()->fileListSize)
+ return (res()->fileList[file] != 0);
+ return false;
+}
+
+void SoundPC_v1::loadSoundFile(uint file) {
+ if (_version == 1 && _type == kPCSpkr)
+ file += 1;
+ if (file < res()->fileListSize)
+ internalLoadFile(res()->fileList[file]);
+}
+
+void SoundPC_v1::loadSoundFile(Common::String file) {
+ internalLoadFile(file);
+}
+
+void SoundPC_v1::internalLoadFile(Common::String file) {
+ file += ((_version == 1) ? ".DAT" : (_type == kPCSpkr ? ".SND" : ".ADL"));
+ if (_soundFileLoaded == file)
+ return;
+
+ if (_soundDataPtr)
+ haltTrack();
+
+ uint8 *fileData = 0; uint32 fileSize = 0;
+
+ fileData = _vm->resource()->fileData(file.c_str(), &fileSize);
+ if (!fileData) {
+ warning("Couldn't find music file: '%s'", file.c_str());
+ return;
+ }
+
+ playSoundEffect(0);
+ playSoundEffect(0);
+
+ _driver->stopAllChannels();
+ _soundDataPtr = 0;
+
+ int soundDataSize = fileSize;
+ uint8 *p = fileData;
+
+ if (_version == 4) {
+ memcpy(_trackEntries, p, 500);
+ p += 500;
+ soundDataSize -= 500;
+ } else {
+ memcpy(_trackEntries, p, 120);
+ p += 120;
+ soundDataSize -= 120;
+ }
+
+ _soundDataPtr = new uint8[soundDataSize];
+ assert(_soundDataPtr);
+
+ memcpy(_soundDataPtr, p, soundDataSize);
+
+ delete[] fileData;
+ fileData = p = 0;
+ fileSize = 0;
+
+ _driver->setSoundData(_soundDataPtr, soundDataSize);
+
+ _soundFileLoaded = file;
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/sound/sound_pc_v1.h b/engines/kyra/sound/sound_pc_v1.h
new file mode 100644
index 0000000..850c1fc
--- /dev/null
+++ b/engines/kyra/sound/sound_pc_v1.h
@@ -0,0 +1,106 @@
+/* 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 KYRA_SOUND_ADLIB_H
+#define KYRA_SOUND_ADLIB_H
+
+#include "kyra/sound/sound.h"
+
+#include "common/mutex.h"
+
+namespace Kyra {
+class PCSoundDriver;
+
+/**
+ * AdLib/PC Speaker (early version) implementation of the
+ * sound output device.
+ *
+ * It uses a special sound file format special to EoB I, II,
+ * Dune II, Kyrandia 1 and 2 and LoL. EoB I has a slightly
+ * different (oldest) file format, EoB II, Dune II and
+ * Kyrandia 1 have the exact same format, Kyrandia 2 and
+ * LoL have a slightly different format.
+ *
+ * For PC Speaker this is a little different. Only the EoB
+ * games use the old driver with this data file format. The
+ * newer games use a MIDI-like driver (see pcspeaker_v2.cpp).
+ *
+ * See AdLibDriver / PCSpeakerDriver for more information.
+ * @see AdLibDriver
+ */
+class SoundPC_v1 : public Sound {
+public:
+ SoundPC_v1(KyraEngine_v1 *vm, Audio::Mixer *mixer, kType type);
+ ~SoundPC_v1();
+
+ virtual kType getMusicType() const { return _type; }
+
+ virtual bool init();
+ virtual void process();
+
+ virtual void updateVolumeSettings();
+
+ virtual void initAudioResourceInfo(int set, void *info);
+ virtual void selectAudioResourceSet(int set);
+ virtual bool hasSoundFile(uint file) const;
+ virtual void loadSoundFile(uint file);
+ virtual void loadSoundFile(Common::String file);
+
+ virtual void playTrack(uint8 track);
+ virtual void haltTrack();
+ virtual bool isPlaying() const;
+
+ virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF);
+
+ virtual void beginFadeOut();
+
+ virtual int checkTrigger();
+ virtual void resetTrigger();
+private:
+ void internalLoadFile(Common::String file);
+
+ void play(uint8 track, uint8 volume);
+
+ const SoundResourceInfo_PC *res() const {return _resInfo[_currentResourceSet]; }
+ SoundResourceInfo_PC *_resInfo[3];
+ int _currentResourceSet;
+
+ PCSoundDriver *_driver;
+
+ int _version;
+ kType _type;
+ uint8 _trackEntries[500];
+ uint8 *_soundDataPtr;
+ int _sfxPlayingSound;
+
+ Common::String _soundFileLoaded;
+
+ int _numSoundTriggers;
+ const int *_soundTriggers;
+
+ static const int _kyra1NumSoundTriggers;
+ static const int _kyra1SoundTriggers[];
+};
+
+} // End of namespace Kyra
+
+#endif
Commit: 0820b0617d5cf565ab65a4448a3cb17bf330cd7a
https://github.com/scummvm/scummvm/commit/0820b0617d5cf565ab65a4448a3cb17bf330cd7a
Author: athrxx (athrxx at scummvm.org)
Date: 2020-02-01T21:09:49+01:00
Commit Message:
KYRA: (EOB) - add PC Speaker driver
Changed paths:
engines/kyra/sequence/sequences_darkmoon.cpp
engines/kyra/sound/drivers/adlib.cpp
engines/kyra/sound/drivers/pc_base.h
engines/kyra/sound/drivers/pcspeaker_v1.cpp
engines/kyra/sound/sound.h
engines/kyra/sound/sound_pc_v1.cpp
diff --git a/engines/kyra/sequence/sequences_darkmoon.cpp b/engines/kyra/sequence/sequences_darkmoon.cpp
index a252276..c43e7f5 100644
--- a/engines/kyra/sequence/sequences_darkmoon.cpp
+++ b/engines/kyra/sequence/sequences_darkmoon.cpp
@@ -1719,7 +1719,7 @@ void DarkmoonSequenceHelper::delay(uint32 ticks) {
void DarkmoonSequenceHelper::waitForSongNotifier(int index, bool introUpdateAnim) {
if (_vm->gameFlags().platform == Common::kPlatformFMTowns)
index = _sndMarkersFMTowns[index - 1];
- else if (_vm->gameFlags().platform == Common::kPlatformAmiga)
+ else if (_vm->sound()->getMusicType() != Sound::kAdLib)
return;
int seq = 0;
diff --git a/engines/kyra/sound/drivers/adlib.cpp b/engines/kyra/sound/drivers/adlib.cpp
index a576eb2..05fbb23 100644
--- a/engines/kyra/sound/drivers/adlib.cpp
+++ b/engines/kyra/sound/drivers/adlib.cpp
@@ -54,13 +54,13 @@ public:
virtual void initDriver() override;
virtual void setSoundData(uint8 *data, uint32 size) override;
- virtual void queueTrack(int track, int volume) override;
+ virtual void startSound(int track, int volume) override;
virtual bool isChannelPlaying(int channel) const override;
virtual void stopAllChannels() override;
- virtual int getSoundTrigger() const override { return _soundTrigger; }
- virtual void resetSoundTrigger() override { _soundTrigger = 0; }
+ int getSoundTrigger() const { return _soundTrigger; }
+ void resetSoundTrigger() { _soundTrigger = 0; }
- virtual void callback() override;
+ void callback();
virtual void setSyncJumpMask(uint16 mask) override { _syncJumpMask = mask; }
@@ -179,24 +179,6 @@ private:
// * One for programs, starting at offset 0.
// * One for instruments, starting at offset 500.
- uint8 *getProgram(int progId) {
- const uint16 offset = READ_LE_UINT16(_soundData + 2 * progId);
-
- // In case an invalid offset is specified we return nullptr to
- // indicate an error. 0xFFFF seems to indicate "this is not a valid
- // program/instrument". However, 0 is also invalid because it points
- // inside the offset table itself. We also ignore any offsets outside
- // of the actual data size.
- // The original does not contain any safety checks and will simply
- // read outside of the valid sound data in case an invalid offset is
- // encountered.
- if (offset == 0 || offset >= _soundDataSize) {
- return nullptr;
- } else {
- return _soundData + offset;
- }
- }
-
const uint8 *getInstrument(int instrumentId) {
return getProgram(_numPrograms + instrumentId);
}
@@ -326,9 +308,6 @@ private:
OPL::OPL *_adlib;
- uint8 *_soundData;
- uint32 _soundDataSize;
-
struct QueueEntry {
QueueEntry() : data(0), id(0), volume(0) {}
QueueEntry(uint8 *ptr, uint8 track, uint8 vol) : data(ptr), id(track), volume(vol) {}
@@ -390,8 +369,6 @@ AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) : PCSoundDriver() {
error("Failed to create OPL");
memset(_channels, 0, sizeof(_channels));
- _soundData = 0;
- _soundDataSize = 0;
_vibratoAndAMDepthBits = _curRegOffset = 0;
@@ -504,7 +481,7 @@ void AdLibDriver::setSoundData(uint8 *data, uint32 size) {
_soundDataSize = size;
}
-void AdLibDriver::queueTrack(int track, int volume) {
+void AdLibDriver::startSound(int track, int volume) {
Common::StackLock lock(_mutex);
uint8 *trackData = getProgram(track);
@@ -624,7 +601,7 @@ void AdLibDriver::setupPrograms() {
if (retrySound.data) {
debugC(9, kDebugLevelSound, "AdLibDriver::setupPrograms(): WORKAROUND - Restarting skipped sound %d)", retrySound.id);
- queueTrack(retrySound.id, retrySound.volume);
+ startSound(retrySound.id, retrySound.volume);
}
}
diff --git a/engines/kyra/sound/drivers/pc_base.h b/engines/kyra/sound/drivers/pc_base.h
index b13b4a2..ce85950 100644
--- a/engines/kyra/sound/drivers/pc_base.h
+++ b/engines/kyra/sound/drivers/pc_base.h
@@ -34,28 +34,50 @@ namespace Kyra {
class PCSoundDriver {
public:
- PCSoundDriver() {}
+ PCSoundDriver() : _soundData(0), _soundDataSize(0) {}
virtual ~PCSoundDriver() {}
virtual void initDriver() = 0;
virtual void setSoundData(uint8 *data, uint32 size) = 0;
- virtual void queueTrack(int track, int volume) = 0;
+ virtual void startSound(int track, int volume) = 0;
virtual bool isChannelPlaying(int channel) const = 0;
virtual void stopAllChannels() = 0;
- virtual int getSoundTrigger() const = 0;
- virtual void resetSoundTrigger() = 0;
- virtual void callback() = 0;
-
- // AdLiB specific
- virtual void setSyncJumpMask(uint16) {}
+ virtual int getSoundTrigger() const { return 0; }
+ virtual void resetSoundTrigger() {}
virtual void setMusicVolume(uint8 volume) = 0;
virtual void setSfxVolume(uint8 volume) = 0;
+ // AdLiB (Kyra 1) specific
+ virtual void setSyncJumpMask(uint16) {}
+
+protected:
+ uint8 *getProgram(int progId) {
+ const uint16 offset = READ_LE_UINT16(_soundData + 2 * progId);
+
+ // In case an invalid offset is specified we return nullptr to
+ // indicate an error. 0xFFFF seems to indicate "this is not a valid
+ // program/instrument". However, 0 is also invalid because it points
+ // inside the offset table itself. We also ignore any offsets outside
+ // of the actual data size.
+ // The original does not contain any safety checks and will simply
+ // read outside of the valid sound data in case an invalid offset is
+ // encountered.
+ if (offset == 0 || offset >= _soundDataSize) {
+ return nullptr;
+ } else {
+ return _soundData + offset;
+ }
+ }
+
+ uint8 *_soundData;
+ uint32 _soundDataSize;
+
+public:
static PCSoundDriver *createAdLib(Audio::Mixer *mixer, int version);
#ifdef ENABLE_EOB
- static PCSoundDriver *createPCSpk(Audio::Mixer *mixer);
+ static PCSoundDriver *createPCSpk(Audio::Mixer *mixer, bool pcJRMode);
#endif
};
diff --git a/engines/kyra/sound/drivers/pcspeaker_v1.cpp b/engines/kyra/sound/drivers/pcspeaker_v1.cpp
index 01dcbab..2d0a296 100644
--- a/engines/kyra/sound/drivers/pcspeaker_v1.cpp
+++ b/engines/kyra/sound/drivers/pcspeaker_v1.cpp
@@ -23,75 +23,371 @@
#ifdef ENABLE_EOB
#include "kyra/sound/drivers/pc_base.h"
-#include "audio/mixer.h"
-#include "audio/softsynth/pcspk.h"
+#include "audio/audiostream.h"
+#include "common/mutex.h"
namespace Kyra {
-class PCSpeakerDriver : public PCSoundDriver {
+class PCSpeakerDriver : public PCSoundDriver, public Audio::AudioStream {
public:
- PCSpeakerDriver(Audio::Mixer *mixer);
+ PCSpeakerDriver(Audio::Mixer *mixer, bool pcJRMode);
virtual ~PCSpeakerDriver();
virtual void initDriver() override;
virtual void setSoundData(uint8 *data, uint32 size) override;
- virtual void queueTrack(int track, int volume) override;
+ virtual void startSound(int id, int) override;
virtual bool isChannelPlaying(int channel) const override;
virtual void stopAllChannels() override;
- virtual int getSoundTrigger() const override { return _soundTrigger; }
- virtual void resetSoundTrigger() override { _soundTrigger = 0; }
-
- virtual void callback() override;
virtual void setMusicVolume(uint8 volume) override;
- virtual void setSfxVolume(uint8 volume) override;
+ virtual void setSfxVolume(uint8) override {}
+
+ void update();
+
+ // AudioStream interface
+ virtual int readBuffer(int16 *buffer, const int numSamples) override;
+ virtual bool isStereo() const override { return false; }
+ virtual int getRate() const override { return _outputRate; }
+ virtual bool endOfData() const override { return false; }
private:
- int _soundTrigger;
+ void noteOn(int chan, uint16 period);
+ void chanOff(int chan);
+ void generateSamples(int16 *buffer, int numSamples);
+
+ struct Channel {
+ Channel(uint8 attnDB) : curSample(32767.0 / pow(2.0, (double)attnDB / 6.0)),
+ dataPtr(0), timer(0), timerScale(0), repeatCounter1(0), repeatCounter2(0), period(-1), samplesLeft(0) {}
+ const uint8 *dataPtr;
+ int16 timer;
+ uint8 timerScale;
+ uint8 repeatCounter1;
+ uint8 repeatCounter2;
+ int32 period;
+ int32 curSample;
+ uint32 samplesLeft;
+ };
+
+ Channel **_channels;
+ int _numChannels;
+
+ const uint8 *_newTrackData;
+ const uint8 *_trackData;
+
+ Common::Mutex _mutex;
+ Audio::Mixer *_mixer;
+ Audio::SoundHandle _handle;
+
+ uint _outputRate;
+ int _samplesUpdateIntv;
+ int _samplesUpdateIntvRem;
+ int _samplesUpdateTmr;
+ int _samplesUpdateTmrRem;
+
+ int _masterVolume;
+ bool _ready;
+
+ const int _clock;
+ const int _updateRate;
+ const bool _pcJR;
+ const int _periodDiv;
+ const int _levelAdjust;
+ const uint16 * const _periodsTable;
+
+ static const uint16 _periodsPCSpk[96];
+ static const uint16 _periodsPCjr[96];
};
-PCSpeakerDriver::PCSpeakerDriver(Audio::Mixer *mixer) : PCSoundDriver() {
+PCSpeakerDriver::PCSpeakerDriver(Audio::Mixer *mixer, bool pcJRMode) : PCSoundDriver(), _mixer(mixer), _samplesUpdateIntv(0), _samplesUpdateIntvRem(0),
+ _outputRate(0), _samplesUpdateTmr(0), _samplesUpdateTmrRem(0), _newTrackData(0), _trackData(0), _pcJR(pcJRMode), _numChannels(pcJRMode ? 3 : 1), _channels(0),
+ _clock(pcJRMode ? 111860 : 1193180), _updateRate(292), _masterVolume(63), _periodsTable(pcJRMode ? _periodsPCjr : _periodsPCSpk), _periodDiv(pcJRMode ? 2 : 2),
+ _levelAdjust(pcJRMode ? 1 : 0), _ready(false) {
+ _outputRate = _mixer->getOutputRate();
+ _samplesUpdateIntv = _outputRate / _updateRate;
+ _samplesUpdateIntvRem = _outputRate % _updateRate;
+ _channels = new Channel*[_numChannels];
+ assert(_channels);
+ for (int i = 0; i < _numChannels; ++i) {
+ _channels[i] = new Channel(i * 10);
+ assert(_channels[i]);
+ }
}
PCSpeakerDriver::~PCSpeakerDriver() {
+ _ready = false;
+ _mixer->stopHandle(_handle);
+ if (_channels) {
+ for (int i = 0; i < _numChannels; ++i)
+ delete _channels[i];
+ delete[] _channels;
+ }
}
void PCSpeakerDriver::initDriver() {
-
+ if (_ready)
+ return;
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+ _ready = true;
}
void PCSpeakerDriver::setSoundData(uint8 *data, uint32 size) {
+ Common::StackLock lock(_mutex);
+ if (!_ready)
+ return;
-}
+ if (_soundData) {
+ delete[] _soundData;
+ _soundData = 0;
+ }
-void PCSpeakerDriver::queueTrack(int track, int volume) {
+ _soundData = data;
+ _soundDataSize = size;
+}
+void PCSpeakerDriver::startSound(int id, int) {
+ Common::StackLock lock(_mutex);
+ if (!_ready)
+ return;
+ _newTrackData = getProgram(id & 0x7F);
}
bool PCSpeakerDriver::isChannelPlaying(int channel) const {
- return true;
+ Common::StackLock lock(_mutex);
+ if (!_ready)
+ return false;
+ return _trackData;
}
void PCSpeakerDriver::stopAllChannels() {
+ Common::StackLock lock(_mutex);
+ if (!_ready)
+ return;
+ for (int i = 0; i < _numChannels; ++i)
+ chanOff(i);
+ _trackData = 0;
+}
+void PCSpeakerDriver::setMusicVolume(uint8 volume) {
+ Common::StackLock lock(_mutex);
+ _masterVolume = volume >> 2;
}
-void PCSpeakerDriver::callback() {
+void PCSpeakerDriver::update() {
+ Common::StackLock lock(_mutex);
+ if (!_ready)
+ return;
+
+ if (_newTrackData) {
+ _trackData = _newTrackData;
+ _newTrackData = 0;
+
+ for (int i = _numChannels - 1; i >= 0; --i) {
+ _channels[i]->dataPtr = _trackData;
+ _channels[i]->timer = i * 35;
+ _channels[i]->timerScale = 1;
+ }
+ }
+
+ for (int i = _numChannels - 1; i >= 0; --i) {
+ const uint8 *pos = _channels[i]->dataPtr;
+ if (!pos)
+ continue;
+
+ for (bool runloop = true; runloop; ) {
+ if (--_channels[i]->timer > -1)
+ break;
+ _channels[i]->timer = 0;
+
+ int8 cmd = (int8)*pos++;
+ if (cmd >= 0) {
+ if (cmd > 95)
+ cmd = 0;
+
+ noteOn(i, _periodsTable[cmd]);
+ uint8 nextTimer = 1 + *pos++;
+ _channels[i]->timer = _channels[i]->timerScale * nextTimer;
+
+ } else {
+ switch (cmd) {
+ case -23: {
+ uint16 ts = _channels[i]->timerScale + *pos++;
+ _channels[i]->timerScale = (uint8)MIN<uint16>(ts, 0xFF);
+ } break;
+
+ case -24: {
+ int16 ts = _channels[i]->timerScale - *pos++;
+ _channels[i]->timerScale = (uint8)MAX<int16>(ts, 1);
+ } break;
+
+ case -26: {
+ uint16 prd = _clock / READ_LE_UINT16(pos);
+ if (_pcJR && prd >= 0x400)
+ prd = 0x3FF;
+ pos += 2;
+ noteOn(i, prd);
+ uint8 nextTimer = 1 + *pos++;
+ _channels[i]->timer = _channels[i]->timerScale * nextTimer;
+ } break;
+
+ case -30: {
+ _channels[i]->timerScale = *pos++;
+ if (!_channels[i]->timerScale)
+ _channels[i]->timerScale = 1;
+ } break;
+
+ case -46: {
+ if (--_channels[i]->repeatCounter2)
+ pos -= *pos;
+ else
+ pos += 2;
+ } break;
+ case -47: {
+ _channels[i]->repeatCounter2 = *pos++;
+ if (!_channels[i]->repeatCounter2)
+ _channels[i]->repeatCounter2 = 1;
+ } break;
+
+ case -50: {
+ if (--_channels[i]->repeatCounter1)
+ pos -= *pos;
+ else
+ pos += 2;
+ } break;
+
+ case -51: {
+ _channels[i]->repeatCounter1 = *pos++;
+ if (!_channels[i]->repeatCounter1)
+ _channels[i]->repeatCounter1 = 1;
+ } break;
+
+ default:
+ chanOff(i);
+ pos = 0;
+ runloop = false;
+ }
+ }
+ }
+
+ _channels[i]->dataPtr = pos;
+ }
}
-void PCSpeakerDriver::setMusicVolume(uint8 volume) {
+int PCSpeakerDriver::readBuffer(int16 *buffer, const int numSamples) {
+ Common::StackLock lock(_mutex);
+ if (!_ready)
+ return 0;
+
+ int render = 0;
+ for (int samplesLeft = numSamples; samplesLeft; samplesLeft -= render) {
+ if (_samplesUpdateTmr <= 0) {
+ _samplesUpdateTmr += _samplesUpdateIntv;
+ update();
+ }
+
+ _samplesUpdateTmrRem += _samplesUpdateIntvRem;
+ while (_samplesUpdateTmrRem >= _updateRate) {
+ _samplesUpdateTmr++;
+ _samplesUpdateTmrRem -= _updateRate;
+ }
+
+ render = MIN<int>(_samplesUpdateTmr, samplesLeft);
+ _samplesUpdateTmr -= render;
+
+ generateSamples(buffer, render);
+ buffer += render;
+ }
+
+ return numSamples;
}
-void PCSpeakerDriver::setSfxVolume(uint8 volume) {
+void PCSpeakerDriver::noteOn(int chan, uint16 period) {
+ if (chan >= _numChannels)
+ return;
+
+ if (period == 0) {
+ chanOff(chan);
+ return;
+ }
+
+ uint32 p = (_outputRate << 10) / ((_clock << 10) / period);
+ if (_channels[chan]->period == -1 || _channels[chan]->samplesLeft == 0)
+ _channels[chan]->samplesLeft = p / _periodDiv;
+ _channels[chan]->period = p & 0xFFFF;
+}
+void PCSpeakerDriver::chanOff(int chan) {
+ if (chan >= _numChannels)
+ return;
+ _channels[chan]->period = -1;
}
-PCSoundDriver *PCSoundDriver::createPCSpk(Audio::Mixer *mixer) {
- return new PCSpeakerDriver(mixer);
+void PCSpeakerDriver::generateSamples(int16 *buffer, int numSamples) {
+ int render = 0;
+ for (int samplesLeft = numSamples; samplesLeft; samplesLeft -= render) {
+ render = samplesLeft;
+
+ for (int i = _numChannels - 1; i >= 0; --i)
+ if (_channels[i]->period != -1)
+ render = MIN<int>(render, _channels[i]->samplesLeft);
+
+ int32 smp = 0;
+ for (int i = _numChannels - 1; i >= 0; --i)
+ if (_channels[i]->period != -1)
+ smp += _channels[i]->curSample;
+ smp = (smp * _masterVolume) >> (8 + _levelAdjust);
+
+ Common::fill<int16*, int16>(buffer, &buffer[render], smp);
+ buffer += render;
+
+ for (int i = _numChannels - 1; i >= 0; --i) {
+ if (_channels[i]->period == -1)
+ continue;
+
+ _channels[i]->samplesLeft -= render;
+ if (_channels[i]->samplesLeft == 0) {
+ _channels[i]->samplesLeft = _channels[i]->period / _periodDiv;
+ _channels[i]->curSample = ~_channels[i]->curSample;
+ }
+ }
+ }
+}
+
+const uint16 PCSpeakerDriver::_periodsPCSpk[96] = {
+ 0x0000, 0xfdff, 0xefa2, 0xe241, 0xd582, 0xc998, 0xbe3d, 0xb38a,
+ 0xa97c, 0x9ff2, 0x96fc, 0x8e89, 0x8683, 0x7ef7, 0x77d9, 0x7121,
+ 0x6ac7, 0x64c6, 0x5f1f, 0x59ca, 0x54be, 0x4ffd, 0x4b7e, 0x4742,
+ 0x4342, 0x3f7b, 0x3bdb, 0x388f, 0x3562, 0x3263, 0x2f8f, 0x2ce4,
+ 0x2a5f, 0x27fe, 0x25c0, 0x23a1, 0x21a1, 0x1fbe, 0x1df6, 0x1c48,
+ 0x1ab1, 0x1932, 0x17c8, 0x1672, 0x1530, 0x13ff, 0x12e0, 0x11d1,
+ 0x10d1, 0x0fdf, 0x0efb, 0x0e24, 0x0d59, 0x0c99, 0x0be4, 0x0b39,
+ 0x0a98, 0x0a00, 0x0970, 0x08e8, 0x0868, 0x07f0, 0x077e, 0x0712,
+ 0x06ac, 0x064c, 0x05f2, 0x059d, 0x054c, 0x0500, 0x04b8, 0x0474,
+ 0x0434, 0x03f8, 0x03bf, 0x0382, 0x0356, 0x0326, 0x02f9, 0x02ce,
+ 0x02a6, 0x0280, 0x025c, 0x023a, 0x021a, 0x01fc, 0x01df, 0x01c4,
+ 0x01ab, 0x0193, 0x017c, 0x0167, 0x0153, 0x0140, 0x012e, 0x011d
+};
+
+const uint16 PCSpeakerDriver::_periodsPCjr[96] = {
+ 0x0000, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff,
+ 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff,
+ 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff,
+ 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff,
+ 0x03f9, 0x03c0, 0x038a, 0x0357, 0x0327, 0x02fa, 0x02cf, 0x02a7,
+ 0x0281, 0x025d, 0x023b, 0x021b, 0x01fc, 0x01e0, 0x01c5, 0x01ac,
+ 0x0194, 0x017d, 0x0168, 0x0153, 0x0140, 0x012e, 0x011d, 0x010d,
+ 0x00fe, 0x00f0, 0x00e2, 0x00d6, 0x00ca, 0x00be, 0x00b4, 0x00aa,
+ 0x00a0, 0x0097, 0x008f, 0x0087, 0x007f, 0x0078, 0x0071, 0x006b,
+ 0x0065, 0x005f, 0x005a, 0x0054, 0x0050, 0x004c, 0x0047, 0x0043,
+ 0x0040, 0x003c, 0x0039, 0x0035, 0x0032, 0x0030, 0x002d, 0x002a,
+ 0x0028, 0x0026, 0x0024, 0x0022, 0x0020, 0x001e, 0x001c, 0x001b
+};
+
+PCSoundDriver *PCSoundDriver::createPCSpk(Audio::Mixer *mixer, bool pcJRMode) {
+ return new PCSpeakerDriver(mixer, pcJRMode);
}
} // End of namespace Kyra
diff --git a/engines/kyra/sound/sound.h b/engines/kyra/sound/sound.h
index 531496b..e5a063b 100644
--- a/engines/kyra/sound/sound.h
+++ b/engines/kyra/sound/sound.h
@@ -101,6 +101,7 @@ public:
kTowns,
kPC98,
kPCSpkr,
+ kPCjr,
kAmiga
};
diff --git a/engines/kyra/sound/sound_pc_v1.cpp b/engines/kyra/sound/sound_pc_v1.cpp
index 94b489b..4d52993 100644
--- a/engines/kyra/sound/sound_pc_v1.cpp
+++ b/engines/kyra/sound/sound_pc_v1.cpp
@@ -74,10 +74,10 @@ SoundPC_v1::SoundPC_v1(KyraEngine_v1 *vm, Audio::Mixer *mixer, kType type)
}
// Correct the type to someting we support. NullSound is treated as a silent AdLib driver.
- if (_type != kAdLib && _type != kPCSpkr)
+ if (_type != kAdLib && _type != kPCSpkr && _type != kPCjr)
_type = kAdLib;
- _driver = (type == kAdLib) ? PCSoundDriver::createAdLib(mixer, _version) : PCSoundDriver::createPCSpk(mixer);
+ _driver = (type == kAdLib) ? PCSoundDriver::createAdLib(mixer, _version) : PCSoundDriver::createPCSpk(mixer, _type == kPCjr);
assert(_driver);
}
@@ -165,7 +165,7 @@ void SoundPC_v1::play(uint8 track, uint8 volume) {
if ((soundId == 0xFFFF && _version == 4) || (soundId == 0xFF && _version < 4) || !_soundDataPtr)
return;
- _driver->queueTrack(soundId, volume);
+ _driver->startSound(soundId, volume);
}
void SoundPC_v1::beginFadeOut() {
@@ -201,7 +201,7 @@ bool SoundPC_v1::hasSoundFile(uint file) const {
}
void SoundPC_v1::loadSoundFile(uint file) {
- if (_version == 1 && _type == kPCSpkr)
+ if (_version == 1 && (_type == kPCSpkr || _type == kPCjr))
file += 1;
if (file < res()->fileListSize)
internalLoadFile(res()->fileList[file]);
Commit: 2c91759a05c267b5530c61a0fd0d03bdce99b4be
https://github.com/scummvm/scummvm/commit/2c91759a05c267b5530c61a0fd0d03bdce99b4be
Author: athrxx (athrxx at scummvm.org)
Date: 2020-02-01T21:09:49+01:00
Commit Message:
KYRA: (LOK) - add missing delay (bug #11330)
Changed paths:
engines/kyra/sequence/sequences_lok.cpp
diff --git a/engines/kyra/sequence/sequences_lok.cpp b/engines/kyra/sequence/sequences_lok.cpp
index 0d3b5fc..e6e6457 100644
--- a/engines/kyra/sequence/sequences_lok.cpp
+++ b/engines/kyra/sequence/sequences_lok.cpp
@@ -1120,9 +1120,12 @@ int KyraEngine_LoK::seq_playEnd() {
_finalA->displayFrame(i, 0, 8, 8, 0, 0, 0);
_screen->updateScreen();
}
- delete _finalA;
+ nextTime = _system->getMillis() + 300 * _tickLength;
+ delete _finalA;
_finalA = 0;
+ delayUntil(nextTime);
+
seq_playEnding();
return 1;
}
Commit: 0239ed5e963da9ce6f824386b87ef1144d3c1d9d
https://github.com/scummvm/scummvm/commit/0239ed5e963da9ce6f824386b87ef1144d3c1d9d
Author: athrxx (athrxx at scummvm.org)
Date: 2020-02-01T21:09:50+01:00
Commit Message:
KYRA: (HOF) - fix bug #11331
This reverts the change from 1d5fd780 which was clearly wrong and caused this new bug (no idea what I was thinking there). To prevent the revival of bug #3721 I now added the proper code for a fix after tracking the whole bug with the DOSBox debugger.
Changed paths:
engines/kyra/engine/kyra_hof.cpp
engines/kyra/engine/kyra_hof.h
engines/kyra/engine/kyra_v1.cpp
engines/kyra/kyra_v1.h
engines/kyra/script/script_hof.cpp
engines/kyra/script/script_v1.cpp
diff --git a/engines/kyra/engine/kyra_hof.cpp b/engines/kyra/engine/kyra_hof.cpp
index 43849d5..11d82f2 100644
--- a/engines/kyra/engine/kyra_hof.cpp
+++ b/engines/kyra/engine/kyra_hof.cpp
@@ -1420,10 +1420,10 @@ void KyraEngine_HoF::snd_playVoiceFile(int id) {
sprintf(vocFile, "%07d", id);
if (_sound->isVoicePresent(vocFile)) {
// Unlike the original I have added a timeout here. I have chosen a size that makes sure that it
- // won't get triggered in any of the bug #11309 or bug #3721 situations, but still avoids infinite
- // hangups if something goes wrong.
+ // won't get triggered in bug #11309 or similiar situations, but still avoids infinite hangups
+ // if something goes wrong.
uint32 timeout = _system->getMillis() + 5000;
- while (_sound->voiceIsPlaying() && _system->getMillis() < timeout && !skipFlag())
+ while (snd_voiceIsPlaying() && _system->getMillis() < timeout && !skipFlag() && !shouldQuit())
delay(10);
_chatEndTime += (_system->getMillis() + 5000 - timeout);
if (_system->getMillis() >= timeout && !skipFlag())
diff --git a/engines/kyra/engine/kyra_hof.h b/engines/kyra/engine/kyra_hof.h
index 04ee2b6..60e2d1f 100644
--- a/engines/kyra/engine/kyra_hof.h
+++ b/engines/kyra/engine/kyra_hof.h
@@ -522,6 +522,7 @@ protected:
int o2_wipeDownMouseItem(EMCState *script);
int o2_getElapsedSecs(EMCState *script);
int o2_getTimerDelay(EMCState *script);
+ int o2_playCompleteSoundEffect(EMCState *script);
int o2_delaySecs(EMCState *script);
int o2_setTimerDelay(EMCState *script);
int o2_setScaleTableItem(EMCState *script);
diff --git a/engines/kyra/engine/kyra_v1.cpp b/engines/kyra/engine/kyra_v1.cpp
index b83aa3b..97cb69e 100644
--- a/engines/kyra/engine/kyra_v1.cpp
+++ b/engines/kyra/engine/kyra_v1.cpp
@@ -55,6 +55,7 @@ KyraEngine_v1::KyraEngine_v1(OSystem *system, const GameFlags &flags)
_trackMapSize = 0;
_lastMusicCommand = -1;
_curSfxFile = _curMusicTheme = -1;
+ _preventScriptSfx = false;
_gameToLoad = -1;
diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h
index 03c77b0..1f15f0e 100644
--- a/engines/kyra/kyra_v1.h
+++ b/engines/kyra/kyra_v1.h
@@ -365,6 +365,8 @@ protected:
const int8 *_trackMap;
int _trackMapSize;
+ bool _preventScriptSfx;
+
virtual int convertVolumeToMixer(int value);
virtual int convertVolumeFromMixer(int value);
diff --git a/engines/kyra/script/script_hof.cpp b/engines/kyra/script/script_hof.cpp
index ebc10c2..74c0638 100644
--- a/engines/kyra/script/script_hof.cpp
+++ b/engines/kyra/script/script_hof.cpp
@@ -445,6 +445,23 @@ int KyraEngine_HoF::o2_getTimerDelay(EMCState *script) {
return _timer->getDelay(stackPos(0));
}
+int KyraEngine_HoF::o2_playCompleteSoundEffect(EMCState *script) {
+ debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_playCompleteSoundEffect(%p) (%d)", (const void *)script, stackPos(0));
+ snd_playSoundEffect(stackPos(0));
+ // The following code is derived from HOFCD MAINWIN.EXE. MAINDOS.EXE has the "normal" o1_playSoundEffect() opcode here (the
+ // way we had implemented it before this fix). This was actually the cause of bug #3721. I have verified with the DOSBox
+ // debugger that it really runs the MAINWIN.EXE code and in particular this opcode with the wait loop.
+ // This loop waits for complete silence on all 4 pcm sound channels. Meanwhile it prevents any scripts from starting more
+ // sound effects while the loop is active (actually, only opcode 0x59 gets blocked). The whole thing looks a bit like a last
+ // minute hack....
+ while (_sound->voiceIsPlaying() && !skipFlag() && !shouldQuit()) {
+ _preventScriptSfx = true;
+ delay(10, true);
+ _preventScriptSfx = false;
+ }
+ return 0;
+}
+
int KyraEngine_HoF::o2_delaySecs(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "KyraEngine_HoF::o2_delaySecs(%p) (%d)", (const void *)script, stackPos(0));
delay(stackPos(0) * 1000, true);
@@ -1548,7 +1565,7 @@ void KyraEngine_HoF::setupOpcodeTable() {
Opcode(o2_getElapsedSecs);
// 0x34
Opcode(o2_getTimerDelay);
- Opcode(o1_playSoundEffect);
+ Opcode(o2_playCompleteSoundEffect);
Opcode(o2_delaySecs);
Opcode(o2_delay);
// 0x38
diff --git a/engines/kyra/script/script_v1.cpp b/engines/kyra/script/script_v1.cpp
index 356460d..b053358 100644
--- a/engines/kyra/script/script_v1.cpp
+++ b/engines/kyra/script/script_v1.cpp
@@ -118,7 +118,8 @@ int KyraEngine_v1::o1_blockOutWalkableRegion(EMCState *script) {
int KyraEngine_v1::o1_playSoundEffect(EMCState *script) {
debugC(3, kDebugLevelScriptFuncs, "KyraEngine_v1::o1_playSoundEffect(%p) (%d)", (const void *)script, stackPos(0));
- snd_playSoundEffect(stackPos(0));
+ if (!_preventScriptSfx)
+ snd_playSoundEffect(stackPos(0));
return 0;
}
More information about the Scummvm-git-logs
mailing list