From d1645e2fbfbf7246de3ab3ca61dea0b4ffe1a296 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sat, 24 Mar 2007 00:04:08 +0000 Subject: Commit of patch #1686414 ("Kyra: FM-Towns music"). svn-id: r26285 --- engines/kyra/gui.cpp | 22 +- engines/kyra/kyra.h | 3 +- engines/kyra/resource.cpp | 5 + engines/kyra/sound.cpp | 13 +- engines/kyra/sound.h | 57 ++- engines/kyra/sound_towns.cpp | 1065 +++++++++++++++++++++++++++++++++++++++--- sound/softsynth/ym2612.cpp | 169 +------ sound/softsynth/ym2612.h | 156 +++++++ 8 files changed, 1236 insertions(+), 254 deletions(-) create mode 100644 sound/softsynth/ym2612.h 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 + 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 +#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 + -- cgit v1.2.3