diff options
Diffstat (limited to 'engines/kyra/sound_towns.cpp')
-rw-r--r-- | engines/kyra/sound_towns.cpp | 2101 |
1 files changed, 1920 insertions, 181 deletions
diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp index 4265533507..e96cef735c 100644 --- a/engines/kyra/sound_towns.cpp +++ b/engines/kyra/sound_towns.cpp @@ -34,18 +34,16 @@ #include "common/util.h" -#include <math.h> - #define EUPHONY_FADEOUT_TICKS 600 namespace Kyra { -enum ChannelState { _s_ready, _s_attacking, _s_decaying, _s_sustaining, _s_releasing }; +enum EnvelopeState { s_ready, s_attacking, s_decaying, s_sustaining, s_releasing }; -class MidiChannel_EuD : public MidiChannel { +class Towns_EuphonyChannel : public MidiChannel { public: - MidiChannel_EuD() {} - ~MidiChannel_EuD() {} + Towns_EuphonyChannel() {} + ~Towns_EuphonyChannel() {} virtual void nextTick(int32 *outbuf, int buflen) = 0; virtual void rate(uint16 r) = 0; @@ -54,10 +52,10 @@ protected: uint16 _rate; }; -class MidiChannel_EuD_FM : public MidiChannel_EuD { +class Towns_EuphonyFmChannel : public Towns_EuphonyChannel { public: - MidiChannel_EuD_FM(); - virtual ~MidiChannel_EuD_FM(); + Towns_EuphonyFmChannel(); + virtual ~Towns_EuphonyFmChannel(); void nextTick(int32 *outbuf, int buflen); void rate(uint16 r); @@ -79,13 +77,13 @@ protected: Voice2612 *_voice; }; -class MidiChannel_EuD_WAVE : public MidiChannel_EuD { +class Towns_EuphonyPcmChannel : public Towns_EuphonyChannel { public: void nextTick(int32 *outbuf, int buflen); void rate(uint16 r); - MidiChannel_EuD_WAVE(); - virtual ~MidiChannel_EuD_WAVE(); + Towns_EuphonyPcmChannel(); + virtual ~Towns_EuphonyPcmChannel(); // MidiChannel interface MidiDriver *device() { return 0; } @@ -126,9 +124,9 @@ protected: int32 keyOffset; int32 keyNote; const int8 *_samples; - } * _snd[8]; + } *_snd[8]; struct Env { - ChannelState state; + EnvelopeState state; int32 currentLevel; int32 rate; int32 tickCount; @@ -141,40 +139,39 @@ protected: int32 releaseRate; int32 rootKeyOffset; int32 size; - } * _env[8]; - } * _voice; + } *_env[8]; + } *_voice; }; -class SoundTowns_EuphonyTrackQueue { +class Towns_EuphonyTrackQueue { public: - SoundTowns_EuphonyTrackQueue(SoundTowns_EuphonyDriver *driver, SoundTowns_EuphonyTrackQueue *last); - ~SoundTowns_EuphonyTrackQueue() {} + Towns_EuphonyTrackQueue(Towns_EuphonyDriver *driver, Towns_EuphonyTrackQueue *last); + ~Towns_EuphonyTrackQueue() {} - void release(); + Towns_EuphonyTrackQueue *release(); void initDriver(); - void loadDataToCurrentPosition(uint8 * trackdata, uint32 size, bool loop = 0); - void loadDataToEndOfQueue(uint8 * trackdata, uint32 size, bool loop = 0); + void loadDataToCurrentPosition(uint8 *trackdata, uint32 size, bool loop = 0); + void loadDataToEndOfQueue(uint8 *trackdata, uint32 size, bool loop = 0); void setPlayBackStatus(bool playing); - SoundTowns_EuphonyTrackQueue * reset(); bool isPlaying() {return _playing; } - uint8 * trackData() {return _trackData; } + uint8 *trackData() {return _trackData; } bool _loop; - SoundTowns_EuphonyTrackQueue * _next; + Towns_EuphonyTrackQueue *_next; private: - uint8 * _trackData; - uint8 * _used; - uint8 * _fchan; - uint8 * _wchan; + uint8 *_trackData; + uint8 *_used; + uint8 *_fchan; + uint8 *_wchan; bool _playing; - SoundTowns_EuphonyDriver * _driver; - SoundTowns_EuphonyTrackQueue * _last; + Towns_EuphonyDriver *_driver; + Towns_EuphonyTrackQueue *_last; }; -class MidiParser_EuD : public MidiParser { +class Towns_EuphonyParser : public MidiParser { public: - MidiParser_EuD(SoundTowns_EuphonyTrackQueue * queue); + Towns_EuphonyParser(Towns_EuphonyTrackQueue * queue); bool loadMusic (byte *data, uint32 size); int32 calculateTempo(int16 val); @@ -183,11 +180,11 @@ protected: void resetTracking(); void setup(); - byte * _enable; - byte * _mode; - byte * _channel; - byte * _adjVelo; - int8 * _adjNote; + byte *_enable; + byte *_mode; + byte *_channel; + byte *_adjVelo; + int8 *_adjNote; uint8 _firstBaseTickStep; uint8 _nextBaseTickStep; @@ -195,13 +192,13 @@ protected: uint32 _baseTick; byte _tempo[3]; - SoundTowns_EuphonyTrackQueue * _queue; + Towns_EuphonyTrackQueue *_queue; }; -class SoundTowns_EuphonyDriver : public MidiDriver_Emulated { +class Towns_EuphonyDriver : public MidiDriver_Emulated { public: - SoundTowns_EuphonyDriver(Audio::Mixer *mixer); - virtual ~SoundTowns_EuphonyDriver(); + Towns_EuphonyDriver(Audio::Mixer *mixer); + virtual ~Towns_EuphonyDriver(); int open(); void close(); @@ -213,7 +210,7 @@ public: void loadFmInstruments(const byte *instr); void loadWaveInstruments(const byte *instr); - SoundTowns_EuphonyTrackQueue * queue() { return _queue; } + Towns_EuphonyTrackQueue *queue() { return _queue; } MidiChannel *allocateChannel() { return 0; } MidiChannel *getPercussionChannel() { return 0; } @@ -237,10 +234,10 @@ protected: void generateSamples(int16 *buf, int len); - MidiChannel_EuD_FM *_fChannel[6]; - MidiChannel_EuD_WAVE *_wChannel[8]; - MidiChannel_EuD * _channel[16]; - SoundTowns_EuphonyTrackQueue * _queue; + Towns_EuphonyFmChannel *_fChannel[6]; + Towns_EuphonyPcmChannel *_wChannel[8]; + Towns_EuphonyChannel *_channel[16]; + Towns_EuphonyTrackQueue *_queue; int _volume; bool _fading; @@ -251,23 +248,23 @@ protected: int8 * _waveSounds[10]; }; -MidiChannel_EuD_FM::MidiChannel_EuD_FM() { +Towns_EuphonyFmChannel::Towns_EuphonyFmChannel() { _voice = new Voice2612; } -MidiChannel_EuD_FM::~MidiChannel_EuD_FM() { +Towns_EuphonyFmChannel::~Towns_EuphonyFmChannel() { delete _voice; } -void MidiChannel_EuD_FM::noteOn(byte note, byte onVelo) { +void Towns_EuphonyFmChannel::noteOn(byte note, byte onVelo) { _voice->noteOn(note, onVelo); } -void MidiChannel_EuD_FM::noteOff(byte note) { +void Towns_EuphonyFmChannel::noteOff(byte note) { _voice->noteOff(note); } -void MidiChannel_EuD_FM::controlChange(byte control, byte value) { +void Towns_EuphonyFmChannel::controlChange(byte control, byte value) { if (control == 121) { // Reset controller delete _voice; @@ -279,25 +276,25 @@ void MidiChannel_EuD_FM::controlChange(byte control, byte value) { } } -void MidiChannel_EuD_FM::sysEx_customInstrument(uint32, const byte *fmInst) { +void Towns_EuphonyFmChannel::sysEx_customInstrument(uint32, const byte *fmInst) { _voice->_rate = _rate; _voice->setInstrument(fmInst); } -void MidiChannel_EuD_FM::pitchBend(int16 value) { +void Towns_EuphonyFmChannel::pitchBend(int16 value) { _voice->pitchBend(value); } -void MidiChannel_EuD_FM::nextTick(int32 *outbuf, int buflen) { +void Towns_EuphonyFmChannel::nextTick(int32 *outbuf, int buflen) { _voice->nextTick((int*) outbuf, buflen); } -void MidiChannel_EuD_FM::rate(uint16 r) { +void Towns_EuphonyFmChannel::rate(uint16 r) { _rate = r; _voice->_rate = r; } -MidiChannel_EuD_WAVE::MidiChannel_EuD_WAVE() { +Towns_EuphonyPcmChannel::Towns_EuphonyPcmChannel() { _voice = new Voice; for (uint8 i = 0; i < 8; i++) { _voice->_env[i] = new Voice::Env; @@ -310,7 +307,7 @@ MidiChannel_EuD_WAVE::MidiChannel_EuD_WAVE() { _current = -1; } -MidiChannel_EuD_WAVE::~MidiChannel_EuD_WAVE() { +Towns_EuphonyPcmChannel::~Towns_EuphonyPcmChannel() { for (uint8 i = 0; i < 8; i++) { if (_voice->_snd[i]) delete _voice->_snd[i]; @@ -319,7 +316,7 @@ MidiChannel_EuD_WAVE::~MidiChannel_EuD_WAVE() { delete _voice; } -void MidiChannel_EuD_WAVE::noteOn(byte note, byte onVelo) { +void Towns_EuphonyPcmChannel::noteOn(byte note, byte onVelo) { _note = note; velocity(onVelo); _phase = 0; @@ -329,24 +326,24 @@ void MidiChannel_EuD_WAVE::noteOn(byte note, byte onVelo) { break; } - _voice->_env[_current]->state = _s_attacking; + _voice->_env[_current]->state = s_attacking; _voice->_env[_current]->currentLevel = 0; _voice->_env[_current]->rate = _rate; _voice->_env[_current]->tickCount = 0; } -void MidiChannel_EuD_WAVE::noteOff(byte note) { +void Towns_EuphonyPcmChannel::noteOff(byte note) { if (_current == -1) return; - if (_voice->_env[_current]->state == _s_ready) + if (_voice->_env[_current]->state == s_ready) return; - _voice->_env[_current]->state = _s_releasing; + _voice->_env[_current]->state = s_releasing; _voice->_env[_current]->releaseLevel = _voice->_env[_current]->currentLevel; _voice->_env[_current]->tickCount = 0; } -void MidiChannel_EuD_WAVE::controlChange(byte control, byte value) { +void Towns_EuphonyPcmChannel::controlChange(byte control, byte value) { switch (control) { case 0x07: // volume @@ -377,7 +374,7 @@ void MidiChannel_EuD_WAVE::controlChange(byte control, byte value) { } } -void MidiChannel_EuD_WAVE::sysEx_customInstrument(uint32 type, const byte *fmInst) { +void Towns_EuphonyPcmChannel::sysEx_customInstrument(uint32 type, const byte *fmInst) { if (type == 0x80) { for (uint8 i = 0; i < 8; i++) { const byte * const* pos = (const byte * const*) fmInst; @@ -406,7 +403,7 @@ void MidiChannel_EuD_WAVE::sysEx_customInstrument(uint32 type, const byte *fmIns _voice->split[i] = READ_LE_UINT16(fmInst + 16 + 2 * i); _voice->id[i] = READ_LE_UINT32(fmInst + 32 + 4 * i); _voice->_snd[i] = 0; - _voice->_env[i]->state = _s_ready; + _voice->_env[i]->state = s_ready; _voice->_env[i]->currentLevel = 0; _voice->_env[i]->totalLevel = *(fmInst + 64 + 8 * i); _voice->_env[i]->attackRate = *(fmInst + 65 + 8 * i) * 10; @@ -419,11 +416,11 @@ void MidiChannel_EuD_WAVE::sysEx_customInstrument(uint32 type, const byte *fmIns } } -void MidiChannel_EuD_WAVE::pitchBend(int16 value) { +void Towns_EuphonyPcmChannel::pitchBend(int16 value) { _frequencyOffs = value; } -void MidiChannel_EuD_WAVE::nextTick(int32 *outbuf, int buflen) { +void Towns_EuphonyPcmChannel::nextTick(int32 *outbuf, int buflen) { if (_current == -1 || !_voice->_snd[_current] || !_voice->_env[_current]->state || !_velocity) { velocity(0); _current = -1; @@ -475,13 +472,13 @@ void MidiChannel_EuD_WAVE::nextTick(int32 *outbuf, int buflen) { } } -void MidiChannel_EuD_WAVE::evpNextTick() { +void Towns_EuphonyPcmChannel::evpNextTick() { switch (_voice->_env[_current]->state) { - case _s_ready: + case s_ready: _voice->_env[_current]->currentLevel = 0; return; - case _s_attacking: + case s_attacking: if (_voice->_env[_current]->attackRate == 0) _voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel; else if (_voice->_env[_current]->attackRate >= 1270) @@ -493,12 +490,12 @@ void MidiChannel_EuD_WAVE::evpNextTick() { if (_voice->_env[_current]->currentLevel >= _voice->_env[_current]->totalLevel) { _voice->_env[_current]->currentLevel = _voice->_env[_current]->totalLevel; - _voice->_env[_current]->state = _s_decaying; + _voice->_env[_current]->state = s_decaying; _voice->_env[_current]->tickCount = 0; } break; - case _s_decaying: + case s_decaying: if (_voice->_env[_current]->decayRate == 0) _voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel; else if (_voice->_env[_current]->decayRate >= 1270) @@ -512,12 +509,12 @@ void MidiChannel_EuD_WAVE::evpNextTick() { if (_voice->_env[_current]->currentLevel <= _voice->_env[_current]->sustainLevel) { _voice->_env[_current]->currentLevel = _voice->_env[_current]->sustainLevel; - _voice->_env[_current]->state = _s_sustaining; + _voice->_env[_current]->state = s_sustaining; _voice->_env[_current]->tickCount = 0; } break; - case _s_sustaining: + case s_sustaining: if (_voice->_env[_current]->sustainRate == 0) _voice->_env[_current]->currentLevel = 0; else if (_voice->_env[_current]->sustainRate >= 2540) @@ -531,12 +528,12 @@ void MidiChannel_EuD_WAVE::evpNextTick() { if (_voice->_env[_current]->currentLevel <= 0) { _voice->_env[_current]->currentLevel = 0; - _voice->_env[_current]->state = _s_ready; + _voice->_env[_current]->state = s_ready; _voice->_env[_current]->tickCount = 0; } break; - case _s_releasing: + case s_releasing: if (_voice->_env[_current]->releaseRate == 0) _voice->_env[_current]->currentLevel = 0; else if (_voice->_env[_current]->releaseRate >= 1270) @@ -550,7 +547,7 @@ void MidiChannel_EuD_WAVE::evpNextTick() { if (_voice->_env[_current]->currentLevel <= 0) { _voice->_env[_current]->currentLevel = 0; - _voice->_env[_current]->state = _s_ready; + _voice->_env[_current]->state = s_ready; } break; @@ -559,15 +556,15 @@ void MidiChannel_EuD_WAVE::evpNextTick() { } } -void MidiChannel_EuD_WAVE::rate(uint16 r) { +void Towns_EuphonyPcmChannel::rate(uint16 r) { _rate = r; } -void MidiChannel_EuD_WAVE::velocity(int velo) { +void Towns_EuphonyPcmChannel::velocity(int velo) { _velocity = velo; } -SoundTowns_EuphonyDriver::SoundTowns_EuphonyDriver(Audio::Mixer *mixer) +Towns_EuphonyDriver::Towns_EuphonyDriver(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) { _volume = 255; _fadestate = EUPHONY_FADEOUT_TICKS; @@ -576,9 +573,9 @@ SoundTowns_EuphonyDriver::SoundTowns_EuphonyDriver(Audio::Mixer *mixer) MidiDriver_YM2612::createLookupTables(); for (uint8 i = 0; i < 6; i++) - _channel[i] = _fChannel[i] = new MidiChannel_EuD_FM; + _channel[i] = _fChannel[i] = new Towns_EuphonyFmChannel; for (uint8 i = 0; i < 8; i++) - _channel[i + 6] = _wChannel[i] = new MidiChannel_EuD_WAVE; + _channel[i + 6] = _wChannel[i] = new Towns_EuphonyPcmChannel; _channel[14] = _channel[15] = 0; _fmInstruments = _waveInstruments = 0; @@ -587,10 +584,10 @@ SoundTowns_EuphonyDriver::SoundTowns_EuphonyDriver(Audio::Mixer *mixer) rate(getRate()); fading(0); - _queue = new SoundTowns_EuphonyTrackQueue(this, 0); + _queue = new Towns_EuphonyTrackQueue(this, 0); } -SoundTowns_EuphonyDriver::~SoundTowns_EuphonyDriver() { +Towns_EuphonyDriver::~Towns_EuphonyDriver() { for (int i = 0; i < 6; i++) delete _fChannel[i]; for (int i = 0; i < 8; i++) @@ -622,7 +619,7 @@ SoundTowns_EuphonyDriver::~SoundTowns_EuphonyDriver() { } } -int SoundTowns_EuphonyDriver::open() { +int Towns_EuphonyDriver::open() { if (_isOpen) return MERR_ALREADY_OPEN; MidiDriver_Emulated::open(); @@ -633,18 +630,18 @@ int SoundTowns_EuphonyDriver::open() { return 0; } -void SoundTowns_EuphonyDriver::close() { +void Towns_EuphonyDriver::close() { if (!_isOpen) return; _isOpen = false; _mixer->stopHandle(_mixerSoundHandle); } -void SoundTowns_EuphonyDriver::send(uint32 b) { +void Towns_EuphonyDriver::send(uint32 b) { send(b & 0xF, b & 0xFFFFFFF0); } -void SoundTowns_EuphonyDriver::send(byte chan, uint32 b) { +void Towns_EuphonyDriver::send(byte chan, uint32 b) { byte param2 = (byte) ((b >> 16) & 0xFF); byte param1 = (byte) ((b >> 8) & 0xFF); byte cmd = (byte) (b & 0xF0); @@ -703,18 +700,18 @@ void SoundTowns_EuphonyDriver::send(byte chan, uint32 b) { _channel[chan]->pitchBend((param1 | (param2 << 7)) - 0x2000); break; default: - warning("SoundTowns_EuphonyDriver: Unknown send() command 0x%02X", cmd); + warning("Towns_EuphonyDriver: Unknown send() command 0x%02X", cmd); } } -void SoundTowns_EuphonyDriver::loadFmInstruments(const byte *instr) { +void Towns_EuphonyDriver::loadFmInstruments(const byte *instr) { if (_fmInstruments) delete[] _fmInstruments; _fmInstruments = new uint8[0x1800]; memcpy(_fmInstruments, instr, 0x1800); } -void SoundTowns_EuphonyDriver::loadWaveInstruments(const byte *instr) { +void Towns_EuphonyDriver::loadWaveInstruments(const byte *instr) { if (_waveInstruments) delete[] _waveInstruments; _waveInstruments = new uint8[0x1000]; @@ -739,24 +736,24 @@ void SoundTowns_EuphonyDriver::loadWaveInstruments(const byte *instr) { } -void SoundTowns_EuphonyDriver::assignFmChannel(uint8 midiChannelNumber, uint8 fmChannelNumber) { +void Towns_EuphonyDriver::assignFmChannel(uint8 midiChannelNumber, uint8 fmChannelNumber) { _channel[midiChannelNumber] = _fChannel[fmChannelNumber]; } -void SoundTowns_EuphonyDriver::assignWaveChannel(uint8 midiChannelNumber, uint8 waveChannelNumber) { +void Towns_EuphonyDriver::assignWaveChannel(uint8 midiChannelNumber, uint8 waveChannelNumber) { _channel[midiChannelNumber] = _wChannel[waveChannelNumber]; } -void SoundTowns_EuphonyDriver::removeChannel(uint8 midiChannelNumber) { +void Towns_EuphonyDriver::removeChannel(uint8 midiChannelNumber) { _channel[midiChannelNumber] = 0; } -void SoundTowns_EuphonyDriver::generateSamples(int16 *data, int len) { +void Towns_EuphonyDriver::generateSamples(int16 *data, int len) { memset(data, 0, 2 * sizeof(int16) * len); nextTick(data, len); } -void SoundTowns_EuphonyDriver::nextTick(int16 *buf1, int buflen) { +void Towns_EuphonyDriver::nextTick(int16 *buf1, int buflen) { int32 *buf0 = (int32 *)buf1; for (int i = 0; i < ARRAYSIZE(_channel); i++) { @@ -779,26 +776,26 @@ void SoundTowns_EuphonyDriver::nextTick(int16 *buf1, int buflen) { } } -void SoundTowns_EuphonyDriver::rate(uint16 r) { +void Towns_EuphonyDriver::rate(uint16 r) { for (uint8 i = 0; i < 16; i++) { if (_channel[i]) _channel[i]->rate(r); } } -void SoundTowns_EuphonyDriver::fading(bool status) { +void Towns_EuphonyDriver::fading(bool status) { _fading = status; if (!_fading) _fadestate = EUPHONY_FADEOUT_TICKS; } -MidiParser_EuD::MidiParser_EuD(SoundTowns_EuphonyTrackQueue * queue) : MidiParser(), +Towns_EuphonyParser::Towns_EuphonyParser(Towns_EuphonyTrackQueue * queue) : MidiParser(), _firstBaseTickStep(0x33), _nextBaseTickStep(0x33) { _initialTempo = calculateTempo(0x5a); _queue = queue; } -void MidiParser_EuD::parseNextEvent(EventInfo &info) { +void Towns_EuphonyParser::parseNextEvent(EventInfo &info) { byte *pos = _position._play_pos; if (_queue->_next) { @@ -878,7 +875,7 @@ void MidiParser_EuD::parseNextEvent(EventInfo &info) { pos += 6; } } else if (cmd == 0xF2) { - static uint16 tickTable [] = { 0x180, 0xC0, 0x80, 0x60, 0x40, 0x30, 0x20, 0x18 }; + static const uint16 tickTable[] = { 0x180, 0xC0, 0x80, 0x60, 0x40, 0x30, 0x20, 0x18 }; _baseTick += tickTable[_nextBaseTickStep >> 4] * ((_nextBaseTickStep & 0x0f) + 1); _nextBaseTickStep = pos[1]; pos += 6; @@ -920,15 +917,14 @@ void MidiParser_EuD::parseNextEvent(EventInfo &info) { _position._play_pos = pos; } -bool MidiParser_EuD::loadMusic(byte *data, uint32 size) { +bool Towns_EuphonyParser::loadMusic(byte *data, uint32 size) { bool loop = _autoLoop; if (_queue->isPlaying() && !_queue->_loop) { _queue->loadDataToEndOfQueue(data, size, loop); } else { unloadMusic(); - _queue = _queue->reset(); - _queue->release(); + _queue = _queue->release(); _queue->loadDataToCurrentPosition(data, size, loop); setup(); setTrack(0); @@ -937,7 +933,7 @@ bool MidiParser_EuD::loadMusic(byte *data, uint32 size) { return true; } -int32 MidiParser_EuD::calculateTempo(int16 val) { +int32 Towns_EuphonyParser::calculateTempo(int16 val) { int32 tempo = val; if (tempo < 0) @@ -953,7 +949,7 @@ int32 MidiParser_EuD::calculateTempo(int16 val) { return tempo; } -void MidiParser_EuD::resetTracking() { +void Towns_EuphonyParser::resetTracking() { MidiParser::resetTracking(); _nextBaseTickStep = _firstBaseTickStep; @@ -962,7 +958,7 @@ void MidiParser_EuD::resetTracking() { _queue->setPlayBackStatus(false); } -void MidiParser_EuD::setup() { +void Towns_EuphonyParser::setup() { uint8 *data = _queue->trackData(); if (!data) return; @@ -984,7 +980,7 @@ void MidiParser_EuD::setup() { _tracks[0] = data + 0x806; } -SoundTowns_EuphonyTrackQueue::SoundTowns_EuphonyTrackQueue(SoundTowns_EuphonyDriver * driver, SoundTowns_EuphonyTrackQueue * last) { +Towns_EuphonyTrackQueue::Towns_EuphonyTrackQueue(Towns_EuphonyDriver * driver, Towns_EuphonyTrackQueue * last) { _trackData = 0; _next = 0; _driver = driver; @@ -993,22 +989,15 @@ SoundTowns_EuphonyTrackQueue::SoundTowns_EuphonyTrackQueue(SoundTowns_EuphonyDri _playing = false; } -void SoundTowns_EuphonyTrackQueue::setPlayBackStatus(bool playing) { - SoundTowns_EuphonyTrackQueue * i = this; +void Towns_EuphonyTrackQueue::setPlayBackStatus(bool playing) { + Towns_EuphonyTrackQueue * i = this; do { i->_playing = playing; i = i->_next; } while (i); } -SoundTowns_EuphonyTrackQueue * SoundTowns_EuphonyTrackQueue::reset() { - SoundTowns_EuphonyTrackQueue * i = this; - while (i->_last) - i = i->_last; - return i; -} - -void SoundTowns_EuphonyTrackQueue::loadDataToCurrentPosition(uint8 * trackdata, uint32 size, bool loop) { +void Towns_EuphonyTrackQueue::loadDataToCurrentPosition(uint8 * trackdata, uint32 size, bool loop) { if (_trackData) delete[] _trackData; _trackData = new uint8[0xC58A]; @@ -1022,17 +1011,17 @@ void SoundTowns_EuphonyTrackQueue::loadDataToCurrentPosition(uint8 * trackdata, _playing = false; } -void SoundTowns_EuphonyTrackQueue::loadDataToEndOfQueue(uint8 * trackdata, uint32 size, bool loop) { +void Towns_EuphonyTrackQueue::loadDataToEndOfQueue(uint8 * trackdata, uint32 size, bool loop) { if (!_trackData) { loadDataToCurrentPosition(trackdata, size, loop); return; } - SoundTowns_EuphonyTrackQueue * i = this; + Towns_EuphonyTrackQueue * i = this; while (i->_next) i = i->_next; - i = i->_next = new SoundTowns_EuphonyTrackQueue(_driver, i); + i = i->_next = new Towns_EuphonyTrackQueue(_driver, i); i->_trackData = new uint8[0xC58A]; memset(i->_trackData, 0, 0xC58A); Screen::decodeFrame4(trackdata, i->_trackData, size); @@ -1044,29 +1033,39 @@ void SoundTowns_EuphonyTrackQueue::loadDataToEndOfQueue(uint8 * trackdata, uint3 i->_playing = _playing; } -void SoundTowns_EuphonyTrackQueue::release() { - SoundTowns_EuphonyTrackQueue * i = _next; - _next = 0; - _playing = false; - _used = _fchan = _wchan = 0; +Towns_EuphonyTrackQueue *Towns_EuphonyTrackQueue::release() { + Towns_EuphonyTrackQueue *i = this; + while (i->_next) + i = i->_next; - if (_trackData) { - delete[] _trackData; - _trackData = 0; - } + Towns_EuphonyTrackQueue *res = i; while (i) { + i->_playing = false; + i->_used = i->_fchan = i->_wchan = 0; if (i->_trackData) { delete[] i->_trackData; i->_trackData = 0; } - i = i->_next; - if (i) - delete i->_last; + i = i->_last; + if (i) { + res = i; + if (i->_next) { + delete i->_next; + i->_next = 0; + } + } } + + if (res->_trackData) { + delete[] res->_trackData; + res->_trackData = 0; + } + + return res; } -void SoundTowns_EuphonyTrackQueue::initDriver() { +void Towns_EuphonyTrackQueue::initDriver() { for (uint8 i = 0; i < 6; i++) { if (_used[_fchan[i]]) _driver->assignFmChannel(_fchan[i], i); @@ -1084,11 +1083,1685 @@ void SoundTowns_EuphonyTrackQueue::initDriver() { _driver->send(0x79B0); } +class TownsPC98_OpnOperator { +public: + TownsPC98_OpnOperator(double rate, const uint8 *rateTable, + const uint8 *shiftTable, const uint8 *attackDecayTable, const uint32 *frqTable, + const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable); + ~TownsPC98_OpnOperator() {} + + void keyOn(); + void keyOff(); + void frequency(int freq); + void updatePhaseIncrement(); + void recalculateRates(); + void generateOutput(int phasebuf, int *_feedbuf, int &out); + + void feedbackLevel(int32 level) {_feedbackLevel = level ? level + 6 : 0; } + void detune(int value) { _detn = &_detnTbl[value << 5]; } + void multiple(uint32 value) { _multiple = value ? (value << 1) : 1; } + void attackRate(uint32 value) { _specifiedAttackRate = value; } + bool scaleRate(uint8 value); + void decayRate(uint32 value) { _specifiedDecayRate = value; recalculateRates(); } + void sustainRate(uint32 value) { _specifiedSustainRate = value; recalculateRates(); } + void sustainLevel(uint32 value) { _sustainLevel = (value == 0x0f) ? 0x3e0 : value << 5; } + void releaseRate(uint32 value) { _specifiedReleaseRate = value; recalculateRates(); } + void totalLevel(uint32 value) { _totalLevel = value << 3; } + void reset(); + +protected: + EnvelopeState _state; + uint32 _feedbackLevel; + uint32 _multiple; + uint32 _totalLevel; + uint8 _keyScale1; + uint8 _keyScale2; + uint32 _specifiedAttackRate; + uint32 _specifiedDecayRate; + uint32 _specifiedSustainRate; + uint32 _specifiedReleaseRate; + uint32 _tickCount; + uint32 _sustainLevel; + + uint32 _frequency; + uint8 _kcode; + uint32 _phase; + uint32 _phaseIncrement; + const int32 *_detn; + + const uint8 *_rateTbl; + const uint8 *_rshiftTbl; + const uint8 *_adTbl; + const uint32 *_fTbl; + const uint32 *_sinTbl; + const int32 *_tLvlTbl; + const int32 *_detnTbl; + + const double _tickLength; + double _tick; + int32 _currentLevel; + + struct EvpState { + uint8 rate; + uint8 shift; + } fs_a, fs_d, fs_s, fs_r; +}; + +TownsPC98_OpnOperator::TownsPC98_OpnOperator(double rate, const uint8 *rateTable, + const uint8 *shiftTable, const uint8 *attackDecayTable, const uint32 *frqTable, + const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable) : + _rateTbl(rateTable), _rshiftTbl(shiftTable), _adTbl(attackDecayTable), _fTbl(frqTable), + _sinTbl(sineTable), _tLvlTbl(tlevelOut), _detnTbl(detuneTable), _tickLength(rate * 65536.0), + _specifiedAttackRate(0), _specifiedDecayRate(0), _specifiedReleaseRate(0), _specifiedSustainRate(0), + _phase(0), _state(s_ready) { + + reset(); +} + +void TownsPC98_OpnOperator::keyOn() { + _state = s_attacking; + _phase = 0; +} + +void TownsPC98_OpnOperator::keyOff() { + if (_state != s_ready) + _state = s_releasing; +} + +void TownsPC98_OpnOperator::frequency(int freq) { + uint8 block = (freq >> 11); + uint16 pos = (freq & 0x7ff); + uint8 c = pos >> 7; + _kcode = (block << 2) | ((c < 7) ? 0 : ((c > 8) ? 3 : c - 6 )); + _frequency = _fTbl[pos << 1] >> (7 - block); +} + +void TownsPC98_OpnOperator::updatePhaseIncrement() { + _phaseIncrement = ((_frequency + _detn[_kcode]) * _multiple) >> 1; + uint8 keyscale = _kcode >> _keyScale1; + if (_keyScale2 != keyscale) { + _keyScale2 = keyscale; + recalculateRates(); + } +} + +void TownsPC98_OpnOperator::recalculateRates() { + int k = _keyScale2; + int r = _specifiedAttackRate ? (_specifiedAttackRate << 1) + 0x20 : 0; + fs_a.rate = ((r + k) < 94) ? _rateTbl[r + k] : 136; + fs_a.shift = ((r + k) < 94) ? _rshiftTbl[r + k] : 0; + + r = _specifiedDecayRate ? (_specifiedDecayRate << 1) + 0x20 : 0; + fs_d.rate = _rateTbl[r + k]; + fs_d.shift = _rshiftTbl[r + k]; + + r = _specifiedSustainRate ? (_specifiedSustainRate << 1) + 0x20 : 0; + fs_s.rate = _rateTbl[r + k]; + fs_s.shift = _rshiftTbl[r + k]; + + r = (_specifiedReleaseRate << 2) + 0x22; + fs_r.rate = _rateTbl[r + k]; + fs_r.shift = _rshiftTbl[r + k]; +} + +void TownsPC98_OpnOperator::generateOutput(int phasebuf, int *_feedbuf, int &out) { + if (_state == s_ready) + return; + + _tick += _tickLength; + while (_tick > 0x30000) { + _tick -= 0x30000; + ++_tickCount; + + int32 levelIncrement = 0; + uint32 targetTime = 0; + int32 targetLevel = 0; + EnvelopeState next_state = s_ready; + + switch (_state) { + case s_ready: + return; + case s_attacking: + next_state = s_decaying; + targetTime = (1 << fs_a.shift) - 1; + targetLevel = 0; + levelIncrement = (~_currentLevel * _adTbl[fs_a.rate + ((_tickCount >> fs_a.shift) & 7)]) >> 4; + break; + case s_decaying: + targetTime = (1 << fs_d.shift) - 1; + next_state = s_sustaining; + targetLevel = _sustainLevel; + levelIncrement = _adTbl[fs_d.rate + ((_tickCount >> fs_d.shift) & 7)]; + break; + case s_sustaining: + targetTime = (1 << fs_s.shift) - 1; + next_state = s_ready; + targetLevel = 1023; + levelIncrement = _adTbl[fs_s.rate + ((_tickCount >> fs_s.shift) & 7)]; + break; + case s_releasing: + targetTime = (1 << fs_r.shift) - 1; + next_state = s_ready; + targetLevel = 1023; + levelIncrement = _adTbl[fs_r.rate + ((_tickCount >> fs_r.shift) & 7)]; + break; + } + + if (!(_tickCount & targetTime)) { + _currentLevel += levelIncrement; + if ((!targetLevel && _currentLevel <= targetLevel) || (targetLevel && _currentLevel >= targetLevel)) { + if (_state != s_decaying) + _currentLevel = targetLevel; + if (_state != s_sustaining) + _state = next_state; + } + } + } + + uint32 lvlout = _totalLevel + (uint32) _currentLevel; + + int outp = 0; + int *i = &outp, *o = &outp; + int phaseShift = 0; + + if (_feedbuf) { + o = &_feedbuf[0]; + i = &_feedbuf[1]; + phaseShift = _feedbackLevel ? ((_feedbuf[0] + _feedbuf[1]) << _feedbackLevel) : 0; + if (phasebuf == -1) + *i = 0; + *o = *i; + } else { + phaseShift = phasebuf << 15; + } + + if (lvlout < 832) { + uint32 index = (lvlout << 3) + _sinTbl[(((int32)((_phase & 0xffff0000) + + phaseShift)) >> 16) & 0x3ff]; + *i = ((index < 6656) ? _tLvlTbl[index] : 0); + } else { + *i = 0; + } + + _phase += _phaseIncrement; + out += *o; + if (out > 32767) + out = 32767; + if (out < -32767) + out = -32767; +} + +void TownsPC98_OpnOperator::reset(){ + keyOff(); + _tick = 0; + _keyScale2 = 0; + _currentLevel = 1023; + + frequency(0); + detune(0); + scaleRate(0); + multiple(0); + updatePhaseIncrement(); + attackRate(0); + decayRate(0); + releaseRate(0); + sustainRate(0); + feedbackLevel(0); + totalLevel(127); +} + +bool TownsPC98_OpnOperator::scaleRate(uint8 value) { + value = 3 - value; + if (_keyScale1 != value) { + _keyScale1 = value; + return true; + } + + int k = _keyScale2; + int r = _specifiedAttackRate ? (_specifiedAttackRate << 1) + 0x20 : 0; + fs_a.rate = ((r + k) < 94) ? _rateTbl[r + k] : 136; + fs_a.shift = ((r + k) < 94) ? _rshiftTbl[r + k] : 0; + return false; +} + +class TownsPC98_OpnDriver; +class TownsPC98_OpnChannel { +public: + TownsPC98_OpnChannel(TownsPC98_OpnDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, + uint8 key, uint8 prt, uint8 id); + virtual ~TownsPC98_OpnChannel(); + virtual void init(); + + typedef bool (TownsPC98_OpnChannel::*ControlEventFunc)(uint8 para); + + typedef enum channelState { + CHS_RECALCFREQ = 0x01, + CHS_KEYOFF = 0x02, + CHS_SSG = 0x04, + CHS_PITCHWHEELOFF = 0x08, + CHS_ALL_BUT_EOT = 0x0f, + CHS_EOT = 0x80 + } ChannelState; + + virtual void loadData(uint8 *data); + virtual void processEvents(); + virtual void processFrequency(); + bool processControlEvent(uint8 cmd); + void writeReg(uint8 regAdress, uint8 value); + + virtual void keyOn(); + virtual void keyOff(); + + void setOutputLevel(); + void fadeStep(); + void reset(); + + void updateEnv(); + void generateOutput(int16 &leftSample, int16 &rightSample, int *del, int *feed); + + bool _enableLeft; + bool _enableRight; + bool _updateEnvelopes; + const uint8 _idFlag; + int _feedbuf[3]; + +protected: + bool control_dummy(uint8 para); + bool control_f0_setPatch(uint8 para); + bool control_f1_presetOutputLevel(uint8 para); + bool control_f2_setKeyOffTime(uint8 para); + bool control_f3_setFreqLSB(uint8 para); + bool control_f4_setOutputLevel(uint8 para); + bool control_f5_setTempo(uint8 para); + bool control_f6_repeatSection(uint8 para); + bool control_f7_setupPitchWheel(uint8 para); + bool control_f8_togglePitchWheel(uint8 para); + bool control_fa_writeReg(uint8 para); + bool control_fb_incOutLevel(uint8 para); + bool control_fc_decOutLevel(uint8 para); + bool control_fd_jump(uint8 para); + bool control_ff_endOfTrack(uint8 para); + + bool control_f0_setPatchSSG(uint8 para); + bool control_f1_setTotalLevel(uint8 para); + bool control_f4_setAlgorithm(uint8 para); + bool control_f9_unkSSG(uint8 para); + bool control_fb_incOutLevelSSG(uint8 para); + bool control_fc_decOutLevelSSG(uint8 para); + bool control_ff_endOfTrackSSG(uint8 para); + + uint8 _ticksLeft; + uint8 _algorithm; + uint8 _instrID; + uint8 _totalLevel; + uint8 _frqBlockMSB; + int8 _frqLSB; + uint8 _keyOffTime; + bool _protect; + uint8 *_dataPtr; + uint8 _ptchWhlInitDelayLo; + uint8 _ptchWhlInitDelayHi; + int16 _ptchWhlModInitVal; + uint8 _ptchWhlDuration; + uint8 _ptchWhlCurDelay; + int16 _ptchWhlModCurVal; + uint8 _ptchWhlDurLeft; + uint16 frequency; + uint8 _regOffset; + uint8 _flags; + uint8 _ssg1; + uint8 _ssg2; + + const uint8 _chanNum; + const uint8 _keyNum; + const uint8 _part; + + TownsPC98_OpnDriver *_drv; + TownsPC98_OpnOperator **_opr; + uint16 _frqTemp; + + const ControlEventFunc *controlEvents; +}; + +class TownsPC98_OpnChannelSSG : public TownsPC98_OpnChannel { +public: + TownsPC98_OpnChannelSSG(TownsPC98_OpnDriver *driver, uint8 regOffs, + uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id); + ~TownsPC98_OpnChannelSSG() {} + void init(); + + void processEvents(); + void processFrequency(); + + void keyOn(); + void keyOff(); + void loadData(uint8 *data); + +private: + void opn_SSG_UNK(uint8 a); +}; + + +class TownsPC98_OpnDriver : public Audio::AudioStream { +friend class TownsPC98_OpnChannel; +friend class TownsPC98_OpnChannelSSG; +public: + enum OpnType { + OD_TOWNS, + OD_TYPE26, + OD_TYPE86 + }; + + TownsPC98_OpnDriver(Audio::Mixer *mixer, OpnType type); + ~TownsPC98_OpnDriver(); + + bool init(); + void loadData(uint8 *data, bool loadPaused = false); + void reset(); + void fadeOut(); + + void pause() { _playing = false; } + void cont() { _playing = true; } + + void callback(); + void nextTick(int16 *buffer, uint32 bufferSize); + + bool looping() { return _looping == _updateChannelsFlag ? true : false; } + + // AudioStream interface + int inline readBuffer(int16 *buffer, const int numSamples); + bool isStereo() const { return true; } + bool endOfData() const { return false; } + int getRate() const { return _mixer->getOutputRate(); } + +protected: + void generateTables(); + + TownsPC98_OpnChannel **_channels; + TownsPC98_OpnChannelSSG **_ssgChannels; + //TownsPC98_OpnChannel *_adpcmChannel; + + void setTempo(uint8 tempo); + + void lock() { _mutex.lock(); } + void unlock() { _mutex.unlock(); } + + Audio::Mixer *_mixer; + Common::Mutex _mutex; + Audio::SoundHandle _soundHandle; + + const uint8 *_opnCarrier; + const uint8 *_opnFreqTable; + const uint8 *_opnFxCmdLen; + const uint8 *_opnLvlPresets; + + uint8 *_oprRates; + uint8 *_oprRateshift; + uint8 *_oprAttackDecay; + uint32 *_oprFrq; + uint32 *_oprSinTbl; + int32 *_oprLevelOut; + int32 *_oprDetune; + + uint8 *_trackData; + uint8 *_patches; + + uint8 _cbCounter; + uint8 _updateChannelsFlag; + uint8 _finishedChannelsFlag; + uint16 _tempo; + bool _playing; + bool _fading; + uint8 _looping; + uint32 _tickCounter; + + bool _updateEnvelopes; + int _ssgFlag; + + int32 _samplesTillCallback; + int32 _samplesTillCallbackRemainder; + int32 _samplesPerCallback; + int32 _samplesPerCallbackRemainder; + + const int _numChan; + const int _numSSG; + const bool _hasADPCM; + const bool _hasStereo; + + double _baserate; + static const uint8 _drvTables[]; + static const uint32 _adtStat[]; + bool _ready; +}; + +TownsPC98_OpnChannel::TownsPC98_OpnChannel(TownsPC98_OpnDriver *driver, uint8 regOffs, uint8 flgs, uint8 num, + uint8 key, uint8 prt, uint8 id) : _drv(driver), _regOffset(regOffs), _flags(flgs), _chanNum(num), _keyNum(key), + _part(prt), _idFlag(id) { + + _ticksLeft = _algorithm = _instrID = _totalLevel = _frqBlockMSB = _keyOffTime = _ssg1 = _ssg2 = 0; + _ptchWhlInitDelayLo = _ptchWhlInitDelayHi = _ptchWhlDuration = _ptchWhlCurDelay = _ptchWhlDurLeft = 0; + _frqLSB = 0; + _protect = _updateEnvelopes = false; + _enableLeft = _enableRight = true; + _dataPtr = 0; + _ptchWhlModInitVal = _ptchWhlModCurVal = 0; + frequency = _frqTemp = 0; + memset(&_feedbuf, 0, sizeof(int) * 3); + _opr = 0; +} + +TownsPC98_OpnChannel::~TownsPC98_OpnChannel() { + if (_opr) { + for (int i = 0; i < 4; i++) + delete _opr[i]; + delete [] _opr; + } +} + +void TownsPC98_OpnChannel::init() { + + _opr = new TownsPC98_OpnOperator*[4]; + for (int i = 0; i < 4; i++) + _opr[i] = new TownsPC98_OpnOperator(_drv->_baserate, _drv->_oprRates, _drv->_oprRateshift, + _drv->_oprAttackDecay, _drv->_oprFrq, _drv->_oprSinTbl, _drv->_oprLevelOut, _drv->_oprDetune); + + #define Control(x) &TownsPC98_OpnChannel::control_##x + static const ControlEventFunc ctrlEvents[] = { + Control(f0_setPatch), + Control(f1_presetOutputLevel), + Control(f2_setKeyOffTime), + Control(f3_setFreqLSB), + Control(f4_setOutputLevel), + Control(f5_setTempo), + Control(f6_repeatSection), + Control(f7_setupPitchWheel), + Control(f8_togglePitchWheel), + Control(dummy), + Control(fa_writeReg), + Control(fb_incOutLevel), + Control(fc_decOutLevel), + Control(fd_jump), + Control(dummy), + Control(ff_endOfTrack) + }; + #undef Control + + controlEvents = ctrlEvents; +} + +void TownsPC98_OpnChannel::keyOff() { + // all operators off + uint8 value = _keyNum & 0x0f; + uint8 regAdress = 0x28; + writeReg(regAdress, value); + _flags |= CHS_KEYOFF; +} + +void TownsPC98_OpnChannel::keyOn() { + // all operators on + uint8 value = _keyNum | 0xf0; + uint8 regAdress = 0x28; + writeReg(regAdress, value); +} + +void TownsPC98_OpnChannel::loadData(uint8 *data) { + _flags = (_flags & ~CHS_EOT) | CHS_ALL_BUT_EOT; + _ticksLeft = 1; + _dataPtr = data; + _totalLevel = 0x7F; + + uint8 *src_b = _dataPtr; + int loop = 1; + uint8 cmd = 0; + while (loop) { + if (loop == 1) { + cmd = *src_b++; + if (cmd < 0xf0) { + src_b++; + loop = 1; + } else { + if (cmd == 0xff) { + loop = *src_b ? 2 : 0; + if (READ_LE_UINT16(src_b)) + _drv->_looping |= _idFlag; + } else if (cmd == 0xf6) { + loop = 3; + } else { + loop = 2; + } + } + } else if (loop == 2) { + src_b += _drv->_opnFxCmdLen[cmd - 240]; + loop = 1; + } else if (loop == 3) { + src_b[0] = src_b[1]; + src_b += 4; + loop = 1; + } + } +} + +void TownsPC98_OpnChannel::processEvents() { + if (_flags & CHS_EOT) + return; + + if (_protect == false && _ticksLeft == _keyOffTime) + keyOff(); + + if (--_ticksLeft) + return; + + if (_protect == false) + keyOff(); + + uint8 cmd = 0; + bool loop = true; + + while (loop) { + cmd = *_dataPtr++; + if (cmd < 0xf0) + loop = false; + else if (!processControlEvent(cmd)) + return; + } + + uint8 para = *_dataPtr++; + + if (cmd == 0x80) { + keyOff(); + _protect = false; + } else { + keyOn(); + + if (_protect == false || cmd != _frqBlockMSB) + _flags |= CHS_RECALCFREQ; + + _protect = (para & 0x80) ? true : false; + _frqBlockMSB = cmd; + } + + _ticksLeft = para & 0x7f; +} + +void TownsPC98_OpnChannel::processFrequency() { + if (_flags & CHS_RECALCFREQ) { + uint8 block = (_frqBlockMSB & 0x70) >> 1; + uint16 bfreq = ((const uint16*)_drv->_opnFreqTable)[_frqBlockMSB & 0x0f]; + frequency = (bfreq + _frqLSB) | (block << 8); + + writeReg(_regOffset + 0xa4, (frequency >> 8)); + writeReg(_regOffset + 0xa0, (frequency & 0xff)); + + _ptchWhlCurDelay = _ptchWhlInitDelayHi; + if (_flags & CHS_KEYOFF) { + _ptchWhlModCurVal = _ptchWhlModInitVal; + _ptchWhlCurDelay += _ptchWhlInitDelayLo; + } + + _ptchWhlDurLeft = (_ptchWhlDuration >> 1); + _flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ); + } + + if (!(_flags & CHS_PITCHWHEELOFF)) { + if (--_ptchWhlCurDelay) + return; + _ptchWhlCurDelay = _ptchWhlInitDelayHi; + frequency += _ptchWhlModCurVal; + + writeReg(_regOffset + 0xa4, (frequency >> 8)); + writeReg(_regOffset + 0xa0, (frequency & 0xff)); + + if(!--_ptchWhlDurLeft) { + _ptchWhlDurLeft = _ptchWhlDuration; + _ptchWhlModCurVal = -_ptchWhlModCurVal; + } + } +} + +bool TownsPC98_OpnChannel::processControlEvent(uint8 cmd) { + uint8 para = *_dataPtr++; + return (this->*controlEvents[cmd & 0x0f])(para); +} + +void TownsPC98_OpnChannel::setOutputLevel() { + uint8 outopr = _drv->_opnCarrier[_algorithm]; + uint8 reg = 0x40 + _regOffset; + + for (int i = 0; i < 4; i++) { + if (outopr & 1) + writeReg(reg, _totalLevel); + outopr >>= 1; + reg += 4; + } +} + +void TownsPC98_OpnChannel::fadeStep() { + _totalLevel += 3; + if (_totalLevel > 0x7f) + _totalLevel = 0x7f; + setOutputLevel(); +} + +void TownsPC98_OpnChannel::reset() { + for (int i = 0; i < 4; i++) + _opr[i]->reset(); + + _updateEnvelopes = false; + _enableLeft = _enableRight = true; + memset(&_feedbuf, 0, sizeof(int) * 3); +} + +void TownsPC98_OpnChannel::updateEnv() { + for (int i = 0; i < 4 ; i++) + _opr[i]->updatePhaseIncrement(); +} + +void TownsPC98_OpnChannel::generateOutput(int16 &leftSample, int16 &rightSample, int *del, int *feed) { + int phbuf1, phbuf2, output; + phbuf1 = phbuf2 = output = 0; + + switch (_algorithm) { + case 0: + _opr[0]->generateOutput(0, feed, phbuf1); + _opr[2]->generateOutput(*del, 0, phbuf2); + *del = 0; + _opr[1]->generateOutput(phbuf1, 0, *del); + _opr[3]->generateOutput(phbuf2, 0, output); + break; + case 1: + _opr[0]->generateOutput(0, feed, phbuf1); + _opr[2]->generateOutput(*del, 0, phbuf2); + _opr[1]->generateOutput(0, 0, phbuf1); + _opr[3]->generateOutput(phbuf2, 0, output); + *del = phbuf1; + break; + case 2: + _opr[0]->generateOutput(0, feed, phbuf2); + _opr[2]->generateOutput(*del, 0, phbuf2); + _opr[1]->generateOutput(0, 0, phbuf1); + _opr[3]->generateOutput(phbuf2, 0, output); + *del = phbuf1; + break; + case 3: + _opr[0]->generateOutput(0, feed, phbuf2); + _opr[2]->generateOutput(0, 0, *del); + _opr[1]->generateOutput(phbuf2, 0, phbuf1); + _opr[3]->generateOutput(*del, 0, output); + *del = phbuf1; + break; + case 4: + _opr[0]->generateOutput(0, feed, phbuf1); + _opr[2]->generateOutput(0, 0, phbuf2); + _opr[1]->generateOutput(phbuf1, 0, output); + _opr[3]->generateOutput(phbuf2, 0, output); + *del = 0; + break; + case 5: + *del = feed[1]; + _opr[0]->generateOutput(-1, feed, phbuf1); + _opr[2]->generateOutput(*del, 0, output); + _opr[1]->generateOutput(*del, 0, output); + _opr[3]->generateOutput(*del, 0, output); + break; + case 6: + _opr[0]->generateOutput(0, feed, phbuf1); + _opr[2]->generateOutput(0, 0, output); + _opr[1]->generateOutput(phbuf1, 0, output); + _opr[3]->generateOutput(0, 0, output); + *del = 0; + break; + case 7: + _opr[0]->generateOutput(0, feed, output); + _opr[2]->generateOutput(0, 0, output); + _opr[1]->generateOutput(0, 0, output); + _opr[3]->generateOutput(0, 0, output); + *del = 0; + break; + }; + + if (_enableLeft) { + int l = output + leftSample; + if (l > 32767) + l = 32767; + if (l < -32767) + l = -32767; + leftSample = (int16) l; + } + + if (_enableRight) { + int r = output + rightSample; + if (r > 32767) + r = 32767; + if (r < -32767) + r = -32767; + rightSample = (int16) r; + } +} + +void TownsPC98_OpnChannel::writeReg(uint8 regAdress, uint8 value) { + uint8 h = regAdress & 0xf0; + uint8 l = (regAdress & 0x0f); + static const uint8 oprOrdr[] = { 0, 2, 1, 3 }; + uint8 o = oprOrdr[(l - _regOffset) >> 2]; + + switch (h) { + case 0x00: + // ssg + warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress); + break; + case 0x10: + // adpcm + warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress); + break; + case 0x20: + if (l == 8) { + // Key on/off + for (int i = 0; i < 4; i++) { + if ((value >> (4 + i)) & 1) + _opr[i]->keyOn(); + else + _opr[i]->keyOff(); + } + } else if (l == 2) { + // LFO + warning("TownsPC98_OpnDriver: TRYING TO USE LFO (NOT SUPPORTED)"); + } else if (l == 7) { + // Timers; Ch 3/6 special mode + warning("TownsPC98_OpnDriver: TRYING TO USE CH 3/6 SPECIAL MODE (NOT SUPPORTED)"); + } else if (l == 4 || l == 5) { + // Timer A + warning("TownsPC98_OpnDriver: TRYING TO USE TIMER_A (NOT SUPPORTED)"); + } else if (l == 6) { + // Timer B + warning("TownsPC98_OpnDriver: TRYING TO USE TIMER_B (NOT SUPPORTED)"); + } else if (l == 10 || l == 11) { + // DAC + warning("TownsPC98_OpnDriver: TRYING TO USE DAC (NOT SUPPORTED)"); + } + break; + + case 0x30: + // detune, multiple + _opr[o]->detune((value >> 4) & 7); + _opr[o]->multiple(value & 0x0f); + _updateEnvelopes = true; + break; + + case 0x40: + // total level + _opr[o]->totalLevel(value & 0x7f); + break; + + case 0x50: + // rate scaling, attack rate + _opr[o]->attackRate(value & 0x1f); + if (_opr[o]->scaleRate(value >> 6)) + _updateEnvelopes = true; + break; + + case 0x60: + // first decay rate, amplitude modulation + _opr[o]->decayRate(value & 0x1f); + if (value & 0x80) + warning("TownsPC98_OpnDriver: TRYING TO USE AMP MODULATION (NOT SUPPORTED)"); + + break; + + case 0x70: + // secondary decay rate + _opr[o]->sustainRate(value & 0x1f); + break; + + case 0x80: + // secondary amplitude, release rate; + _opr[o]->sustainLevel(value >> 4); + _opr[o]->releaseRate(value & 0x0f); + break; + + case 0x90: + // ssg + warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress); + break; + + case 0xa0: + // frequency + l -= _regOffset; + if (l == 0) { + _frqTemp = (_frqTemp & 0xff00) | value; + _updateEnvelopes = true; + for (int i = 0; i < 4; i++) + _opr[i]->frequency(_frqTemp); + } else if (l == 4) { + _frqTemp = (_frqTemp & 0xff) | (value << 8); + } else if (l == 8) { + // Ch 3/6 special mode frq + warning("TownsPC98_OpnDriver: TRYING TO USE CH 3/6 SPECIAL MODE FREQ (NOT SUPPORTED)"); + } else if (l == 12) { + // Ch 3/6 special mode frq + warning("TownsPC98_OpnDriver: TRYING TO USE CH 3/6 SPECIAL MODE FREQ (NOT SUPPORTED)"); + } + break; + + case 0xb0: + l -= _regOffset; + if (l == 0) { + // feedback, _algorithm + _opr[0]->feedbackLevel((value >> 3) & 7); + _opr[1]->feedbackLevel(0); + _opr[2]->feedbackLevel(0); + _opr[3]->feedbackLevel(0); + } else if (l == 4) { + // stereo, LFO sensitivity + _enableLeft = value & 0x80 ? true : false; + _enableRight = value & 0x40 ? true : false; + uint8 ams = (value & 0x3F) >> 3; + if (ams) + warning("TownsPC98_OpnDriver: TRYING TO USE AMP MODULATION SENSITIVITY (NOT SUPPORTED)"); + uint8 fms = value & 3; + if (fms) + warning("TownsPC98_OpnDriver: TRYING TO USE FREQ MODULATION SENSITIVITY (NOT SUPPORTED)"); + } + break; + + default: + warning("TownsPC98_OpnDriver: UNKNOWN ADDRESS %d", regAdress); + break; + } +} + +bool TownsPC98_OpnChannel::control_f0_setPatch(uint8 para) { + _instrID = para; + uint8 reg = _regOffset + 0x80; + + for (int i = 0; i < 4; i++) { + // set release rate for each operator + writeReg(reg, 0x0f); + reg += 4; + } + + const uint8 *tptr = _drv->_patches + ((uint32)_instrID << 5); + reg = _regOffset + 0x30; + + // write registers 0x30 to 0x8f + for (int i = 0; i < 6; i++) { + writeReg(reg, tptr[0]); + reg += 4; + writeReg(reg, tptr[2]); + reg += 4; + writeReg(reg, tptr[1]); + reg += 4; + writeReg(reg, tptr[3]); + reg += 4; + tptr += 4; + } + + reg = _regOffset + 0xB0; + _algorithm = tptr[0] & 7; + // set feedback and algorithm + writeReg(reg, tptr[0]); + + setOutputLevel(); + return true; +} + +bool TownsPC98_OpnChannel::control_f1_presetOutputLevel(uint8 para) { + if (_drv->_fading) + return true; + + _totalLevel = _drv->_opnLvlPresets[para]; + setOutputLevel(); + return true; +} + +bool TownsPC98_OpnChannel::control_f2_setKeyOffTime(uint8 para) { + _keyOffTime = para; + return true; +} + +bool TownsPC98_OpnChannel::control_f3_setFreqLSB(uint8 para) { + _frqLSB = (int8) para; + return true; +} + +bool TownsPC98_OpnChannel::control_f4_setOutputLevel(uint8 para) { + if (_drv->_fading) + return true; + + _totalLevel = para; + setOutputLevel(); + return true; +} + +bool TownsPC98_OpnChannel::control_f5_setTempo(uint8 para) { + _drv->setTempo(para); + return true; +} + +bool TownsPC98_OpnChannel::control_f6_repeatSection(uint8 para) { + _dataPtr--; + _dataPtr[0]--; + + if (*_dataPtr) { + // repeat section until counter has reached zero + _dataPtr = _drv->_trackData + READ_LE_UINT16(_dataPtr + 2); + } else { + // reset counter, advance to next section + _dataPtr[0] = _dataPtr[1]; + _dataPtr += 4; + } + return true; +} + +bool TownsPC98_OpnChannel::control_f7_setupPitchWheel(uint8 para) { + _ptchWhlInitDelayLo = _dataPtr[0]; + _ptchWhlInitDelayHi = para; + _ptchWhlModInitVal = (int16) READ_LE_UINT16(_dataPtr + 1); + _ptchWhlDuration = _dataPtr[3]; + _dataPtr += 4; + _flags = (_flags & ~CHS_PITCHWHEELOFF) | CHS_KEYOFF | CHS_RECALCFREQ; + return true; +} + +bool TownsPC98_OpnChannel::control_f8_togglePitchWheel(uint8 para) { + if (para == 0x10) { + if (*_dataPtr++) { + _flags = (_flags & ~CHS_PITCHWHEELOFF) | CHS_KEYOFF; + } else { + _flags |= CHS_PITCHWHEELOFF; + } + } else { + //uint8 skipChannels = para / 36; + //uint8 entry = para % 36; + //TownsPC98_OpnDriver::TownsPC98_OpnChannel *t = &chan[skipChannels]; + ////// NOT IMPLEMENTED + //t->unnamedEntries[entry] = *_dataPtr++; + } + return true; +} + +bool TownsPC98_OpnChannel::control_fa_writeReg(uint8 para) { + writeReg(para, *_dataPtr++); + return true; +} + +bool TownsPC98_OpnChannel::control_fb_incOutLevel(uint8 para) { + _dataPtr--; + if (_drv->_fading) + return true; + + uint8 val = (_totalLevel + 3); + if (val > 0x7f) + val = 0x7f; + + _totalLevel = val; + setOutputLevel(); + return true; +} + +bool TownsPC98_OpnChannel::control_fc_decOutLevel(uint8 para) { + _dataPtr--; + if (_drv->_fading) + return true; + + int8 val = (int8) (_totalLevel - 3); + if (val < 0) + val = 0; + + _totalLevel = (uint8) val; + setOutputLevel(); + return true; +} + +bool TownsPC98_OpnChannel::control_fd_jump(uint8 para) { + uint8 *tmp = _drv->_trackData + READ_LE_UINT16(_dataPtr - 1); + _dataPtr = (tmp[1] == 1) ? tmp : ++_dataPtr; + return true; +} + +bool TownsPC98_OpnChannel::control_dummy(uint8 para) { + _dataPtr--; + return true; +} + +bool TownsPC98_OpnChannel::control_ff_endOfTrack(uint8 para) { + uint16 val = READ_LE_UINT16(--_dataPtr); + if (val) { + // loop + _dataPtr = _drv->_trackData + val; + return true; + } else { + // quit parsing for active channel + --_dataPtr; + _flags |= CHS_EOT; + _drv->_finishedChannelsFlag |= _idFlag; + keyOff(); + return false; + } +} + +bool TownsPC98_OpnChannel::control_f0_setPatchSSG(uint8 para) { + _instrID = para << 4; + para = (para >> 3) & 0x1e; + if (para) + return control_f4_setAlgorithm(para | 0x40); + return true; +} + +bool TownsPC98_OpnChannel::control_f1_setTotalLevel(uint8 para) { + if (!_drv->_fading) + _totalLevel = para; + return true; +} + +bool TownsPC98_OpnChannel::control_f4_setAlgorithm(uint8 para) { + _algorithm = para; + return true; +} + +bool TownsPC98_OpnChannel::control_f9_unkSSG(uint8 para) { + _dataPtr += 5; + return true; +} + +bool TownsPC98_OpnChannel::control_fb_incOutLevelSSG(uint8 para) { + _dataPtr--; + if (_drv->_fading) + return true; + + _totalLevel--; + if ((int8)_totalLevel < 0) + _totalLevel = 0; + + return true; +} + +bool TownsPC98_OpnChannel::control_fc_decOutLevelSSG(uint8 para) { + _dataPtr--; + if (_drv->_fading) + return true; + + if(_totalLevel + 1 < 0x10) + _totalLevel++; + + return true; +} + +bool TownsPC98_OpnChannel::control_ff_endOfTrackSSG(uint8 para) { + uint16 val = READ_LE_UINT16(--_dataPtr); + if (val) { + // loop + _dataPtr = _drv->_trackData + val; + return true; + } else { + // quit parsing for active channel + --_dataPtr; + _flags |= CHS_EOT; + //_finishedChannelsFlag |= _idFlag; + keyOff(); + return false; + } +} + +TownsPC98_OpnChannelSSG::TownsPC98_OpnChannelSSG(TownsPC98_OpnDriver *driver, uint8 regOffs, + uint8 flgs, uint8 num, uint8 key, uint8 prt, uint8 id) : + TownsPC98_OpnChannel(driver, regOffs, flgs, num, key, prt, id) { +} + +void TownsPC98_OpnChannelSSG::init() { + _algorithm = 0x80; + + _opr = new TownsPC98_OpnOperator*[4]; + for (int i = 0; i < 4; i++) + _opr[i] = new TownsPC98_OpnOperator(_drv->_baserate, _drv->_oprRates, _drv->_oprRateshift, + _drv->_oprAttackDecay, _drv->_oprFrq, _drv->_oprSinTbl, _drv->_oprLevelOut, _drv->_oprDetune); + + #define Control(x) &TownsPC98_OpnChannelSSG::control_##x + static const ControlEventFunc ctrlEventsSSG[] = { + Control(f0_setPatchSSG), + Control(f1_setTotalLevel), + Control(f2_setKeyOffTime), + Control(f3_setFreqLSB), + Control(f4_setOutputLevel), + Control(f5_setTempo), + Control(f6_repeatSection), + Control(f7_setupPitchWheel), + Control(f8_togglePitchWheel), + Control(f9_unkSSG), + Control(fa_writeReg), + Control(fb_incOutLevelSSG), + Control(fc_decOutLevelSSG), + Control(fd_jump), + Control(dummy), + Control(ff_endOfTrackSSG) + }; + #undef Control + + controlEvents = ctrlEventsSSG; +} + +void TownsPC98_OpnChannelSSG::processEvents() { + if (_flags & CHS_EOT) + return; + + _drv->_ssgFlag = (_flags & CHS_SSG) ? -1 : 0; + + if (_protect == false && _ticksLeft == _keyOffTime) + keyOff(); + + if (--_ticksLeft) + return; + + if (_protect == false) + keyOff(); + + uint8 cmd = 0; + bool loop = true; + + while (loop) { + cmd = *_dataPtr++; + if (cmd < 0xf0) + loop = false; + else if (!processControlEvent(cmd)) + return; + } + + uint8 para = *_dataPtr++; + + if (cmd == 0x80) { + keyOff(); + _protect = false; + } else { + keyOn(); + + if (_protect == false || cmd != _frqBlockMSB) + _flags |= CHS_RECALCFREQ; + + _protect = (para & 0x80) ? true : false; + _frqBlockMSB = cmd; + } + + _ticksLeft = para & 0x7f; + + if (!(_flags & CHS_SSG)) { + + } +} + +void TownsPC98_OpnChannelSSG::processFrequency() { + if (_flags & CHS_RECALCFREQ) { + uint8 block = (_frqBlockMSB & 0x70) >> 1; + uint16 bfreq = ((const uint16*)_drv->_opnFreqTable)[_frqBlockMSB & 0x0f]; + frequency = (bfreq + _frqLSB) | (block << 8); + + writeReg(_regOffset + 0xa4, (frequency >> 8)); + writeReg(_regOffset + 0xa0, (frequency & 0xff)); + + _ptchWhlCurDelay = _ptchWhlInitDelayHi; + if (_flags & CHS_KEYOFF) { + _ptchWhlModCurVal = _ptchWhlModInitVal; + _ptchWhlCurDelay += _ptchWhlInitDelayLo; + } + + _ptchWhlDurLeft = (_ptchWhlDuration >> 1); + _flags &= ~(CHS_KEYOFF | CHS_RECALCFREQ); + } + + if (!(_flags & CHS_PITCHWHEELOFF)) { + if (--_ptchWhlCurDelay) + return; + _ptchWhlCurDelay = _ptchWhlInitDelayHi; + frequency += _ptchWhlModCurVal; + + writeReg(_regOffset + 0xa4, (frequency >> 8)); + writeReg(_regOffset + 0xa0, (frequency & 0xff)); + + if(!--_ptchWhlDurLeft) { + _ptchWhlDurLeft = _ptchWhlDuration; + _ptchWhlModCurVal = -_ptchWhlModCurVal; + } + } +} + +void TownsPC98_OpnChannelSSG::keyOff() { + // all operators off + uint8 value = _keyNum & 0x0f; + uint8 regAdress = 0x28; + writeReg(regAdress, value); + _flags |= CHS_KEYOFF; +} + +void TownsPC98_OpnChannelSSG::keyOn() { + // all operators on + uint8 value = _keyNum | 0xf0; + uint8 regAdress = 0x28; + writeReg(regAdress, value); +} + +void TownsPC98_OpnChannelSSG::loadData(uint8 *data) { + _drv->_ssgFlag = (_flags & CHS_SSG) ? -1 : 0; + opn_SSG_UNK(0); + TownsPC98_OpnChannel::loadData(data); + _algorithm = 0x80; +} + +void TownsPC98_OpnChannelSSG::opn_SSG_UNK(uint8 a) { + _ssg1 = a; + uint16 h = (_totalLevel + 1) * a; + if ((h >> 8) == _ssg2) + return; + _ssg2 = (h >> 8); + writeReg(8 + _regOffset, _ssg2); +} + +TownsPC98_OpnDriver::TownsPC98_OpnDriver(Audio::Mixer *mixer, OpnType type) : + _mixer(mixer), _trackData(0), _playing(false), _fading(false), _channels(0), _ssgChannels(0), + _looping(0), _opnCarrier(_drvTables + 76), _opnFreqTable(_drvTables + 84), + _opnFxCmdLen(_drvTables + 36), _opnLvlPresets(_drvTables + (type == OD_TOWNS ? 52 : 220)) , + _oprRates(0), _oprRateshift(0), _oprAttackDecay(0), _oprFrq(0), _oprSinTbl(0), _oprLevelOut(0), + _oprDetune(0), _cbCounter(4), _tickCounter(0), _updateChannelsFlag(type == OD_TYPE26 ? 0x07 : 0x3F), + _finishedChannelsFlag(0), _samplesTillCallback(0), _samplesTillCallbackRemainder(0), _ready(false), + _numSSG(type == OD_TOWNS ? 0 : 3), _hasADPCM(type == OD_TYPE86 ? true : false), + _numChan(type == OD_TYPE26 ? 3 : 6), _hasStereo(type == OD_TYPE26 ? false : true) { + setTempo(84); + _baserate = (3579545.0 / (double)getRate()) / 144.0; +} + +TownsPC98_OpnDriver::~TownsPC98_OpnDriver() { + _mixer->stopHandle(_soundHandle); + + if (_channels) { + for (int i = 0; i < _numChan; i++) + delete _channels[i]; + delete [] _channels; + } + + if (_ssgChannels) { + for (int i = 0; i < _numSSG; i++) + delete _ssgChannels[i]; + delete [] _ssgChannels; + } + + delete [] _oprRates; + delete [] _oprRateshift; + delete [] _oprFrq; + delete [] _oprAttackDecay; + delete [] _oprSinTbl; + delete [] _oprLevelOut; + delete [] _oprDetune; +} + +bool TownsPC98_OpnDriver::init() { + if (_ready) { + reset(); + return true; + } + + generateTables(); + + if (_channels) { + for (int i = 0; i < _numChan; i++) { + if (_channels[i]) + delete _channels[i]; + } + delete [] _channels; + } + _channels = new TownsPC98_OpnChannel*[_numChan]; + for (int i = 0; i < _numChan; i++) { + int ii = i * 6; + _channels[i] = new TownsPC98_OpnChannel(this, _drvTables[ii], _drvTables[ii + 1], + _drvTables[ii + 2], _drvTables[ii + 3], _drvTables[ii + 4], _drvTables[ii + 5]); + _channels[i]->init(); + } + + if (_ssgChannels) { + for (int i = 0; i < _numSSG; i++) { + if (_ssgChannels[i]) + delete _ssgChannels[i]; + } + delete [] _ssgChannels; + } + if (_numSSG) { + _ssgChannels = new TownsPC98_OpnChannelSSG*[_numSSG]; + for (int i = 0; i < _numSSG; i++) { + int ii = i * 6; + _ssgChannels[i] = new TownsPC98_OpnChannelSSG(this, _drvTables[ii], _drvTables[ii + 1], + _drvTables[ii + 2], _drvTables[ii + 3], _drvTables[ii + 4], _drvTables[ii + 5]); + _ssgChannels[i]->init(); + } + } + + _mixer->playInputStream(Audio::Mixer::kMusicSoundType, + &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true); + + _ready = true; + return true; +} + +int inline TownsPC98_OpnDriver::readBuffer(int16 *buffer, const int numSamples) { + memset(buffer, 0, sizeof(int16) * numSamples); + int32 samplesLeft = numSamples >> 1; + while (samplesLeft) { + if (!_samplesTillCallback) { + callback(); + _samplesTillCallback = _samplesPerCallback; + _samplesTillCallbackRemainder += _samplesPerCallbackRemainder; + if (_samplesTillCallbackRemainder >= _tempo) { + _samplesTillCallback++; + _samplesTillCallbackRemainder -= _tempo; + } + } + + int32 render = MIN(samplesLeft, _samplesTillCallback); + samplesLeft -= render; + _samplesTillCallback -= render; + + nextTick(buffer, render); + + for (int i = 0; i < render; ++i) { + buffer[i << 1] <<= 2; + buffer[(i << 1) + 1] <<= 2; + } + + buffer += (render << 1); + } + + return numSamples; +} + +void TownsPC98_OpnDriver::loadData(uint8 *data, bool loadPaused) { + if (!_ready) { + warning("TownsPC98_OpnDriver: Driver must be initialized before loading data"); + return; + } + + if (!data) { + warning("TownsPC98_OpnDriver: Invalid music file data"); + return; + } + + lock(); + _trackData = data; + + reset(); + + uint8 *src_a = data; + + for (uint8 i = 0; i < 3; i++) { + _channels[i]->loadData(data + READ_LE_UINT16(src_a)); + src_a += 2; + } + + for (int i = 0; i < _numSSG; i++) { + _ssgChannels[i]->loadData(data + READ_LE_UINT16(src_a)); + src_a += 2; + } + + for (uint8 i = 3; i < _numChan; i++) { + _channels[i]->loadData(data + READ_LE_UINT16(src_a)); + src_a += 2; + } + + if (_hasADPCM) { + //_adpcmChannel->loadData(data + READ_LE_UINT16(src_a)); + src_a += 2; + } + + _ssgFlag = 0; + + _patches = src_a + 4; + _cbCounter = 4; + _finishedChannelsFlag = 0; + + // AH 0x17 + unlock(); + _playing = (loadPaused ? false : true); +} + +void TownsPC98_OpnDriver::reset() { + for (int i = 0; i < (_numChan); i++) + _channels[i]->reset(); + for (int i = 0; i < (_numSSG); i++) + _ssgChannels[i]->reset(); + + _playing = _fading = false; + _looping = 0; + _tickCounter = 0; +} + +void TownsPC98_OpnDriver::fadeOut() { + if (!_playing) + return; + + _fading = true; + + for (int i = 0; i < 20; i++) { + lock(); + uint32 dTime = _tickCounter + 2; + for (int j = 0; j < _numChan; j++) { + if (_updateChannelsFlag & _channels[j]->_idFlag) + _channels[j]->fadeStep(); + } + for (int j = 0; j < _numSSG; j++) + _ssgChannels[j]->fadeStep(); + + unlock(); + + while (_playing) { + if (_tickCounter >= dTime) + break; + } + } + + _fading = false; + + reset(); +} + +void TownsPC98_OpnDriver::callback() { + if (!_playing || --_cbCounter) + return; + + _cbCounter = 4; + _tickCounter++; + + lock(); + + for (int i = 0; i < _numChan; i++) { + if (_updateChannelsFlag & _channels[i]->_idFlag) { + _channels[i]->processEvents(); + _channels[i]->processFrequency(); + } + } + + if (_numSSG) { + for (int i = 0; i < _numSSG; i++) { + _ssgChannels[i]->processEvents(); + _ssgChannels[i]->processFrequency(); + } + } + + _ssgFlag = 0; + + unlock(); + + if (_finishedChannelsFlag == _updateChannelsFlag) + reset(); +} + +void TownsPC98_OpnDriver::nextTick(int16 *buffer, uint32 bufferSize) { + if (!_playing) + return; + + for (int i = 0; i < _numChan ; i++) { + if (_channels[i]->_updateEnvelopes) { + _channels[i]->_updateEnvelopes = false; + _channels[i]->updateEnv(); + } + + for (uint32 ii = 0; ii < bufferSize ; ii++) + _channels[i]->generateOutput(buffer[ii * 2], + buffer[ii * 2 + 1], &_channels[i]->_feedbuf[2], _channels[i]->_feedbuf); + } + + for (int i = 0; i < _numSSG ; i++) { + if (_ssgChannels[i]->_updateEnvelopes) { + _ssgChannels[i]->_updateEnvelopes = false; + _ssgChannels[i]->updateEnv(); + } + + for (uint32 ii = 0; ii < bufferSize ; ii++) + _ssgChannels[i]->generateOutput(buffer[ii * 2], + buffer[ii * 2 + 1], &_ssgChannels[i]->_feedbuf[2], _ssgChannels[i]->_feedbuf); + } +} + +void TownsPC98_OpnDriver::generateTables() { + delete [] _oprRates; + _oprRates = new uint8[128]; + memset(_oprRates, 0x90, 32); + uint8 *dst = (uint8*) _oprRates + 32; + for (int i = 0; i < 48; i += 4) + WRITE_BE_UINT32(dst + i, 0x00081018); + dst += 48; + for (uint8 i = 0; i < 16; i ++) { + uint8 v = (i < 12) ? i : 12; + *dst++ = ((4 + v) << 3); + } + memset(dst, 0x80, 32); + + delete [] _oprRateshift; + _oprRateshift = new uint8[128]; + memset(_oprRateshift, 0, 128); + dst = (uint8*) _oprRateshift + 32; + for (int i = 11; i; i--) { + memset(dst, i, 4); + dst += 4; + } + + delete [] _oprFrq; + _oprFrq = new uint32[0x1000]; + for (uint32 i = 0; i < 0x1000; i++) + _oprFrq[i] = (uint32)(_baserate * (double)(i << 11)); + + delete [] _oprAttackDecay; + _oprAttackDecay = new uint8[152]; + memset(_oprAttackDecay, 0, 152); + for (int i = 0; i < 36; i++) + WRITE_BE_UINT32(_oprAttackDecay + (i << 2), _adtStat[i]); + + delete [] _oprSinTbl; + _oprSinTbl = new uint32[1024]; + for (int i = 0; i < 1024; i++) { + double val = sin((double) (((i << 1) + 1) * PI / 1024.0)); + double d_dcb = log(1.0 / (double)ABS(val)) / log(2.0) * 256.0; + int32 i_dcb = (int32)(2.0 * d_dcb); + i_dcb = (i_dcb & 1) ? (i_dcb >> 1) + 1 : (i_dcb >> 1); + _oprSinTbl[i] = (i_dcb << 1) + (val >= 0.0 ? 0 : 1); + } + + delete [] _oprLevelOut; + _oprLevelOut = new int32[0x1a00]; + for (int i = 0; i < 256; i++) { + double val = floor(65536.0 / pow(2.0, 0.00390625 * (double)(1 + i))); + int32 val_int = ((int32) val) >> 4; + _oprLevelOut[i << 1] = (val_int & 1) ? ((val_int >> 1) + 1) << 2 : (val_int >> 1) << 2; + _oprLevelOut[(i << 1) + 1] = -_oprLevelOut[i << 1]; + for (int ii = 1; ii < 13; ii++) { + _oprLevelOut[(i << 1) + (ii << 9)] = _oprLevelOut[i << 1] >> ii; + _oprLevelOut[(i << 1) + (ii << 9) + 1] = -_oprLevelOut[(i << 1) + (ii << 9)]; + } + } + + uint8 *dtt = new uint8[128]; + memset(dtt, 0, 36); + memset(dtt + 36, 1, 8); + memcpy(dtt + 44, _drvTables + 144, 84); + + delete [] _oprDetune; + _oprDetune = new int32[256]; + for (int i = 0; i < 128; i++) { + _oprDetune[i] = (int32) ((double)dtt[i] * _baserate * 64.0); + _oprDetune[i + 128] = -_oprDetune[i]; + } + + delete [] dtt; +} + +void TownsPC98_OpnDriver::setTempo(uint8 tempo) { + _tempo = tempo; + _samplesPerCallback = getRate() / _tempo; + _samplesPerCallbackRemainder = getRate() % _tempo; +} + +const uint8 TownsPC98_OpnDriver::_drvTables[] = { + // channel presets + 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x80, 0x01, 0x01, 0x00, 0x02, + 0x02, 0x80, 0x02, 0x02, 0x00, 0x04, + 0x00, 0x80, 0x03, 0x04, 0x01, 0x08, + 0x01, 0x80, 0x04, 0x05, 0x01, 0x10, + 0x02, 0x80, 0x05, 0x06, 0x01, 0x20, + + // control event size + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x05, + 0x02, 0x06, 0x02, 0x00, 0x00, 0x02, 0x00, 0x02, + + // fmt level presets + 0x54, 0x50, 0x4C, 0x48, 0x44, 0x40, 0x3C, 0x38, + 0x34, 0x30, 0x2C, 0x28, 0x24, 0x20, 0x1C, 0x18, + 0x14, 0x10, 0x0C, 0x08, 0x04, 0x90, 0x90, 0x90, + + // carriers + 0x08, 0x08, 0x08, 0x08, 0x0C, 0x0E, 0x0E, 0x0F, + + // frequencies + 0x6A, 0x02, 0x8F, 0x02, 0xB6, 0x02, 0xDF, 0x02, + 0x0B, 0x03, 0x39, 0x03, 0x6A, 0x03, 0x9E, 0x03, + 0xD5, 0x03, 0x10, 0x04, 0x4E, 0x04, 0x8F, 0x04, + 0x00, 0x00, 0x00, 0x00, + + // unused + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + + // detune + 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, + 0x08, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x10, 0x10, 0x10, 0x10, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, + 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x13, 0x14, + 0x16, 0x16, 0x16, 0x16, + + // pc98 level presets + 0x40, 0x3B, 0x38, 0x34, 0x30, 0x2A, 0x28, 0x25, + 0x22, 0x20, 0x1D, 0x1A, 0x18, 0x15, 0x12, 0x10, + 0x0D, 0x0A, 0x08, 0x05, 0x02, 0x90, 0x90, 0x90 +}; + +const uint32 TownsPC98_OpnDriver::_adtStat[] = { + 0x00010001, 0x00010001, 0x00010001, 0x01010001, + 0x00010101, 0x00010101, 0x00010101, 0x01010101, + 0x01010101, 0x01010101, 0x01010102, 0x01010102, + 0x01020102, 0x01020102, 0x01020202, 0x01020202, + 0x02020202, 0x02020202, 0x02020204, 0x02020204, + 0x02040204, 0x02040204, 0x02040404, 0x02040404, + 0x04040404, 0x04040404, 0x04040408, 0x04040408, + 0x04080408, 0x04080408, 0x04080808, 0x04080808, + 0x08080808, 0x08080808, 0x10101010, 0x10101010 +}; + SoundTowns::SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer) : Sound(vm, mixer), _lastTrack(-1), _currentSFX(0), _sfxFileData(0), _sfxFileIndex((uint)-1), _sfxWDTable(0), _sfxBTTable(0), _parser(0) { - _driver = new SoundTowns_EuphonyDriver(_mixer); + _driver = new Towns_EuphonyDriver(_mixer); int ret = open(); if (ret != MERR_ALREADY_OPEN && ret != 0) error("couldn't open midi driver"); @@ -1124,7 +2797,7 @@ void SoundTowns::playTrack(uint8 track) { return; track -= 2; - const int32 * const tTable = (const int32 * const) cdaData(); + const int32 *const tTable = (const int32 *const) cdaData(); int tTableIndex = 3 * track; int trackNum = (int) READ_LE_UINT32(&tTable[tTableIndex + 2]); @@ -1195,12 +2868,12 @@ void SoundTowns::playSoundEffect(uint8 track) { } } - uint8 * fileBody = _sfxFileData + 0x01b8; + uint8 *fileBody = _sfxFileData + 0x01b8; int32 offset = (int32)READ_LE_UINT32(_sfxFileData + (track - 0x0b) * 4); if (offset == -1) return; - uint32 * sfxHeader = (uint32*)(fileBody + offset); + uint32 *sfxHeader = (uint32*)(fileBody + offset); uint32 sfxHeaderID = READ_LE_UINT32(sfxHeader); uint32 sfxHeaderInBufferSize = READ_LE_UINT32(&sfxHeader[1]); @@ -1220,7 +2893,7 @@ void SoundTowns::playSoundEffect(uint8 track) { } else if (sfxHeaderID == 1) { Screen::decodeFrame4(sfxBody, sfxPlaybackBuffer, playbackBufferSize); } else if (_sfxWDTable) { - uint8 * tgt = sfxPlaybackBuffer; + uint8 *tgt = sfxPlaybackBuffer; uint32 sfx_BtTable_Offset = 0; uint32 sfx_WdTable_Offset = 0; uint32 sfx_WdTable_Number = 5; @@ -1285,7 +2958,7 @@ uint32 SoundTowns::getBaseTempo(void) { } bool SoundTowns::loadInstruments() { - uint8 * twm = _vm->resource()->fileData("twmusic.pak", 0); + uint8 *twm = _vm->resource()->fileData("twmusic.pak", 0); if (!twm) return false; _driver->queue()->loadDataToCurrentPosition(twm, 0x8BF0); @@ -1300,11 +2973,11 @@ bool SoundTowns::loadInstruments() { } void SoundTowns::playEuphonyTrack(uint32 offset, int loop) { - uint8 * twm = _vm->resource()->fileData("twmusic.pak", 0); + uint8 *twm = _vm->resource()->fileData("twmusic.pak", 0); Common::StackLock lock(_mutex); if (!_parser) { - _parser = new MidiParser_EuD(_driver->queue()); + _parser = new Towns_EuphonyParser(_driver->queue()); _parser->setMidiDriver(this); _parser->setTimerRate(getBaseTempo()); } @@ -1315,7 +2988,7 @@ void SoundTowns::playEuphonyTrack(uint32 offset, int loop) { delete[] twm; } -void SoundTowns::onTimer(void * data) { +void SoundTowns::onTimer(void *data) { SoundTowns *music = (SoundTowns *)data; Common::StackLock lock(music->_mutex); if (music->_parser) @@ -1356,22 +3029,75 @@ float SoundTowns::semitoneAndSampleRate_to_sampleStep(int8 semiTone, int8 semiTo return (float) sampleRate * 10.0f * rateshift / outputRate; } +SoundPC98::SoundPC98(KyraEngine_v1 *vm, Audio::Mixer *mixer) : + Sound(vm, mixer), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0) { +} + +SoundPC98::~SoundPC98() { + delete[] _musicTrackData; + delete[] _sfxTrackData; + delete _driver; +} + +bool SoundPC98::init() { + _driver = new TownsPC98_OpnDriver(_mixer, TownsPC98_OpnDriver::OD_TYPE26); + _sfxTrackData = _vm->resource()->fileData("se.dat", 0); + if (!_sfxTrackData) + return false; + return _driver->init(); +} + +void SoundPC98::playTrack(uint8 track) { + if (--track >= 56) + track -= 55; + + if (track == _lastTrack && _musicEnabled) + return; + + haltTrack(); + + char musicfile[13]; + sprintf(musicfile, fileListEntry(0), track); + delete[] _musicTrackData; + _musicTrackData = _vm->resource()->fileData(musicfile, 0); + if (_musicEnabled) + _driver->loadData(_musicTrackData); + + _lastTrack = track; +} + +void SoundPC98::haltTrack() { + _lastTrack = -1; + AudioCD.stop(); + AudioCD.updateCD(); + _driver->reset(); +} + +void SoundPC98::beginFadeOut() { + _driver->fadeOut(); + haltTrack(); +} + +void SoundPC98::playSoundEffect(uint8) { + /// TODO /// +} + + // KYRA 2 -SoundTowns_v2::SoundTowns_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer) - : Sound(vm, mixer), _lastTrack(-1), _currentSFX(0), /*_driver(0),*/ - _twnTrackData(0) { +SoundTownsPC98_v2::SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer) : + Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _lastTrack(-1), _driver(0), _useFmSfx(false) { } -SoundTowns_v2::~SoundTowns_v2() { - /*if (_driver) - delete _driver;*/ - if (_twnTrackData) - delete[] _twnTrackData; +SoundTownsPC98_v2::~SoundTownsPC98_v2() { + delete[] _musicTrackData; + delete _driver; } -bool SoundTowns_v2::init() { - //_driver = new SoundTowns_v2_TwnDriver(_mixer); +bool SoundTownsPC98_v2::init() { + _driver = new TownsPC98_OpnDriver(_mixer, _vm->gameFlags().platform == Common::kPlatformPC98 ? + TownsPC98_OpnDriver::OD_TYPE86 : TownsPC98_OpnDriver::OD_TOWNS); + _useFmSfx = _vm->gameFlags().platform == Common::kPlatformPC98 ? true : false; _vm->checkCD(); // FIXME: While checking for 'track1.XXX(X)' looks like // a good idea, we should definitely not be doing this @@ -1384,55 +3110,61 @@ bool SoundTowns_v2::init() { (Common::File::exists("track1.mp3") || Common::File::exists("track1.ogg") || Common::File::exists("track1.flac") || Common::File::exists("track1.fla"))) _musicEnabled = 2; - return true;//_driver->init(); + return _driver->init(); } -void SoundTowns_v2::process() { +void SoundTownsPC98_v2::process() { AudioCD.updateCD(); } -void SoundTowns_v2::playTrack(uint8 track) { +void SoundTownsPC98_v2::playTrack(uint8 track) { if (track == _lastTrack && _musicEnabled) return; - const uint16 * const cdaTracks = (const uint16 * const) cdaData(); + const uint16 *const cdaTracks = (const uint16 *const) cdaData(); int trackNum = -1; - for (int i = 0; i < cdaTrackNum(); i++) { - if (track == (uint8) READ_LE_UINT16(&cdaTracks[i * 2])) { - trackNum = (int) READ_LE_UINT16(&cdaTracks[i * 2 + 1]) - 1; - break; + if (_vm->gameFlags().platform == Common::kPlatformFMTowns) { + for (int i = 0; i < cdaTrackNum(); i++) { + if (track == (uint8) READ_LE_UINT16(&cdaTracks[i * 2])) { + trackNum = (int) READ_LE_UINT16(&cdaTracks[i * 2 + 1]) - 1; + break; + } } } - haltTrack(); + beginFadeOut(); - // TODO: figure out when to loop and when not for CD Audio - bool loop = false; + char musicfile[13]; + sprintf(musicfile, fileListEntry(0), track); + delete[] _musicTrackData; + + _musicTrackData = _vm->resource()->fileData(musicfile, 0); + _driver->loadData(_musicTrackData, true); if (_musicEnabled == 2 && trackNum != -1) { - AudioCD.play(trackNum+1, loop ? -1 : 1, 0, 0); + AudioCD.play(trackNum+1, _driver->looping() ? -1 : 1, 0, 0); AudioCD.updateCD(); } else if (_musicEnabled) { - char musicfile[13]; - sprintf(musicfile, fileListEntry(0), track); - if (_twnTrackData) - delete[] _twnTrackData; - _twnTrackData = _vm->resource()->fileData(musicfile, 0); - //_driver->loadData(_twnTrackData); + _driver->cont(); } _lastTrack = track; } -void SoundTowns_v2::haltTrack() { +void SoundTownsPC98_v2::haltTrack() { _lastTrack = -1; AudioCD.stop(); AudioCD.updateCD(); - //_driver->reset(); + _driver->reset(); } -int32 SoundTowns_v2::voicePlay(const char *file, bool) { +void SoundTownsPC98_v2::beginFadeOut() { + _driver->fadeOut(); + haltTrack(); +} + +int32 SoundTownsPC98_v2::voicePlay(const char *file, bool) { static const uint16 rates[] = { 0x10E1, 0x0CA9, 0x0870, 0x0654, 0x0438, 0x032A, 0x021C, 0x0194 }; int h = 0; @@ -1443,11 +3175,11 @@ int32 SoundTowns_v2::voicePlay(const char *file, bool) { return 0; } - char filename [13]; + char filename[13]; sprintf(filename, "%s.PCM", file); - uint8 * data = _vm->resource()->fileData(filename, 0); - uint8 * src = data; + uint8 *data = _vm->resource()->fileData(filename, 0); + uint8 *src = data; uint16 sfxRate = rates[READ_LE_UINT16(src)]; src += 2; @@ -1500,9 +3232,16 @@ int32 SoundTowns_v2::voicePlay(const char *file, bool) { return 1; } -void SoundTowns_v2::beginFadeOut() { - //_driver->fadeOut(); - haltTrack(); +void SoundTownsPC98_v2::playSoundEffect(uint8 track) { + if (!_useFmSfx) + return; + + uint8 *sd = _vm->resource()->fileData("sound.dat", 0); + + + //TODO + + delete [] sd; } } // end of namespace Kyra |