aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Schickel2007-03-24 00:04:08 +0000
committerJohannes Schickel2007-03-24 00:04:08 +0000
commitd1645e2fbfbf7246de3ab3ca61dea0b4ffe1a296 (patch)
treea4992d087358d688271f4b92083bb0ca09e5261c
parentc8c3dd04a780a106905cc9582058099a7ead343f (diff)
downloadscummvm-rg350-d1645e2fbfbf7246de3ab3ca61dea0b4ffe1a296.tar.gz
scummvm-rg350-d1645e2fbfbf7246de3ab3ca61dea0b4ffe1a296.tar.bz2
scummvm-rg350-d1645e2fbfbf7246de3ab3ca61dea0b4ffe1a296.zip
Commit of patch #1686414 ("Kyra: FM-Towns music").
svn-id: r26285
-rw-r--r--engines/kyra/gui.cpp22
-rw-r--r--engines/kyra/kyra.h3
-rw-r--r--engines/kyra/resource.cpp5
-rw-r--r--engines/kyra/sound.cpp13
-rw-r--r--engines/kyra/sound.h57
-rw-r--r--engines/kyra/sound_towns.cpp1065
-rw-r--r--sound/softsynth/ym2612.cpp169
-rw-r--r--sound/softsynth/ym2612.h156
8 files changed, 1236 insertions, 254 deletions
diff --git a/engines/kyra/gui.cpp b/engines/kyra/gui.cpp
index 69d24c244c..3573157277 100644
--- a/engines/kyra/gui.cpp
+++ b/engines/kyra/gui.cpp
@@ -38,6 +38,7 @@ void KyraEngine::registerDefaultSettings() {
// Most settings already have sensible defaults. This one, however, is
// specific to the Kyra engine.
ConfMan.registerDefault("walkspeed", 2);
+ ConfMan.registerDefault("cdaudio", _flags.platform == Common::kPlatformFMTowns);
}
void KyraEngine::readSettings() {
@@ -55,7 +56,7 @@ void KyraEngine::readSettings() {
_configTextspeed = 2; // Fast
_configWalkspeed = ConfMan.getInt("walkspeed");
- _configMusic = ConfMan.getBool("music_mute") ? 0 : 1;
+ _configMusic = ConfMan.getBool("music_mute") ? 0 : ((ConfMan.getBool("cdaudio") && _flags.platform == Common::kPlatformFMTowns) ? 2 : 1);
_configSounds = ConfMan.getBool("sfx_mute") ? 0 : 1;
_sound->enableMusic(_configMusic);
@@ -96,6 +97,7 @@ void KyraEngine::writeSettings() {
ConfMan.setInt("talkspeed", talkspeed);
ConfMan.setInt("walkspeed", _configWalkspeed);
ConfMan.setBool("music_mute", _configMusic == 0);
+ ConfMan.setBool("cdaudio", _configMusic == 2);
ConfMan.setBool("sfx_mute", _configSounds == 0);
switch (_configVoice) {
@@ -563,6 +565,7 @@ void KyraEngine::setGUILabels() {
_textSpeedString = _guiStrings[25 + offsetOptions];
_onString = _guiStrings[20 + offsetOn];
_offString = _guiStrings[21 + offset];
+ _onCDString = _guiStrings[21];
}
int KyraEngine::buttonMenuCallback(Button *caller) {
@@ -1214,10 +1217,17 @@ int KyraEngine::gui_gameControlsMenu(Button *button) {
void KyraEngine::gui_setupControls(Menu &menu) {
debugC(9, kDebugLevelGUI, "KyraEngine::gui_setupControls()");
- if (_configMusic)
- menu.item[0].itemString = _onString; //"On"
- else
- menu.item[0].itemString = _offString; //"Off"
+ switch (_configMusic) {
+ case 0:
+ menu.item[0].itemString = _offString; //"Off"
+ break;
+ case 1:
+ menu.item[0].itemString = _onString; //"On"
+ break;
+ case 2:
+ menu.item[0].itemString = _onCDString; //"On + CD"
+ break;
+ }
if (_configSounds)
menu.item[1].itemString = _onString; //"On"
@@ -1299,7 +1309,7 @@ int KyraEngine::gui_controlsChangeMusic(Button *button) {
debugC(9, kDebugLevelGUI, "KyraEngine::gui_controlsChangeMusic()");
processMenuButton(button);
- _configMusic = !_configMusic;
+ _configMusic = ++_configMusic % (_flags.platform == Common::kPlatformFMTowns ? 3 : 2);
gui_setupControls(_menu[5]);
return 0;
}
diff --git a/engines/kyra/kyra.h b/engines/kyra/kyra.h
index 8b337eb058..d5fb11e44d 100644
--- a/engines/kyra/kyra.h
+++ b/engines/kyra/kyra.h
@@ -790,7 +790,7 @@ protected:
uint8 _configTextspeed;
uint8 _configWalkspeed;
- bool _configMusic;
+ int _configMusic;
bool _configSounds;
uint8 _configVoice;
@@ -891,6 +891,7 @@ protected:
const char *_textSpeedString;
const char *_onString;
const char *_offString;
+ const char *_onCDString;
int _itemList_Size;
int _takenList_Size;
diff --git a/engines/kyra/resource.cpp b/engines/kyra/resource.cpp
index 569a4cf036..e8b6801bc2 100644
--- a/engines/kyra/resource.cpp
+++ b/engines/kyra/resource.cpp
@@ -88,6 +88,11 @@ Resource::Resource(KyraEngine *engine) {
for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
Common::String filename = file->name();
filename.toUppercase();
+
+ // No real PAK file!
+ if (filename == "TWMUSIC.PAK")
+ continue;
+
if (filename.hasSuffix("PAK") || filename.hasSuffix("APK")) {
if (!loadPakFile(file->name())) {
error("couldn't open pakfile '%s'", file->name().c_str());
diff --git a/engines/kyra/sound.cpp b/engines/kyra/sound.cpp
index 9463e1e933..ad0933464f 100644
--- a/engines/kyra/sound.cpp
+++ b/engines/kyra/sound.cpp
@@ -37,7 +37,7 @@ namespace Kyra {
Sound::Sound(KyraEngine *engine, Audio::Mixer *mixer)
: _engine(engine), _mixer(mixer), _currentVocFile(0), _vocHandle(), _compressHandle(),
- _musicEnabled(true), _sfxEnabled(true), _soundFileList(0), _soundFileListSize(0) {
+ _musicEnabled(1), _sfxEnabled(true), _soundFileList(0), _soundFileListSize(0) {
}
Sound::~Sound() {
@@ -450,14 +450,9 @@ void KyraEngine::snd_playTheme(int file, int track) {
void KyraEngine::snd_playSoundEffect(int track) {
debugC(9, kDebugLevelMain | kDebugLevelSound, "KyraEngine::snd_playSoundEffect(%d)", track);
- if (_flags.platform == Common::kPlatformFMTowns) {
- if (track == 49) {
- snd_playWanderScoreViaMap(56, 1);
- return;
- } else if (track == 0 || track == 1 || track == 10) {
- // I don't know what's supposed to happen here, but calling playSoundEffect will lead to crash
- return;
- }
+ if (_flags.platform == Common::kPlatformFMTowns && track == 49) {
+ snd_playWanderScoreViaMap(56, 1);
+ return;
}
_sound->playSoundEffect(track);
}
diff --git a/engines/kyra/sound.h b/engines/kyra/sound.h
index a0975c6d66..5b08166242 100644
--- a/engines/kyra/sound.h
+++ b/engines/kyra/sound.h
@@ -49,9 +49,9 @@
#include "common/file.h"
#include "common/mutex.h"
-#include "sound/mididrv.h"
#include "sound/midiparser.h"
#include "sound/mixer.h"
+#include "sound/softsynth/ym2612.h"
#include "kyra/kyra.h"
@@ -84,8 +84,8 @@ public:
virtual void beginFadeOut() = 0;
- void enableMusic(bool enable) { _musicEnabled = enable; }
- bool musicEnabled() const { return _musicEnabled; }
+ void enableMusic(int enable) { _musicEnabled = enable; }
+ int musicEnabled() const { return _musicEnabled; }
void enableSFX(bool enable) { _sfxEnabled = enable; }
bool sfxEnabled() const { return _sfxEnabled; }
@@ -93,10 +93,10 @@ public:
void voiceUnload() {}
bool voiceIsPlaying();
void voiceStop();
-
+
protected:
const char *soundFilename(uint file) { return (file < _soundFileListSize) ? _soundFileList[file] : ""; }
- bool _musicEnabled;
+ int _musicEnabled;
bool _sfxEnabled;
KyraEngine *_engine;
@@ -129,14 +129,14 @@ public:
void setVolume(int volume);
int getVolume();
-
+
void loadSoundFile(uint file);
-
+
void playTrack(uint8 track);
void haltTrack();
-
+
void playSoundEffect(uint8 track);
-
+
void beginFadeOut();
private:
void play(uint8 track);
@@ -231,7 +231,8 @@ private:
Common::Mutex _mutex;
};
-class SoundTowns : public Sound {
+class FMT_EuphonyDriver;
+class SoundTowns : public MidiDriver, public Sound {
public:
SoundTowns(KyraEngine *engine, Audio::Mixer *mixer);
~SoundTowns();
@@ -249,23 +250,47 @@ public:
void playSoundEffect(uint8);
- void beginFadeOut() { /* TODO */ }
+ void beginFadeOut();
+
+ //MidiDriver interface implementation
+ int open();
+ void close();
+ void send(uint32 b);
+ void metaEvent(byte type, byte *data, uint16 length) {}
+
+ void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { }
+ uint32 getBaseTempo(void);
+
+ //Channel allocation functions
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+
+ static float semitoneAndSampleRate_to_sampleStep(int8 semiTone, int8 semiToneRootkey,
+ uint32 sampleRate, uint32 outputRate, int32 pitchWheel);
private:
- void stopSoundEffect();
- void setPitch(uint8 *&data, uint32 &size, int8 sourcePitch, int8 targetPitch);
+ bool loadInstruments();
+ void playEuphonyTrack(uint32 offset, int loop);
+
+ static void onTimer(void *data);
int _lastTrack;
Audio::AudioStream *_currentSFX;
Audio::SoundHandle _sfxHandle;
+
int _currentTrackTable;
- bool _sfxIsPlaying;
uint _sfxFileIndex;
uint8 *_sfxFileData;
+ FMT_EuphonyDriver * _driver;
+ MidiParser * _parser;
+ uint8 *_musicTrackData;
+
+ Common::Mutex _mutex;
+
static const char *_sfxFiles[];
static const int _sfxFilenum;
static const uint8 _sfxBTTable[256];
- const uint8 *_sfxWDTable;
+ const uint8 *_sfxWDTable;
};
class MixedSoundDriver : public Sound {
@@ -300,7 +325,7 @@ public:
~SoundDigital();
bool init();
-
+
int playSound(Common::File *fileHandle, bool loop = false, bool fadeIn = false, int channel = -1);
bool isPlaying(int channel);
void stopSound(int channel);
diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp
index 84beeef453..67ec81d5db 100644
--- a/engines/kyra/sound_towns.cpp
+++ b/engines/kyra/sound_towns.cpp
@@ -29,23 +29,859 @@
#include "sound/audiocd.h"
#include "sound/audiostream.h"
+#include "common/util.h"
+#include <math.h>
+
namespace Kyra {
+enum EuD_ChannelState { _s_ready, _s_attacking, _s_decaying, _s_sustaining, _s_releasing };
+
+class MidiChannel_EuD : public MidiChannel {
+public:
+ MidiChannel_EuD() {}
+ ~MidiChannel_EuD() {}
+
+ virtual void nextTick(int32 *outbuf, int buflen) = 0;
+ virtual void rate(uint16 r) = 0;
+
+protected:
+ uint16 _rate;
+};
+
+class MidiChannel_EuD_FM : public MidiChannel_EuD {
+public:
+ MidiChannel_EuD_FM();
+ virtual ~MidiChannel_EuD_FM();
+
+ void nextTick(int32 *outbuf, int buflen);
+ void rate(uint16 r);
+
+ // MidiChannel interface
+ MidiDriver *device() { return 0; }
+ byte getNumber() { return 0; }
+ void release() { }
+ void send(uint32 b) { }
+ void noteOff(byte note);
+ void noteOn(byte note, byte onVelo);
+ void programChange(byte program) {}
+ void pitchBend(int16 value);
+ void controlChange(byte control, byte value);
+ void pitchBendFactor(byte value) { }
+ void sysEx_customInstrument(uint32 unused, const byte *instr);
+
+protected:
+ Voice2612 *_voice;
+};
+
+class MidiChannel_EuD_WAVE : public MidiChannel_EuD {
+public:
+ void nextTick(int32 *outbuf, int buflen);
+ void rate(uint16 r);
+
+ MidiChannel_EuD_WAVE();
+ virtual ~MidiChannel_EuD_WAVE();
+
+ // MidiChannel interface
+ MidiDriver *device() { return 0; }
+ byte getNumber() { return 0; }
+ void release() { }
+ void send(uint32 b) { }
+ void noteOff(byte note);
+ void noteOn(byte note, byte onVelo);
+ void programChange(byte program) {}
+ void pitchBend(int16 value);
+ void controlChange(byte control, byte value);
+ void pitchBendFactor(byte value) { }
+ void sysEx_customInstrument(uint32 type, const byte *instr);
+
+protected:
+ void velocity(int velo);
+ void panPosition(int8 pan);
+ void evpNextTick();
+
+ int _ctrl7_volume;
+ int16 _velocity;
+ int16 _note;
+ int32 _frequencyOffs;
+ float _phase;
+ int8 _current;
+
+ struct Voice {
+ char name[9];
+ uint16 split[8];
+ uint32 id[8];
+ struct Snd {
+ char name[9];
+ int32 id;
+ int32 numSamples;
+ int32 loopStart;
+ int32 loopLength;
+ int32 samplingRate;
+ int32 keyOffset;
+ int32 keyNote;
+ int8 *_samples;
+ } * _snd[8];
+ struct Env {
+ EuD_ChannelState state;
+ int32 currentLevel;
+ int32 rate;
+ int32 tickCount;
+ int32 totalLevel;
+ int32 attackRate;
+ int32 decayRate;
+ int32 sustainLevel;
+ int32 sustainRate;
+ int32 releaseLevel;
+ int32 releaseRate;
+ int32 rootKeyOffset;
+ int32 size;
+ } * _env[8];
+ } * _voice;
+};
+
+class MidiParser_EuD : public MidiParser {
+public:
+ MidiParser_EuD();
+
+ bool loadMusic (byte *data, uint32 unused = 0);
+ void setTempo(uint32 tempo);
+protected:
+ void parseNextEvent (EventInfo &info);
+ void resetTracking();
+
+ byte * _enable;
+ byte * _mode;
+ byte * _channel;
+ byte * _adjVelo;
+ int8 * _adjNote;
+
+ uint8 _firstBaseTickStep;
+ uint8 _nextBaseTickStep;
+ uint8 _initialTempo;
+ uint32 _baseTick;
+};
+
+class FMT_EuphonyDriver : public MidiDriver_Emulated {
+public:
+ FMT_EuphonyDriver(Audio::Mixer *mixer);
+ virtual ~FMT_EuphonyDriver();
+
+ int open();
+ void close();
+ void send(uint32 b);
+ void send(byte channel, uint32 b);
+ uint32 property(int prop, uint32 param) { return 0; }
+
+ void setPitchBendRange(byte channel, uint range) { }
+ //void sysEx(const byte *msg, uint16 length);
+ void loadFmInstruments(const byte *instr);
+ void loadWaveInstruments(const byte *instr);
+
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+
+ void assignFmChannel(uint8 midiChannelNumber, uint8 fmChannelNumber);
+ void assignWaveChannel(uint8 midiChannelNumber, uint8 waveChannelNumber);
+ void removeChannel(uint8 midiChannelNumber);
+
+ // AudioStream API
+ bool isStereo() const { return true; }
+ int getRate() const { return _mixer->getOutputRate(); }
+
+protected:
+ void nextTick(int16 *buf1, int buflen);
+ int volume(int val = -1) { if (val >= 0) _volume = val; return _volume; }
+ void rate(uint16 r);
+
+ void generateSamples(int16 *buf, int len);
+
+ MidiChannel_EuD_FM *_fChannel[6];
+ MidiChannel_EuD_WAVE *_wChannel[8];
+ MidiChannel_EuD * _channel[16];
+
+ int _volume;
+
+ uint8 *_fmInstruments;
+ uint8 *_waveInstruments;
+ int8 * _waveSounds[10];
+};
+
+MidiChannel_EuD_FM::MidiChannel_EuD_FM() {
+ _voice = new Voice2612;
+}
+
+MidiChannel_EuD_FM::~MidiChannel_EuD_FM() {
+ delete _voice;
+}
+
+void MidiChannel_EuD_FM::noteOn(byte note, byte onVelo) {
+ _voice->noteOn(note, onVelo);
+}
+
+void MidiChannel_EuD_FM::noteOff(byte note) {
+ _voice->noteOff(note);
+}
+
+void MidiChannel_EuD_FM::controlChange(byte control, byte value) {
+ if (control == 121) {
+ // Reset controller
+ delete _voice;
+ _voice = new Voice2612;
+ } else if (control == 10) {
+ // pan position
+ } else {
+ _voice->setControlParameter(control, value);
+ }
+}
+
+void MidiChannel_EuD_FM::sysEx_customInstrument(uint32, const byte *fmInst) {
+ _voice->_rate = _rate;
+ _voice->setInstrument(fmInst);
+}
+
+void MidiChannel_EuD_FM::pitchBend(int16 value) {
+ _voice->pitchBend(value);
+}
+
+void MidiChannel_EuD_FM::nextTick(int32 *outbuf, int buflen) {
+ _voice->nextTick((int*) outbuf, buflen);
+}
+
+void MidiChannel_EuD_FM::rate(uint16 r) {
+ _rate = r;
+ _voice->_rate = r;
+}
+
+MidiChannel_EuD_WAVE::MidiChannel_EuD_WAVE() {
+ _voice = new Voice;
+ for (uint8 i = 0; i < 8; i++) {
+ _voice->_env[i] = new Voice::Env;
+ _voice->_snd[i] = 0;
+ }
+
+ _ctrl7_volume = 127;
+ velocity(0);
+ _frequencyOffs = 0x2000;
+ _current = -1;
+}
+
+MidiChannel_EuD_WAVE::~MidiChannel_EuD_WAVE() {
+ for (uint8 i = 0; i < 8; i++) {
+ if (_voice->_snd[i])
+ delete _voice->_snd[i];
+ delete _voice->_env[i];
+ }
+ delete _voice;
+}
+
+void MidiChannel_EuD_WAVE::noteOn(byte note, byte onVelo) {
+ _note = note;
+ velocity(onVelo);
+ _phase = 0;
+
+ for (_current = 0; _current < 7; _current++) {
+ if (note <= _voice->split[_current])
+ break;
+ }
+
+ _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) {
+ if (_current == -1)
+ return;
+ if (_voice->_env[_current]->state == _s_ready)
+ return;
+
+ _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) {
+ switch (control) {
+ case 0x07:
+ // volume
+ _ctrl7_volume = value;
+ break;
+ case 0x0A:
+ // pan position
+ break;
+ case 0x79:
+ // Reset controller
+ for (uint8 i = 0; i < 8; i++) {
+ if (_voice->_snd[i])
+ delete _voice->_snd[i];
+ delete _voice->_env[i];
+ }
+ delete _voice;
+ _voice = new Voice;
+ for (uint8 i = 0; i < 8; i++) {
+ _voice->_env[i] = new Voice::Env;
+ _voice->_snd[i] = 0;
+ }
+ break;
+ case 0x7B:
+ noteOff(_note);
+ break;
+ default:
+ break;
+ }
+}
+
+void MidiChannel_EuD_WAVE::sysEx_customInstrument(uint32 type, const byte *fmInst) {
+ if (type == 0x80) {
+ for (uint8 i = 0; i < 8; i++) {
+ byte ** pos = (byte **) fmInst;
+ for (uint8 ii = 0; ii < 10; ii++) {
+ if (_voice->id[i] == *(pos[ii] + 8)) {
+ if (!_voice->_snd[i])
+ _voice->_snd[i] = new Voice::Snd;
+ memset (_voice->_snd[i]->name, 0, 9);
+ memcpy (_voice->_snd[i]->name, (const char*) pos[ii], 8);
+ _voice->_snd[i]->id = READ_LE_UINT32(pos[ii] + 8);
+ _voice->_snd[i]->numSamples = READ_LE_UINT32(pos[ii] + 12);
+ _voice->_snd[i]->loopStart = READ_LE_UINT32(pos[ii] + 16);
+ _voice->_snd[i]->loopLength = READ_LE_UINT32(pos[ii] + 20);
+ _voice->_snd[i]->samplingRate = READ_LE_UINT16(pos[ii] + 24);
+ _voice->_snd[i]->keyOffset = READ_LE_UINT16(pos[ii] + 26);
+ _voice->_snd[i]->keyNote = *(uint8*)(pos[ii] + 28);
+ _voice->_snd[i]->_samples = (int8*)(pos[ii] + 32);
+ }
+ }
+ }
+ } else {
+ memset (_voice->name, 0, 9);
+ memcpy (_voice->name, (const char*) fmInst, 8);
+
+ for (uint8 i = 0; i < 8; i++) {
+ _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]->currentLevel = 0;
+ _voice->_env[i]->totalLevel = *(fmInst + 64 + 8 * i);
+ _voice->_env[i]->attackRate = *(fmInst + 65 + 8 * i) * 10;
+ _voice->_env[i]->decayRate = *(fmInst + 66 + 8 * i) * 10;
+ _voice->_env[i]->sustainLevel = *(fmInst + 67 + 8 * i);
+ _voice->_env[i]->sustainRate = *(fmInst + 68 + 8 * i) * 20;
+ _voice->_env[i]->releaseRate = *(fmInst + 69 + 8 * i) * 10;
+ _voice->_env[i]->rootKeyOffset = *(fmInst + 70 + 8 * i);
+ }
+ }
+}
+
+void MidiChannel_EuD_WAVE::pitchBend(int16 value) {
+ _frequencyOffs = value;
+}
+
+void MidiChannel_EuD_WAVE::nextTick(int32 *outbuf, int buflen) {
+ if (_current == -1 || !_voice->_snd[_current] || !_voice->_env[_current]->state || !_velocity) {
+ velocity(0);
+ _current = -1;
+ return;
+ }
+
+ float phaseStep = SoundTowns::semitoneAndSampleRate_to_sampleStep(_note, _voice->_snd[_current]->keyNote -
+ _voice->_env[_current]->rootKeyOffset, _voice->_snd[_current]->samplingRate, _rate, _frequencyOffs);
+
+ int32 looplength = _voice->_snd[_current]->loopLength;
+ int32 numsamples = _voice->_snd[_current]->numSamples;
+ int8 * samples = _voice->_snd[_current]->_samples;
+
+ for (int i = 0; i < buflen; i++) {
+ if (looplength > 0) {
+ while (_phase >= numsamples)
+ _phase -= looplength;
+ } else {
+ if (_phase >= numsamples) {
+ velocity(0);
+ _current = -1;
+ break;
+ }
+ }
+
+ int32 output;
+
+ int32 phase0 = int32(_phase);
+ int32 phase1 = int32(_phase + 1);
+ if (phase1 >= numsamples)
+ phase1 -= looplength;
+ float weight0 = _phase - phase0;
+ float weight1 = phase1 - _phase;
+ output = int32(samples[phase0] * weight0 + samples[phase1] * weight1);
+
+ output *= _velocity;
+ output <<= 1;
+
+ evpNextTick();
+ output *= _voice->_env[_current]->currentLevel;
+ output >>= 7;
+ output *= _ctrl7_volume;
+ output >>= 7;
+
+ output *= 185;
+ output >>= 8;
+ outbuf[i] += output;
+ _phase += phaseStep;
+ }
+}
+
+void MidiChannel_EuD_WAVE::evpNextTick() {
+ switch (_voice->_env[_current]->state) {
+ case _s_ready:
+ _voice->_env[_current]->currentLevel = 0;
+ return;
+
+ case _s_attacking:
+ if (_voice->_env[_current]->attackRate == 0)
+ _voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel;
+ else if (_voice->_env[_current]->attackRate >= 1270)
+ _voice->_env[_current]->currentLevel = 0;
+ else
+ _voice->_env[_current]->currentLevel = (_voice->_env[_current]->totalLevel *
+ _voice->_env[_current]->tickCount++ * 1000) /
+ (_voice->_env[_current]->attackRate * _voice->_env[_current]->rate);
+
+ 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]->tickCount = 0;
+ }
+ break;
+
+ case _s_decaying:
+ if (_voice->_env[_current]->decayRate == 0)
+ _voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel;
+ else if (_voice->_env[_current]->decayRate >= 1270)
+ _voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel;
+ else {
+ _voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel;
+ _voice->_env[_current]->currentLevel -= ((_voice->_env[_current]->totalLevel -
+ _voice->_env[_current]->sustainLevel) * _voice->_env[_current]->tickCount++ * 1000) /
+ (_voice->_env[_current]->decayRate * _voice->_env[_current]->rate);
+ }
+
+ 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]->tickCount = 0;
+ }
+ break;
+
+ case _s_sustaining:
+ if (_voice->_env[_current]->sustainRate == 0)
+ _voice->_env[_current]->currentLevel = 0;
+ else if (_voice->_env[_current]->sustainRate >= 2540)
+ _voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel;
+ else {
+ _voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel;
+ _voice->_env[_current]->currentLevel -= (_voice->_env[_current]->sustainLevel *
+ _voice->_env[_current]->tickCount++ * 1000) / (_voice->_env[_current]->sustainRate *
+ _voice->_env[_current]->rate);
+ }
+
+ if (_voice->_env[_current]->currentLevel <= 0) {
+ _voice->_env[_current]->currentLevel = 0;
+ _voice->_env[_current]->state = _s_ready;
+ _voice->_env[_current]->tickCount = 0;
+ }
+ break;
+
+ case _s_releasing:
+ if (_voice->_env[_current]->releaseRate == 0)
+ _voice->_env[_current]->currentLevel = 0;
+ else if (_voice->_env[_current]->releaseRate >= 1270)
+ _voice->_env[_current]->currentLevel = _voice->_env[_current]->releaseLevel;
+ else {
+ _voice->_env[_current]->currentLevel = _voice->_env[_current]->releaseLevel;
+ _voice->_env[_current]->currentLevel -= (_voice->_env[_current]->releaseLevel *
+ _voice->_env[_current]->tickCount++ * 1000) / (_voice->_env[_current]->releaseRate *
+ _voice->_env[_current]->rate);
+ }
+
+ if (_voice->_env[_current]->currentLevel <= 0) {
+ _voice->_env[_current]->currentLevel = 0;
+ _voice->_env[_current]->state = _s_ready;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void MidiChannel_EuD_WAVE::rate(uint16 r) {
+ _rate = r;
+}
+
+void MidiChannel_EuD_WAVE::velocity(int velo) {
+ _velocity = velo;
+}
+
+FMT_EuphonyDriver::FMT_EuphonyDriver(Audio::Mixer *mixer)
+ : MidiDriver_Emulated(mixer) {
+
+ MidiDriver_YM2612::createLookupTables();
+
+ _volume = 255;
+ for (uint8 i = 0; i < 6; i++)
+ _channel[i] = _fChannel[i] = new MidiChannel_EuD_FM;
+ for (uint8 i = 0; i < 8; i++)
+ _channel[i + 6] = _wChannel[i] = new MidiChannel_EuD_WAVE;
+ _channel[14] = _channel[15] = 0;
+
+ _fmInstruments = _waveInstruments = 0;
+ memset(_waveSounds, 0, sizeof(uint8*) * 10);
+
+ rate(getRate());
+}
+
+FMT_EuphonyDriver::~FMT_EuphonyDriver() {
+ for (int i = 0; i < 6; i++)
+ delete _fChannel[i];
+ for (int i = 0; i < 8; i++)
+ delete _wChannel[i];
+
+ MidiDriver_YM2612::removeLookupTables();
+
+ if (_fmInstruments) {
+ delete [] _fmInstruments;
+ _fmInstruments = 0;
+ }
+
+ if (_waveInstruments) {
+ delete [] _waveInstruments;
+ _waveInstruments = 0;
+ }
+
+ for (int i = 0; i < 10; i++) {
+ if (_waveSounds[i]) {
+ delete [] _waveSounds[i];
+ _waveSounds[i] = 0;
+ }
+ }
+}
+
+int FMT_EuphonyDriver::open() {
+ if (_isOpen)
+ return MERR_ALREADY_OPEN;
+
+ MidiDriver_Emulated::open();
+
+ _mixer->playInputStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle,
+ this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true);
+ return 0;
+}
+
+void FMT_EuphonyDriver::close() {
+ if (!_isOpen)
+ return;
+ _isOpen = false;
+ _mixer->stopHandle(_mixerSoundHandle);
+}
+
+void FMT_EuphonyDriver::send(uint32 b) {
+ send(b & 0xF, b & 0xFFFFFFF0);
+}
+
+void FMT_EuphonyDriver::send(byte chan, uint32 b) {
+ //byte param3 = (byte) ((b >> 24) & 0xFF);
+
+ byte param2 = (byte) ((b >> 16) & 0xFF);
+ byte param1 = (byte) ((b >> 8) & 0xFF);
+ byte cmd = (byte) (b & 0xF0);
+ if (chan > ARRAYSIZE(_channel))
+ return;
+
+ switch (cmd) {
+ case 0x80:// Note Off
+ if (_channel[chan])
+ _channel[chan]->noteOff(param1);
+ break;
+ case 0x90: // Note On
+ if (_channel[chan])
+ _channel[chan]->noteOn(param1, param2);
+ break;
+ case 0xA0: // Aftertouch
+ break; // Not supported.
+ case 0xB0: // Control Change
+ if (param1 == 0x79) {
+ for (int i = 0; i < 15; i++) {
+ if (_channel[i]) {
+ _channel[i]->controlChange(param1, param2);
+ _channel[i]->programChange(0);
+ }
+ }
+ } else if (param1 == 0x7B) {
+ for (int i = 0; i < 15; i++) {
+ if (_channel[i])
+ _channel[i]->controlChange(param1, param2);
+ }
+ } else {
+ if (_channel[chan])
+ _channel[chan]->controlChange(param1, param2);
+ }
+ break;
+ case 0xC0: // Program Change
+ for (int i = 0; i < 6; i++) {
+ if (_channel[chan] == _fChannel[i]) {
+ _channel[chan]->sysEx_customInstrument(0, _fmInstruments + param1 * 0x30);
+ break;
+ }
+ }
+ for (int i = 0; i < 8; i++) {
+ if (_channel[chan] == _wChannel[i]) {
+ _channel[chan]->sysEx_customInstrument(0, _waveInstruments + param1 * 0x80);
+ _channel[chan]->sysEx_customInstrument(0x80, (const byte*) _waveSounds);
+ break;
+ }
+ }
+ break;
+ case 0xD0: // Channel Pressure
+ break; // Not supported.
+ case 0xE0: // Pitch Bend
+ if (_channel[chan])
+ _channel[chan]->pitchBend((param1 | (param2 << 7)) - 0x2000);
+ break;
+ default:
+ warning("FMT_EuphonyDriver: Unknown send() command 0x%02X", cmd);
+ }
+}
+
+void FMT_EuphonyDriver::loadFmInstruments(const byte *instr) {
+ if (_fmInstruments)
+ delete [] _fmInstruments;
+ _fmInstruments = new uint8[0x1800];
+ memcpy(_fmInstruments, instr, 0x1800);
+}
+
+void FMT_EuphonyDriver::loadWaveInstruments(const byte *instr) {
+ if (_waveInstruments)
+ delete [] _waveInstruments;
+ _waveInstruments = new uint8[0x1000];
+ memcpy(_waveInstruments, instr, 0x1000);
+
+ const uint8 *pos = (const uint8 *)(instr + 0x1000);
+
+ for (uint8 i = 0; i < 10; i++) {
+ if (_waveSounds[i])
+ delete [] _waveSounds[i];
+ uint32 numsamples = READ_LE_UINT32(pos + 0x0C);
+ _waveSounds[i] = new int8[numsamples + 0x20];
+ memcpy(_waveSounds[i], pos, 0x20);
+ pos += 0x20;
+ for (uint32 ii = 0; ii < numsamples; ii++) {
+ uint8 s = *(pos + ii);
+ s = (s < 0x80) ? 0x80 - s : s;
+ _waveSounds[i][ii + 0x20] = s ^ 0x80;
+ }
+ pos += numsamples;
+ }
+}
+
+
+void FMT_EuphonyDriver::assignFmChannel(uint8 midiChannelNumber, uint8 fmChannelNumber) {
+ _channel[midiChannelNumber] = _fChannel[fmChannelNumber];
+}
+
+void FMT_EuphonyDriver::assignWaveChannel(uint8 midiChannelNumber, uint8 waveChannelNumber) {
+ _channel[midiChannelNumber] = _wChannel[waveChannelNumber];
+}
+
+void FMT_EuphonyDriver::removeChannel(uint8 midiChannelNumber) {
+ _channel[midiChannelNumber] = 0;
+}
+
+void FMT_EuphonyDriver::generateSamples(int16 *data, int len) {
+ memset(data, 0, 2 * sizeof(int16) * len);
+ nextTick(data, len);
+}
+
+void FMT_EuphonyDriver::nextTick(int16 *buf1, int buflen) {
+ int32 *buf0 = (int32 *)buf1;
+
+ for (int i = 0; i < ARRAYSIZE(_channel); i++) {
+ if (_channel[i])
+ _channel[i]->nextTick(buf0, buflen);
+ }
+
+ for (int i = 0; i < buflen; ++i) {
+ buf1[i*2] = buf1[i*2+1] =((buf0[i] * volume()) >> 9) & 0xffff;
+ }
+}
+
+void FMT_EuphonyDriver::rate(uint16 r)
+{
+ for (uint8 i = 0; i < 16; i++) {
+ if (_channel[i])
+ _channel[i]->rate(r);
+ }
+}
+
+MidiParser_EuD::MidiParser_EuD() : MidiParser() {
+}
+
+void MidiParser_EuD::parseNextEvent(EventInfo &info) {
+ byte *pos = _position._play_pos;
+
+ while (true) {
+ byte cmd = *pos;
+ byte evt = (cmd & 0xF0);
+
+ if (evt == 0x90) {
+ byte chan = pos[1];
+
+ if (_enable[chan]) {
+ uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _baseTick;
+ info.start = pos + 6;
+ uint32 last = _position._last_event_tick;
+ info.delta = (tick < last) ? 0 : (tick - last);
+
+ info.event = 0x90 | _channel[chan];
+ info.length = pos[7] | (pos[8] << 4);
+
+ int8 note = (int8) pos[4];
+ if (_adjNote[chan]) {
+ note = (note & 0x7f) & _adjNote[chan];
+ if (note > 0x7c)
+ note -= 0x0c;
+ else if (note < 0)
+ note += 0x0c;
+ }
+ info.basic.param1 = (byte) note;
+
+ uint8 onVelo = (pos[5] & 0x7f) + _adjVelo[chan];
+ if (onVelo > 0x7f)
+ onVelo = 0x7f;
+ if (onVelo < 1)
+ onVelo = 1;
+ info.basic.param2 = onVelo;
+
+ pos += 12;
+ break;
+ } else {
+ pos += 6;
+ }
+ } else if (evt == 0xB0 || evt == 0xC0 || evt == 0xe0) {
+ byte chan = pos[1];
+
+ if (_enable[chan]) {
+ info.start = pos;
+ uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _baseTick;
+ uint32 last = _position._last_event_tick;
+ info.delta = (tick < last) ? 0 : (tick - last);
+ info.event = evt | _channel[chan];
+ info.length = 0;
+ info.basic.param1 = pos[4];
+ info.basic.param2 = pos[5];
+ pos += 6;
+ break;
+ } else {
+ pos += 6;
+ }
+ } else if (cmd == 0xF2) {
+ static uint16 tickTable [] = { 0x180, 0xC0, 0x80, 0x60, 0x40, 0x30, 0x20, 0x18 };
+ _baseTick += tickTable[_nextBaseTickStep >> 4] * ((_nextBaseTickStep & 0x0f) + 1);
+ _nextBaseTickStep = pos[1];
+ pos += 6;
+ } else if (cmd == 0xF8) {
+ uint16 tempo = pos[4] | (pos[5] << 7);
+ setTempo(tempo);
+ pos += 6;
+ } else if (cmd == 0xFD || cmd == 0xFE) {
+ // End of track.
+ if (_autoLoop)
+ pos = info.start = _tracks[0];
+ else
+ info.start = pos;
+
+ uint32 last = _position._last_event_tick;
+ uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _baseTick;
+ info.delta = (tick < last) ? 0 : (tick - last);
+ info.event = 0xFF;
+ info.ext.type = 0x2F;
+ info.ext.data = pos;
+ break;
+ } else {
+ error("Unknown Euphony music event 0x%02X", (int) cmd);
+ memset(&info, 0, sizeof(info));
+ pos = 0;
+ break;
+ }
+ }
+ _position._play_pos = pos;
+}
+
+bool MidiParser_EuD::loadMusic(byte *data, uint32) {
+ unloadMusic();
+
+ _enable = data + 0x354;
+ _mode = data + 0x374;
+ _channel = data + 0x394;
+ _adjVelo = data + 0x3B4;
+ _adjNote = (int8*) data + 0x3D4;
+
+ _firstBaseTickStep = data[0x804];
+ _initialTempo = (data[0x805] > 0xfc) ? 0x5a : data[0x805];
+
+ _num_tracks = 1;
+ _ppqn = 120;
+ _tracks[0] = data + 0x806;
+
+ resetTracking();
+ setTrack (0);
+
+ return true;
+}
+
+void MidiParser_EuD::setTempo(uint32 tempo) {
+ if (tempo)
+ MidiParser::setTempo(60000000 / tempo);
+}
+
+void MidiParser_EuD::resetTracking() {
+ MidiParser::resetTracking();
+
+ _nextBaseTickStep = _firstBaseTickStep;
+ _baseTick = 0;
+ setTempo(_initialTempo);
+}
+
SoundTowns::SoundTowns(KyraEngine *engine, Audio::Mixer *mixer) : Sound(engine, mixer), _lastTrack(-1),
- _currentSFX(0), _sfxFileData(0), _sfxFileIndex((uint)-1), _sfxWDTable(0) {
+ _currentSFX(0), _sfxFileData(0), _sfxFileIndex((uint)-1), _sfxWDTable(0), _parser(0), _musicTrackData(0) {
+
+ _driver = new FMT_EuphonyDriver(_mixer);
+ int ret = open();
+ if (ret != MERR_ALREADY_OPEN && ret != 0) {
+ error("couldn't open midi driver");
+ }
}
SoundTowns::~SoundTowns() {
AudioCD.stop();
+ haltTrack();
delete [] _sfxFileData;
- stopSoundEffect();
+
+ Common::StackLock lock(_mutex);
+ _driver->setTimerCallback(0, 0);
+ close();
+
+ if (_musicTrackData)
+ delete [] _musicTrackData;
+
+ _driver = 0;
}
bool SoundTowns::init() {
_engine->checkCD();
int unused = 0;
_sfxWDTable = _engine->staticres()->loadRawData(kKyra1TownsSFXTable, unused);
- return true;
+
+ return loadInstruments();
}
void SoundTowns::process() {
@@ -131,22 +967,38 @@ void SoundTowns::playTrack(uint8 track) {
int trackNum = tTable[track].track;
bool loop = tTable[track].loop;
- // could be that if the trackNum is -1, the music should be stopped
- // instead of letting the old music play on
- if (trackNum == -1 || trackNum == _lastTrack)
+
+ if (track == _lastTrack && _musicEnabled)
return;
haltTrack();
- AudioCD.play(trackNum+1, loop ? -1 : 1, 0, 0);
- AudioCD.updateCD();
- _lastTrack = trackNum;
+ if (_musicEnabled == 2 && trackNum != -1) {
+ AudioCD.play(trackNum+1, loop ? -1 : 1, 0, 0);
+ AudioCD.updateCD();
+ } else if (_musicEnabled) {
+ playEuphonyTrack(tTable[track].fileOffset, loop);
+ }
+
+ _lastTrack = track;
}
void SoundTowns::haltTrack() {
_lastTrack = -1;
AudioCD.stop();
AudioCD.updateCD();
+ if (_parser) {
+ Common::StackLock lock(_mutex);
+
+ _parser->setTrack(0);
+ _parser->jumpToTick(0);
+
+ _parser->unloadMusic();
+ delete _parser;
+ _parser = 0;
+
+ setVolume(255);
+ }
}
void SoundTowns::loadSoundFile(uint file) {
@@ -157,27 +1009,29 @@ void SoundTowns::loadSoundFile(uint file) {
_sfxFileData = _engine->resource()->fileData(soundFilename(file), 0);
}
-void SoundTowns::stopSoundEffect() {
- _sfxIsPlaying = false;
- _mixer->stopHandle(_sfxHandle);
-}
-
void SoundTowns::playSoundEffect(uint8 track) {
if (!_sfxEnabled || !_sfxFileData)
return;
- _sfxIsPlaying = true;
-
- uint8 pitch = 0x3c;
- if (_sfxFileIndex == 5) {
+ if (track == 0 || track == 10) {
+ _mixer->stopHandle(_sfxHandle);
+ return;
+ } else if (track == 1) {
+ // sfx fadeout
+ _mixer->stopHandle(_sfxHandle);
+ return;
+ }
+
+ uint8 note = 0x3c;
+ if (_sfxFileIndex == 5) {
if (track == 0x10) {
- pitch = 0x3e;
+ note = 0x3e;
track = 0x0f;
} else if (track == 0x11) {
- pitch = 0x40;
+ note = 0x40;
track = 0x0f;
} else if (track == 0x12) {
- pitch = 0x41;
+ note = 0x41;
track = 0x0f;
}
}
@@ -194,18 +1048,18 @@ void SoundTowns::playSoundEffect(uint8 track) {
uint32 outBufferSize;
uint32 unused2;
uint32 unused3;
- uint32 unknown1;
- uint32 pitch;
+ uint32 rate;
+ uint32 rootNoteOffs;
} *sfxHeader = (SfxHeader*)(fileBody + offset);
uint32 sfxHeaderID = TO_LE_32(sfxHeader->id);
uint32 sfxHeaderInBufferSize = TO_LE_32(sfxHeader->inBufferSize);
uint32 sfxHeaderOutBufferSize = TO_LE_32(sfxHeader->outBufferSize);
- sfxHeader->pitch = TO_LE_32(sfxHeader->pitch);
+ uint32 sfxRootNoteOffs = TO_LE_32(sfxHeader->rootNoteOffs);
+ uint32 sfxRate = TO_LE_32(sfxHeader->rate);
uint32 playbackBufferSize = (sfxHeaderID == 1) ? sfxHeaderInBufferSize : sfxHeaderOutBufferSize;
- stopSoundEffect();
uint8 *sfxPlaybackBuffer = (uint8 *)malloc(playbackBufferSize);
memset(sfxPlaybackBuffer, 0x80, playbackBufferSize);
@@ -220,7 +1074,7 @@ void SoundTowns::playSoundEffect(uint8 track) {
uint32 sfx_BtTable_Offset = 0;
uint32 sfx_WdTable_Offset = 0;
uint32 sfx_WdTable_Number = 5;
-
+
for (uint32 i = 0; i < sfxHeaderInBufferSize; i++) {
sfx_WdTable_Offset = (sfx_WdTable_Number * 3 << 9) + sfxBody[i] * 6;
sfx_WdTable_Number = READ_LE_UINT16(_sfxWDTable + sfx_WdTable_Offset);
@@ -233,34 +1087,129 @@ void SoundTowns::playSoundEffect(uint8 track) {
}
}
- for (uint32 i = 0; i < playbackBufferSize; i++) {
+ for (uint32 i = 0; i < playbackBufferSize; i++) {
if (sfxPlaybackBuffer[i] < 0x80)
sfxPlaybackBuffer[i] = 0x80 - sfxPlaybackBuffer[i];
}
playbackBufferSize -= 0x20;
- setPitch(sfxPlaybackBuffer, playbackBufferSize, sfxHeader->pitch, pitch);
-
- _currentSFX = Audio::makeLinearInputStream(sfxPlaybackBuffer, playbackBufferSize,
- 0x2b11,
- Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_LITTLE_ENDIAN | Audio::Mixer::FLAG_AUTOFREE,
- 0, 0);
+ uint32 outputRate = uint32(11025 * semitoneAndSampleRate_to_sampleStep(note, sfxRootNoteOffs, sfxRate, 11025, 0x2000));
+
+ _currentSFX = Audio::makeLinearInputStream(sfxPlaybackBuffer, playbackBufferSize,
+ outputRate, Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_LITTLE_ENDIAN | Audio::Mixer::FLAG_AUTOFREE, 0, 0);
_mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_sfxHandle, _currentSFX);
}
-void SoundTowns::setPitch(uint8 *&data, uint32 &size, int8 sourcePitch, int8 targetPitch) {
- if (sourcePitch == targetPitch)
- return;
+void SoundTowns::beginFadeOut() {
+ //int vol = 255;
+ haltTrack();
+}
+
+int SoundTowns::open() {
+ if (!_driver)
+ return 255;
+
+ int ret = _driver->open();
+ if (ret)
+ return ret;
+
+ _driver->setTimerCallback(this, &onTimer);
+ return 0;
+}
+
+void SoundTowns::close() {
+ if (_driver)
+ _driver->close();
+}
+
+void SoundTowns::send(uint32 b) {
+ _driver->send(b);
+}
+
+uint32 SoundTowns::getBaseTempo(void) {
+ return _driver ? _driver->getBaseTempo() : 0;
+}
+
+bool SoundTowns::loadInstruments() {
+ if (!_musicTrackData)
+ _musicTrackData = new uint8[0xC58A];
+
+ memset(_musicTrackData, 0, 0xC58A);
+ uint8 * twm = _engine->resource()->fileData("twmusic.pak", 0);
+ if (!twm)
+ return false;
+ Screen::decodeFrame4(twm, _musicTrackData, 0x8BF0);
+ _driver->loadFmInstruments(_musicTrackData + 8);
+
+ memset (_musicTrackData, 0, 0xC58A);
+ Screen::decodeFrame4(twm + 0x0CA0, _musicTrackData, 0xC58A);
+ delete [] twm;
+ _driver->loadWaveInstruments(_musicTrackData + 8);
+
+ return true;
+}
+
+void SoundTowns::playEuphonyTrack(uint32 offset, int loop) {
+ if (!_musicTrackData)
+ _musicTrackData = new uint8[0xC58A];
+
+ memset(_musicTrackData, 0, 0xC58A);
+ uint8 * twm = _engine->resource()->fileData("twmusic.pak", 0);
+ Screen::decodeFrame4(twm + 0x4b70 + offset, _musicTrackData, 0xC58A);
+ delete [] twm;
+
+ Common::StackLock lock(_mutex);
+
+ uint8 * used = _musicTrackData + 0x374;
+ uint8 * fchan = _musicTrackData + 0x6d4;
+ uint8 * wchan = _musicTrackData + 0x6dA;
+
+ for (uint8 i = 0; i < 6; i++) {
+ if (used[fchan[i]])
+ _driver->assignFmChannel(fchan[i], i);
+ }
+
+ for (uint8 i = 0; i < 8; i++) {
+ if (used[wchan[i]])
+ _driver->assignWaveChannel(wchan[i], i);
+ }
+
+ for (uint8 i = 0; i < 16; i++) {
+ if (!used[i])
+ _driver->removeChannel(i);
+ }
+ _driver->send(0x79B0);
+
+ if (_parser)
+ delete _parser;
+
+ _parser = new MidiParser_EuD;
+ _parser->property(MidiParser::mpAutoLoop, loop);
+ _parser->loadMusic(_musicTrackData, 0);
+ _parser->jumpToTick(0);
+
+ _parser->setMidiDriver(this);
+ _parser->setTimerRate(getBaseTempo());
+}
+
+void SoundTowns::onTimer(void * data) {
+ SoundTowns *music = (SoundTowns *)data;
+ Common::StackLock lock(music->_mutex);
+ if (music->_parser)
+ music->_parser->onTimer();
+}
+
+float SoundTowns::semitoneAndSampleRate_to_sampleStep(int8 semiTone, int8 semiToneRootkey,
+ uint32 sampleRate, uint32 outputRate, int32 pitchWheel) {
+ if (semiTone < 0)
+ semiTone = 0;
+ if (semiTone > 119)
+ semiTone = 119;
+ if (semiTone < 0)
+ semiTone = 0;
+ if (semiTone > 119)
+ semiTone = 119;
- if (sourcePitch < 0)
- sourcePitch = 0;
- if (sourcePitch > 119)
- sourcePitch = 119;
- if (targetPitch < 0)
- targetPitch = 0;
- if (targetPitch > 119)
- targetPitch = 119;
-
static const float noteFrq[] = {
0004.13f, 0004.40f, 0004.64f, 0004.95f, 0005.16f, 0005.50f, 0005.80f, 0006.19f, 0006.60f, 0006.86f,
0007.43f, 0007.73f, 0008.25f, 0008.80f, 0009.28f, 0009.90f, 0010.31f, 0011.00f, 0011.60f, 0012.38f,
@@ -276,28 +1225,12 @@ void SoundTowns::setPitch(uint8 *&data, uint32 &size, int8 sourcePitch, int8 tar
2376.00f, 2534.40f, 2640.00f, 2816.00f, 2970.40f, 3168.00f, 3379.20f, 3520.00f, 3801.60f, 3960.00f
};
- const float inc = noteFrq[targetPitch] / noteFrq[sourcePitch];
-
- uint32 estimatedSize = (uint32)(((float) size / inc) + 1);
- uint32 exactSize = 0;
- uint8 * tmp = new uint8[estimatedSize];
- memset(tmp, 0x80, estimatedSize);
-
- int last = 0;
- for (float i = 0; i < size; i += inc) {
- int cur = (int) i;
- if (cur == last + 2)
- tmp[exactSize++] = (data[last] + data[cur - 1] + data[cur]) / 3;
- else if (cur == last)
- tmp[exactSize++] = (data[cur] + data[cur + 1]) / 2;
- else
- tmp[exactSize++] = data[cur];
- last = (int) i;
- }
+ float pwModifier = (pitchWheel - 0x2000) / 0x2000;
+ int8 d = pwModifier ? (pwModifier < 0 ? -1 : 1) : 0;
+ float rateshift = (noteFrq[semiTone] - ((noteFrq[semiTone] -
+ noteFrq[semiTone + d]) * pwModifier * d)) / noteFrq[semiToneRootkey];
- size = MIN(exactSize, estimatedSize);
- delete[] data;
- data = tmp;
+ return (float) sampleRate * 10.0f * rateshift / outputRate;
}
const uint8 SoundTowns::_sfxBTTable[256] = {
diff --git a/sound/softsynth/ym2612.cpp b/sound/softsynth/ym2612.cpp
index 823e371a88..5ee864b168 100644
--- a/sound/softsynth/ym2612.cpp
+++ b/sound/softsynth/ym2612.cpp
@@ -23,10 +23,9 @@
* $Id$
*/
-#include "sound/softsynth/emumidi.h"
-
#include <math.h>
+#include "sound/softsynth/ym2612.h"
#include "common/util.h"
////////////////////////////////////////
@@ -42,153 +41,6 @@ static int *keycodeTable = 0;
static int *keyscaleTable = 0;
static int *attackOut = 0;
-////////////////////////////////////////
-//
-// Class declarations
-//
-////////////////////////////////////////
-
-class Operator2612;
-class Voice2612;
-class MidiChannel_YM2612;
-class MidiDriver_YM2612;
-
-class Operator2612 {
-protected:
- Voice2612 *_owner;
- enum State { _s_ready, _s_attacking, _s_decaying, _s_sustaining, _s_releasing };
- State _state;
- int32 _currentLevel;
- int _frequency;
- uint32 _phase;
- int _lastOutput;
- int _feedbackLevel;
- int _detune;
- int _multiple;
- int32 _totalLevel;
- int _keyScale;
- int _velocity;
- int _specifiedTotalLevel;
- int _specifiedAttackRate;
- int _specifiedDecayRate;
- int _specifiedSustainLevel;
- int _specifiedSustainRate;
- int _specifiedReleaseRate;
- int _tickCount;
- int _attackTime;
- int32 _decayRate;
- int32 _sustainLevel;
- int32 _sustainRate;
- int32 _releaseRate;
-
-public:
- Operator2612 (Voice2612 *owner);
- ~Operator2612();
- void feedbackLevel(int level);
- void setInstrument(byte const *instrument);
- void velocity(int velo);
- void keyOn();
- void keyOff();
- void frequency(int freq);
- void nextTick(const int *phaseShift, int *outbuf, int buflen);
- bool inUse() { return (_state != _s_ready); }
-};
-
-class Voice2612 {
-public:
- Voice2612 *next;
- uint16 _rate;
-
-protected:
- Operator2612 *_opr[4];
- int _velocity;
- int _control7;
- int _note;
- int _frequencyOffs;
- int _frequency;
- int _algorithm;
-
- int *_buffer;
- int _buflen;
-
-public:
- Voice2612();
- ~Voice2612();
- void setControlParameter(int control, int value);
- void setInstrument(byte const *instrument);
- void velocity(int velo);
- void nextTick(int *outbuf, int buflen);
- void noteOn(int n, int onVelo);
- bool noteOff(int note);
- void pitchBend(int value);
- void recalculateFrequency();
-};
-
-class MidiChannel_YM2612 : public MidiChannel {
-protected:
- uint16 _rate;
- Voice2612 *_voices;
- Voice2612 *_next_voice;
-
-public:
- void removeAllVoices();
- void nextTick(int *outbuf, int buflen);
- void rate(uint16 r);
-
-public:
- MidiChannel_YM2612();
- virtual ~MidiChannel_YM2612();
-
- // MidiChannel interface
- MidiDriver *device() { return 0; }
- byte getNumber() { return 0; }
- void release() { }
- void send(uint32 b) { }
- void noteOff(byte note);
- void noteOn(byte note, byte onVelo);
- void programChange(byte program) { }
- void pitchBend(int16 value);
- void controlChange(byte control, byte value);
- void pitchBendFactor(byte value) { }
- void sysEx_customInstrument(uint32 type, const byte *instr);
-};
-
-class MidiDriver_YM2612 : public MidiDriver_Emulated {
-protected:
- MidiChannel_YM2612 *_channel[16];
-
- int _next_voice;
- int _volume;
-
-protected:
- static void createLookupTables();
- void nextTick(int16 *buf1, int buflen);
- int volume(int val = -1) { if (val >= 0) _volume = val; return _volume; }
- void rate(uint16 r);
-
- void generateSamples(int16 *buf, int len);
-
-public:
- MidiDriver_YM2612(Audio::Mixer *mixer);
- virtual ~MidiDriver_YM2612();
-
- int open();
- void close();
- void send(uint32 b);
- void send(byte channel, uint32 b); // Supports higher than channel 15
- uint32 property(int prop, uint32 param) { return 0; }
-
- void setPitchBendRange(byte channel, uint range) { }
- void sysEx(const byte *msg, uint16 length);
-
- MidiChannel *allocateChannel() { return 0; }
- MidiChannel *getPercussionChannel() { return 0; }
-
-
- // AudioStream API
- bool isStereo() const { return true; }
- int getRate() const { return _mixer->getOutputRate(); }
-};
////////////////////////////////////////
//
@@ -726,13 +578,7 @@ MidiDriver_YM2612::~MidiDriver_YM2612() {
int i;
for (i = 0; i < ARRAYSIZE(_channel); i++)
delete _channel[i];
- delete sintbl;
- delete powtbl;
- delete frequencyTable;
- delete keycodeTable;
- delete keyscaleTable;
- delete attackOut;
- sintbl = powtbl = frequencyTable = keycodeTable = keyscaleTable = attackOut = 0;
+ removeLookupTables();
}
int MidiDriver_YM2612::open() {
@@ -896,6 +742,16 @@ void MidiDriver_YM2612::createLookupTables() {
}
}
+void MidiDriver_YM2612::removeLookupTables() {
+ delete [] sintbl;
+ delete [] powtbl;
+ delete [] frequencyTable;
+ delete [] keycodeTable;
+ delete [] keyscaleTable;
+ delete [] attackOut;
+ sintbl = powtbl = frequencyTable = keycodeTable = keyscaleTable = attackOut = 0;
+}
+
////////////////////////////////////////
//
// MidiDriver_YM2612 factory
@@ -905,3 +761,4 @@ void MidiDriver_YM2612::createLookupTables() {
MidiDriver *MidiDriver_YM2612_create(Audio::Mixer *mixer) {
return new MidiDriver_YM2612(mixer);
}
+
diff --git a/sound/softsynth/ym2612.h b/sound/softsynth/ym2612.h
new file mode 100644
index 0000000000..2831229903
--- /dev/null
+++ b/sound/softsynth/ym2612.h
@@ -0,0 +1,156 @@
+#ifndef SOUND_SOFTSYNTH_Y2612_H
+#define SOUND_SOFTSYNTH_Y2612_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+
+#include "sound/softsynth/emumidi.h"
+
+////////////////////////////////////////
+//
+// Class declarations
+//
+////////////////////////////////////////
+
+class Voice2612;
+class Operator2612 {
+protected:
+ Voice2612 *_owner;
+ enum State { _s_ready, _s_attacking, _s_decaying, _s_sustaining, _s_releasing };
+ State _state;
+ int32 _currentLevel;
+ int _frequency;
+ uint32 _phase;
+ int _lastOutput;
+ int _feedbackLevel;
+ int _detune;
+ int _multiple;
+ int32 _totalLevel;
+ int _keyScale;
+ int _velocity;
+ int _specifiedTotalLevel;
+ int _specifiedAttackRate;
+ int _specifiedDecayRate;
+ int _specifiedSustainLevel;
+ int _specifiedSustainRate;
+ int _specifiedReleaseRate;
+ int _tickCount;
+ int _attackTime;
+ int32 _decayRate;
+ int32 _sustainLevel;
+ int32 _sustainRate;
+ int32 _releaseRate;
+
+public:
+ Operator2612 (Voice2612 *owner);
+ ~Operator2612();
+ void feedbackLevel(int level);
+ void setInstrument(byte const *instrument);
+ void velocity(int velo);
+ void keyOn();
+ void keyOff();
+ void frequency(int freq);
+ void nextTick(const int *phaseShift, int *outbuf, int buflen);
+ bool inUse() { return (_state != _s_ready); }
+};
+
+class Voice2612 {
+public:
+ Voice2612 *next;
+ uint16 _rate;
+
+protected:
+ Operator2612 *_opr[4];
+ int _velocity;
+ int _control7;
+ int _note;
+ int _frequencyOffs;
+ int _frequency;
+ int _algorithm;
+
+ int *_buffer;
+ int _buflen;
+
+public:
+ Voice2612();
+ ~Voice2612();
+ void setControlParameter(int control, int value);
+ void setInstrument(byte const *instrument);
+ void velocity(int velo);
+ void nextTick(int *outbuf, int buflen);
+ void noteOn(int n, int onVelo);
+ bool noteOff(int note);
+ void pitchBend(int value);
+ void recalculateFrequency();
+};
+
+class MidiChannel_YM2612 : public MidiChannel {
+protected:
+ uint16 _rate;
+ Voice2612 *_voices;
+ Voice2612 *_next_voice;
+
+public:
+ void removeAllVoices();
+ void nextTick(int *outbuf, int buflen);
+ void rate(uint16 r);
+
+public:
+ MidiChannel_YM2612();
+ virtual ~MidiChannel_YM2612();
+
+ // MidiChannel interface
+ MidiDriver *device() { return 0; }
+ byte getNumber() { return 0; }
+ void release() { }
+ void send(uint32 b) { }
+ void noteOff(byte note);
+ void noteOn(byte note, byte onVelo);
+ void programChange(byte program) { }
+ void pitchBend(int16 value);
+ void controlChange(byte control, byte value);
+ void pitchBendFactor(byte value) { }
+ void sysEx_customInstrument(uint32 type, const byte *instr);
+};
+
+class MidiDriver_YM2612 : public MidiDriver_Emulated {
+protected:
+ MidiChannel_YM2612 *_channel[16];
+
+ int _next_voice;
+ int _volume;
+
+protected:
+ void nextTick(int16 *buf1, int buflen);
+ int volume(int val = -1) { if (val >= 0) _volume = val; return _volume; }
+ void rate(uint16 r);
+
+ void generateSamples(int16 *buf, int len);
+
+public:
+ MidiDriver_YM2612(Audio::Mixer *mixer);
+ virtual ~MidiDriver_YM2612();
+
+ static void createLookupTables();
+ static void removeLookupTables();
+
+ int open();
+ void close();
+ void send(uint32 b);
+ void send(byte channel, uint32 b); // Supports higher than channel 15
+ uint32 property(int prop, uint32 param) { return 0; }
+
+ void setPitchBendRange(byte channel, uint range) { }
+ void sysEx(const byte *msg, uint16 length);
+
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+
+
+ // AudioStream API
+ bool isStereo() const { return true; }
+ int getRate() const { return _mixer->getOutputRate(); }
+};
+
+#endif
+