aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/kyra/detection.cpp59
-rw-r--r--engines/kyra/kyra_v1.cpp12
-rw-r--r--engines/kyra/screen.cpp16
-rw-r--r--engines/kyra/sound.h55
-rw-r--r--engines/kyra/sound_lok.cpp9
-rw-r--r--engines/kyra/sound_towns.cpp1725
-rw-r--r--engines/kyra/staticres.cpp36
7 files changed, 1709 insertions, 203 deletions
diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp
index 344121b503..1e174bdfcf 100644
--- a/engines/kyra/detection.cpp
+++ b/engines/kyra/detection.cpp
@@ -44,6 +44,7 @@ namespace {
#define FLAGS(x, y, z, a, b, c, id) { Common::UNK_LANG, Common::kPlatformUnknown, x, y, z, a, b, c, id }
#define KYRA1_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, true, Kyra::GI_KYRA1)
#define KYRA1_AMIGA_FLAGS FLAGS(false, false, false, false, false, false, Kyra::GI_KYRA1)
#define KYRA1_TOWNS_FLAGS FLAGS(false, true, false, false, false, false, Kyra::GI_KYRA1)
#define KYRA1_TOWNS_SJIS_FLAGS FLAGS(false, true, false, true, false, false, Kyra::GI_KYRA1)
@@ -66,6 +67,28 @@ const KYRAGameDescription adGameDescs[] = {
{
"kyra1",
0,
+ AD_ENTRY1("DISK1.EXE", "c8641d0414d6c966d0a3dad79db07bf4"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ Common::ADGF_NO_FLAGS
+ },
+ KYRA1_FLOPPY_CMP_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ 0,
+ AD_ENTRY1("DISK1.EXE", "5d5cee4c3d0b68d586788b74243d254a"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ Common::ADGF_NO_FLAGS
+ },
+ KYRA1_FLOPPY_CMP_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ "Extracted",
AD_ENTRY1("GEMCUT.EMC", "3c244298395520bb62b5edfe41688879"),
Common::EN_ANY,
Common::kPlatformPC,
@@ -76,7 +99,7 @@ const KYRAGameDescription adGameDescs[] = {
{
{
"kyra1",
- 0,
+ "Extracted",
AD_ENTRY1("GEMCUT.EMC", "796e44863dd22fa635b042df1bf16673"),
Common::EN_ANY,
Common::kPlatformPC,
@@ -87,7 +110,7 @@ const KYRAGameDescription adGameDescs[] = {
{
{
"kyra1",
- 0,
+ "Extracted",
AD_ENTRY1("GEMCUT.EMC", "abf8eb360e79a6c2a837751fbd4d3d24"),
Common::FR_FRA,
Common::kPlatformPC,
@@ -98,7 +121,7 @@ const KYRAGameDescription adGameDescs[] = {
{
{
"kyra1",
- 0,
+ "Extracted",
AD_ENTRY1("GEMCUT.EMC", "6018e1dfeaca7fe83f8d0b00eb0dd049"),
Common::DE_DEU,
Common::kPlatformPC,
@@ -109,7 +132,7 @@ const KYRAGameDescription adGameDescs[] = {
{ // from Arne.F
{
"kyra1",
- 0,
+ "Extracted",
AD_ENTRY1("GEMCUT.EMC", "f0b276781f47c130f423ec9679fe9ed9"),
Common::DE_DEU,
Common::kPlatformPC,
@@ -120,7 +143,7 @@ const KYRAGameDescription adGameDescs[] = {
{ // from VooD
{
"kyra1",
- 0,
+ "Extracted",
AD_ENTRY1("GEMCUT.EMC", "8909b41596913b3f5deaf3c9f1017b01"),
Common::ES_ESP,
Common::kPlatformPC,
@@ -131,7 +154,7 @@ const KYRAGameDescription adGameDescs[] = {
{ // floppy 1.8 from clemmy
{
"kyra1",
- 0,
+ "Extracted",
AD_ENTRY1("GEMCUT.EMC", "747861d2a9c643c59fdab570df5b9093"),
Common::ES_ESP,
Common::kPlatformPC,
@@ -142,7 +165,7 @@ const KYRAGameDescription adGameDescs[] = {
{ // from gourry
{
"kyra1",
- 0,
+ "Extracted",
AD_ENTRY1("GEMCUT.EMC", "ef08c8c237ee1473fd52578303fc36df"),
Common::IT_ITA,
Common::kPlatformPC,
@@ -463,6 +486,28 @@ const KYRAGameDescription adGameDescs[] = {
},
KYRA2_TOWNS_SJIS_FLAGS
},
+ { // PC-9821
+ {
+ "kyra2",
+ 0,
+ AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
+ Common::EN_ANY,
+ Common::kPlatformPC98,
+ Common::ADGF_NO_FLAGS
+ },
+ KYRA2_TOWNS_FLAGS
+ },
+ {
+ {
+ "kyra2",
+ 0,
+ AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
+ Common::JA_JPN,
+ Common::kPlatformPC98,
+ Common::ADGF_NO_FLAGS
+ },
+ KYRA2_TOWNS_SJIS_FLAGS
+ },
// Kyra3
diff --git a/engines/kyra/kyra_v1.cpp b/engines/kyra/kyra_v1.cpp
index 1cc1d728bf..980c181c4e 100644
--- a/engines/kyra/kyra_v1.cpp
+++ b/engines/kyra/kyra_v1.cpp
@@ -107,14 +107,16 @@ int KyraEngine_v1::init() {
// "KYRA1: Crash on exceeded polyphony" for more information).
int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB/* | MDT_PREFER_MIDI*/);
- if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) {
- // TODO: currently we don't support the PC98 sound data,
- // but since it has the FM-Towns data files, we just use the
- // FM-Towns driver
+ if (_flags.platform == Common::kPlatformFMTowns) {
if (_flags.gameID == GI_KYRA1)
_sound = new SoundTowns(this, _mixer);
else
- _sound = new SoundTowns_v2(this, _mixer);
+ _sound = new SoundTownsPC98_v2(this, _mixer);
+ } else if (_flags.platform == Common::kPlatformPC98) {
+ if (_flags.gameID == GI_KYRA1)
+ _sound = new SoundPC98(this, _mixer);
+ else
+ _sound = new SoundTownsPC98_v2(this, _mixer);
} else if (midiDriver == MD_ADLIB) {
_sound = new SoundAdlibPC(this, _mixer);
assert(_sound);
diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp
index f00c47ceea..6e7a88b1a3 100644
--- a/engines/kyra/screen.cpp
+++ b/engines/kyra/screen.cpp
@@ -92,9 +92,19 @@ bool Screen::init() {
if (_useSJIS) {
if (!_sjisFontData) {
- _sjisFontData = _vm->resource()->fileData("FMT_FNT.ROM", 0);
- if (!_sjisFontData)
- error("missing font rom ('FMT_FNT.ROM') required for this version");
+ // we use the FM-Towns font rom for PC-98, too, until we feel
+ // like adding support for the PC-98 font
+ //if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+ // FM-Towns
+ _sjisFontData = _vm->resource()->fileData("FMT_FNT.ROM", 0);
+ if (!_sjisFontData)
+ error("missing font rom ('FMT_FNT.ROM') required for this version");
+ /*} else {
+ // PC-98
+ _sjisFontData = _vm->resource()->fileData("FONT.ROM", 0);
+ if (!_sjisFontData)
+ error("missing font rom ('FONT.ROM') required for this version");
+ }*/
}
if (!_sjisTempPage) {
diff --git a/engines/kyra/sound.h b/engines/kyra/sound.h
index 1baeb3064a..c08c531ab9 100644
--- a/engines/kyra/sound.h
+++ b/engines/kyra/sound.h
@@ -73,7 +73,8 @@ public:
kAdlib,
kMidiMT32,
kMidiGM,
- kTowns
+ kTowns,
+ kPC98
};
virtual kType getMusicType() const = 0;
@@ -382,7 +383,9 @@ private:
Common::Mutex _mutex;
};
-class SoundTowns_EuphonyDriver;
+class Towns_EuphonyDriver;
+class TownsPC98_OpnDriver;
+
class SoundTowns : public MidiDriver, public Sound {
public:
SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer);
@@ -417,6 +420,7 @@ public:
static float semitoneAndSampleRate_to_sampleStep(int8 semiTone, int8 semiToneRootkey,
uint32 sampleRate, uint32 outputRate, int32 pitchWheel);
+
private:
bool loadInstruments();
void playEuphonyTrack(uint32 offset, int loop);
@@ -430,7 +434,7 @@ private:
uint _sfxFileIndex;
uint8 *_sfxFileData;
- SoundTowns_EuphonyDriver * _driver;
+ Towns_EuphonyDriver * _driver;
MidiParser * _parser;
Common::Mutex _mutex;
@@ -439,13 +443,38 @@ private:
const uint8 *_sfxWDTable;
};
-//class SoundTowns_v2_TwnDriver;
-class SoundTowns_v2 : public Sound {
+class SoundPC98 : public Sound {
public:
- SoundTowns_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer);
- ~SoundTowns_v2();
+ SoundPC98(KyraEngine_v1 *vm, Audio::Mixer *mixer);
+ ~SoundPC98();
- kType getMusicType() const { return kTowns; }
+ virtual kType getMusicType() const { return kPC98; }
+
+ bool init();
+
+ void process() {}
+ void loadSoundFile(uint file) {}
+
+ void playTrack(uint8 track);
+ void haltTrack();
+ void beginFadeOut();
+
+ int32 voicePlay(const char *file, bool isSfx = false) { return -1; }
+ void playSoundEffect(uint8);
+
+protected:
+ int _lastTrack;
+ uint8 *_musicTrackData;
+ uint8 *_sfxTrackData;
+ TownsPC98_OpnDriver *_driver;
+};
+
+class SoundTownsPC98_v2 : public Sound {
+public:
+ SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer);
+ ~SoundTownsPC98_v2();
+
+ kType getMusicType() const { return _vm->gameFlags().platform == Common::kPlatformFMTowns ? kTowns : kPC98; }
bool init();
void process();
@@ -459,13 +488,11 @@ public:
int32 voicePlay(const char *file, bool isSfx = false);
void playSoundEffect(uint8) {}
-private:
- int _lastTrack;
-
+protected:
Audio::AudioStream *_currentSFX;
-
- //SoundTowns_v2_TwnDriver *_driver;
- uint8 *_twnTrackData;
+ int _lastTrack;
+ uint8 *_musicTrackData;
+ TownsPC98_OpnDriver *_driver;
};
class MixedSoundDriver : public Sound {
diff --git a/engines/kyra/sound_lok.cpp b/engines/kyra/sound_lok.cpp
index 8a1d16a6b1..d320536507 100644
--- a/engines/kyra/sound_lok.cpp
+++ b/engines/kyra/sound_lok.cpp
@@ -43,7 +43,7 @@ void KyraEngine_LoK::snd_playWanderScoreViaMap(int command, int restart) {
if (restart)
_lastMusicCommand = -1;
- if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) {
+ if (_flags.platform == Common::kPlatformFMTowns) {
if (command == 1) {
_sound->beginFadeOut();
} else if (command >= 35 && command <= 38) {
@@ -56,6 +56,13 @@ void KyraEngine_LoK::snd_playWanderScoreViaMap(int command, int restart) {
} else {
_sound->haltTrack();
}
+ } else if (_flags.platform == Common::kPlatformPC98) {
+
+ //////////////
+ //// TODO ////
+ //////////////
+ _sound->playTrack(command);
+
} else {
KyraEngine_v1::snd_playWanderScoreViaMap(command, restart);
}
diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp
index c0e4dd70a9..a6457f509b 100644
--- a/engines/kyra/sound_towns.cpp
+++ b/engines/kyra/sound_towns.cpp
@@ -34,18 +34,21 @@
#include "common/util.h"
+#ifdef _MSC_VER
+#define _USE_MATH_DEFINES
+#endif
#include <math.h>
#define EUPHONY_FADEOUT_TICKS 600
namespace Kyra {
-enum ChannelState { _s_ready, _s_attacking, _s_decaying, _s_sustaining, _s_releasing };
+enum EnvelopeState { s_ready, s_attacking, s_decaying, s_sustaining, s_releasing };
-class MidiChannel_EuD : public MidiChannel {
+class Towns_EuphonyChannel : public MidiChannel {
public:
- MidiChannel_EuD() {}
- ~MidiChannel_EuD() {}
+ Towns_EuphonyChannel() {}
+ ~Towns_EuphonyChannel() {}
virtual void nextTick(int32 *outbuf, int buflen) = 0;
virtual void rate(uint16 r) = 0;
@@ -54,10 +57,10 @@ protected:
uint16 _rate;
};
-class MidiChannel_EuD_FM : public MidiChannel_EuD {
+class Towns_EuphonyFmChannel : public Towns_EuphonyChannel {
public:
- MidiChannel_EuD_FM();
- virtual ~MidiChannel_EuD_FM();
+ Towns_EuphonyFmChannel();
+ virtual ~Towns_EuphonyFmChannel();
void nextTick(int32 *outbuf, int buflen);
void rate(uint16 r);
@@ -79,13 +82,13 @@ protected:
Voice2612 *_voice;
};
-class MidiChannel_EuD_WAVE : public MidiChannel_EuD {
+class Towns_EuphonyPcmChannel : public Towns_EuphonyChannel {
public:
void nextTick(int32 *outbuf, int buflen);
void rate(uint16 r);
- MidiChannel_EuD_WAVE();
- virtual ~MidiChannel_EuD_WAVE();
+ Towns_EuphonyPcmChannel();
+ virtual ~Towns_EuphonyPcmChannel();
// MidiChannel interface
MidiDriver *device() { return 0; }
@@ -126,9 +129,9 @@ protected:
int32 keyOffset;
int32 keyNote;
const int8 *_samples;
- } * _snd[8];
+ } *_snd[8];
struct Env {
- ChannelState state;
+ EnvelopeState state;
int32 currentLevel;
int32 rate;
int32 tickCount;
@@ -141,40 +144,39 @@ protected:
int32 releaseRate;
int32 rootKeyOffset;
int32 size;
- } * _env[8];
- } * _voice;
+ } *_env[8];
+ } *_voice;
};
-class SoundTowns_EuphonyTrackQueue {
+class Towns_EuphonyTrackQueue {
public:
- SoundTowns_EuphonyTrackQueue(SoundTowns_EuphonyDriver *driver, SoundTowns_EuphonyTrackQueue *last);
- ~SoundTowns_EuphonyTrackQueue() {}
+ Towns_EuphonyTrackQueue(Towns_EuphonyDriver *driver, Towns_EuphonyTrackQueue *last);
+ ~Towns_EuphonyTrackQueue() {}
- void release();
+ Towns_EuphonyTrackQueue *release();
void initDriver();
- void loadDataToCurrentPosition(uint8 * trackdata, uint32 size, bool loop = 0);
- void loadDataToEndOfQueue(uint8 * trackdata, uint32 size, bool loop = 0);
+ void loadDataToCurrentPosition(uint8 *trackdata, uint32 size, bool loop = 0);
+ void loadDataToEndOfQueue(uint8 *trackdata, uint32 size, bool loop = 0);
void setPlayBackStatus(bool playing);
- SoundTowns_EuphonyTrackQueue * reset();
bool isPlaying() {return _playing; }
- uint8 * trackData() {return _trackData; }
+ const uint8 * trackData() {return _trackData; }
bool _loop;
- SoundTowns_EuphonyTrackQueue * _next;
+ Towns_EuphonyTrackQueue *_next;
private:
- uint8 * _trackData;
- uint8 * _used;
- uint8 * _fchan;
- uint8 * _wchan;
+ uint8 *_trackData;
+ uint8 *_used;
+ uint8 *_fchan;
+ uint8 *_wchan;
bool _playing;
- SoundTowns_EuphonyDriver * _driver;
- SoundTowns_EuphonyTrackQueue * _last;
+ Towns_EuphonyDriver *_driver;
+ Towns_EuphonyTrackQueue *_last;
};
-class MidiParser_EuD : public MidiParser {
+class Towns_EuphonyParser : public MidiParser {
public:
- MidiParser_EuD(SoundTowns_EuphonyTrackQueue * queue);
+ Towns_EuphonyParser(Towns_EuphonyTrackQueue * queue);
bool loadMusic (byte *data, uint32 size);
int32 calculateTempo(int16 val);
@@ -183,11 +185,11 @@ protected:
void resetTracking();
void setup();
- byte * _enable;
- byte * _mode;
- byte * _channel;
- byte * _adjVelo;
- int8 * _adjNote;
+ byte *_enable;
+ byte *_mode;
+ byte *_channel;
+ byte *_adjVelo;
+ int8 *_adjNote;
uint8 _firstBaseTickStep;
uint8 _nextBaseTickStep;
@@ -195,13 +197,13 @@ protected:
uint32 _baseTick;
byte _tempo[3];
- SoundTowns_EuphonyTrackQueue * _queue;
+ Towns_EuphonyTrackQueue *_queue;
};
-class SoundTowns_EuphonyDriver : public MidiDriver_Emulated {
+class Towns_EuphonyDriver : public MidiDriver_Emulated {
public:
- SoundTowns_EuphonyDriver(Audio::Mixer *mixer);
- virtual ~SoundTowns_EuphonyDriver();
+ Towns_EuphonyDriver(Audio::Mixer *mixer);
+ virtual ~Towns_EuphonyDriver();
int open();
void close();
@@ -213,7 +215,7 @@ public:
void loadFmInstruments(const byte *instr);
void loadWaveInstruments(const byte *instr);
- SoundTowns_EuphonyTrackQueue * queue() { return _queue; }
+ Towns_EuphonyTrackQueue *queue() { return _queue; }
MidiChannel *allocateChannel() { return 0; }
MidiChannel *getPercussionChannel() { return 0; }
@@ -237,10 +239,10 @@ protected:
void generateSamples(int16 *buf, int len);
- MidiChannel_EuD_FM *_fChannel[6];
- MidiChannel_EuD_WAVE *_wChannel[8];
- MidiChannel_EuD * _channel[16];
- SoundTowns_EuphonyTrackQueue * _queue;
+ Towns_EuphonyFmChannel *_fChannel[6];
+ Towns_EuphonyPcmChannel *_wChannel[8];
+ Towns_EuphonyChannel *_channel[16];
+ Towns_EuphonyTrackQueue *_queue;
int _volume;
bool _fading;
@@ -251,23 +253,23 @@ protected:
int8 * _waveSounds[10];
};
-MidiChannel_EuD_FM::MidiChannel_EuD_FM() {
+Towns_EuphonyFmChannel::Towns_EuphonyFmChannel() {
_voice = new Voice2612;
}
-MidiChannel_EuD_FM::~MidiChannel_EuD_FM() {
+Towns_EuphonyFmChannel::~Towns_EuphonyFmChannel() {
delete _voice;
}
-void MidiChannel_EuD_FM::noteOn(byte note, byte onVelo) {
+void Towns_EuphonyFmChannel::noteOn(byte note, byte onVelo) {
_voice->noteOn(note, onVelo);
}
-void MidiChannel_EuD_FM::noteOff(byte note) {
+void Towns_EuphonyFmChannel::noteOff(byte note) {
_voice->noteOff(note);
}
-void MidiChannel_EuD_FM::controlChange(byte control, byte value) {
+void Towns_EuphonyFmChannel::controlChange(byte control, byte value) {
if (control == 121) {
// Reset controller
delete _voice;
@@ -279,25 +281,25 @@ void MidiChannel_EuD_FM::controlChange(byte control, byte value) {
}
}
-void MidiChannel_EuD_FM::sysEx_customInstrument(uint32, const byte *fmInst) {
+void Towns_EuphonyFmChannel::sysEx_customInstrument(uint32, const byte *fmInst) {
_voice->_rate = _rate;
_voice->setInstrument(fmInst);
}
-void MidiChannel_EuD_FM::pitchBend(int16 value) {
+void Towns_EuphonyFmChannel::pitchBend(int16 value) {
_voice->pitchBend(value);
}
-void MidiChannel_EuD_FM::nextTick(int32 *outbuf, int buflen) {
+void Towns_EuphonyFmChannel::nextTick(int32 *outbuf, int buflen) {
_voice->nextTick((int*) outbuf, buflen);
}
-void MidiChannel_EuD_FM::rate(uint16 r) {
+void Towns_EuphonyFmChannel::rate(uint16 r) {
_rate = r;
_voice->_rate = r;
}
-MidiChannel_EuD_WAVE::MidiChannel_EuD_WAVE() {
+Towns_EuphonyPcmChannel::Towns_EuphonyPcmChannel() {
_voice = new Voice;
for (uint8 i = 0; i < 8; i++) {
_voice->_env[i] = new Voice::Env;
@@ -310,7 +312,7 @@ MidiChannel_EuD_WAVE::MidiChannel_EuD_WAVE() {
_current = -1;
}
-MidiChannel_EuD_WAVE::~MidiChannel_EuD_WAVE() {
+Towns_EuphonyPcmChannel::~Towns_EuphonyPcmChannel() {
for (uint8 i = 0; i < 8; i++) {
if (_voice->_snd[i])
delete _voice->_snd[i];
@@ -319,7 +321,7 @@ MidiChannel_EuD_WAVE::~MidiChannel_EuD_WAVE() {
delete _voice;
}
-void MidiChannel_EuD_WAVE::noteOn(byte note, byte onVelo) {
+void Towns_EuphonyPcmChannel::noteOn(byte note, byte onVelo) {
_note = note;
velocity(onVelo);
_phase = 0;
@@ -329,24 +331,24 @@ void MidiChannel_EuD_WAVE::noteOn(byte note, byte onVelo) {
break;
}
- _voice->_env[_current]->state = _s_attacking;
+ _voice->_env[_current]->state = s_attacking;
_voice->_env[_current]->currentLevel = 0;
_voice->_env[_current]->rate = _rate;
_voice->_env[_current]->tickCount = 0;
}
-void MidiChannel_EuD_WAVE::noteOff(byte note) {
+void Towns_EuphonyPcmChannel::noteOff(byte note) {
if (_current == -1)
return;
- if (_voice->_env[_current]->state == _s_ready)
+ if (_voice->_env[_current]->state == s_ready)
return;
- _voice->_env[_current]->state = _s_releasing;
+ _voice->_env[_current]->state = s_releasing;
_voice->_env[_current]->releaseLevel = _voice->_env[_current]->currentLevel;
_voice->_env[_current]->tickCount = 0;
}
-void MidiChannel_EuD_WAVE::controlChange(byte control, byte value) {
+void Towns_EuphonyPcmChannel::controlChange(byte control, byte value) {
switch (control) {
case 0x07:
// volume
@@ -377,7 +379,7 @@ void MidiChannel_EuD_WAVE::controlChange(byte control, byte value) {
}
}
-void MidiChannel_EuD_WAVE::sysEx_customInstrument(uint32 type, const byte *fmInst) {
+void Towns_EuphonyPcmChannel::sysEx_customInstrument(uint32 type, const byte *fmInst) {
if (type == 0x80) {
for (uint8 i = 0; i < 8; i++) {
const byte * const* pos = (const byte * const*) fmInst;
@@ -406,7 +408,7 @@ void MidiChannel_EuD_WAVE::sysEx_customInstrument(uint32 type, const byte *fmIns
_voice->split[i] = READ_LE_UINT16(fmInst + 16 + 2 * i);
_voice->id[i] = READ_LE_UINT32(fmInst + 32 + 4 * i);
_voice->_snd[i] = 0;
- _voice->_env[i]->state = _s_ready;
+ _voice->_env[i]->state = s_ready;
_voice->_env[i]->currentLevel = 0;
_voice->_env[i]->totalLevel = *(fmInst + 64 + 8 * i);
_voice->_env[i]->attackRate = *(fmInst + 65 + 8 * i) * 10;
@@ -419,11 +421,11 @@ void MidiChannel_EuD_WAVE::sysEx_customInstrument(uint32 type, const byte *fmIns
}
}
-void MidiChannel_EuD_WAVE::pitchBend(int16 value) {
+void Towns_EuphonyPcmChannel::pitchBend(int16 value) {
_frequencyOffs = value;
}
-void MidiChannel_EuD_WAVE::nextTick(int32 *outbuf, int buflen) {
+void Towns_EuphonyPcmChannel::nextTick(int32 *outbuf, int buflen) {
if (_current == -1 || !_voice->_snd[_current] || !_voice->_env[_current]->state || !_velocity) {
velocity(0);
_current = -1;
@@ -475,13 +477,13 @@ void MidiChannel_EuD_WAVE::nextTick(int32 *outbuf, int buflen) {
}
}
-void MidiChannel_EuD_WAVE::evpNextTick() {
+void Towns_EuphonyPcmChannel::evpNextTick() {
switch (_voice->_env[_current]->state) {
- case _s_ready:
+ case s_ready:
_voice->_env[_current]->currentLevel = 0;
return;
- case _s_attacking:
+ case s_attacking:
if (_voice->_env[_current]->attackRate == 0)
_voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel;
else if (_voice->_env[_current]->attackRate >= 1270)
@@ -493,12 +495,12 @@ void MidiChannel_EuD_WAVE::evpNextTick() {
if (_voice->_env[_current]->currentLevel >= _voice->_env[_current]->totalLevel) {
_voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel;
- _voice->_env[_current]->state = _s_decaying;
+ _voice->_env[_current]->state = s_decaying;
_voice->_env[_current]->tickCount = 0;
}
break;
- case _s_decaying:
+ case s_decaying:
if (_voice->_env[_current]->decayRate == 0)
_voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel;
else if (_voice->_env[_current]->decayRate >= 1270)
@@ -512,12 +514,12 @@ void MidiChannel_EuD_WAVE::evpNextTick() {
if (_voice->_env[_current]->currentLevel <= _voice->_env[_current]->sustainLevel) {
_voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel;
- _voice->_env[_current]->state = _s_sustaining;
+ _voice->_env[_current]->state = s_sustaining;
_voice->_env[_current]->tickCount = 0;
}
break;
- case _s_sustaining:
+ case s_sustaining:
if (_voice->_env[_current]->sustainRate == 0)
_voice->_env[_current]->currentLevel = 0;
else if (_voice->_env[_current]->sustainRate >= 2540)
@@ -531,12 +533,12 @@ void MidiChannel_EuD_WAVE::evpNextTick() {
if (_voice->_env[_current]->currentLevel <= 0) {
_voice->_env[_current]->currentLevel = 0;
- _voice->_env[_current]->state = _s_ready;
+ _voice->_env[_current]->state = s_ready;
_voice->_env[_current]->tickCount = 0;
}
break;
- case _s_releasing:
+ case s_releasing:
if (_voice->_env[_current]->releaseRate == 0)
_voice->_env[_current]->currentLevel = 0;
else if (_voice->_env[_current]->releaseRate >= 1270)
@@ -550,7 +552,7 @@ void MidiChannel_EuD_WAVE::evpNextTick() {
if (_voice->_env[_current]->currentLevel <= 0) {
_voice->_env[_current]->currentLevel = 0;
- _voice->_env[_current]->state = _s_ready;
+ _voice->_env[_current]->state = s_ready;
}
break;
@@ -559,15 +561,15 @@ void MidiChannel_EuD_WAVE::evpNextTick() {
}
}
-void MidiChannel_EuD_WAVE::rate(uint16 r) {
+void Towns_EuphonyPcmChannel::rate(uint16 r) {
_rate = r;
}
-void MidiChannel_EuD_WAVE::velocity(int velo) {
+void Towns_EuphonyPcmChannel::velocity(int velo) {
_velocity = velo;
}
-SoundTowns_EuphonyDriver::SoundTowns_EuphonyDriver(Audio::Mixer *mixer)
+Towns_EuphonyDriver::Towns_EuphonyDriver(Audio::Mixer *mixer)
: MidiDriver_Emulated(mixer) {
_volume = 255;
_fadestate = EUPHONY_FADEOUT_TICKS;
@@ -576,9 +578,9 @@ SoundTowns_EuphonyDriver::SoundTowns_EuphonyDriver(Audio::Mixer *mixer)
MidiDriver_YM2612::createLookupTables();
for (uint8 i = 0; i < 6; i++)
- _channel[i] = _fChannel[i] = new MidiChannel_EuD_FM;
+ _channel[i] = _fChannel[i] = new Towns_EuphonyFmChannel;
for (uint8 i = 0; i < 8; i++)
- _channel[i + 6] = _wChannel[i] = new MidiChannel_EuD_WAVE;
+ _channel[i + 6] = _wChannel[i] = new Towns_EuphonyPcmChannel;
_channel[14] = _channel[15] = 0;
_fmInstruments = _waveInstruments = 0;
@@ -587,10 +589,10 @@ SoundTowns_EuphonyDriver::SoundTowns_EuphonyDriver(Audio::Mixer *mixer)
rate(getRate());
fading(0);
- _queue = new SoundTowns_EuphonyTrackQueue(this, 0);
+ _queue = new Towns_EuphonyTrackQueue(this, 0);
}
-SoundTowns_EuphonyDriver::~SoundTowns_EuphonyDriver() {
+Towns_EuphonyDriver::~Towns_EuphonyDriver() {
for (int i = 0; i < 6; i++)
delete _fChannel[i];
for (int i = 0; i < 8; i++)
@@ -622,7 +624,7 @@ SoundTowns_EuphonyDriver::~SoundTowns_EuphonyDriver() {
}
}
-int SoundTowns_EuphonyDriver::open() {
+int Towns_EuphonyDriver::open() {
if (_isOpen)
return MERR_ALREADY_OPEN;
MidiDriver_Emulated::open();
@@ -633,18 +635,18 @@ int SoundTowns_EuphonyDriver::open() {
return 0;
}
-void SoundTowns_EuphonyDriver::close() {
+void Towns_EuphonyDriver::close() {
if (!_isOpen)
return;
_isOpen = false;
_mixer->stopHandle(_mixerSoundHandle);
}
-void SoundTowns_EuphonyDriver::send(uint32 b) {
+void Towns_EuphonyDriver::send(uint32 b) {
send(b & 0xF, b & 0xFFFFFFF0);
}
-void SoundTowns_EuphonyDriver::send(byte chan, uint32 b) {
+void Towns_EuphonyDriver::send(byte chan, uint32 b) {
byte param2 = (byte) ((b >> 16) & 0xFF);
byte param1 = (byte) ((b >> 8) & 0xFF);
byte cmd = (byte) (b & 0xF0);
@@ -703,18 +705,18 @@ void SoundTowns_EuphonyDriver::send(byte chan, uint32 b) {
_channel[chan]->pitchBend((param1 | (param2 << 7)) - 0x2000);
break;
default:
- warning("SoundTowns_EuphonyDriver: Unknown send() command 0x%02X", cmd);
+ warning("Towns_EuphonyDriver: Unknown send() command 0x%02X", cmd);
}
}
-void SoundTowns_EuphonyDriver::loadFmInstruments(const byte *instr) {
+void Towns_EuphonyDriver::loadFmInstruments(const byte *instr) {
if (_fmInstruments)
delete[] _fmInstruments;
_fmInstruments = new uint8[0x1800];
memcpy(_fmInstruments, instr, 0x1800);
}
-void SoundTowns_EuphonyDriver::loadWaveInstruments(const byte *instr) {
+void Towns_EuphonyDriver::loadWaveInstruments(const byte *instr) {
if (_waveInstruments)
delete[] _waveInstruments;
_waveInstruments = new uint8[0x1000];
@@ -739,24 +741,24 @@ void SoundTowns_EuphonyDriver::loadWaveInstruments(const byte *instr) {
}
-void SoundTowns_EuphonyDriver::assignFmChannel(uint8 midiChannelNumber, uint8 fmChannelNumber) {
+void Towns_EuphonyDriver::assignFmChannel(uint8 midiChannelNumber, uint8 fmChannelNumber) {
_channel[midiChannelNumber] = _fChannel[fmChannelNumber];
}
-void SoundTowns_EuphonyDriver::assignWaveChannel(uint8 midiChannelNumber, uint8 waveChannelNumber) {
+void Towns_EuphonyDriver::assignWaveChannel(uint8 midiChannelNumber, uint8 waveChannelNumber) {
_channel[midiChannelNumber] = _wChannel[waveChannelNumber];
}
-void SoundTowns_EuphonyDriver::removeChannel(uint8 midiChannelNumber) {
+void Towns_EuphonyDriver::removeChannel(uint8 midiChannelNumber) {
_channel[midiChannelNumber] = 0;
}
-void SoundTowns_EuphonyDriver::generateSamples(int16 *data, int len) {
+void Towns_EuphonyDriver::generateSamples(int16 *data, int len) {
memset(data, 0, 2 * sizeof(int16) * len);
nextTick(data, len);
}
-void SoundTowns_EuphonyDriver::nextTick(int16 *buf1, int buflen) {
+void Towns_EuphonyDriver::nextTick(int16 *buf1, int buflen) {
int32 *buf0 = (int32 *)buf1;
for (int i = 0; i < ARRAYSIZE(_channel); i++) {
@@ -779,33 +781,33 @@ void SoundTowns_EuphonyDriver::nextTick(int16 *buf1, int buflen) {
}
}
-void SoundTowns_EuphonyDriver::rate(uint16 r) {
+void Towns_EuphonyDriver::rate(uint16 r) {
for (uint8 i = 0; i < 16; i++) {
if (_channel[i])
_channel[i]->rate(r);
}
}
-void SoundTowns_EuphonyDriver::fading(bool status) {
+void Towns_EuphonyDriver::fading(bool status) {
_fading = status;
if (!_fading)
_fadestate = EUPHONY_FADEOUT_TICKS;
}
-MidiParser_EuD::MidiParser_EuD(SoundTowns_EuphonyTrackQueue * queue) : MidiParser(),
+Towns_EuphonyParser::Towns_EuphonyParser(Towns_EuphonyTrackQueue * queue) : MidiParser(),
_firstBaseTickStep(0x33), _nextBaseTickStep(0x33) {
_initialTempo = calculateTempo(0x5a);
_queue = queue;
}
-void MidiParser_EuD::parseNextEvent(EventInfo &info) {
+void Towns_EuphonyParser::parseNextEvent(EventInfo &info) {
byte *pos = _position._play_pos;
if (_queue->_next) {
if (info.ext.type == 0x2F) {
unloadMusic();
memset(&info, 0, sizeof(EventInfo));
- pos = _position._play_pos = _tracks[0] = _queue->trackData() + 0x806;
+ pos = _position._play_pos = _tracks[0] = (byte*) _queue->trackData() + 0x806;
} else if (_active_track == 255) {
_queue = _queue->_next;
setup();
@@ -920,15 +922,14 @@ void MidiParser_EuD::parseNextEvent(EventInfo &info) {
_position._play_pos = pos;
}
-bool MidiParser_EuD::loadMusic(byte *data, uint32 size) {
+bool Towns_EuphonyParser::loadMusic(byte *data, uint32 size) {
bool loop = _autoLoop;
if (_queue->isPlaying() && !_queue->_loop) {
_queue->loadDataToEndOfQueue(data, size, loop);
} else {
unloadMusic();
- _queue = _queue->reset();
- _queue->release();
+ _queue = _queue->release();
_queue->loadDataToCurrentPosition(data, size, loop);
setup();
setTrack(0);
@@ -937,7 +938,7 @@ bool MidiParser_EuD::loadMusic(byte *data, uint32 size) {
return true;
}
-int32 MidiParser_EuD::calculateTempo(int16 val) {
+int32 Towns_EuphonyParser::calculateTempo(int16 val) {
int32 tempo = val;
if (tempo < 0)
@@ -953,7 +954,7 @@ int32 MidiParser_EuD::calculateTempo(int16 val) {
return tempo;
}
-void MidiParser_EuD::resetTracking() {
+void Towns_EuphonyParser::resetTracking() {
MidiParser::resetTracking();
_nextBaseTickStep = _firstBaseTickStep;
@@ -962,8 +963,8 @@ void MidiParser_EuD::resetTracking() {
_queue->setPlayBackStatus(false);
}
-void MidiParser_EuD::setup() {
- uint8 *data = _queue->trackData();
+void Towns_EuphonyParser::setup() {
+ uint8 *data = (uint8 *) _queue->trackData();
if (!data)
return;
_queue->initDriver();
@@ -984,7 +985,7 @@ void MidiParser_EuD::setup() {
_tracks[0] = data + 0x806;
}
-SoundTowns_EuphonyTrackQueue::SoundTowns_EuphonyTrackQueue(SoundTowns_EuphonyDriver * driver, SoundTowns_EuphonyTrackQueue * last) {
+Towns_EuphonyTrackQueue::Towns_EuphonyTrackQueue(Towns_EuphonyDriver * driver, Towns_EuphonyTrackQueue * last) {
_trackData = 0;
_next = 0;
_driver = driver;
@@ -993,22 +994,15 @@ SoundTowns_EuphonyTrackQueue::SoundTowns_EuphonyTrackQueue(SoundTowns_EuphonyDri
_playing = false;
}
-void SoundTowns_EuphonyTrackQueue::setPlayBackStatus(bool playing) {
- SoundTowns_EuphonyTrackQueue * i = this;
+void Towns_EuphonyTrackQueue::setPlayBackStatus(bool playing) {
+ Towns_EuphonyTrackQueue * i = this;
do {
i->_playing = playing;
i = i->_next;
} while (i);
}
-SoundTowns_EuphonyTrackQueue * SoundTowns_EuphonyTrackQueue::reset() {
- SoundTowns_EuphonyTrackQueue * i = this;
- while (i->_last)
- i = i->_last;
- return i;
-}
-
-void SoundTowns_EuphonyTrackQueue::loadDataToCurrentPosition(uint8 * trackdata, uint32 size, bool loop) {
+void Towns_EuphonyTrackQueue::loadDataToCurrentPosition(uint8 * trackdata, uint32 size, bool loop) {
if (_trackData)
delete[] _trackData;
_trackData = new uint8[0xC58A];
@@ -1022,17 +1016,17 @@ void SoundTowns_EuphonyTrackQueue::loadDataToCurrentPosition(uint8 * trackdata,
_playing = false;
}
-void SoundTowns_EuphonyTrackQueue::loadDataToEndOfQueue(uint8 * trackdata, uint32 size, bool loop) {
+void Towns_EuphonyTrackQueue::loadDataToEndOfQueue(uint8 * trackdata, uint32 size, bool loop) {
if (!_trackData) {
loadDataToCurrentPosition(trackdata, size, loop);
return;
}
- SoundTowns_EuphonyTrackQueue * i = this;
+ Towns_EuphonyTrackQueue * i = this;
while (i->_next)
i = i->_next;
- i = i->_next = new SoundTowns_EuphonyTrackQueue(_driver, i);
+ i = i->_next = new Towns_EuphonyTrackQueue(_driver, i);
i->_trackData = new uint8[0xC58A];
memset(i->_trackData, 0, 0xC58A);
Screen::decodeFrame4(trackdata, i->_trackData, size);
@@ -1044,29 +1038,39 @@ void SoundTowns_EuphonyTrackQueue::loadDataToEndOfQueue(uint8 * trackdata, uint3
i->_playing = _playing;
}
-void SoundTowns_EuphonyTrackQueue::release() {
- SoundTowns_EuphonyTrackQueue * i = _next;
- _next = 0;
- _playing = false;
- _used = _fchan = _wchan = 0;
+Towns_EuphonyTrackQueue *Towns_EuphonyTrackQueue::release() {
+ Towns_EuphonyTrackQueue *i = this;
+ while (i->_next)
+ i = i->_next;
- if (_trackData) {
- delete[] _trackData;
- _trackData = 0;
- }
+ Towns_EuphonyTrackQueue *res = i;
while (i) {
+ i->_playing = false;
+ i->_used = i->_fchan = i->_wchan = 0;
if (i->_trackData) {
delete[] i->_trackData;
i->_trackData = 0;
}
- i = i->_next;
- if (i)
- delete i->_last;
+ i = i->_last;
+ if (i) {
+ res = i;
+ if (i->_next) {
+ delete i->_next;
+ i->_next = 0;
+ }
+ }
}
+
+ if (res->_trackData) {
+ delete[] res->_trackData;
+ res->_trackData = 0;
+ }
+
+ return res;
}
-void SoundTowns_EuphonyTrackQueue::initDriver() {
+void Towns_EuphonyTrackQueue::initDriver() {
for (uint8 i = 0; i < 6; i++) {
if (_used[_fchan[i]])
_driver->assignFmChannel(_fchan[i], i);
@@ -1084,11 +1088,1339 @@ void SoundTowns_EuphonyTrackQueue::initDriver() {
_driver->send(0x79B0);
}
+class TownsPC98_OpnOperator {
+public:
+ TownsPC98_OpnOperator::TownsPC98_OpnOperator(double rate, uint8 id, const uint8 *rateTable,
+ const uint8 *shiftTable, const uint8 *attackDecayTable, const uint32 *frqTable,
+ const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable);
+ TownsPC98_OpnOperator::~TownsPC98_OpnOperator() {}
+
+ void keyOn();
+ void keyOff();
+ void frequency(int freq);
+ void updatePhaseIncrement();
+ void recalculateRates();
+ void generateOutput(int phasebuf, int *feedbuf, int &out);
+
+ void feedbackLevel(int32 level) {_feedbackLevel = level ? level + 6 : 0; }
+ void detune(int value) { _detn = (int32*) &_detnTbl[value << 5]; }
+ void multiple(uint32 value) { _multiple = value ? (value << 1) : 1; }
+ void attackRate(uint32 value) { _specifiedAttackRate = value; }
+ bool scaleRate(uint8 value);
+ void decayRate(uint32 value) { _specifiedDecayRate = value; recalculateRates(); }
+ void sustainRate(uint32 value) { _specifiedSustainRate = value; recalculateRates(); }
+ void sustainLevel(uint32 value) { _sustainLevel = (value == 0x0f) ? 0x3e0 : value << 5; }
+ void releaseRate(uint32 value) { _specifiedReleaseRate = value; recalculateRates(); }
+ void totalLevel(uint32 value) { _totalLevel = value << 3; }
+ void reset();
+
+protected:
+ EnvelopeState _state;
+ uint32 _feedbackLevel;
+ uint32 _multiple;
+ uint32 _totalLevel;
+ uint8 _keyScale1;
+ uint8 _keyScale2;
+ uint32 _specifiedAttackRate;
+ uint32 _specifiedDecayRate;
+ uint32 _specifiedSustainRate;
+ uint32 _specifiedReleaseRate;
+ uint32 _tickCount;
+ uint32 _sustainLevel;
+
+ uint32 _frequency;
+ uint8 _kcode;
+ uint32 _phase;
+ uint32 _phaseIncrement;
+ int32 *_detn;
+
+ const uint8 *_rateTbl;
+ const uint8 *_rshiftTbl;
+ const uint8 *_adTbl;
+ const uint32 *_fTbl;
+ const uint32 *_sinTbl;
+ const int32 *_tLvlTbl;
+ const int32 *_detnTbl;
+
+ const double _tickLength;
+ double _tick;
+ int32 _currentLevel;
+
+ struct EvpState {
+ uint8 rate;
+ uint8 shift;
+ } fs_a, fs_d, fs_s, fs_r;
+};
+
+TownsPC98_OpnOperator::TownsPC98_OpnOperator(double rate, uint8 id,
+ const uint8 *rateTable, const uint8 *shiftTable, const uint8 *attackDecayTable,
+ const uint32 *frqTable, const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable) :
+ _rateTbl(rateTable), _rshiftTbl(shiftTable), _adTbl(attackDecayTable), _fTbl(frqTable),
+ _sinTbl(sineTable), _tLvlTbl(tlevelOut), _detnTbl(detuneTable), _tickLength(rate * 65536.0),
+ _specifiedAttackRate(0), _specifiedDecayRate(0), _specifiedReleaseRate(0), _specifiedSustainRate(0),
+ _phase(0), _state(s_ready) {
+
+ reset();
+}
+
+void TownsPC98_OpnOperator::keyOn() {
+ _state = s_attacking;
+ _phase = 0;
+}
+
+void TownsPC98_OpnOperator::keyOff() {
+ if (_state != s_ready)
+ _state = s_releasing;
+}
+
+void TownsPC98_OpnOperator::frequency(int freq) {
+ uint8 block = (freq >> 11);
+ uint16 pos = (freq & 0x7ff);
+ uint8 c = pos >> 7;
+ _kcode = (block << 2) | ((c < 7) ? 0 : ((c > 8) ? 3 : c - 6 ));
+ _frequency = _fTbl[pos << 1] >> (7 - block);
+}
+
+void TownsPC98_OpnOperator::updatePhaseIncrement() {
+ _phaseIncrement = ((_frequency + _detn[_kcode]) * _multiple) >> 1;
+ uint8 keyscale = _kcode >> _keyScale1;
+ if (_keyScale2 != keyscale) {
+ _keyScale2 = keyscale;
+ recalculateRates();
+ }
+}
+
+void TownsPC98_OpnOperator::recalculateRates() {
+ int k = _keyScale2;
+ int r = _specifiedAttackRate ? (_specifiedAttackRate << 1) + 0x20 : 0;
+ fs_a.rate = ((r + k) < 94) ? _rateTbl[r + k] : 136;
+ fs_a.shift = ((r + k) < 94) ? _rshiftTbl[r + k] : 0;
+
+ r = _specifiedDecayRate ? (_specifiedDecayRate << 1) + 0x20 : 0;
+ fs_d.rate = _rateTbl[r + k];
+ fs_d.shift = _rshiftTbl[r + k];
+
+ r = _specifiedSustainRate ? (_specifiedSustainRate << 1) + 0x20 : 0;
+ fs_s.rate = _rateTbl[r + k];
+ fs_s.shift = _rshiftTbl[r + k];
+
+ r = (_specifiedReleaseRate << 2) + 0x22;
+ fs_r.rate = _rateTbl[r + k];
+ fs_r.shift = _rshiftTbl[r + k];
+}
+
+void TownsPC98_OpnOperator::generateOutput(int phasebuf, int *feedbuf, int &out) {
+ if (_state == s_ready)
+ return;
+
+ _tick += _tickLength;
+ while (_tick > 0x30000) {
+ _tick -= 0x30000;
+ ++_tickCount;
+
+ int32 levelIncrement = 0;
+ uint32 targetTime = 0;
+ int32 targetLevel = 0;
+ EnvelopeState next_state = s_ready;
+
+ switch (_state) {
+ case s_ready:
+ return;
+ case s_attacking:
+ next_state = s_decaying;
+ targetTime = (1 << fs_a.shift) - 1;
+ targetLevel = 0;
+ levelIncrement = (~_currentLevel * _adTbl[fs_a.rate + ((_tickCount >> fs_a.shift) & 7)]) >> 4;
+ break;
+ case s_decaying:
+ targetTime = (1 << fs_d.shift) - 1;
+ next_state = s_sustaining;
+ targetLevel = _sustainLevel;
+ levelIncrement = _adTbl[fs_d.rate + ((_tickCount >> fs_d.shift) & 7)];
+ break;
+ case s_sustaining:
+ targetTime = (1 << fs_s.shift) - 1;
+ next_state = s_ready;
+ targetLevel = 1023;
+ levelIncrement = _adTbl[fs_s.rate + ((_tickCount >> fs_s.shift) & 7)];
+ break;
+ case s_releasing:
+ targetTime = (1 << fs_r.shift) - 1;
+ next_state = s_ready;
+ targetLevel = 1023;
+ levelIncrement = _adTbl[fs_r.rate + ((_tickCount >> fs_r.shift) & 7)];
+ break;
+ }
+
+ if (!(_tickCount & targetTime)) {
+ _currentLevel += levelIncrement;
+ if ((!targetLevel && _currentLevel <= targetLevel) || (targetLevel && _currentLevel >= targetLevel)) {
+ if (_state != s_decaying)
+ _currentLevel = targetLevel;
+ if (_state != s_sustaining)
+ _state = next_state;
+ }
+ }
+ }
+
+ uint32 lvlout = _totalLevel + (uint32) _currentLevel;
+
+ int outp = 0;
+ int *i = &outp, *o = &outp;
+ int phaseShift = 0;
+
+ if (feedbuf) {
+ o = &feedbuf[0];
+ i = &feedbuf[1];
+ phaseShift = _feedbackLevel ? ((feedbuf[0] + feedbuf[1]) << _feedbackLevel) : 0;
+ *o = *i;
+ } else {
+ phaseShift = phasebuf << 15;
+ }
+
+ if (lvlout < 832) {
+ uint32 index = (lvlout << 3) + _sinTbl[(((int32)((_phase & 0xffff0000)
+ + phaseShift)) >> 16) & 0x3ff];
+ *i = ((index < 6656) ? _tLvlTbl[index] : 0);
+ } else {
+ *i = 0;
+ }
+
+ _phase += _phaseIncrement;
+ out += *o;
+ if (out > 32767)
+ out = 32767;
+ if (out < -32767)
+ out = -32767;
+}
+
+void TownsPC98_OpnOperator::reset(){
+ keyOff();
+ _tick = 0;
+ _keyScale2 = -1;
+ _currentLevel = 1023;
+
+ frequency(0);
+ detune(0);
+ scaleRate(0);
+ multiple(0);
+ updatePhaseIncrement();
+ attackRate(0);
+ decayRate(0);
+ releaseRate(0);
+ sustainRate(0);
+ feedbackLevel(0);
+ totalLevel(127);
+}
+
+bool TownsPC98_OpnOperator::scaleRate(uint8 value) {
+ value = 3 - value;
+ if (_keyScale1 != value) {
+ _keyScale1 = value;
+ return true;
+ }
+
+ int k = _keyScale2;
+ int r = _specifiedAttackRate ? (_specifiedAttackRate << 1) + 0x20 : 0;
+ fs_a.rate = ((r + k) < 94) ? _rateTbl[r + k] : 136;
+ fs_a.shift = ((r + k) < 94) ? _rshiftTbl[r + k] : 0;
+ return false;
+}
+
+class TownsPC98_OpnDriver : public Audio::AudioStream {
+public:
+ enum OpnType {
+ OD_TOWNS,
+ OD_TYPE26,
+ OD_TYPE86
+ };
+
+ TownsPC98_OpnDriver(Audio::Mixer *mixer, OpnType type);
+ ~TownsPC98_OpnDriver();
+
+ bool init();
+ void loadData(uint8 *data, bool loadPaused = false);
+ void reset();
+ void fadeOut();
+
+ void pause() { _playing = false; }
+ void cont() { _playing = true; }
+
+ void callback();
+ void nextTick(int16 *buffer, uint32 bufferSize);
+
+ bool looping() { return _looping == _updateChannelsFlag ? true : false; }
+
+ // AudioStream interface
+ int inline readBuffer(int16 *buffer, const int numSamples);
+ bool isStereo() const { return true; }
+ bool endOfData() const { return false; }
+ int getRate() const { return _mixer->getOutputRate(); }
+
+protected:
+ void generateTables();
+
+ typedef enum channelState {
+ CHS_RECALCFREQ = 0x01,
+ CHS_KEYOFF = 0x02,
+ CHS_PITCHWHEELOFF = 0x08,
+ CHS_ALL_BUT_EOT = 0x0f,
+ CHS_EOT = 0x80
+ } ChannelState;
+
+ struct TwnChannel {
+ uint8 ticksLeft;
+ uint8 unk1, unk2, unk3;
+ uint8 algorithm;
+ uint8 instrID;
+ uint8 totalLevel;
+ uint8 frqBlockMSB;
+ int8 frqLSB;
+ uint8 keyOffTime;
+ bool protect;
+ uint8 *dataPtr;
+ uint8 unk15, unk16;
+ uint8 ptchWhlInitDelayLo;
+ uint8 ptchWhlInitDelayHi;
+ int16 ptchWhlModInitVal;
+ uint8 ptchWhlDuration;
+ uint8 ptchWhlCurDelay;
+ int16 ptchWhlModCurVal;
+ uint8 ptchWhlDurLeft;
+ uint16 frequency;
+ uint8 unk28, unk29;
+ uint8 regOffset;
+ uint8 flags;
+ uint8 chanNum;
+ uint8 keyNum;
+ uint8 part;
+ uint8 idFlag;
+
+ TownsPC98_OpnOperator *opr[4];
+ uint16 frqTemp;
+ bool enableLeft;
+ bool enableRight;
+ bool updateEnvelopes;
+ int feedbuf[3];
+ } **_channels;
+
+ void processEvents(TwnChannel *chan);
+ void processFrequency(TwnChannel *chan);
+ bool processControlEvent(TwnChannel *chan, uint8 cmd);
+
+ void setOutputLevel(TwnChannel *chan);
+ void setTempo(uint8 tempo);
+
+ void keyOn(TwnChannel *chan);
+ void keyOff(TwnChannel *chan);
+ void writeReg(TwnChannel *chan, uint8 regAdress, uint8 value);
+
+ void lock() { _mutex.lock(); }
+ void unlock() { _mutex.unlock(); }
+
+ bool control_f0_setPatch(TwnChannel *chan, uint8 para);
+ bool control_f1_presetOutputLevel(TwnChannel *chan, uint8 para);
+ bool control_f2_setKeyOffTime(TwnChannel *chan, uint8 para);
+ bool control_f3_setFreqLSB(TwnChannel *chan, uint8 para);
+ bool control_f4_setOutputLevel(TwnChannel *chan, uint8 para);
+ bool control_f5_setTempo(TwnChannel *chan, uint8 para);
+ bool control_f6_repeatSection(TwnChannel *chan, uint8 para);
+ bool control_f7_setupPitchWheel(TwnChannel *chan, uint8 para);
+ bool control_f8_togglePitchWheel(TwnChannel *chan, uint8 para);
+ bool control_f9_unk(TwnChannel *chan, uint8 para);
+ bool control_fa_writeReg(TwnChannel *chan, uint8 para);
+ bool control_fb_incOutLevel(TwnChannel *chan, uint8 para);
+ bool control_fc_decOutLevel(TwnChannel *chan, uint8 para);
+ bool control_fd_jump(TwnChannel *chan, uint8 para);
+ bool control_fe_unk(TwnChannel *chan, uint8 para);
+ bool control_ff_endOfTrack(TwnChannel *chan, uint8 para);
+
+ typedef bool (TownsPC98_OpnDriver::*ControlEventFunc)(TwnChannel * chan, uint8 para);
+
+ Audio::Mixer *_mixer;
+ TownsPC98_OpnOperator **_operators;
+ Common::Mutex _mutex;
+ Audio::SoundHandle _soundHandle;
+
+ const uint8 *_twnCarrier;
+ const uint8 *_twnFreqTable;
+ const uint8 *_twnFxCmdLen;
+ const uint8 *_twnLvlPresets;
+
+ uint8 *_oprRates;
+ uint8 *_oprRateshift;
+ uint8 *_oprAttackDecay;
+ uint32 *_oprFrq;
+ uint32 *_oprSinTbl;
+ int32 *_oprLevelOut;
+ int32 *_oprDetune;
+
+ const uint8 *_trackData;
+ const uint8 *_patches;
+ uint8 _cbCounter;
+ uint8 _updateChannelsFlag;
+ uint8 _finishedChannelsFlag;
+ uint16 _tempo;
+ bool _playing;
+ bool _fading;
+ uint8 _looping;
+
+ bool _updateEnvelopes;
+
+ int32 _samplesTillCallback;
+ int32 _samplesTillCallbackRemainder;
+ int32 _samplesPerCallback;
+ int32 _samplesPerCallbackRemainder;
+
+ const int _numChan;
+ const int _numSSG;
+ const bool _hasADPCM;
+ const bool _hasStereo;
+
+ double _baserate;
+ static const uint8 _drvTables[];
+ static const uint32 _adtStat[];
+};
+
+TownsPC98_OpnDriver::TownsPC98_OpnDriver(Audio::Mixer *mixer, OpnType type) :
+ _mixer(mixer), _trackData(0), _playing(false), _fading(false), _channels(0),
+ _operators(0), _looping(0), _twnCarrier(_drvTables + 76), _twnFreqTable(_drvTables + 84),
+ _twnFxCmdLen(_drvTables + 36), _twnLvlPresets(_drvTables + (type == OD_TOWNS ? 52 : 220)) ,
+ _oprRates(0), _oprRateshift(0), _oprAttackDecay(0), _oprFrq(0), _oprSinTbl(0), _oprLevelOut(0),
+ _oprDetune(0), _cbCounter(4), _updateChannelsFlag(type == OD_TYPE26 ? 0x07 : 0x3F),
+ _finishedChannelsFlag(0), _samplesTillCallback(0), _samplesTillCallbackRemainder(0),
+ _numSSG(type == OD_TOWNS ? 0 : 3), _hasADPCM(type == OD_TYPE86 ? true : false),
+ _numChan(type == OD_TYPE26 ? 3 : 6), _hasStereo(type == OD_TYPE26 ? false : true) {
+ setTempo(84);
+ _baserate = (3579545.0 / (double)getRate()) / 144.0;
+}
+
+TownsPC98_OpnDriver::~TownsPC98_OpnDriver() {
+ _mixer->stopHandle(_soundHandle);
+
+ if (_operators) {
+ for (int i = 0; i < (_numChan << 2); i++)
+ delete _operators[i];
+ delete [] _operators;
+ }
+
+ if (_channels) {
+ for (int i = 0; i < _numChan; i++)
+ delete _channels[i];
+ delete [] _channels;
+ }
+
+ delete [] _oprRates;
+ delete [] _oprRateshift;
+ delete [] _oprFrq;
+ delete [] _oprAttackDecay;
+ delete [] _oprSinTbl;
+ delete [] _oprLevelOut;
+ delete [] _oprDetune;
+}
+
+bool TownsPC98_OpnDriver::init() {
+ generateTables();
+
+ if (_operators) {
+ for (int i = 0; i < (_numChan << 2); i++) {
+ if (_operators[i]) {
+ delete _operators[i];
+ }
+ }
+ delete [] _operators;
+ }
+
+ _operators = new TownsPC98_OpnOperator*[(_numChan << 2)];
+ for (int i = 0; i < (_numChan << 2); i++)
+ _operators[i] = new TownsPC98_OpnOperator(_baserate, i & 3, _oprRates,
+ _oprRateshift, _oprAttackDecay, _oprFrq, _oprSinTbl, _oprLevelOut, _oprDetune);
+
+ if (_channels) {
+ for (int i = 0; i < _numChan; i++) {
+ if (_channels[i])
+ delete _channels[i];
+ }
+ delete [] _channels;
+ }
+ _channels = new TwnChannel*[_numChan];
+ for (int i = 0; i < _numChan; i++) {
+ _channels[i] = new TwnChannel;
+ for (int ii = 0; ii < 4; ii++) {
+ _channels[i]->opr[ii] = _operators[(i << 2) + ii];
+ _channels[i]->updateEnvelopes = false;
+ }
+ }
+
+ for (int i = 0; i < _numChan; i++) {
+ int ix = i * 6;
+ memset(_channels[i], 0, sizeof(TwnChannel));
+ _channels[i]->regOffset = _drvTables[ix];
+ _channels[i]->flags = _drvTables[ix + 1];
+ _channels[i]->chanNum = _drvTables[ix + 2];
+ _channels[i]->keyNum = _drvTables[ix + 3];
+ _channels[i]->part = _drvTables[ix + 4];
+ _channels[i]->idFlag = _drvTables[ix + 5];
+ }
+
+ _mixer->playInputStream(Audio::Mixer::kMusicSoundType,
+ &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true);
+
+ return true;
+}
+
+int inline TownsPC98_OpnDriver::readBuffer(int16 *buffer, const int numSamples) {
+ memset(buffer, 0, sizeof(int16) * numSamples);
+ int32 samplesLeft = numSamples >> 1;
+ while (samplesLeft) {
+ if (!_samplesTillCallback) {
+ callback();
+ _samplesTillCallback = _samplesPerCallback;
+ _samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
+ if (_samplesTillCallbackRemainder >= _tempo) {
+ _samplesTillCallback++;
+ _samplesTillCallbackRemainder -= _tempo;
+ }
+ }
+
+ int32 render = MIN(samplesLeft, _samplesTillCallback);
+ samplesLeft -= render;
+ _samplesTillCallback -= render;
+
+ nextTick(buffer, render);
+
+ for (int i = 0; i < render; ++i) {
+ buffer[i << 1] <<= 2;
+ buffer[(i << 1) + 1] <<= 2;
+ }
+
+ buffer += (render << 1);
+ }
+
+ return numSamples;
+}
+
+void TownsPC98_OpnDriver::loadData(uint8 * data, bool loadPaused) {
+
+ lock();
+ _trackData = data;
+
+ reset();
+
+ for (uint8 i = 0; i < _numChan; i++) {
+ uint8 tmp1 = _channels[i]->regOffset;
+ uint8 tmp2 = _channels[i]->flags;
+ uint8 tmp3 = _channels[i]->chanNum;
+ uint8 tmp4 = _channels[i]->keyNum;
+ uint8 tmp5 = _channels[i]->part;
+ uint8 tmp6 = _channels[i]->idFlag;
+ memset(_channels[i], 0, sizeof(TwnChannel));
+ _channels[i]->regOffset = tmp1;
+ _channels[i]->flags = tmp2;
+ _channels[i]->chanNum = tmp3;
+ _channels[i]->keyNum = tmp4;
+ _channels[i]->part = tmp5;
+ _channels[i]->idFlag = tmp6;
+ _channels[i]->enableLeft = _channels[i]->enableRight = true;
+ for (int ii = 0; ii < 4; ii++)
+ _channels[i]->opr[ii] = _operators[(i << 2) + ii];
+ }
+
+ const uint8 *src_a = (const uint8*) data;
+ uint8 bl = 0;
+
+ for (uint8 i = 0; i < _numChan; i++) {
+ _channels[i]->flags = (_channels[i]->flags & ~CHS_EOT) | CHS_ALL_BUT_EOT;
+ _channels[i]->ticksLeft = 1;
+ _channels[i]->dataPtr = data + READ_LE_UINT16(src_a);
+ src_a += 2;
+ _channels[i]->totalLevel = 0x7F;
+ if (bl > 2)
+ bl -= 3;
+ _channels[i]->regOffset = bl++;
+
+ uint8 * src_b = _channels[i]->dataPtr;
+ int loop = 1;
+ uint8 cmd = 0;
+ while (loop) {
+ if (loop == 1) {
+ cmd = *src_b++;
+ if (cmd < 0xf0) {
+ src_b++;
+ loop = 1;
+ } else {
+ if (cmd == 0xff) {
+ loop = *src_b ? 2 : 0;
+ if (READ_LE_UINT16(src_b))
+ _looping |= _channels[i]->idFlag;
+ } else if (cmd == 0xf6) {
+ loop = 3;
+ } else {
+ loop = 2;
+ }
+ }
+ } else if (loop == 2) {
+ src_b += _twnFxCmdLen[cmd - 240];
+ loop = 1;
+ } else if (loop == 3) {
+ src_b[0] = src_b[1];
+ src_b += 4;
+ loop = 1;
+ }
+ }
+ }
+
+ for (int i = 0; i < _numSSG; i++) {
+ // TODO
+ //_channels[i]->flags = (_channels[i]->flags & ~CHS_EOT) | CHS_ALL_BUT_EOT;
+ //_channels[i]->ticksLeft = 1;
+ //_channels[i]->dataPtr = data + READ_LE_UINT16(src_a);
+ uint8 *tmp = data + READ_LE_UINT16(src_a);
+ src_a += 2;
+ }
+
+ if (_hasADPCM) {
+ // TODO
+ src_a += 2;
+ }
+
+ _patches = src_a + 4;
+ _cbCounter = 4;
+ _finishedChannelsFlag = 0;
+
+ // AH 0x17
+ unlock();
+ _playing = (loadPaused ? false : true);
+}
+
+void TownsPC98_OpnDriver::reset() {
+ for (int i = 0; i < (_numChan << 2); i++)
+ _operators[i]->reset();
+
+ _playing = false;
+ _looping = 0;
+}
+
+void TownsPC98_OpnDriver::fadeOut() {
+ if (!_playing)
+ return;
+
+ _fading = true;
+
+ for (int i = 0; i < 20; i++) {
+
+ /// TODO ///
+ // twnFade();
+ //waitTicks(s);
+
+ }
+
+ _fading = false;
+
+ //haltTrack();
+}
+
+void TownsPC98_OpnDriver::callback() {
+ if (!_playing || --_cbCounter)
+ return;
+
+ _cbCounter = 4;
+
+ lock();
+ for (int i = 0; i < _numChan; i++) {
+ if (_updateChannelsFlag & _channels[i]->idFlag) {
+ processEvents(_channels[i]);
+ processFrequency(_channels[i]);
+ }
+ }
+ unlock();
+
+ if (_finishedChannelsFlag == _updateChannelsFlag)
+ reset();
+}
+
+void TownsPC98_OpnDriver::nextTick(int16 *buffer, uint32 bufferSize) {
+ if (!_playing)
+ return;
+
+ for (int i = 0; i < _numChan ; i++) {
+ if (_channels[i]->updateEnvelopes) {
+ _channels[i]->updateEnvelopes = false;
+ for (int ii = 0; ii < 4 ; ii++)
+ _channels[i]->opr[ii]->updatePhaseIncrement();
+ }
+
+ int phbuf1, phbuf2, output;
+ int *feed = _channels[i]->feedbuf;
+ int *del = &feed[2];
+
+ for (int ii = 0; ii < bufferSize ; ii++) {
+ phbuf1 = phbuf2 = output = 0;
+
+ switch (_channels[i]->algorithm) {
+ case 0:
+ _channels[i]->opr[0]->generateOutput(0, feed, phbuf1);
+ _channels[i]->opr[2]->generateOutput(*del, 0, phbuf2);
+ *del = 0;
+ _channels[i]->opr[1]->generateOutput(phbuf1, 0, *del);
+ _channels[i]->opr[3]->generateOutput(phbuf2, 0, output);
+ break;
+ case 1:
+ _channels[i]->opr[0]->generateOutput(0, feed, phbuf1);
+ _channels[i]->opr[2]->generateOutput(*del, 0, phbuf2);
+ _channels[i]->opr[1]->generateOutput(0, 0, phbuf1);
+ _channels[i]->opr[3]->generateOutput(phbuf2, 0, output);
+ *del = phbuf1;
+ break;
+ case 2:
+ _channels[i]->opr[0]->generateOutput(0, feed, phbuf2);
+ _channels[i]->opr[2]->generateOutput(*del, 0, phbuf2);
+ _channels[i]->opr[1]->generateOutput(0, 0, phbuf1);
+ _channels[i]->opr[3]->generateOutput(phbuf2, 0, output);
+ *del = phbuf1;
+ break;
+ case 3:
+ _channels[i]->opr[0]->generateOutput(0, feed, phbuf2);
+ _channels[i]->opr[2]->generateOutput(0, 0, *del);
+ _channels[i]->opr[1]->generateOutput(phbuf2, 0, phbuf1);
+ _channels[i]->opr[3]->generateOutput(*del, 0, output);
+ *del = phbuf1;
+ break;
+ case 4:
+ _channels[i]->opr[0]->generateOutput(0, feed, phbuf1);
+ _channels[i]->opr[2]->generateOutput(0, 0, phbuf2);
+ _channels[i]->opr[1]->generateOutput(phbuf1, 0, output);
+ _channels[i]->opr[3]->generateOutput(phbuf2, 0, output);
+ *del = 0;
+ break;
+ case 5:
+ *del = feed[1];
+ _channels[i]->opr[0]->generateOutput(0, feed, phbuf1);
+ _channels[i]->opr[2]->generateOutput(*del, 0, output);
+ _channels[i]->opr[1]->generateOutput(*del, 0, output);
+ _channels[i]->opr[3]->generateOutput(*del, 0, output);
+ break;
+ case 6:
+ _channels[i]->opr[0]->generateOutput(0, feed, phbuf1);
+ _channels[i]->opr[2]->generateOutput(0, 0, output);
+ _channels[i]->opr[1]->generateOutput(phbuf1, 0, output);
+ _channels[i]->opr[3]->generateOutput(0, 0, output);
+ *del = 0;
+ break;
+ case 7:
+ _channels[i]->opr[0]->generateOutput(0, feed, output);
+ _channels[i]->opr[2]->generateOutput(0, 0, output);
+ _channels[i]->opr[1]->generateOutput(0, 0, output);
+ _channels[i]->opr[3]->generateOutput(0, 0, output);
+ *del = 0;
+ break;
+ };
+
+ if (_channels[i]->enableLeft) {
+ int l = output + buffer[ii * 2];
+ if (l > 32767)
+ l = 32767;
+ if (l < -32767)
+ l = -32767;
+ buffer[ii * 2] = (int16) l;
+ }
+
+ if (_channels[i]->enableRight) {
+ int r = output + buffer[ii * 2 + 1];
+ if (r > 32767)
+ r = 32767;
+ if (r < -32767)
+ r = -32767;
+ buffer[ii * 2 + 1] = (int16) r;
+ }
+ }
+ }
+}
+
+void TownsPC98_OpnDriver::generateTables() {
+ delete [] _oprRates;
+ _oprRates = new uint8[128];
+ memset(_oprRates, 0x90, 32);
+ uint8 * dst = (uint8*) _oprRates + 32;
+ for (int i = 0; i < 48; i += 4)
+ WRITE_BE_UINT32(dst + i, 0x00081018);
+ dst += 48;
+ for (uint8 i = 0; i < 16; i ++) {
+ uint8 v = (i < 12) ? i : 12;
+ *dst++ = ((4 + v) << 3);
+ }
+ memset(dst, 0x80, 32);
+
+ delete [] _oprRateshift;
+ _oprRateshift = new uint8[128];
+ memset(_oprRateshift, 0, 128);
+ dst = (uint8*) _oprRateshift + 32;
+ for (int i = 11; i; i--) {
+ memset(dst, i, 4);
+ dst += 4;
+ }
+
+ delete [] _oprFrq;
+ _oprFrq = new uint32[0x1000];
+ for (uint32 i = 0; i < 0x1000; i++)
+ _oprFrq[i] = (uint32)(_baserate * (double)(i << 11));
+
+ delete [] _oprAttackDecay;
+ _oprAttackDecay = new uint8[152];
+ memset(_oprAttackDecay, 0, 152);
+ for (int i = 0; i < 36; i++)
+ WRITE_BE_UINT32(_oprAttackDecay + (i << 2), _adtStat[i]);
+
+ delete [] _oprSinTbl;
+ _oprSinTbl = new uint32[1024];
+ for (int i = 0; i < 1024; i++) {
+ double val = sin((double) (((i << 1) + 1) * M_PI / 1024.0));
+ double d_dcb = log(1.0 / (double)ABS(val)) / log(2.0) * 256.0;
+ int32 i_dcb = (int32)(2.0 * d_dcb);
+ i_dcb = (i_dcb & 1) ? (i_dcb >> 1) + 1 : (i_dcb >> 1);
+ _oprSinTbl[i] = (i_dcb << 1) + (val >= 0.0 ? 0 : 1);
+ }
+
+ delete [] _oprLevelOut;
+ _oprLevelOut = new int32[0x1a00];
+ for (int i = 0; i < 256; i++) {
+ double val = floor(65536.0 / pow(2.0, 0.00390625 * (double)(1 + i)));
+ int32 val_int = ((int32) val) >> 4;
+ _oprLevelOut[i << 1] = (val_int & 1) ? ((val_int >> 1) + 1) << 2 : (val_int >> 1) << 2;
+ _oprLevelOut[(i << 1) + 1] = -_oprLevelOut[i << 1];
+ for (int ii = 1; ii < 13; ii++) {
+ _oprLevelOut[(i << 1) + (ii << 9)] = _oprLevelOut[i << 1] >> ii;
+ _oprLevelOut[(i << 1) + (ii << 9) + 1] = -_oprLevelOut[(i << 1) + (ii << 9)];
+ }
+ }
+
+ uint8 * dtt = new uint8[128];
+ memset(dtt, 0, 36);
+ memset(dtt + 36, 1, 8);
+ memcpy(dtt + 44, _drvTables + 144, 84);
+
+ delete [] _oprDetune;
+ _oprDetune = new int32[256];
+ for (int i = 0; i < 128; i++) {
+ double rate = ((double)dtt[i]) * 1024.0 * _baserate * (1<<16) / ((double)(1<<20));
+ _oprDetune[i] = (int32) ((double)dtt[i] * _baserate * 64.0);
+ _oprDetune[i + 128] = -_oprDetune[i];
+ }
+
+ delete [] dtt;
+}
+
+void TownsPC98_OpnDriver::processEvents(TwnChannel *chan) {
+ if (chan->flags & CHS_EOT)
+ return;
+
+ if (chan->protect == false && chan->ticksLeft == chan->keyOffTime)
+ keyOff(chan);
+
+ if (--chan->ticksLeft)
+ return;
+
+ if (chan->protect == false)
+ keyOff(chan);
+
+ uint8 cmd = 0;
+ bool loop = true;
+
+ while (loop) {
+ cmd = *chan->dataPtr++;
+ if (cmd < 0xf0)
+ loop = false;
+ else if (!processControlEvent(chan, cmd))
+ return;
+ }
+
+ uint8 para = *chan->dataPtr++;
+
+ if (cmd == 0x80) {
+ keyOff(chan);
+ chan->protect = false;
+ } else {
+ keyOn(chan);
+
+ if (chan->protect == false || cmd != chan->frqBlockMSB)
+ chan->flags |= CHS_RECALCFREQ;
+
+ chan->protect = (para & 0x80) ? true : false;
+ chan->frqBlockMSB = cmd;
+ }
+
+ chan->ticksLeft = para & 0x7f;
+}
+
+void TownsPC98_OpnDriver::processFrequency(TwnChannel *chan) {
+ if (chan->flags & CHS_RECALCFREQ) {
+ uint8 block = (chan->frqBlockMSB & 0x70) >> 1;
+ uint16 bfreq = ((uint16*)_twnFreqTable)[chan->frqBlockMSB & 0x0f];
+ chan->frequency = (bfreq + chan->frqLSB) | (block << 8);
+
+ writeReg(chan, (chan->regOffset + 0xa4), (chan->frequency >> 8));
+ writeReg(chan, (chan->regOffset + 0xa0), (chan->frequency & 0xff));
+
+ chan->ptchWhlCurDelay = chan->ptchWhlInitDelayHi;
+ if (chan->flags & CHS_KEYOFF) {
+ chan->ptchWhlModCurVal = chan->ptchWhlModInitVal;
+ chan->ptchWhlCurDelay += chan->ptchWhlInitDelayLo;
+ }
+
+ chan->ptchWhlDurLeft = (chan->ptchWhlDuration >> 1);
+ chan->flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ);
+ }
+
+ if (!(chan->flags & CHS_PITCHWHEELOFF)) {
+ if (--chan->ptchWhlCurDelay)
+ return;
+ chan->ptchWhlCurDelay = chan->ptchWhlInitDelayHi;
+ chan->frequency += chan->ptchWhlModCurVal;
+
+ writeReg(chan, (chan->regOffset + 0xa4), (chan->frequency >> 8));
+ writeReg(chan, (chan->regOffset + 0xa0), (chan->frequency & 0xff));
+
+ if(!--chan->ptchWhlDurLeft) {
+ chan->ptchWhlDurLeft = chan->ptchWhlDuration;
+ chan->ptchWhlModCurVal = -chan->ptchWhlModCurVal;
+ }
+ }
+}
+
+bool TownsPC98_OpnDriver::processControlEvent(TwnChannel *chan, uint8 cmd) {
+ #define Control(x) &TownsPC98_OpnDriver::control_##x
+ static const ControlEventFunc twnFxCommands[] = {
+ Control(f0_setPatch),
+ Control(f1_presetOutputLevel),
+ Control(f2_setKeyOffTime),
+ Control(f3_setFreqLSB),
+ Control(f4_setOutputLevel),
+ Control(f5_setTempo),
+ Control(f6_repeatSection),
+ Control(f7_setupPitchWheel),
+ Control(f8_togglePitchWheel),
+ Control(f9_unk),
+ Control(fa_writeReg),
+ Control(fb_incOutLevel),
+ Control(fc_decOutLevel),
+ Control(fd_jump),
+ Control(fe_unk),
+ Control(ff_endOfTrack)
+ };
+ #undef Control
+
+ uint8 para = *chan->dataPtr++;
+ return (this->*twnFxCommands[cmd & 0x0f])(chan, para);
+}
+
+void TownsPC98_OpnDriver::setOutputLevel(TwnChannel *chan) {
+ uint8 outopr = _twnCarrier[chan->algorithm];
+ uint8 reg = 0x40 + chan->regOffset;
+
+ for (int i = 0; i < 4; i++) {
+ if (outopr & 1)
+ writeReg(chan, reg, chan->totalLevel);
+ outopr >>= 1;
+ reg += 4;
+ }
+}
+
+void TownsPC98_OpnDriver::setTempo(uint8 tempo) {
+ _tempo = tempo;
+ _samplesPerCallback = getRate() / _tempo;
+ _samplesPerCallbackRemainder = getRate() % _tempo;
+}
+
+bool TownsPC98_OpnDriver::control_f0_setPatch(TwnChannel *chan, uint8 para) {
+ chan->instrID = para;
+ uint8 reg = chan->regOffset + 0x80;
+
+ for (int i = 0; i < 4; i++) {
+ // set release rate for each operator
+ writeReg(chan, reg, 0x0f);
+ reg += 4;
+ }
+
+ const uint8 *tptr = (uint8*) _patches + ((uint32)chan->instrID << 5);
+ reg = chan->regOffset + 0x30;
+
+ // write registers 0x30 to 0x8f
+ for (int i = 0; i < 6; i++) {
+ writeReg(chan, reg, tptr[0]);
+ reg += 4;
+ writeReg(chan, reg, tptr[2]);
+ reg += 4;
+ writeReg(chan, reg, tptr[1]);
+ reg += 4;
+ writeReg(chan, reg, tptr[3]);
+ reg += 4;
+ tptr += 4;
+ }
+
+ reg = chan->regOffset + 0xB0;
+ chan->algorithm = tptr[0] & 7;
+ // set feedback and algorithm
+ writeReg(chan, reg, tptr[0]);
+
+ setOutputLevel(chan);
+ return true;
+}
+
+bool TownsPC98_OpnDriver::control_f1_presetOutputLevel(TwnChannel *chan, uint8 para) {
+ if (_fading)
+ return true;
+
+ chan->totalLevel = _twnLvlPresets[para];
+ setOutputLevel(chan);
+ return true;
+}
+
+bool TownsPC98_OpnDriver::control_f2_setKeyOffTime(TwnChannel *chan, uint8 para) {
+ chan->keyOffTime = para;
+ return true;
+}
+
+bool TownsPC98_OpnDriver::control_f3_setFreqLSB(TwnChannel *chan, uint8 para) {
+ chan->frqLSB = (int8) para;
+ return true;
+}
+
+bool TownsPC98_OpnDriver::control_f4_setOutputLevel(TwnChannel *chan, uint8 para) {
+ if (_fading)
+ return true;
+
+ chan->totalLevel = para;
+ setOutputLevel(chan);
+ return true;
+}
+
+bool TownsPC98_OpnDriver::control_f5_setTempo(TwnChannel *chan, uint8 para) {
+ setTempo(para);
+ return true;
+}
+
+bool TownsPC98_OpnDriver::control_f6_repeatSection(TwnChannel *chan, uint8 para) {
+ chan->dataPtr--;
+ chan->dataPtr[0]--;
+
+ if (*chan->dataPtr) {
+ // repeat section until counter has reached zero
+ chan->dataPtr = (uint8*) _trackData + READ_LE_UINT16(chan->dataPtr + 2);
+ } else {
+ // reset counter, advance to next section
+ chan->dataPtr[0] = chan->dataPtr[1];
+ chan->dataPtr += 4;
+ }
+ return true;
+}
+
+bool TownsPC98_OpnDriver::control_f7_setupPitchWheel(TwnChannel *chan, uint8 para) {
+ chan->ptchWhlInitDelayLo = chan->dataPtr[0];
+ chan->ptchWhlInitDelayHi = para;
+ chan->ptchWhlModInitVal = (int16) READ_LE_UINT16(chan->dataPtr + 1);
+ chan->ptchWhlDuration = chan->dataPtr[3];
+ chan->dataPtr += 4;
+ chan->flags = (chan->flags & ~CHS_PITCHWHEELOFF) | CHS_KEYOFF | CHS_RECALCFREQ;
+ return true;
+}
+
+bool TownsPC98_OpnDriver::control_f8_togglePitchWheel(TwnChannel *chan, uint8 para) {
+ if (para == 0x10) {
+ if (*chan->dataPtr++) {
+ chan->flags = (chan->flags & ~CHS_PITCHWHEELOFF) | CHS_KEYOFF;
+ } else {
+ chan->flags |= CHS_PITCHWHEELOFF;
+ }
+ } else {
+ uint8 skipChannels = para / 36;
+ uint8 entry = para % 36;
+ TownsPC98_OpnDriver::TwnChannel *t = &chan[skipChannels];
+ ////// NOT IMPLEMENTED
+ //t->unnamedEntries[entry] = *chan->dataPtr++;
+ }
+ return true;
+}
+
+bool TownsPC98_OpnDriver::control_f9_unk(TwnChannel *chan, uint8 para) {
+ //chan->dataPtr += 5;
+ return true;
+}
+
+bool TownsPC98_OpnDriver::control_fa_writeReg(TwnChannel *chan, uint8 para) {
+ writeReg(chan, para, *chan->dataPtr++);
+ return true;
+}
+
+bool TownsPC98_OpnDriver::control_fb_incOutLevel(TwnChannel *chan, uint8 para) {
+ chan->dataPtr--;
+ if (_fading)
+ return true;
+
+ uint8 val = (chan->totalLevel + 3);
+ if (val > 0x7f)
+ val = 0x7f;
+
+ chan->totalLevel = val;
+ setOutputLevel(chan);
+ return true;
+}
+
+bool TownsPC98_OpnDriver::control_fc_decOutLevel(TwnChannel *chan, uint8 para) {
+ chan->dataPtr--;
+ if (_fading)
+ return true;
+
+ int8 val = (int8) (chan->totalLevel - 3);
+ if (val < 0)
+ val = 0;
+
+ chan->totalLevel = (uint8) val;
+ setOutputLevel(chan);
+ return true;
+}
+
+bool TownsPC98_OpnDriver::control_fd_jump(TwnChannel *chan, uint8 para) {
+ uint8 *tmp = (uint8*) _trackData + READ_LE_UINT16(chan->dataPtr - 1);
+ chan->dataPtr = (tmp[1] == 1) ? tmp : ++chan->dataPtr;
+ return true;
+}
+
+bool TownsPC98_OpnDriver::control_fe_unk(TwnChannel *chan, uint8 para) {
+ chan->dataPtr--;
+ return true;
+}
+
+bool TownsPC98_OpnDriver::control_ff_endOfTrack(TwnChannel *chan, uint8 para) {
+ uint16 val = READ_LE_UINT16(--chan->dataPtr);
+ if (val) {
+ // loop
+ chan->dataPtr = (uint8 *) _trackData + val;
+ return true;
+ } else {
+ // quit parsing for active channel
+ --chan->dataPtr;
+ chan->flags |= CHS_EOT;
+ _finishedChannelsFlag |= chan->idFlag;
+ keyOff(chan);
+ return false;
+ }
+}
+
+void TownsPC98_OpnDriver::keyOff(TwnChannel *chan) {
+ // all operators off
+ uint8 value = chan->keyNum & 0x0f;
+ uint8 regAdress = 0x28;
+ writeReg(chan, regAdress, value);
+ chan->flags |= CHS_KEYOFF;
+}
+
+void TownsPC98_OpnDriver::keyOn(TwnChannel *chan) {
+ // all operators on
+ uint8 value = chan->keyNum | 0xf0;
+ uint8 regAdress = 0x28;
+ writeReg(chan, regAdress, value);
+}
+
+void TownsPC98_OpnDriver::writeReg(TwnChannel *chan, uint8 regAdress, uint8 value) {
+ uint8 h = regAdress & 0xf0;
+ uint8 l = (regAdress & 0x0f);
+ static const uint8 opr[] = { 0, 2, 1, 3 };
+ uint8 o = opr[(l - chan->regOffset) >> 2];
+
+ switch (h) {
+ case 0x00:
+ // ssg
+ warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+ break;
+ case 0x10:
+ // adpcm
+ warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+ break;
+ case 0x20:
+ if (l == 8) {
+ // Key on/off
+ for (int i = 0; i < 4; i++) {
+ if ((value >> (4 + i)) & 1)
+ chan->opr[i]->keyOn();
+ else
+ chan->opr[i]->keyOff();
+ }
+ } else if (l == 2) {
+ // LFO
+ warning("TownsPC98_OpnDriver: TRYING TO USE LFO (NOT SUPPORTED)");
+ } else if (l == 7) {
+ // Timers; Ch 3/6 special mode
+ warning("TownsPC98_OpnDriver: TRYING TO USE CH 3/6 SPECIAL MODE (NOT SUPPORTED)");
+ } else if (l == 4 || l == 5) {
+ // Timer A
+ warning("TownsPC98_OpnDriver: TRYING TO USE TIMER_A (NOT SUPPORTED)");
+ } else if (l == 6) {
+ // Timer B
+ warning("TownsPC98_OpnDriver: TRYING TO USE TIMER_B (NOT SUPPORTED)");
+ } else if (l == 10 || l == 11) {
+ // DAC
+ warning("TownsPC98_OpnDriver: TRYING TO USE DAC (NOT SUPPORTED)");
+ }
+ break;
+
+ case 0x30:
+ // detune, multiple
+ chan->opr[o]->detune((value >> 4) & 7);
+ chan->opr[o]->multiple(value & 0x0f);
+ chan->updateEnvelopes = true;
+ break;
+
+ case 0x40:
+ // total level
+ chan->opr[o]->totalLevel(value & 0x7f);
+ break;
+
+ case 0x50:
+ // rate scaling, attack rate
+ chan->opr[o]->attackRate(value & 0x1f);
+ if (chan->opr[o]->scaleRate(value >> 6))
+ chan->updateEnvelopes = true;
+ break;
+
+ case 0x60:
+ // first decay rate, amplitude modulation
+ chan->opr[o]->decayRate(value & 0x1f);
+ if (value & 0x80)
+ warning("TownsPC98_OpnDriver: TRYING TO USE AMP MODULATION (NOT SUPPORTED)");
+
+ break;
+
+ case 0x70:
+ // secondary decay rate
+ chan->opr[o]->sustainRate(value & 0x1f);
+ break;
+
+ case 0x80:
+ // secondary amplitude, release rate;
+ chan->opr[o]->sustainLevel(value >> 4);
+ chan->opr[o]->releaseRate(value & 0x0f);
+ break;
+
+ case 0x90:
+ // ssg
+ warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+ break;
+
+ case 0xa0:
+ // frequency
+ l -= chan->regOffset;
+ if (l == 0) {
+ chan->frqTemp = (chan->frqTemp & 0xff00) | value;
+ chan->updateEnvelopes = true;
+ for (int i = 0; i < 4; i++)
+ chan->opr[i]->frequency(chan->frqTemp);
+ } else if (l == 4) {
+ chan->frqTemp = (chan->frqTemp & 0xff) | (value << 8);
+ } else if (l == 8) {
+ // Ch 3/6 special mode frq
+ warning("TownsPC98_OpnDriver: TRYING TO USE CH 3/6 SPECIAL MODE FREQ (NOT SUPPORTED)");
+ } else if (l == 12) {
+ // Ch 3/6 special mode frq
+ warning("TownsPC98_OpnDriver: TRYING TO USE CH 3/6 SPECIAL MODE FREQ (NOT SUPPORTED)");
+ }
+ break;
+
+ case 0xb0:
+ l -= chan->regOffset;
+ if (l == 0) {
+ // feedback, algorithm
+ chan->opr[0]->feedbackLevel((value >> 3) & 7);
+ chan->opr[1]->feedbackLevel(0);
+ chan->opr[2]->feedbackLevel(0);
+ chan->opr[3]->feedbackLevel(0);
+ } else if (l == 4) {
+ // stereo, LFO sensitivity
+ chan->enableLeft = value & 0x80 ? true : false;
+ chan->enableRight = value & 0x40 ? true : false;
+ uint8 ams = (value & 0x3F) >> 3;
+ if (ams)
+ warning("TownsPC98_OpnDriver: TRYING TO USE AMP MODULATION SENSITIVITY (NOT SUPPORTED)");
+ uint8 fms = value & 3;
+ if (fms)
+ warning("TownsPC98_OpnDriver: TRYING TO USE FREQ MODULATION SENSITIVITY (NOT SUPPORTED)");
+ }
+ break;
+
+ default:
+ warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress);
+ break;
+ }
+}
+
+const uint8 TownsPC98_OpnDriver::_drvTables[] = {
+ // channel presets
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x80, 0x01, 0x01, 0x00, 0x02,
+ 0x02, 0x80, 0x02, 0x02, 0x00, 0x04,
+ 0x00, 0x80, 0x03, 0x04, 0x01, 0x08,
+ 0x01, 0x80, 0x04, 0x05, 0x01, 0x10,
+ 0x02, 0x80, 0x05, 0x06, 0x01, 0x20,
+
+ // control event size
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x05,
+ 0x02, 0x06, 0x02, 0x00, 0x00, 0x02, 0x00, 0x02,
+
+ // fmt level presets
+ 0x54, 0x50, 0x4C, 0x48, 0x44, 0x40, 0x3C, 0x38,
+ 0x34, 0x30, 0x2C, 0x28, 0x24, 0x20, 0x1C, 0x18,
+ 0x14, 0x10, 0x0C, 0x08, 0x04, 0x90, 0x90, 0x90,
+
+ // carriers
+ 0x08, 0x08, 0x08, 0x08, 0x0C, 0x0E, 0x0E, 0x0F,
+
+ // frequencies
+ 0x6A, 0x02, 0x8F, 0x02, 0xB6, 0x02, 0xDF, 0x02,
+ 0x0B, 0x03, 0x39, 0x03, 0x6A, 0x03, 0x9E, 0x03,
+ 0xD5, 0x03, 0x10, 0x04, 0x4E, 0x04, 0x8F, 0x04,
+ 0x00, 0x00, 0x00, 0x00,
+
+ // unused
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+
+ // detune
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03,
+ 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
+ 0x08, 0x08, 0x08, 0x08, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03,
+ 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07,
+ 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x10, 0x10, 0x10, 0x10, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05,
+ 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a,
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x13, 0x14,
+ 0x16, 0x16, 0x16, 0x16,
+
+ // pc98 level presets
+ 0x40, 0x3B, 0x38, 0x34, 0x30, 0x2A, 0x28, 0x25,
+ 0x22, 0x20, 0x1D, 0x1A, 0x18, 0x15, 0x12, 0x10,
+ 0x0D, 0x0A, 0x08, 0x05, 0x02, 0x90, 0x90, 0x90
+};
+
+const uint32 TownsPC98_OpnDriver::_adtStat[] = {
+ 0x00010001, 0x00010001, 0x00010001, 0x01010001,
+ 0x00010101, 0x00010101, 0x00010101, 0x01010101,
+ 0x01010101, 0x01010101, 0x01010102, 0x01010102,
+ 0x01020102, 0x01020102, 0x01020202, 0x01020202,
+ 0x02020202, 0x02020202, 0x02020204, 0x02020204,
+ 0x02040204, 0x02040204, 0x02040404, 0x02040404,
+ 0x04040404, 0x04040404, 0x04040408, 0x04040408,
+ 0x04080408, 0x04080408, 0x04080808, 0x04080808,
+ 0x08080808, 0x08080808, 0x10101010, 0x10101010
+};
+
SoundTowns::SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer)
: Sound(vm, mixer), _lastTrack(-1), _currentSFX(0), _sfxFileData(0),
_sfxFileIndex((uint)-1), _sfxWDTable(0), _sfxBTTable(0), _parser(0) {
- _driver = new SoundTowns_EuphonyDriver(_mixer);
+ _driver = new Towns_EuphonyDriver(_mixer);
int ret = open();
if (ret != MERR_ALREADY_OPEN && ret != 0)
error("couldn't open midi driver");
@@ -1304,7 +2636,7 @@ void SoundTowns::playEuphonyTrack(uint32 offset, int loop) {
Common::StackLock lock(_mutex);
if (!_parser) {
- _parser = new MidiParser_EuD(_driver->queue());
+ _parser = new Towns_EuphonyParser(_driver->queue());
_parser->setMidiDriver(this);
_parser->setTimerRate(getBaseTempo());
}
@@ -1356,22 +2688,73 @@ float SoundTowns::semitoneAndSampleRate_to_sampleStep(int8 semiTone, int8 semiTo
return (float) sampleRate * 10.0f * rateshift / outputRate;
}
+SoundPC98::SoundPC98(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
+ Sound(vm, mixer), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0) {
+}
+
+SoundPC98::~SoundPC98() {
+ delete[] _musicTrackData;
+ delete[] _sfxTrackData;
+ delete _driver;
+}
+
+bool SoundPC98::init() {
+ _driver = new TownsPC98_OpnDriver(_mixer, TownsPC98_OpnDriver::OD_TYPE26);
+ _sfxTrackData = _vm->resource()->fileData("se.dat", 0);
+ if (!_sfxTrackData)
+ return false;
+ return _driver->init();
+}
+
+void SoundPC98::playTrack(uint8 track) {
+ if (track == _lastTrack && _musicEnabled)
+ return;
+
+ haltTrack();
+
+ char musicfile[13];
+ sprintf(musicfile, fileListEntry(0), track);
+ delete[] _musicTrackData;
+ // This is just for testing purposes atm since we haven't found a way
+ // to determine the correct file yet
+ _musicTrackData = _vm->resource()->fileData("kyram40.dat"/*musicfile*/, 0);
+ if (_musicEnabled)
+ _driver->loadData(_musicTrackData);
+
+ _lastTrack = track;
+}
+
+void SoundPC98::haltTrack() {
+ _lastTrack = -1;
+ AudioCD.stop();
+ AudioCD.updateCD();
+ _driver->reset();
+}
+
+void SoundPC98::beginFadeOut() {
+ _driver->fadeOut();
+ haltTrack();
+}
+
+void SoundPC98::playSoundEffect(uint8) {
+ /// TODO ///
+}
+
+
// KYRA 2
-SoundTowns_v2::SoundTowns_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer)
- : Sound(vm, mixer), _lastTrack(-1), _currentSFX(0), /*_driver(0),*/
- _twnTrackData(0) {
+SoundTownsPC98_v2::SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
+ Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _lastTrack(-1), _driver(0) {
}
-SoundTowns_v2::~SoundTowns_v2() {
- /*if (_driver)
- delete _driver;*/
- if (_twnTrackData)
- delete[] _twnTrackData;
+SoundTownsPC98_v2::~SoundTownsPC98_v2() {
+ delete[] _musicTrackData;
+ delete _driver;
}
-bool SoundTowns_v2::init() {
- //_driver = new SoundTowns_v2_TwnDriver(_mixer);
+bool SoundTownsPC98_v2::init() {
+ _driver = new TownsPC98_OpnDriver(_mixer, _vm->gameFlags().platform == Common::kPlatformPC98 ?
+ TownsPC98_OpnDriver::OD_TYPE86 : TownsPC98_OpnDriver::OD_TOWNS);
_vm->checkCD();
// FIXME: While checking for 'track1.XXX(X)' looks like
// a good idea, we should definitely not be doing this
@@ -1384,55 +2767,60 @@ bool SoundTowns_v2::init() {
(Common::File::exists("track1.mp3") || Common::File::exists("track1.ogg") ||
Common::File::exists("track1.flac") || Common::File::exists("track1.fla")))
_musicEnabled = 2;
- return true;//_driver->init();
+ return _driver->init();
}
-void SoundTowns_v2::process() {
+void SoundTownsPC98_v2::process() {
AudioCD.updateCD();
}
-void SoundTowns_v2::playTrack(uint8 track) {
+void SoundTownsPC98_v2::playTrack(uint8 track) {
if (track == _lastTrack && _musicEnabled)
return;
const uint16 * const cdaTracks = (const uint16 * const) cdaData();
int trackNum = -1;
- for (int i = 0; i < cdaTrackNum(); i++) {
- if (track == (uint8) READ_LE_UINT16(&cdaTracks[i * 2])) {
- trackNum = (int) READ_LE_UINT16(&cdaTracks[i * 2 + 1]) - 1;
- break;
+ if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+ for (int i = 0; i < cdaTrackNum(); i++) {
+ if (track == (uint8) READ_LE_UINT16(&cdaTracks[i * 2])) {
+ trackNum = (int) READ_LE_UINT16(&cdaTracks[i * 2 + 1]) - 1;
+ break;
+ }
}
}
haltTrack();
- // TODO: figure out when to loop and when not for CD Audio
- bool loop = false;
+ char musicfile[13];
+ sprintf(musicfile, fileListEntry(0), track);
+ delete[] _musicTrackData;
+ _musicTrackData = _vm->resource()->fileData(musicfile, 0);
+ _driver->loadData(_musicTrackData, true);
if (_musicEnabled == 2 && trackNum != -1) {
- AudioCD.play(trackNum+1, loop ? -1 : 1, 0, 0);
+ AudioCD.play(trackNum+1, _driver->looping() ? -1 : 1, 0, 0);
AudioCD.updateCD();
} else if (_musicEnabled) {
- char musicfile[13];
- sprintf(musicfile, fileListEntry(0), track);
- if (_twnTrackData)
- delete[] _twnTrackData;
- _twnTrackData = _vm->resource()->fileData(musicfile, 0);
- //_driver->loadData(_twnTrackData);
+ _driver->cont();
}
_lastTrack = track;
}
-void SoundTowns_v2::haltTrack() {
+void SoundTownsPC98_v2::haltTrack() {
_lastTrack = -1;
AudioCD.stop();
AudioCD.updateCD();
- //_driver->reset();
+ _driver->reset();
}
-int32 SoundTowns_v2::voicePlay(const char *file, bool) {
+void SoundTownsPC98_v2::beginFadeOut() {
+ _driver->fadeOut();
+ haltTrack();
+}
+
+int32 SoundTownsPC98_v2::voicePlay(const char *file, bool) {
static const uint16 rates[] = { 0x10E1, 0x0CA9, 0x0870, 0x0654, 0x0438, 0x032A, 0x021C, 0x0194 };
int h = 0;
@@ -1443,7 +2831,7 @@ int32 SoundTowns_v2::voicePlay(const char *file, bool) {
return 0;
}
- char filename [13];
+ char filename[13];
sprintf(filename, "%s.PCM", file);
uint8 * data = _vm->resource()->fileData(filename, 0);
@@ -1500,11 +2888,6 @@ int32 SoundTowns_v2::voicePlay(const char *file, bool) {
return 1;
}
-void SoundTowns_v2::beginFadeOut() {
- //_driver->fadeOut();
- haltTrack();
-}
-
} // end of namespace Kyra
#undef EUPHONY_FADEOUT_TICKS
diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp
index abdf115c1e..5d094fa13f 100644
--- a/engines/kyra/staticres.cpp
+++ b/engines/kyra/staticres.cpp
@@ -1034,6 +1034,9 @@ void KyraEngine_LoK::initStaticResource() {
}
// audio data tables
+ static const char *tIntro98[] = { "intro%d.dat" };
+ static const char *tIngame98[] = { "kyram%d.dat" };
+
static const AudioDataStruct soundData_PC[] = {
{ _soundFilesIntro, _soundFilesIntroSize, 0, 0 },
{ _soundFiles, _soundFilesSize, 0, 0 },
@@ -1045,7 +1048,20 @@ void KyraEngine_LoK::initStaticResource() {
{ _soundFiles, _soundFilesSize, _cdaTrackTable, _cdaTrackTableSize },
{ 0, 0, 0, 0}
};
- _soundData = (_flags.platform == Common::kPlatformPC) ? soundData_PC : soundData_TOWNS;
+
+ static const AudioDataStruct soundData_PC98[] = {
+ { tIntro98, 1, 0, 0 },
+ { tIngame98, 1, 0, 0 },
+ { 0, 0, 0, 0}
+ };
+
+ if (_flags.platform == Common::kPlatformPC)
+ _soundData = soundData_PC;
+ else if (_flags.platform == Common::kPlatformFMTowns)
+ _soundData = soundData_TOWNS;
+ else if (_flags.platform == Common::kPlatformPC98)
+ _soundData = soundData_PC98;
+
}
void KyraEngine_LoK::loadMouseShapes() {
@@ -1243,6 +1259,10 @@ void KyraEngine_HoF::initStaticResource() {
static const char *fmtMusicFileListFinale[] = { "finale%d.twn" };
static const char *fmtMusicFileListIngame[] = { "km%02d.twn" };
+ static const char *pc98MusicFileListIntro[] = { "intro%d.86" };
+ static const char *pc98MusicFileListFinale[] = { "finale%d.86" };
+ static const char *pc98MusicFileListIngame[] = { "km%02d.86" };
+
static const AudioDataStruct soundData_PC[] = {
{ _musicFileListIntro, _musicFileListIntroSize, 0, 0 },
{ _musicFileListIngame, _musicFileListIngameSize, 0, 0},
@@ -1254,7 +1274,19 @@ void KyraEngine_HoF::initStaticResource() {
{ fmtMusicFileListIngame, 1, _cdaTrackTableIngame, _cdaTrackTableIngameSize >> 1 },
{ fmtMusicFileListFinale, 1, _cdaTrackTableFinale, _cdaTrackTableFinaleSize >> 1 }
};
- _soundData = (_flags.platform == Common::kPlatformPC) ? soundData_PC : soundData_TOWNS;
+
+ static const AudioDataStruct soundData_PC98[] = {
+ { pc98MusicFileListIntro, 1, 0, 0 },
+ { pc98MusicFileListIngame, 1, 0, 0 },
+ { pc98MusicFileListFinale, 1, 0, 0 }
+ };
+
+ if (_flags.platform == Common::kPlatformPC)
+ _soundData = soundData_PC;
+ else if (_flags.platform == Common::kPlatformFMTowns)
+ _soundData = soundData_TOWNS;
+ else if (_flags.platform == Common::kPlatformPC98)
+ _soundData = soundData_PC98;
// setup sequence data
_sequences = _staticres->loadHofSequenceData(k2SeqplaySeqData, tmpSize);