/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifdef ENABLE_EOB #include "kyra/kyra_v1.h" #include "kyra/sound/drivers/mlalf98.h" #include "audio/softsynth/fmtowns_pc98/pc98_audio.h" namespace Kyra { class SoundChannel { public: SoundChannel(PC98AudioCore *pc98a, int part, int regOffset, int type); virtual ~SoundChannel(); virtual void setData(uint8 *dataStart, uint8 *loopStart, const uint8 *dataEnd, uint8 *instrBuffer); void globalBlock(); void globalUnblock(); void toggleMute(bool mute); virtual void restore() {}; virtual bool checkFinished() { return false; } void update(); void startFadeOut(); void updateFadeOut(); void abortFadeOut(); virtual void keyOff() = 0; int idFlag() const; protected: enum Flags { kInternalMask = 0x0F, kF1_10 = 1 << 4, kFade = 1 << 5, kTremolo = 1 << 6, kEnvelope = 1 << 7 }; enum Flags2 { kEoT = 1 << 0, kF2_02 = 1 << 1, kF2_04 = 1 << 2, kMute = 1 << 3, kF2_10 = 1 << 4, kVbrDelay = 1 << 5, kNoSustain = 1 << 6, kVbrEnable = 1 << 7 }; template class SoundOpcode : public Common::Functor1Mem { public: typedef Common::Functor1Mem SCFunc; SoundOpcode(SoundChannelImpl *sc, const typename SCFunc::FuncType &func, const char *dsc, int chanId, int chanType, int cDataBytes) : SCFunc(sc, func), _dataLen(cDataBytes) { static const char tpstr[4][4] = { "FM ", "SSG", "RHY", "EXT" }; Common::String args; while (cDataBytes--) args += "0x%02x "; _msg = Common::String::format("%s Channel %d: %s() [ %s]", tpstr[chanType], chanId, dsc, args.c_str()); memset(_dt, 0, 7); } ~SoundOpcode() {} void run(uint8 *&arg) { assert(arg); memcpy(_dt, arg, _dataLen); debugC(3, kDebugLevelSound, _msg.c_str(), _dt[0], _dt[1], _dt[2], _dt[3], _dt[4], _dt[5], _dt[6]); if (SCFunc::isValid()) SCFunc::operator()(arg); } private: int _dataLen; uint8 _dt[7]; Common::String _msg; }; void vbrResetDelay(); void vbrReset(); virtual void clear(); virtual void writeDevice(uint8 reg, uint8 val); uint8 _ticksLeft; uint8 _program; uint8 *_dataPtr; const uint8 *_dataEnd; uint8 *_loopStartPtr; uint8 _volume; uint8 _algorithm; const uint8 _regOffset; const uint8 _part; int16 _transpose; uint8 _envCurLvl; uint8 _envRR; uint8 _vbrDelay; uint8 _vbrRem; uint8 _vbrRate; uint8 _vbrTicker; int16 _vbrStepSize; int16 _vbrModifier; uint8 _vbrDepth; uint8 _vbrState; uint8 _duration; uint16 _frequency; uint8 _flags2; uint8 _note; uint8 _flags; uint8 *_backupData; static bool _globalBlock; int8 _fadeVolModifier; uint8 _fadeProgress; uint8 _fadeTicker; uint8 _trmCarrier; uint8 *_instrBuffer; protected: virtual void op_setTranspose(uint8 *&data); void op_setDuration(uint8 *&data); void op_setVibrato(uint8 *&data); void op_repeatSectionBegin(uint8 *&data); void op_repeatSectionJumpIf(uint8 *&data); void op_jumpToSubroutine(uint8 *&data); void op_sustain(uint8 *&data); void op_repeatSectionAbort(uint8 *&data); void op_runOpcode2(uint8 *&data); private: virtual void op2_setExtPara(uint8 *&data); void op2_setEnvGenFlags(uint8 *&data); void op2_setEnvGenTimer(uint8 *&data); void op2_beginFadeout(uint8 *&data); void op2_4(uint8 *&data); void op2_toggleFadeout(uint8 *&data); void op2_returnFromSubroutine(uint8 *&data); void op2_7(uint8 *&data); void op3_vbrInit(uint8 *&data); void op3_vbrDisable(uint8 *&data); void op3_vbrEnable(uint8 *&data); void op3_vbrSetDelay(uint8 *&data); void op3_vbrSetRate(uint8 *&data); void op3_vbrSetStepSize(uint8 *&data); void op3_vbrSetDepth(uint8 *&data); void op3_vbrSetTremolo(uint8 *&data); typedef SoundOpcode Opcode; Common::Array _subOpcodes[2]; private: virtual void parse() = 0; virtual void updateVolume() = 0; void updateSounds(); virtual void updateVibrato() = 0; const int _type; PC98AudioCore *_pc98a; bool _mute; }; class SoundChannelNonSSG : public SoundChannel { public: SoundChannelNonSSG(PC98AudioCore *pc98a, int part, int regOffset, int type); virtual ~SoundChannelNonSSG(); protected: virtual void parse() override; uint8 _statusB4; private: virtual void finish(); void soundOff(); virtual void noteOn(uint8 note) = 0; virtual void reduceVolume() {} virtual void updateVolume() override {} virtual void updateVibrato() override {} virtual void op_programChange(uint8 *&data) = 0; virtual void op_setVolume(uint8 *&data) = 0; virtual void op_setSpecialMode(uint8 *&data); virtual void op_setPanPos(uint8 *&data) = 0; virtual void op_writeDevice(uint8 *&data); virtual void op_modifyVolume(uint8 *&data); void op_enableLFO(uint8 *&data); typedef SoundOpcode Opcode; Common::Array _opcodes; }; class MusicChannelFM : public SoundChannelNonSSG { public: MusicChannelFM(PC98AudioCore *pc98a, int part, int regOffset); virtual ~MusicChannelFM(); virtual void restore() override; virtual void keyOff() override; private: virtual void clear() override; virtual void parse() override; virtual void noteOn(uint8 note) override; virtual void updateVolume() override; virtual void reduceVolume() override; virtual void updateVibrato() override; virtual bool usingSpecialMode() const; virtual uint8 getSpecialFrequencyModifier(uint8 index); virtual void setSpecialFrequencyModifier(uint8 index, uint8 val); virtual void toggleSpecialMode(bool on); void sendVolume(uint8 volume); void sendTrmVolume(uint8 volume); void keyOn(); virtual void writeDevice(uint8 reg, uint8 val) override; virtual void op_programChange(uint8 *&data) override; virtual void op_setVolume(uint8 *&data) override; virtual void op_setSpecialMode(uint8 *&data) override; virtual void op_setPanPos(uint8 *&data) override; virtual void op_modifyVolume(uint8 *&data) override; static uint16 _frequency2; static uint8 _specialModeModifier[4]; static bool _specialMode; static uint8 *_registers; }; class MusicChannelSSG : public SoundChannel { public: MusicChannelSSG(PC98AudioCore *pc98a, int part, int regOffset); virtual ~MusicChannelSSG(); virtual void keyOff() override; private: enum EnvState { kAttack = 0x10, kDecay = 0x20, kSustain = 0x40, kUpdate = 0x80 }; virtual void parse() override; void noteOff(); void noteOn(uint8 note); uint8 processEnvelope(); uint8 envGetAttLevel(); void envSendAttLevel(uint8 val); virtual void updateVolume() override; virtual void updateVibrato() override; virtual void clear() override; void op_programChange(uint8 *&data); void op_setVolume(uint8 *&data); void op_chanEnable(uint8 *&data); void op_setNoiseGenerator(uint8 *&data); void op_setInstrument(uint8 *&data); void op_modifyVolume(uint8 *&data); void op_loadInstrument(uint8 *&data); uint8 *getProgramData(uint8 program) const; bool _externalPrograms; typedef SoundOpcode Opcode; Common::Array _opcodes; uint8 _envStartLvl; uint8 _envAR; uint8 _envDR; uint8 _envSL; uint8 _envSR; static uint8 _ngState; static uint8 _enState; const uint8 _regOffsetAttn; uint8 *_envDataTemp; static const uint8 _envDataPreset[96]; }; class MusicChannelRHY : public SoundChannelNonSSG { public: MusicChannelRHY(PC98AudioCore *pc98a, int part, int regOffset); virtual ~MusicChannelRHY(); virtual void keyOff() override; private: virtual void noteOn(uint8 note) override; virtual void updateVolume() override; virtual void op_programChange(uint8 *&data) override; virtual void op_setVolume(uint8 *&data) override; virtual void op_setPanPos(uint8 *&data) override; virtual void op_modifyVolume(uint8 *&data) override; uint8 _instrLevel[6]; uint8 _activeInstruments; }; class MusicChannelEXT : public SoundChannelNonSSG { public: MusicChannelEXT(PC98AudioCore *pc98a, int part, int regOffset, MLALF98::ADPCMData *const &data); virtual ~MusicChannelEXT(); virtual void keyOff() override; private: virtual void noteOn(uint8 note) override; virtual void updateVibrato() override; virtual void clear() override; virtual void writeDevice(uint8 reg, uint8 val) override; virtual void op_programChange(uint8 *&data) override; virtual void op_setVolume(uint8 *&data) override; virtual void op_setPanPos(uint8 *&data) override; virtual void op_setTranspose(uint8 *&data) override; virtual void op2_setExtPara(uint8 *&data) override; uint8 _panPos; uint8 _useVolPreset; uint8 _volume2; uint8 _instrument; MLALF98::ADPCMData *const &_extBuffer; uint16 _smpStart; uint16 _smpEnd; PC98AudioCore *_pc98a; }; class SoundEffectChannel : public MusicChannelFM { public: SoundEffectChannel(PC98AudioCore *pc98a, int part, int regOffset, SoundChannel *replaceChannel); virtual ~SoundEffectChannel(); void setData(uint8 *dataStart, uint8 *loopStart, const uint8 *dataEnd, uint8 *instrBuffer) override; virtual bool checkFinished() override; private: virtual void finish() override; virtual bool usingSpecialMode() const override; virtual uint8 getSpecialFrequencyModifier(uint8 index) override; virtual void setSpecialFrequencyModifier(uint8 index, uint8 val) override; virtual void toggleSpecialMode(bool on) override; virtual void clear() override; virtual void writeDevice(uint8 reg, uint8 val) override; virtual void op_writeDevice(uint8 *&data) override; uint8 _specialModeModifier[4]; bool _specialMode; bool _isFinished; SoundChannel *_replaceChannel; }; class MLALF98Internal : public PC98AudioPluginDriver { public: MLALF98Internal(Audio::Mixer *mixer, EmuType emuType); ~MLALF98Internal(); static MLALF98Internal *open(Audio::Mixer *mixer, EmuType emuType); static void close(); void loadMusicData(Common::SeekableReadStream *data); void loadSoundEffectData(Common::SeekableReadStream *data); void loadExtData(MLALF98::ADPCMDataArray &data); void startMusic(int track); void fadeOutMusic(); void startSoundEffect(int track); void allChannelsOff(); void resetExtUnit(); void setMusicVolume(int volume); void setSoundEffectVolume(int volume); // Plugin driver interface virtual void timerCallbackA() override; virtual void timerCallbackB() override; private: uint8 *_musicBuffer; int _musicBufferSize; uint8 *_sfxBuffer; int _sfxBufferSize; MLALF98::ADPCMData *_extBuffer; int _extBufferSize; Common::Array _musicChannels; Common::Array _sfxChannels; const bool _type86; PC98AudioCore *_pc98a; static MLALF98Internal *_refInstance; static int _refCount; int _sfxPlaying; bool _ready; }; bool SoundChannel::_globalBlock = false; SoundChannel::SoundChannel(PC98AudioCore *pc98a, int part, int regOffset, int type) : _pc98a(pc98a), _regOffset(regOffset), _part(part), _ticksLeft(0), _program(0), _volume(0), _algorithm(0), _envRR(0), _vbrDelay(0), _vbrRem(0), _vbrRate(0), _vbrTicker(0), _vbrStepSize(0), _vbrModifier(0), _vbrDepth(0), _vbrState(0), _duration(0), _frequency(0), _flags2(0), _note(0), _flags(0), _transpose(0), _envCurLvl(0), _fadeVolModifier(0), _fadeProgress(0), _fadeTicker(0), _trmCarrier(1), _dataPtr(0), _dataEnd(0), _loopStartPtr(0), _instrBuffer(0), _backupData(0), _mute(false), _type(type) { _subOpcodes[0].reserve(8); _subOpcodes[1].reserve(8); #define OPCODE(x, y, z) _subOpcodes[x].push_back(new Opcode(this, &SoundChannel::y, #y, type == 1 ? (_regOffset >> 1) : (type == 0 ? (_regOffset + _part * 3) : 0), type, z)) OPCODE(0, op2_setExtPara, 1); OPCODE(0, op2_setEnvGenFlags, 1); OPCODE(0, op2_setEnvGenTimer, 2); OPCODE(0, op2_beginFadeout, 1); OPCODE(0, op2_4, 1); OPCODE(0, op2_toggleFadeout, 1); OPCODE(0, op2_returnFromSubroutine, 0); OPCODE(0, op2_7, 1); OPCODE(1, op3_vbrInit, 5); OPCODE(1, op3_vbrDisable, 0); OPCODE(1, op3_vbrEnable, 0); OPCODE(1, op3_vbrSetDelay, 1); OPCODE(1, op3_vbrSetRate, 1); OPCODE(1, op3_vbrSetStepSize, 2); OPCODE(1, op3_vbrSetDepth, 1); OPCODE(1, op3_vbrSetTremolo, 2); #undef OPCODE } SoundChannel::~SoundChannel() { for (int c = 0; c < 2; ++c) { for (Common::Array::iterator i = _subOpcodes[c].begin(); i != _subOpcodes[c].end(); ++i) delete (*i); } } void SoundChannel::setData(uint8 *dataStart, uint8 *loopStart, const uint8 *dataEnd, uint8 *instrBuffer) { clear(); _dataPtr = dataStart; _dataEnd = dataEnd; _loopStartPtr = loopStart; _instrBuffer = instrBuffer; _ticksLeft = 1; } void SoundChannel::globalBlock() { _globalBlock = true; } void SoundChannel::globalUnblock() { _globalBlock = false; } void SoundChannel::toggleMute(bool mute) { _mute = mute; } void SoundChannel::update() { if (!_dataPtr) return; if (_flags2 & kMute) globalBlock(); parse(); updateSounds(); if (_flags2 & kMute) globalUnblock(); } void SoundChannel::startFadeOut() { _fadeProgress = 16; } void SoundChannel::updateFadeOut() { if (--_fadeTicker) return; if (!_fadeProgress) return; _fadeVolModifier = (--_fadeProgress) - 16; updateVolume(); if (_fadeProgress) return; _fadeVolModifier = 0; keyOff(); } void SoundChannel::abortFadeOut() { _fadeProgress = 0; _fadeVolModifier = 0; } int SoundChannel::idFlag() const { switch (_type) { case 0: return 1 << (_regOffset + _part * 3); case 1: return 1 << ((_regOffset >> 1) + 6); case 2: return 1 << 9; case 3: return 1 << 10; default: break; } return 0; } void SoundChannel::vbrResetDelay() { _vbrRem = _vbrDelay; _flags2 &= ~kVbrDelay; } void SoundChannel::vbrReset() { _vbrState = _vbrDepth >> 1; _vbrModifier = _vbrStepSize; if (_flags & kTremolo) _frequency = _envCurLvl; } void SoundChannel::clear() { _ticksLeft = _program = _volume = _algorithm = _duration = _envRR = _vbrDelay = _vbrRem = _vbrRate = _vbrTicker = _vbrDepth = _vbrState = _flags2 = _note = _flags = 0; _frequency = _transpose = _vbrStepSize = _vbrModifier = 0; _dataPtr = _loopStartPtr = 0; } void SoundChannel::writeDevice(uint8 reg, uint8 val) { if (!_mute) _pc98a->writeReg(reg > 0x2F ? _part : 0, reg, val); } void SoundChannel::op_setTranspose(uint8 *&data) { _note = 0; int16 trp = READ_LE_INT16(data); data += 2; _transpose = (*data++) ? _transpose + trp : trp; } void SoundChannel::op_setDuration(uint8 *&data) { _duration = *data++; } void SoundChannel::op_setVibrato(uint8 *&data) { uint8 cmd = (*data++) & 0x0F; assert(cmd < _subOpcodes[1].size()); _subOpcodes[1][cmd & 0x0F]->run(data); } void SoundChannel::op_repeatSectionBegin(uint8 *&data) { int16 offset = READ_LE_INT16(data); assert(offset > 0); // reset repeat counter data[offset - 1] = data[offset]; data += 2; } void SoundChannel::op_repeatSectionJumpIf(uint8 *&data) { // reduce the repeat counter if (--data[0]) { // If the counter has not yet reached zero we go back to the beginning of the repeat section. data += 2; int16 offset = READ_LE_INT16(data); assert(offset > 0); data -= offset; } else { // If the counter has reached zero we reset it to the original value and advance to the next section. data[0] = data[1]; data += 4; } } void SoundChannel::op_jumpToSubroutine(uint8 *&data) { #if 0 uint16 offset = READ_BE_UINT16(data); _backupData = data + 2; if (offset > 0x253D) offset -= 0x253D; else if (offset > 0x188F) offset -= 0x188F; else warning("SoundChannel::op_jumpToSubroutine(): invalid offset"); data = _buffer + offset; #else // This is a very ugly opcode which reads and sets an absolute 16 bit offset // inside the music driver segment. Thus, the ip can jump anywhere, not only // from one track or channel to another or from the music buffer to the sfx // buffer or vice versa, but also to any other code or data location within // the segment. Different driver versions are more or less doomed to mutual // incompatibility. // The music data buffer has offset 0x188F and the sfx data buffer 0x253D. So // I could implement this if I have to. I'd prefer if this never comes up, // though... data += 2; warning("SoundChannel::op_jumpToSubroutine(): not implemented"); #endif } void SoundChannel::op_sustain(uint8 *&data) { _flags2 &= ~kNoSustain; } void SoundChannel::op_repeatSectionAbort(uint8 *&data) { int16 offset = READ_LE_INT16(data); assert(offset > 0); data = (data[offset] == 1) ? data + offset + 4 : data + 2; } void SoundChannel::op_runOpcode2(uint8 *&data) { uint8 cmd = (*data++) & 0x0F; assert(cmd < _subOpcodes[0].size()); _subOpcodes[0][cmd & 0x0F]->run(data); } void SoundChannel::op2_setExtPara(uint8 *&data) { data++; } void SoundChannel::op2_setEnvGenFlags(uint8 *&data) { _flags = *data++; writeDevice(0x0D, _flags); _flags |= kEnvelope; } void SoundChannel::op2_setEnvGenTimer(uint8 *&data) { writeDevice(0x0B, *data++); writeDevice(0x0C, *data++); } void SoundChannel::op2_beginFadeout(uint8 *&data) { _envRR = *data++; _flags |= kFade; } void SoundChannel::op2_4(uint8 *&data) { if (*data++) _flags |= kF1_10; else _flags &= ~kF1_10; } void SoundChannel::op2_toggleFadeout(uint8 *&data) { if (*data++) { _flags |= kFade; } else { _flags &= ~kFade; updateVolume(); } } void SoundChannel::op2_returnFromSubroutine(uint8 *&data) { assert(_backupData); data = _backupData; } void SoundChannel::op2_7(uint8 *&data) { /*_unkbyte27 = */data++; } void SoundChannel::op3_vbrInit(uint8 *&data) { op3_vbrSetDelay(data); op3_vbrSetRate(data); _vbrStepSize = _vbrModifier = READ_LE_INT16(data); data += 2; op3_vbrSetDepth(data); _flags2 |= kVbrEnable; } void SoundChannel::op3_vbrDisable(uint8 *&data) { _flags2 &= ~kVbrEnable; } void SoundChannel::op3_vbrEnable(uint8 *&data) { _flags2 |= kVbrEnable; } void SoundChannel::op3_vbrSetDelay(uint8 *&data) { _vbrDelay = _vbrRem = *data++; } void SoundChannel::op3_vbrSetRate(uint8 *&data) { _vbrRate = _vbrTicker = *data++; } void SoundChannel::op3_vbrSetStepSize(uint8 *&data) { _vbrStepSize = _vbrModifier = READ_LE_INT16(data); data += 2; vbrResetDelay(); } void SoundChannel::op3_vbrSetDepth(uint8 *&data) { _vbrDepth = *data++; _vbrState = _vbrDepth >> 1; } void SoundChannel::op3_vbrSetTremolo(uint8 *&data) { uint8 flags = *data++; if (!flags) { _flags &= ~kTremolo; return; } _flags |= kTremolo; _trmCarrier = flags; _frequency = _envCurLvl = *data++; } void SoundChannel::updateSounds() { if (!(_flags2 & kVbrEnable)) return; if (!_dataPtr) return; if (*(_dataPtr - 1) == 0xF0) return; if (!(_flags2 & kVbrDelay)) { vbrResetDelay(); vbrReset(); _vbrTicker = _vbrRate; _flags2 |= kVbrDelay; } if (_vbrRem) { _vbrRem--; return; } if (--_vbrTicker) return; _vbrTicker = _vbrRate; if (!_vbrState) { _vbrModifier *= -1; _vbrState = _vbrDepth; } _vbrState--; updateVibrato(); } SoundChannelNonSSG::SoundChannelNonSSG(PC98AudioCore *pc98a, int part, int regOffset, int type) : SoundChannel(pc98a, part, regOffset, type), _statusB4(0xC0) { _opcodes.reserve(16); #define OPCODE(y, z) _opcodes.push_back(new Opcode(this, &SoundChannelNonSSG::y, #y, _regOffset + _part * 3, type, z)) OPCODE(op_programChange, 1); OPCODE(op_setVolume, type == 2 ? 7 : 1); OPCODE(op_setTranspose, 3); OPCODE(op_setDuration, 1); OPCODE(op_setVibrato, 1); OPCODE(op_repeatSectionBegin, 2); OPCODE(op_repeatSectionJumpIf, 4); OPCODE(op_setSpecialMode, 4); OPCODE(op_setPanPos, 1); OPCODE(op_jumpToSubroutine, 2); OPCODE(op_writeDevice, 2); OPCODE(op_modifyVolume, 1); OPCODE(op_enableLFO, 3); OPCODE(op_sustain, 0); OPCODE(op_repeatSectionAbort, 2); OPCODE(op_runOpcode2, 1); #undef OPCODE } SoundChannelNonSSG::~SoundChannelNonSSG() { for (Common::Array::iterator i = _opcodes.begin(); i != _opcodes.end(); ++i) delete (*i); } void SoundChannelNonSSG::parse() { if (!_ticksLeft && (!_dataPtr || _dataPtr >= _dataEnd)) return; if (--_ticksLeft) { if (_ticksLeft <= _duration) soundOff(); return; } uint8 *pos = _dataPtr; _flags2 |= kNoSustain; uint8 cmd = 0; for (bool loop = true; loop && pos && pos < _dataEnd; ) { if (*pos == 0) { _flags2 |= kEoT; if (!_loopStartPtr) { _dataPtr = 0; finish(); return; } pos = _loopStartPtr; } cmd = *pos++; if (cmd < 0xF0) break; _opcodes[cmd & 0x0F]->run(pos); } _ticksLeft = cmd & 0x7F; if (cmd & 0x80) { if ((_flags & kFade) && !(_flags & kF1_10)) reduceVolume(); keyOff(); } else if (pos && pos < _dataEnd) { if (_flags2 & kNoSustain) keyOff(); noteOn(*pos++); } _dataPtr = pos; } void SoundChannelNonSSG::finish() { keyOff(); } void SoundChannelNonSSG::soundOff() { if (*_dataPtr == 0xFD) return; if (_flags & kFade) reduceVolume(); else keyOff(); } void SoundChannelNonSSG::op_setSpecialMode(uint8 *&data) { data += 4; } void SoundChannelNonSSG::op_writeDevice(uint8 *&data) { uint8 reg = *data++; uint8 val = *data++; writeDevice(reg, val); } void SoundChannelNonSSG::op_modifyVolume(uint8 *&data) { data++; } void SoundChannelNonSSG::op_enableLFO(uint8 *&data) { // This concerns only the fm channels, but the opcode may be called by other channels nonetheless writeDevice(0x22, (*data++) | 8); _statusB4 = (_statusB4 & 0xC0) | data[0] | (data[1] << 4 | data[1] >> 4); data += 2; writeDevice(0xB4, _statusB4); } uint8 *MusicChannelFM::_registers = 0; bool MusicChannelFM::_specialMode = false; uint8 MusicChannelFM::_specialModeModifier[4] = { 0, 0, 0, 0 }; uint16 MusicChannelFM::_frequency2 = 0; MusicChannelFM::MusicChannelFM(PC98AudioCore *pc98a, int part, int regOffset) : SoundChannelNonSSG(pc98a, part, regOffset, 0) { if (!_registers) { _registers = new uint8[512]; memset(_registers, 0, 512); } } MusicChannelFM::~MusicChannelFM() { delete[] _registers; _registers = 0; } void MusicChannelFM::restore() { for (int i = 0x30 + _regOffset; i < 0xA0; i += 4) writeDevice(i, _registers[(_part << 8) + i]); writeDevice(0xB0 + _regOffset, _registers[(_part << 8) + 0xB0 + _regOffset]); writeDevice(0xB4 + _regOffset, _registers[(_part << 8) + 0xB4 + _regOffset]); _note = 0; } void MusicChannelFM::keyOff() { debugC(7, kDebugLevelSound, "FM Channel %d: keyOff() [Ticks: 0x%02x]", _part * 3 + _regOffset, _ticksLeft); writeDevice(0x28, (_part << 2) + _regOffset); } void MusicChannelFM::clear() { SoundChannel::clear(); _specialMode = false; _statusB4 = 0xC0; } void MusicChannelFM::parse() { toggleSpecialMode(usingSpecialMode()); SoundChannelNonSSG::parse(); } void MusicChannelFM::noteOn(uint8 note) { static uint16 freqTableFM[12] = { 0x026a, 0x028f, 0x02b6, 0x02df, 0x030b, 0x0339, 0x036a, 0x039e, 0x03d5, 0x0410, 0x044e, 0x048f }; if (_note == note && !(_flags2 & kNoSustain)) return; _note = note; if ((note & 0x0F) >= 12) return; debugC(5, kDebugLevelSound, "FM Channel %d: noteOn() [Note: 0x%02x Ticks: 0x%02x]", _part * 3 + _regOffset, _note, _ticksLeft); uint16 frq = (((note & 0x70) << 7) | freqTableFM[note & 0x0F]) + _transpose; if (!(_flags & kTremolo)) _frequency = _frequency2 = frq; if (_flags2 & kNoSustain) vbrResetDelay(); vbrReset(); if (usingSpecialMode()) frq += getSpecialFrequencyModifier(0); writeDevice(0xA4 + _regOffset, frq >> 8); writeDevice(0xA0 + _regOffset, frq & 0xFF); keyOn(); if (usingSpecialMode()) { for (int i = 1; i < 4; ++i) { uint16 frqFin = _frequency2 + getSpecialFrequencyModifier(i); writeDevice(0xA9 + i + _regOffset, frqFin >> 8); writeDevice(0xA5 + i + _regOffset, frqFin & 0xFF); keyOn(); } } } void MusicChannelFM::updateVolume() { uint8 val = _fadeVolModifier + _volume; sendVolume(val < 20 ? val : 0); } void MusicChannelFM::reduceVolume() { sendVolume((uint8)(_volume + _envRR) >> 1); _flags2 |= kNoSustain; } void MusicChannelFM::updateVibrato() { _frequency += _vbrModifier; if (_flags & kTremolo) { sendTrmVolume(_frequency & 0xFF); return; } if (!usingSpecialMode()) { writeDevice(0xA4 + _regOffset, _frequency >> 8); writeDevice(0xA0 + _regOffset, _frequency & 0xFF); return; } for (int i = 0; i < 4; ++i) { uint16 frqFin = _frequency + getSpecialFrequencyModifier(i); writeDevice(0xA9 + i + _regOffset, frqFin >> 8); writeDevice(0xA5 + i + _regOffset, frqFin & 0xFF); } } bool MusicChannelFM::usingSpecialMode() const { return _specialMode == true && _regOffset == 2 && _part == 0; } uint8 MusicChannelFM::getSpecialFrequencyModifier(uint8 index) { assert(index < 4); return _specialModeModifier[index]; } void MusicChannelFM::setSpecialFrequencyModifier(uint8 index, uint8 val) { assert(index < 4); _specialModeModifier[index] = val; } void MusicChannelFM::toggleSpecialMode(bool on) { _specialMode = on; uint8 flag = on ? 0x40 : 0; writeDevice(0x27, 0x3D + flag); writeDevice(0x27, 0x3F + flag); } void MusicChannelFM::sendVolume(uint8 volume) { static const uint8 volTable[20] = { 0x36, 0x33, 0x30, 0x2d, 0x2a, 0x28, 0x25, 0x22, 0x20, 0x1d, 0x1a, 0x18, 0x15, 0x12, 0x10, 0x0d, 0x0a, 0x08, 0x05, 0x02 }; static const uint8 carrier[8] = { 0x08, 0x08, 0x08, 0x08, 0x0c, 0x0e, 0x0e, 0x0f }; assert(volume < 20); assert(_algorithm < 8); uint8 reg = 0x40 + _regOffset; for (uint8 c = carrier[_algorithm]; c; c >>= 1) { if (c & 1) writeDevice(reg, volTable[volume]); reg += 4; } } void MusicChannelFM::sendTrmVolume(uint8 volume) { static uint8 cflg[4] = { 1, 4, 2, 8 }; uint8 reg = 0x40 + _regOffset; for (int i = 0; i < 4; ++i) { if (_trmCarrier & cflg[i]) { writeDevice(reg, volume); reg += 4; } } } void MusicChannelFM::keyOn() { writeDevice(0x28, 0xF0 | ((_part << 2) + _regOffset)); if (_flags & kFade) updateVolume(); } void MusicChannelFM::writeDevice(uint8 reg, uint8 val) { _registers[(_part << 8) + reg] = val; SoundChannel::writeDevice(reg, val); } void MusicChannelFM::op_programChange(uint8 *&data) { _program = *data++; keyOff(); for (int i = 0x80 + _regOffset; i < 0x90; i += 4) writeDevice(i, 0x0F); const uint8 *src = _instrBuffer + READ_LE_UINT16(_instrBuffer) + _program * 25; for (int i = 0x30 + _regOffset; i < 0x90; i += 4) writeDevice(i, *src++); _algorithm = *src & 7; writeDevice(0xB0 + _regOffset, *src); updateVolume(); } void MusicChannelFM::op_setVolume(uint8 *&data) { _volume = *data++; updateVolume(); } void MusicChannelFM::op_setSpecialMode(uint8 *&data) { toggleSpecialMode(true); for (int i = 0; i < 4; ++i) setSpecialFrequencyModifier(i, *data++); } void MusicChannelFM::op_setPanPos(uint8 *&data) { uint8 val = *data++; _statusB4 = (_statusB4 & 0x3f) | (val >> 2) | (val << 6); writeDevice(0xB4 + _regOffset, _statusB4); } void MusicChannelFM::op_modifyVolume(uint8 *&data) { _volume += *data++; updateVolume(); } uint8 MusicChannelSSG::_enState = 0x38; uint8 MusicChannelSSG::_ngState = 0; MusicChannelSSG::MusicChannelSSG(PC98AudioCore *pc98a, int part, int regOffset) : SoundChannel(pc98a, part, regOffset, 1), _regOffsetAttn(8 + (regOffset >> 1)), _externalPrograms(0), _envDataTemp(0), _envStartLvl(0), _envAR(0), _envDR(0), _envSL(0), _envSR(0) { _opcodes.reserve(16); #define OPCODE(y, z) _opcodes.push_back(new Opcode(this, &MusicChannelSSG::y, #y, _regOffset >> 1, 1, z)) OPCODE(op_programChange, 1); OPCODE(op_setVolume, 1); OPCODE(op_setTranspose, 3); OPCODE(op_setDuration, 1); OPCODE(op_setVibrato, 1); OPCODE(op_repeatSectionBegin, 2); OPCODE(op_repeatSectionJumpIf, 4); OPCODE(op_chanEnable, 1); OPCODE(op_setNoiseGenerator, 1); OPCODE(op_jumpToSubroutine, 2); OPCODE(op_setInstrument, 6); OPCODE(op_modifyVolume, 1); OPCODE(op_loadInstrument, 7); OPCODE(op_sustain, 0); OPCODE(op_repeatSectionAbort, 2); OPCODE(op_runOpcode2, 1); #undef OPCODE _envDataTemp = new uint8[96]; memcpy(_envDataTemp, _envDataPreset, 96); } MusicChannelSSG::~MusicChannelSSG() { for (Common::Array::iterator i = _opcodes.begin(); i != _opcodes.end(); ++i) delete (*i); delete[] _envDataTemp; } void MusicChannelSSG::keyOff() { debugC(7, kDebugLevelSound, "SSG Channel %d: keyOff() [Ticks: 0x%02x]", _regOffset >> 1, _ticksLeft); _volume = 0; writeDevice(_regOffsetAttn, 0); } void MusicChannelSSG::parse() { if (!_ticksLeft && (!_dataPtr || _dataPtr >= _dataEnd)) return; if (--_ticksLeft) { if (_ticksLeft <= _duration) { if (_dataPtr && _dataPtr < _dataEnd) { if (*_dataPtr == 0xFD) _flags2 &= ~kNoSustain; else noteOff(); } } if (_volume & kUpdate) { uint8 val = processEnvelope(); writeDevice(_regOffsetAttn, _globalBlock ? 0 : val); } return; } uint8 *pos = _dataPtr; if (pos && pos < _dataEnd) { if (*pos == 0xFD) { _flags2 &= ~kNoSustain; pos++; } else { _flags2 |= kNoSustain; } } uint8 cmd = 0; for (bool loop = true && pos && pos < _dataEnd; loop; ) { if (*pos == 0) { _flags2 |= kEoT; if (!_loopStartPtr) { _dataPtr = 0; keyOff(); _flags2 &= ~kVbrEnable; return; } pos = _loopStartPtr; } cmd = *pos++; if (cmd < 0xF0) break; _opcodes[cmd & 0x0F]->run(pos); } _ticksLeft = cmd & 0x7F; if (cmd & 0x80) noteOff(); else if (pos && pos < _dataEnd) noteOn(*pos++); _dataPtr = pos; } void MusicChannelSSG::noteOff() { debugC(7, kDebugLevelSound, "SSG Channel %d: noteOff() [Ticks: 0x%02x]", _regOffset >> 1, _ticksLeft); if (_flags & kEnvelope) writeDevice(_regOffsetAttn, 0); if (_flags & kFade) { _flags2 &= ~kNoSustain; if (_volume & kUpdate) { uint8 val = processEnvelope(); writeDevice(_regOffsetAttn, _globalBlock ? 0 : val); } return; } if (_volume & kUpdate) { _volume &= ~(kAttack | kDecay | kSustain); _envCurLvl = MAX(_envCurLvl - _envRR, 0); envSendAttLevel(envGetAttLevel()); } else { envSendAttLevel(0); } } void MusicChannelSSG::noteOn(uint8 note) { static uint16 freqTableSSG[12] = { 0x0EE8, 0x0E12, 0x0D48, 0x0C89, 0x0BD5, 0x0B2B, 0x0A8A, 0x09F3, 0x0964, 0x08DD, 0x085E, 0x07E6 }; if (_note == note && !(_flags2 & kNoSustain)) return; _note = note; debugC(5, kDebugLevelSound, "SSG Channel %d: noteOn() [Note: 0x%02x Ticks: 0x%02x]", _regOffset >> 1, _note, _ticksLeft); assert((note & 0x0F) < 12); _frequency = freqTableSSG[note & 0x0F] + _transpose; uint16 frq = _frequency >> (_note >> 4); writeDevice(_regOffset, frq & 0xFF); writeDevice(_regOffset + 1, frq >> 8); uint8 lv = 0; if (_flags2 & kNoSustain) { if (_flags & kEnvelope) { writeDevice(_regOffsetAttn, 0x10); writeDevice(0x0D, _flags & kInternalMask); } else { _volume = (kUpdate | kAttack) | (_volume & 0x0F); _envCurLvl = _envStartLvl; _flags2 &= ~kVbrDelay; } _vbrState = _vbrDepth >> 1; _vbrRem = _vbrDelay; lv = envGetAttLevel(); } else { lv = processEnvelope(); } envSendAttLevel(lv); } uint8 MusicChannelSSG::processEnvelope() { if (_volume & kAttack) { _envCurLvl = (uint8)MIN(_envCurLvl + _envAR, 0xFF); if (_envCurLvl == 0xFF) _volume ^= (kAttack | kDecay); } else if (_volume & kDecay) { _envCurLvl = (uint8)MAX(_envCurLvl - _envDR, 0); _envCurLvl = MAX(_envCurLvl, _envSL); if (_envCurLvl == _envSL) _volume ^= (kDecay | kSustain); } else if (_volume & kSustain) { _envCurLvl = (uint8)MAX(_envCurLvl - _envSR, 0); if (_envCurLvl == 0) _volume &= ~(kAttack | kDecay | kSustain); } else { _envCurLvl = (uint8)MAX(_envCurLvl - _envRR, 0); } return envGetAttLevel(); } uint8 MusicChannelSSG::envGetAttLevel() { uint8 val = ((uint16)_envCurLvl * ((_volume & 0x0F) + 2)) >> 8; return !(_flags2 & kNoSustain) && (_flags & kFade) ? (uint8)(val + _envRR) >> 1 : val; } void MusicChannelSSG::envSendAttLevel(uint8 val) { if (!(_flags & kEnvelope)) writeDevice(_regOffsetAttn, _globalBlock ? 0 : val); } void MusicChannelSSG::updateVolume() { uint8 volNew = (_volume & 0x0F) + _fadeVolModifier; _volume &= 0xF0; if (volNew < 16) _volume |= volNew; } void MusicChannelSSG::updateVibrato() { _frequency += _vbrModifier; uint16 frq = _frequency >> (_note >> 4); writeDevice(_regOffset, frq & 0xFF); writeDevice(_regOffset + 1, frq >> 8); } void MusicChannelSSG::clear() { SoundChannel::clear(); memcpy(_envDataTemp, _envDataPreset, 96); _ngState = 0; _enState = 0x38; _envStartLvl = _envAR = _envDR = _envSL = _envSR = 0; } void MusicChannelSSG::op_programChange(uint8 *&data) { uint8 *src = getProgramData(*data++); _envStartLvl = *src++; _envAR = *src++; _envDR = *src++; _envSL = *src++; _envSR = *src++; _envRR = *src++; _volume |= (kUpdate | kAttack); } void MusicChannelSSG::op_setVolume(uint8 *&data) { _flags &= ~kEnvelope; _volume = (_volume & 0xF0) | (*data++); updateVolume(); } void MusicChannelSSG::op_chanEnable(uint8 *&data) { uint8 c = (_regOffset >> 1) + 1; uint8 val = *data++; val = (val >> 1) | (val << 7); val = (val << c) | (val >> (8 - c)); _enState = (((0x7B << c) | (0x7B >> (8 - c))) & _enState) | val; writeDevice(0x07, _enState); } void MusicChannelSSG::op_setNoiseGenerator(uint8 *&data) { _ngState = *data++; writeDevice(0x06, _ngState); } void MusicChannelSSG::op_setInstrument(uint8 *&data) { _envStartLvl = *data++; _envAR = *data++; _envDR = *data++; _envSL = *data++; _envSR = *data++; _envRR = *data++; _volume |= (kUpdate | kAttack); } void MusicChannelSSG::op_modifyVolume(uint8 *&data) { if (_flags & kEnvelope) return; uint8 newVol = (_volume & 0x0F) + (*data++); if (newVol > 15) return; _volume = (_volume & 0xF0) | newVol; } void MusicChannelSSG::op_loadInstrument(uint8 *&data) { uint8 *dst = getProgramData(*data++); memcpy(dst, data, 6); data += 6; } uint8 *MusicChannelSSG::getProgramData(uint8 program) const { return _externalPrograms ? _instrBuffer + READ_LE_UINT16(_instrBuffer + 2) + (program << 4) + 1 : _envDataTemp + program * 6; } const uint8 MusicChannelSSG::_envDataPreset[96] = { 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc8, 0x00, 0x0a, 0xff, 0xff, 0xff, 0xc8, 0x01, 0x0a, 0xff, 0xff, 0xff, 0xbe, 0x00, 0x0a, 0xff, 0xff, 0xff, 0xbe, 0x01, 0x0a, 0xff, 0xff, 0xff, 0xaa, 0x00, 0x0a, 0x28, 0x46, 0x0e, 0xbe, 0x00, 0x0f, 0x78, 0x1e, 0xff, 0xff, 0x00, 0x0a, 0xff, 0xff, 0xff, 0xe1, 0x08, 0x0f, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0x08, 0xff, 0xff, 0xff, 0xff, 0xdc, 0x14, 0x08, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0a, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0a, 0x78, 0x50, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xdc, 0x00, 0xff }; MusicChannelRHY::MusicChannelRHY(PC98AudioCore *pc98a, int part, int regOffset) : SoundChannelNonSSG(pc98a, part, regOffset, 2), _activeInstruments(0) { _instrLevel[0] = _instrLevel[1] = _instrLevel[2] = _instrLevel[3] = _instrLevel[4] = _instrLevel[5] = 0xC0; } MusicChannelRHY::~MusicChannelRHY() { } void MusicChannelRHY::keyOff() { debugC(7, kDebugLevelSound, "RHY Channel 0: keyOff() [Ticks: 0x%02x]", _ticksLeft); writeDevice(0x10, (_activeInstruments & 0x3F) | 0x80); } void MusicChannelRHY::noteOn(uint8 note) { if ((_flags2 & kNoSustain) && !_globalBlock) writeDevice(0x10, _activeInstruments & 0x3F); _note = note; debugC(5, kDebugLevelSound, "RHY Channel 0: noteOn() [Note: 0x%02x Ticks: 0x%02x]", _note, _ticksLeft); } void MusicChannelRHY::updateVolume() { uint8 val = ((MAX((uint8)_fadeVolModifier - 1, 239) + 1) << 2) + (_volume & 0x3F); writeDevice(0x11, val < 64 ? val : 0); } void MusicChannelRHY::op_programChange(uint8 *&data) { _activeInstruments = _program = *data++; } void MusicChannelRHY::op_setVolume(uint8 *&data) { _volume = *data++; updateVolume(); uint8 *p = _instrLevel; for (int i = 6; i; --i) { *p = (*p & 0xC0) | *data++; writeDevice(0x18 - (i - 6), *p++); } } void MusicChannelRHY::op_setPanPos(uint8 *&data) { uint8 val = *data++; uint8 offs = val & 0x0F; _instrLevel[offs] = (((val << 2) | (val >> 6)) & 0xC0) | (_instrLevel[offs] & 0x1F); writeDevice(0x18 + offs, _instrLevel[offs]); } void MusicChannelRHY::op_modifyVolume(uint8 *&data) { _volume += *data++; updateVolume(); } MusicChannelEXT::MusicChannelEXT(PC98AudioCore *pc98a, int part, int regOffset, MLALF98::ADPCMData *const &data) : SoundChannelNonSSG(pc98a, part, regOffset, 3), _pc98a(pc98a), _extBuffer(data), _useVolPreset(0), _volume2(0), _instrument(0), _panPos(3), _smpStart(0), _smpEnd(0) { } MusicChannelEXT::~MusicChannelEXT() { } void MusicChannelEXT::keyOff() { debugC(7, kDebugLevelSound, "EXT Channel 0: keyOff() [Ticks: 0x%02x]", _ticksLeft); writeDevice(0x0B, 0x00); writeDevice(0x01, 0x00); writeDevice(0x00, 0x21); } void MusicChannelEXT::noteOn(uint8 note) { static uint16 freqTableEXT[12] = { 0x4A82, 0x4EE4, 0x5389, 0x5875, 0x5DAC, 0x6332, 0x690C, 0x6F3F, 0x75D1, 0x7C76, 0x8426, 0x8BF5 }; if (!(_flags2 & kNoSustain) && _note == note) return; _note = note; debugC(5, kDebugLevelSound, "EXT Channel 0: noteOn() [Note: 0x%02x Ticks: 0x%02x]", _note, _ticksLeft); assert((note & 0x0F) < 12); _frequency = (freqTableEXT[note & 0x0F] + _transpose) >> (note >> 4); if (!(_flags2 & kNoSustain)) vbrResetDelay(); vbrReset(); if (_globalBlock) return; writeDevice(0x0B, 0x00); // vol zero writeDevice(0x01, 0x00); // ram type default writeDevice(0x00, 0x21); // 0x20: external memory, 0x01: clear busy flag writeDevice(0x10, 0x08); // set irq flag mask writeDevice(0x10, 0x80); // reset irq flag writeDevice(0x02, _smpStart & 0xFF); writeDevice(0x03, _smpStart >> 8); writeDevice(0x04, _smpEnd & 0xFF); writeDevice(0x05, _smpEnd >> 8); writeDevice(0x09, _frequency & 0xFF); writeDevice(0x0A, _frequency >> 8); writeDevice(0x00, 0xA0); // 0x20: external memory, 0x80: start uint8 vol = (uint8)(MAX(_fadeVolModifier, -16) << 2) + _volume; if (_volume < vol) vol = 0; if (_useVolPreset) { vol += _volume2; if (_volume2 < vol) vol = 0; } writeDevice(0x0B, vol); writeDevice(0x01, ((_panPos << 6) | (_panPos >> 2)) & 0xC0); } void MusicChannelEXT::updateVibrato() { _frequency += _vbrModifier; writeDevice(0x09, _frequency & 0xFF); writeDevice(0x0A, _frequency >> 8); } void MusicChannelEXT::clear() { SoundChannel::clear(); _panPos = 3; _useVolPreset = 0; _volume2 = 0; } void MusicChannelEXT::writeDevice(uint8 reg, uint8 val) { _pc98a->writeReg(1, reg, val); } void MusicChannelEXT::op_programChange(uint8 *&data) { _instrument = *data++; _program = _instrument - 1; _smpStart = _extBuffer[_program].smpStart; _smpEnd = _extBuffer[_program].smpEnd; if (_useVolPreset) _volume = _extBuffer[_program].volume; } void MusicChannelEXT::op_setVolume(uint8 *&data) { if (_useVolPreset) _volume2 = *data; else _volume = *data; data++; } void MusicChannelEXT::op_setPanPos(uint8 *&data) { _panPos = *data++; } void MusicChannelEXT::op_setTranspose(uint8 *&data) { _note = 0; int16 trp = READ_LE_INT16(data); data += 2; _transpose = (*data++) ? _transpose + trp : trp; uint16 val = _frequency + _transpose; writeDevice(0x09, val & 0xFF); writeDevice(0x0A, val >> 8); } void MusicChannelEXT::op2_setExtPara(uint8 *&data) { _useVolPreset = *data++; } SoundEffectChannel::SoundEffectChannel(PC98AudioCore *pc98a, int part, int regOffset, SoundChannel *replaceChannel) : MusicChannelFM(pc98a, part, regOffset), _replaceChannel(replaceChannel), _specialMode(false), _isFinished(false) { _specialModeModifier[0] = _specialModeModifier[1] = _specialModeModifier[2] = _specialModeModifier[3] = 0; } SoundEffectChannel::~SoundEffectChannel() { } void SoundEffectChannel::setData(uint8 *dataStart, uint8 *loopStart, const uint8 *dataEnd, uint8 *instrBuffer) { _replaceChannel->toggleMute(dataStart); SoundChannel::setData(dataStart, loopStart, dataEnd, instrBuffer); } bool SoundEffectChannel::checkFinished() { if (!_isFinished) return false; _replaceChannel->toggleMute(false); _replaceChannel->restore(); _isFinished = false; return true; } void SoundEffectChannel::finish() { keyOff(); _isFinished = true; } bool SoundEffectChannel::usingSpecialMode() const { return _specialMode; } uint8 SoundEffectChannel::getSpecialFrequencyModifier(uint8 index) { assert(index < 4); return _specialModeModifier[index]; } void SoundEffectChannel::setSpecialFrequencyModifier(uint8 index, uint8 val) { assert(index < 4); _specialModeModifier[index] = val; } void SoundEffectChannel::toggleSpecialMode(bool on) { _specialMode = on; uint8 flag = on ? 0x40 : 0; writeDevice(0x27, 0x3E + flag); writeDevice(0x27, 0x3F + flag); } void SoundEffectChannel::clear() { SoundChannel::clear(); _specialMode = false; _statusB4 = 0xC0; } void SoundEffectChannel::writeDevice(uint8 reg, uint8 val) { SoundChannel::writeDevice(reg, val); } void SoundEffectChannel::op_writeDevice(uint8 *&data) { uint8 reg = *data++; uint8 val = *data++; if (reg != 0x26) writeDevice(reg, val); if (reg == 0x25 || reg == 0x26) toggleSpecialMode(_specialMode); } #define iterateChannels(arr) for (Common::Array::iterator i = arr.begin(); i != arr.end(); ++i) MLALF98Internal *MLALF98Internal::_refInstance = 0; int MLALF98Internal::_refCount = 0; MLALF98Internal::MLALF98Internal(Audio::Mixer *mixer, EmuType emuType) : PC98AudioPluginDriver(), _type86(emuType == kType86), _musicBuffer(0), _sfxBuffer(0), _extBuffer(0), _musicBufferSize(0), _sfxBufferSize(0), _extBufferSize(0), _pc98a(0), _sfxPlaying(0), _ready(false) { _pc98a = new PC98AudioCore(mixer, this, emuType); assert(_pc98a); _extBuffer = new MLALF98::ADPCMData[8]; for (int i = 0; i < 3; ++i) _musicChannels.push_back(new MusicChannelFM(_pc98a, 0, i)); for (int i = 3; i < 6; ++i) _musicChannels.push_back(new MusicChannelSSG(_pc98a, 0, (i - 3) << 1)); if (_type86) { _musicChannels.push_back(new MusicChannelRHY(_pc98a, 0, 0)); for (int i = 7; i < 10; ++i) _musicChannels.push_back(new MusicChannelFM(_pc98a, 1, i - 7)); _musicChannels.push_back(new MusicChannelEXT(_pc98a, 1, 0, _extBuffer)); } int replChanIndex = 2; _sfxChannels.push_back(new SoundEffectChannel(_pc98a, 0, 2, _musicChannels[replChanIndex++])); _pc98a->init(); _ready = true; } MLALF98Internal::~MLALF98Internal() { _ready = false; delete _pc98a; iterateChannels(_musicChannels) delete (*i); iterateChannels(_sfxChannels) delete (*i); delete[] _musicBuffer; delete[] _sfxBuffer; delete[] _extBuffer; } MLALF98Internal *MLALF98Internal::open(Audio::Mixer *mixer, EmuType emuType) { _refCount++; if (_refCount == 1 && _refInstance == 0) _refInstance = new MLALF98Internal(mixer, emuType); else if (_refCount < 2 || _refInstance == 0) error("MLALF98Internal::open(): Internal instance management failure"); return _refInstance; } void MLALF98Internal::close() { if (!_refCount) return; _refCount--; if (!_refCount) { delete _refInstance; _refInstance = 0; } } void MLALF98Internal::loadMusicData(Common::SeekableReadStream *data) { PC98AudioCore::MutexLock lock = _pc98a->stackLockMutex(); if (!data) error("MLALF98Internal::loadMusicData(): Invalid data."); if (data->size() == 0) error("MLALF98Internal::loadMusicData(): Invalid data size."); iterateChannels(_musicChannels) (*i)->setData(0, 0, 0, 0); delete[] _musicBuffer; _musicBufferSize = data->size(); _musicBuffer = new uint8[_musicBufferSize]; data->read(_musicBuffer, _musicBufferSize); } void MLALF98Internal::loadSoundEffectData(Common::SeekableReadStream *data) { PC98AudioCore::MutexLock lock = _pc98a->stackLockMutex(); if (!data) error("MLALF98Internal::loadSoundEffectData(): Invalid data."); if (data->size() == 0) error("MLALF98Internal::loadSoundEffectData(): Invalid data size."); iterateChannels(_sfxChannels) (*i)->setData(0, 0, 0, 0); delete[] _sfxBuffer; _sfxBufferSize = data->size(); _sfxBuffer = new uint8[_sfxBufferSize]; data->read(_sfxBuffer, _sfxBufferSize); } void MLALF98Internal::loadExtData(MLALF98::ADPCMDataArray &data) { PC98AudioCore::MutexLock lock = _pc98a->stackLockMutex(); if (data.empty()) error("MLALF98Internal::loadExtData(): Invalid data."); delete[] _extBuffer; _extBufferSize = data.size(); _extBuffer = new MLALF98::ADPCMData[_extBufferSize]; MLALF98::ADPCMData *dst = _extBuffer; for (MLALF98::ADPCMDataArray::iterator i = data.begin(); i != data.end(); ++i) *dst++ = *i; } void MLALF98Internal::startMusic(int track) { PC98AudioCore::MutexLock lock = _pc98a->stackLockMutex(); iterateChannels(_musicChannels) { (*i)->abortFadeOut(); (*i)->toggleMute(false); } _sfxPlaying = 0; _pc98a->writeReg(0, 0x27, 0x3C); _pc98a->writeReg(0, 0x10, 0x80); _pc98a->writeReg(0, 0x10, 0x00); _pc98a->writeReg(0, 0x24, 0x18); _pc98a->writeReg(0, 0x25, 0x02); iterateChannels(_sfxChannels) (*i)->setData(0, 0, 0, 0); iterateChannels(_musicChannels) (*i)->keyOff(); iterateChannels(_sfxChannels) (*i)->keyOff(); assert(track * 45 + 5 < _musicBufferSize); const uint8 *header = _musicBuffer + (track * 45) + 5; uint8 tempo = *header++; iterateChannels(_musicChannels) { uint16 offset1 = READ_LE_UINT16(header); assert(offset1 + 5 < _musicBufferSize); header += 2; uint16 offset2 = READ_LE_UINT16(header); assert(offset2 + 5 <= _musicBufferSize); header += 2; (*i)->setData(_musicBuffer + 5 + offset1, offset2 ? _musicBuffer + 5 + offset2 : 0, _musicBuffer + _musicBufferSize, _musicBuffer + 1); } debugC(3, kDebugLevelSound, "\nStarting music. Track: %03d", track); _pc98a->writeReg(0, 0x29, 0x83); for (int i = 0; i < 6; ++i) _pc98a->writeReg(0, i, 0); _pc98a->writeReg(0, 0x07, 0x38); _pc98a->writeReg(0, 0x26, tempo); for (int i = 0; i < 2; ++i) { for (int ii = 0; ii < 3; ++ii) _pc98a->writeReg(i, 0xB4 + ii, 0xC0); } _pc98a->writeReg(0, 0x22, 0x00); _pc98a->writeReg(0, 0x27, 0x3F); } void MLALF98Internal::fadeOutMusic() { PC98AudioCore::MutexLock lock = _pc98a->stackLockMutex(); iterateChannels(_musicChannels) (*i)->startFadeOut(); } void MLALF98Internal::startSoundEffect(int track) { PC98AudioCore::MutexLock lock = _pc98a->stackLockMutex(); const uint8 *header = _sfxBuffer + (track << 1) + 3; uint16 offset = READ_LE_UINT16(header); assert(offset < _sfxBufferSize); _sfxPlaying = 0; int flags = 0; iterateChannels(_sfxChannels) { (*i)->setData(_sfxBuffer + offset, 0, _sfxBuffer + _sfxBufferSize, _sfxBuffer + 1); flags |= (*i)->idFlag(); _sfxPlaying++; } debugC(3, kDebugLevelSound, "\nStarting sound effect. Track: %03d", track); _pc98a->setSoundEffectChanMask(flags); _pc98a->writeReg(0, 0x28, 0x02); _pc98a->writeReg(0, 0x24, 0x18); _pc98a->writeReg(0, 0x25, 0x02); _pc98a->writeReg(0, 0x82, 0x0F); _pc98a->writeReg(0, 0x86, 0x0F); _pc98a->writeReg(0, 0x8A, 0x0F); _pc98a->writeReg(0, 0x8E, 0x0F); _pc98a->writeReg(0, 0xB6, 0xC0); _pc98a->writeReg(0, 0x27, 0x3F); } void MLALF98Internal::allChannelsOff() { PC98AudioCore::MutexLock lock = _pc98a->stackLockMutex(); iterateChannels(_musicChannels) (*i)->keyOff(); iterateChannels(_sfxChannels) (*i)->keyOff(); } void MLALF98Internal::resetExtUnit() { PC98AudioCore::MutexLock lock = _pc98a->stackLockMutex(); _pc98a->writeReg(1, 0x0B, 0x00); // vol zero _pc98a->writeReg(1, 0x01, 0x00); // ram type default _pc98a->writeReg(1, 0x00, 0x21); // 0x20: external memory, 0x01: clear busy flag _pc98a->writeReg(1, 0x10, 0x08); // set irq flag mask _pc98a->writeReg(1, 0x10, 0x80); // reset irq flag _pc98a->writeReg(1, 0x02, _extBuffer[0].smpStart & 0xFF); _pc98a->writeReg(1, 0x03, _extBuffer[0].smpStart >> 8); _pc98a->writeReg(1, 0x04, _extBuffer[0].smpEnd & 0xFF); _pc98a->writeReg(1, 0x05, _extBuffer[0].smpEnd >> 8); _pc98a->writeReg(1, 0x09, 0xCE); _pc98a->writeReg(1, 0x0A, 0x49); _pc98a->writeReg(1, 0x00, 0xA0); // 0x20: external memory, 0x80: start _pc98a->writeReg(1, 0x0B, _extBuffer[0].volume); _pc98a->writeReg(1, 0x01, 0xC0); } void MLALF98Internal::setMusicVolume(int volume) { _pc98a->setMusicVolume(volume); } void MLALF98Internal::setSoundEffectVolume(int volume) { _pc98a->setSoundEffectVolume(volume); } void MLALF98Internal::timerCallbackA() { if (!_ready || !_sfxPlaying) return; iterateChannels(_sfxChannels) { (*i)->update(); if ((*i)->checkFinished()) { if (!--_sfxPlaying) { _pc98a->setSoundEffectChanMask(0); debugC(3, kDebugLevelSound, "Finished sound effect.\n"); } } } //_updateCounterSfx++; } void MLALF98Internal::timerCallbackB() { if (!_ready) return; iterateChannels(_musicChannels) (*i)->update(); iterateChannels(_musicChannels) (*i)->updateFadeOut(); //_updateCounterMusic++; } MLALF98::MLALF98(Audio::Mixer *mixer, EmuType emuType) { _drv = MLALF98Internal::open(mixer, (PC98AudioPluginDriver::EmuType)emuType); } MLALF98::~MLALF98() { MLALF98Internal::close(); _drv = 0; } void MLALF98::loadMusicData(Common::SeekableReadStream *data) { _drv->loadMusicData(data); } void MLALF98::loadSoundEffectData(Common::SeekableReadStream *data) { _drv->loadSoundEffectData(data); } void MLALF98::loadExtData(ADPCMDataArray &data) { _drv->loadExtData(data); } void MLALF98::startMusic(int track) { _drv->startMusic(track); } void MLALF98::fadeOutMusic() { _drv->fadeOutMusic(); } void MLALF98::startSoundEffect(int track) { _drv->startSoundEffect(track); } void MLALF98::allChannelsOff() { _drv->allChannelsOff(); } void MLALF98::resetExtUnit() { _drv->resetExtUnit(); } void MLALF98::setMusicVolume(int volume) { _drv->setMusicVolume(volume); } void MLALF98::setSoundEffectVolume(int volume) { _drv->setSoundEffectVolume(volume); } #undef iterateChannels } // End of namespace Kyra #endif