diff options
Diffstat (limited to 'src/adplug/core/adl.cc')
-rw-r--r-- | src/adplug/core/adl.cc | 2439 |
1 files changed, 0 insertions, 2439 deletions
diff --git a/src/adplug/core/adl.cc b/src/adplug/core/adl.cc deleted file mode 100644 index b27d1bf..0000000 --- a/src/adplug/core/adl.cc +++ /dev/null @@ -1,2439 +0,0 @@ -/* - * adl.cpp - ADL player adaption by Simon Peter <dn.tlp@gmx.net> - * - * Original ADL player by Torbjorn Andersson and Johannes Schickel - * 'lordhoto' <lordhoto at scummvm dot org> of the ScummVM project. - */ - -/* ScummVM - Scumm Interpreter - * - * This file is licensed under both GPL and LGPL - * Copyright (C) 2006 The ScummVM project - * Copyright (C) 2006 Torbjorn Andersson and Johannes Schickel - * - * GPL License - * - * 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. - * - * LPGL License - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - - * This library 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 - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * $URL: https://svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/engines/kyra/sound_adlib.cpp $ - * $Id: adl.cpp,v 1.11 2008/02/11 20:18:27 dynamite Exp $ - * - */ - -#include <cstring> -#include <inttypes.h> -#include <stdarg.h> -#include <assert.h> - -#include "adl.h" -#include "debug.h" - -#ifdef ADL_DEBUG -# define warning(...) AdPlug_LogWrite(__VA_ARGS__); \ -AdPlug_LogWrite("\n") - -# define debugC(i1, i2, ...) AdPlug_LogWrite(__VA_ARGS__); \ -AdPlug_LogWrite("\n") -#else -# define kDebugLevelSound 1 - -static inline void warning(const char *str, ...) -{ -} - -static inline void debugC(int i1, int i2, const char *str, ...) -{ -} -#endif - -// #define warning(...) -// #define debugC(i1, i2, ...) - -#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0]))) - -// Basic Adlib Programming: -// http://www.gamedev.net/reference/articles/article446.asp - -#define CALLBACKS_PER_SECOND 72 - -typedef uint8_t uint8; -typedef int8_t int8; -typedef uint16_t uint16; -typedef int16_t int16; -typedef uint32_t uint32; -typedef int32_t int32; -typedef uint8_t byte; - -static inline uint16 READ_LE_UINT16(const void *ptr) { - const byte *b = (const byte *)ptr; - return (b[1] << 8) + b[0]; -} - -static inline uint16 READ_BE_UINT16(const void *ptr) { - const byte *b = (const byte *)ptr; - return (b[0] << 8) + b[1]; -} - -class AdlibDriver { -public: - AdlibDriver(Copl *opl); - ~AdlibDriver(); - - int callback(int opcode, ...); - void callback(); - - // AudioStream API - // int readBuffer(int16 *buffer, const int numSamples) { - // int32 samplesLeft = numSamples; - // memset(buffer, 0, sizeof(int16) * numSamples); - // while (samplesLeft) { - // if (!_samplesTillCallback) { - // callback(); - // _samplesTillCallback = _samplesPerCallback; - // _samplesTillCallbackRemainder += _samplesPerCallbackRemainder; - // if (_samplesTillCallbackRemainder >= CALLBACKS_PER_SECOND) { - // _samplesTillCallback++; - // _samplesTillCallbackRemainder -= CALLBACKS_PER_SECOND; - // } - // } - - // int32 render = MIN(samplesLeft, _samplesTillCallback); - // samplesLeft -= render; - // _samplesTillCallback -= render; - // YM3812UpdateOne(_adlib, buffer, render); - // buffer += render; - // } - // return numSamples; - // } - - bool isStereo() const { return false; } - bool endOfData() const { return false; } - // int getRate() const { return _mixer->getOutputRate(); } - - struct OpcodeEntry { - typedef int (AdlibDriver::*DriverOpcode)(va_list &list); - DriverOpcode function; - const char *name; - }; - - void setupOpcodeList(); - const OpcodeEntry *_opcodeList; - int _opcodesEntries; - - int snd_ret0x100(va_list &list); - int snd_ret0x1983(va_list &list); - int snd_initDriver(va_list &list); - int snd_deinitDriver(va_list &list); - int snd_setSoundData(va_list &list); - int snd_unkOpcode1(va_list &list); - int snd_startSong(va_list &list); - int snd_unkOpcode2(va_list &list); - int snd_unkOpcode3(va_list &list); - int snd_readByte(va_list &list); - int snd_writeByte(va_list &list); - int snd_getSoundTrigger(va_list &list); - int snd_unkOpcode4(va_list &list); - int snd_dummy(va_list &list); - int snd_getNullvar4(va_list &list); - int snd_setNullvar3(va_list &list); - int snd_setFlag(va_list &list); - int snd_clearFlag(va_list &list); - - // These variables have not yet been named, but some of them are partly - // known nevertheless: - // - // unk16 - Sound-related. Possibly some sort of pitch bend. - // unk18 - Sound-effect. Used for secondaryEffect1() - // unk19 - Sound-effect. Used for secondaryEffect1() - // unk20 - Sound-effect. Used for secondaryEffect1() - // unk21 - Sound-effect. Used for secondaryEffect1() - // unk22 - Sound-effect. Used for secondaryEffect1() - // unk29 - Sound-effect. Used for primaryEffect1() - // unk30 - Sound-effect. Used for primaryEffect1() - // unk31 - Sound-effect. Used for primaryEffect1() - // unk32 - Sound-effect. Used for primaryEffect2() - // unk33 - Sound-effect. Used for primaryEffect2() - // unk34 - Sound-effect. Used for primaryEffect2() - // unk35 - Sound-effect. Used for primaryEffect2() - // unk36 - Sound-effect. Used for primaryEffect2() - // unk37 - Sound-effect. Used for primaryEffect2() - // unk38 - Sound-effect. Used for primaryEffect2() - // unk39 - Currently unused, except for updateCallback56() - // unk40 - Currently unused, except for updateCallback56() - // unk41 - Sound-effect. Used for primaryEffect2() - - struct Channel { - uint8 opExtraLevel2; - uint8 *dataptr; - uint8 duration; - uint8 repeatCounter; - int8 baseOctave; - uint8 priority; - uint8 dataptrStackPos; - uint8 *dataptrStack[4]; - int8 baseNote; - uint8 unk29; - uint8 unk31; - uint16 unk30; - uint16 unk37; - uint8 unk33; - uint8 unk34; - uint8 unk35; - uint8 unk36; - uint8 unk32; - uint8 unk41; - uint8 unk38; - uint8 opExtraLevel1; - uint8 spacing2; - uint8 baseFreq; - uint8 tempo; - uint8 position; - uint8 regAx; - uint8 regBx; - typedef void (AdlibDriver::*Callback)(Channel&); - Callback primaryEffect; - Callback secondaryEffect; - uint8 fractionalSpacing; - uint8 opLevel1; - uint8 opLevel2; - uint8 opExtraLevel3; - uint8 twoChan; - uint8 unk39; - uint8 unk40; - uint8 spacing1; - uint8 durationRandomness; - uint8 unk19; - uint8 unk18; - int8 unk20; - int8 unk21; - uint8 unk22; - uint16 offset; - uint8 tempoReset; - uint8 rawNote; - int8 unk16; - }; - - void primaryEffect1(Channel &channel); - void primaryEffect2(Channel &channel); - void secondaryEffect1(Channel &channel); - - void resetAdlibState(); - void writeOPL(byte reg, byte val); - void initChannel(Channel &channel); - void noteOff(Channel &channel); - void unkOutput2(uint8 num); - - uint16 getRandomNr(); - void setupDuration(uint8 duration, Channel &channel); - - void setupNote(uint8 rawNote, Channel &channel, bool flag = false); - void setupInstrument(uint8 regOffset, uint8 *dataptr, Channel &channel); - void noteOn(Channel &channel); - - void adjustVolume(Channel &channel); - - uint8 calculateOpLevel1(Channel &channel); - uint8 calculateOpLevel2(Channel &channel); - - uint16 checkValue(int16 val) { - if (val < 0) - val = 0; - else if (val > 0x3F) - val = 0x3F; - return val; - } - - // The sound data has at least two lookup tables: - // - // * One for programs, starting at offset 0. - // * One for instruments, starting at offset 500. - - uint8 *getProgram(int progId) { - return _soundData + READ_LE_UINT16(_soundData + 2 * progId); - } - - uint8 *getInstrument(int instrumentId) { - return _soundData + READ_LE_UINT16(_soundData + 500 + 2 * instrumentId); - } - - void setupPrograms(); - void executePrograms(); - - struct ParserOpcode { - typedef int (AdlibDriver::*POpcode)(uint8 *&dataptr, Channel &channel, uint8 value); - POpcode function; - const char *name; - }; - - void setupParserOpcodeTable(); - const ParserOpcode *_parserOpcodeTable; - int _parserOpcodeTableSize; - - int update_setRepeat(uint8 *&dataptr, Channel &channel, uint8 value); - int update_checkRepeat(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupProgram(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value); - int update_jump(uint8 *&dataptr, Channel &channel, uint8 value); - int update_jumpToSubroutine(uint8 *&dataptr, Channel &channel, uint8 value); - int update_returnFromSubroutine(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setBaseOctave(uint8 *&dataptr, Channel &channel, uint8 value); - int update_stopChannel(uint8 *&dataptr, Channel &channel, uint8 value); - int update_playRest(uint8 *&dataptr, Channel &channel, uint8 value); - int update_writeAdlib(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupNoteAndDuration(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setBaseNote(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_stopOtherChannel(uint8 *&dataptr, Channel &channel, uint8 value); - int update_waitForEndOfProgram(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupInstrument(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupPrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_removePrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setBaseFreq(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupPrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setPriority(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback23(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback24(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupDuration(uint8 *&dataptr, Channel &channel, uint8 value); - int update_playNote(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setFractionalNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setTempo(uint8 *&dataptr, Channel &channel, uint8 value); - int update_removeSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setExtraLevel3(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value); - int update_changeExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setAMDepth(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setVibratoDepth(uint8 *&dataptr, Channel &channel, uint8 value); - int update_changeExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback38(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback39(uint8 *&dataptr, Channel &channel, uint8 value); - int update_removePrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback41(uint8 *&dataptr, Channel &channel, uint8 value); - int update_resetToGlobalTempo(uint8 *&dataptr, Channel &channel, uint8 value); - int update_nop1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setDurationRandomness(uint8 *&dataptr, Channel &channel, uint8 value); - int update_changeChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback46(uint8 *&dataptr, Channel &channel, uint8 value); - int update_nop2(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value); - int update_playRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value); - int update_removeRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback51(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback52(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback53(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setSoundTrigger(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setTempoReset(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback56(uint8 *&dataptr, Channel &channel, uint8 value); - - // These variables have not yet been named, but some of them are partly - // known nevertheless: - // - // _unkValue1 - Unknown. Used for updating _unkValue2 - // _unkValue2 - Unknown. Used for updating _unkValue4 - // _unkValue3 - Unknown. Used for updating _unkValue2 - // _unkValue4 - Unknown. Used for updating _unkValue5 - // _unkValue5 - Unknown. Used for controlling updateCallback24(). - // _unkValue6 - Unknown. Rhythm section volume? - // _unkValue7 - Unknown. Rhythm section volume? - // _unkValue8 - Unknown. Rhythm section volume? - // _unkValue9 - Unknown. Rhythm section volume? - // _unkValue10 - Unknown. Rhythm section volume? - // _unkValue11 - Unknown. Rhythm section volume? - // _unkValue12 - Unknown. Rhythm section volume? - // _unkValue13 - Unknown. Rhythm section volume? - // _unkValue14 - Unknown. Rhythm section volume? - // _unkValue15 - Unknown. Rhythm section volume? - // _unkValue16 - Unknown. Rhythm section volume? - // _unkValue17 - Unknown. Rhythm section volume? - // _unkValue18 - Unknown. Rhythm section volume? - // _unkValue19 - Unknown. Rhythm section volume? - // _unkValue20 - Unknown. Rhythm section volume? - // _unkTable[] - Probably frequences for the 12-tone scale. - // _unkTable2[] - Unknown. Currently only used by updateCallback46() - // _unkTable2_1[] - One of the tables in _unkTable2[] - // _unkTable2_2[] - One of the tables in _unkTable2[] - // _unkTable2_3[] - One of the tables in _unkTable2[] - - int32 _samplesPerCallback; - int32 _samplesPerCallbackRemainder; - int32 _samplesTillCallback; - int32 _samplesTillCallbackRemainder; - - int _lastProcessed; - int8 _flagTrigger; - int _curChannel; - uint8 _soundTrigger; - int _soundsPlaying; - - uint16 _rnd; - - uint8 _unkValue1; - uint8 _unkValue2; - uint8 _unkValue3; - uint8 _unkValue4; - uint8 _unkValue5; - uint8 _unkValue6; - uint8 _unkValue7; - uint8 _unkValue8; - uint8 _unkValue9; - uint8 _unkValue10; - uint8 _unkValue11; - uint8 _unkValue12; - uint8 _unkValue13; - uint8 _unkValue14; - uint8 _unkValue15; - uint8 _unkValue16; - uint8 _unkValue17; - uint8 _unkValue18; - uint8 _unkValue19; - uint8 _unkValue20; - - int _flags; - - uint8 *_soundData; - - uint8 _soundIdTable[0x10]; - Channel _channels[10]; - - uint8 _vibratoAndAMDepthBits; - uint8 _rhythmSectionBits; - - uint8 _curRegOffset; - uint8 _tempo; - - const uint8 *_tablePtr1; - const uint8 *_tablePtr2; - - static const uint8 _regOffset[]; - static const uint16 _unkTable[]; - static const uint8 *_unkTable2[]; - static const uint8 _unkTable2_1[]; - static const uint8 _unkTable2_2[]; - static const uint8 _unkTable2_3[]; - static const uint8 _unkTables[][32]; - - Copl *opl; -}; - -AdlibDriver::AdlibDriver(Copl *newopl) - : opl(newopl) -{ - setupOpcodeList(); - setupParserOpcodeTable(); - - // _mixer = mixer; - - _flags = 0; - // _adlib = makeAdlibOPL(getRate()); - // assert(_adlib); - - memset(_channels, 0, sizeof(_channels)); - _soundData = 0; - - _vibratoAndAMDepthBits = _curRegOffset = 0; - - _lastProcessed = _flagTrigger = _curChannel = _rhythmSectionBits = 0; - _soundsPlaying = 0; - _rnd = 0x1234; - - _tempo = 0; - _soundTrigger = 0; - - _unkValue3 = 0xFF; - _unkValue1 = _unkValue2 = _unkValue4 = _unkValue5 = 0; - _unkValue6 = _unkValue7 = _unkValue8 = _unkValue9 = _unkValue10 = 0; - _unkValue11 = _unkValue12 = _unkValue13 = _unkValue14 = _unkValue15 = - _unkValue16 = _unkValue17 = _unkValue18 = _unkValue19 = _unkValue20 = 0; - - _tablePtr1 = _tablePtr2 = 0; - - // _mixer->setupPremix(this); - - // _samplesPerCallback = getRate() / CALLBACKS_PER_SECOND; - // _samplesPerCallbackRemainder = getRate() % CALLBACKS_PER_SECOND; - _samplesTillCallback = 0; - _samplesTillCallbackRemainder = 0; -} - -AdlibDriver::~AdlibDriver() { - // _mixer->setupPremix(0); - // OPLDestroy(_adlib); - // _adlib = 0; -} - -int AdlibDriver::callback(int opcode, ...) { - // lock(); - if (opcode >= _opcodesEntries || opcode < 0) { - warning("AdlibDriver: calling unknown opcode '%d'", opcode); - return 0; - } - - debugC(9, kDebugLevelSound, "Calling opcode '%s' (%d)", _opcodeList[opcode].name, opcode); - - va_list args; - va_start(args, opcode); - int returnValue = (this->*(_opcodeList[opcode].function))(args); - va_end(args); - // unlock(); - return returnValue; -} - -// Opcodes - -int AdlibDriver::snd_ret0x100(va_list &list) { - return 0x100; -} - -int AdlibDriver::snd_ret0x1983(va_list &list) { - return 0x1983; -} - -int AdlibDriver::snd_initDriver(va_list &list) { - _lastProcessed = _soundsPlaying = 0; - resetAdlibState(); - return 0; -} - -int AdlibDriver::snd_deinitDriver(va_list &list) { - resetAdlibState(); - return 0; -} - -int AdlibDriver::snd_setSoundData(va_list &list) { - if (_soundData) { - delete [] _soundData; - _soundData = 0; - } - _soundData = va_arg(list, uint8*); - return 0; -} - -int AdlibDriver::snd_unkOpcode1(va_list &list) { - warning("unimplemented snd_unkOpcode1"); - return 0; -} - -int AdlibDriver::snd_startSong(va_list &list) { - int songId = va_arg(list, int); - _flags |= 8; - _flagTrigger = 1; - - uint8 *ptr = getProgram(songId); - uint8 chan = *ptr; - - if ((songId << 1) != 0) { - if (chan == 9) { - if (_flags & 2) - return 0; - } else { - if (_flags & 1) - return 0; - } - } - - _soundIdTable[_soundsPlaying++] = songId; - _soundsPlaying &= 0x0F; - - return 0; -} - -int AdlibDriver::snd_unkOpcode2(va_list &list) { - warning("unimplemented snd_unkOpcode2"); - return 0; -} - -int AdlibDriver::snd_unkOpcode3(va_list &list) { - int value = va_arg(list, int); - int loop = value; - if (value < 0) { - value = 0; - loop = 9; - } - loop -= value; - ++loop; - - while (loop--) { - _curChannel = value; - Channel &channel = _channels[_curChannel]; - channel.priority = 0; - channel.dataptr = 0; - if (value != 9) { - noteOff(channel); - } - ++value; - } - - return 0; -} - -int AdlibDriver::snd_readByte(va_list &list) { - int a = va_arg(list, int); - int b = va_arg(list, int); - uint8 *ptr = getProgram(a) + b; - return *ptr; -} - -int AdlibDriver::snd_writeByte(va_list &list) { - int a = va_arg(list, int); - int b = va_arg(list, int); - int c = va_arg(list, int); - uint8 *ptr = getProgram(a) + b; - uint8 oldValue = *ptr; - *ptr = (uint8)c; - return oldValue; -} - -int AdlibDriver::snd_getSoundTrigger(va_list &list) { - return _soundTrigger; -} - -int AdlibDriver::snd_unkOpcode4(va_list &list) { - warning("unimplemented snd_unkOpcode4"); - return 0; -} - -int AdlibDriver::snd_dummy(va_list &list) { - return 0; -} - -int AdlibDriver::snd_getNullvar4(va_list &list) { - warning("unimplemented snd_getNullvar4"); - return 0; -} - -int AdlibDriver::snd_setNullvar3(va_list &list) { - warning("unimplemented snd_setNullvar3"); - return 0; -} - -int AdlibDriver::snd_setFlag(va_list &list) { - int oldFlags = _flags; - _flags |= va_arg(list, int); - return oldFlags; -} - -int AdlibDriver::snd_clearFlag(va_list &list) { - int oldFlags = _flags; - _flags &= ~(va_arg(list, int)); - return oldFlags; -} - -// timer callback - -void AdlibDriver::callback() { - // lock(); - --_flagTrigger; - if (_flagTrigger < 0) - _flags &= ~8; - setupPrograms(); - executePrograms(); - - uint8 temp = _unkValue3; - _unkValue3 += _tempo; - if (_unkValue3 < temp) { - if (!(--_unkValue2)) { - _unkValue2 = _unkValue1; - ++_unkValue4; - } - } - // unlock(); -} - -void AdlibDriver::setupPrograms() { - while (_lastProcessed != _soundsPlaying) { - uint8 *ptr = getProgram(_soundIdTable[_lastProcessed]); - uint8 chan = *ptr++; - uint8 priority = *ptr++; - - // Only start this sound if its priority is higher than the one - // already playing. - - Channel &channel = _channels[chan]; - - if (priority >= channel.priority) { - initChannel(channel); - channel.priority = priority; - channel.dataptr = ptr; - channel.tempo = 0xFF; - channel.position = 0xFF; - channel.duration = 1; - unkOutput2(chan); - } - - ++_lastProcessed; - _lastProcessed &= 0x0F; - } -} - -// A few words on opcode parsing and timing: -// -// First of all, We simulate a timer callback 72 times per second. Each timeout -// we update each channel that has something to play. -// -// Each channel has its own individual tempo, which is added to its position. -// This will frequently cause the position to "wrap around" but that is -// intentional. In fact, it's the signal to go ahead and do more stuff with -// that channel. -// -// Each channel also has a duration, indicating how much time is left on the -// its current task. This duration is decreased by one. As long as it still has -// not reached zero, the only thing that can happen is that the note is turned -// off depending on manual or automatic note spacing. Once the duration reaches -// zero, a new set of musical opcodes are executed. -// -// An opcode is one byte, followed by a variable number of parameters. Since -// most opcodes have at least one one-byte parameter, we read that as well. Any -// opcode that doesn't have that one parameter is responsible for moving the -// data pointer back again. -// -// If the most significant bit of the opcode is 1, it's a function; call it. -// The opcode functions return either 0 (continue), 1 (stop) or 2 (stop, and do -// not run the effects callbacks). -// -// If the most significant bit of the opcode is 0, it's a note, and the first -// parameter is its duration. (There are cases where the duration is modified -// but that's an exception.) The note opcode is assumed to return 1, and is the -// last opcode unless its duration is zero. -// -// Finally, most of the times that the callback is called, it will invoke the -// effects callbacks. The final opcode in a set can prevent this, if it's a -// function and it returns anything other than 1. - -void AdlibDriver::executePrograms() { - // Each channel runs its own program. There are ten channels: One for - // each Adlib channel (0-8), plus one "control channel" (9) which is - // the one that tells the other channels what to do. - - for (_curChannel = 9; _curChannel >= 0; --_curChannel) { - int result = 1; - - if (!_channels[_curChannel].dataptr) { - continue; - } - - Channel &channel = _channels[_curChannel]; - if (_curChannel != 9) { - _curRegOffset = _regOffset[_curChannel]; - } - - if (channel.tempoReset) { - channel.tempo = _tempo; - } - - uint8 backup = channel.position; - channel.position += channel.tempo; - if (channel.position < backup) { - if (--channel.duration) { - if (channel.duration == channel.spacing2) - noteOff(channel); - if (channel.duration == channel.spacing1 && _curChannel != 9) - noteOff(channel); - } else { - // An opcode is not allowed to modify its own - // data pointer except through the 'dataptr' - // parameter. To enforce that, we have to work - // on a copy of the data pointer. - // - // This fixes a subtle music bug where the - // wrong music would play when getting the - // quill in Kyra 1. - uint8 *dataptr = channel.dataptr; - while (dataptr) { - uint8 opcode = *dataptr++; - uint8 param = *dataptr++; - - if (opcode & 0x80) { - opcode &= 0x7F; - if (opcode >= _parserOpcodeTableSize) - opcode = _parserOpcodeTableSize - 1; - debugC(9, kDebugLevelSound, "Calling opcode '%s' (%d) (channel: %d)", _parserOpcodeTable[opcode].name, opcode, _curChannel); - result = (this->*(_parserOpcodeTable[opcode].function))(dataptr, channel, param); - channel.dataptr = dataptr; - if (result) - break; - } else { - debugC(9, kDebugLevelSound, "Note on opcode 0x%02X (duration: %d) (channel: %d)", opcode, param, _curChannel); - setupNote(opcode, channel); - noteOn(channel); - setupDuration(param, channel); - if (param) { - channel.dataptr = dataptr; - break; - } - } - } - } - } - - if (result == 1) { - if (channel.primaryEffect) - (this->*(channel.primaryEffect))(channel); - if (channel.secondaryEffect) - (this->*(channel.secondaryEffect))(channel); - } - } -} - -// - -void AdlibDriver::resetAdlibState() { - debugC(9, kDebugLevelSound, "resetAdlibState()"); - _rnd = 0x1234; - - // Authorize the control of the waveforms - writeOPL(0x01, 0x20); - - // Select FM music mode - writeOPL(0x08, 0x00); - - // I would guess the main purpose of this is to turn off the rhythm, - // thus allowing us to use 9 melodic voices instead of 6. - writeOPL(0xBD, 0x00); - - int loop = 10; - while (loop--) { - if (loop != 9) { - // Silence the channel - writeOPL(0x40 + _regOffset[loop], 0x3F); - writeOPL(0x43 + _regOffset[loop], 0x3F); - } - initChannel(_channels[loop]); - } -} - -// Old calling style: output0x388(0xABCD) -// New calling style: writeOPL(0xAB, 0xCD) - -void AdlibDriver::writeOPL(byte reg, byte val) { - opl->write(reg, val); -} - -void AdlibDriver::initChannel(Channel &channel) { - debugC(9, kDebugLevelSound, "initChannel(%lu)", (long)(&channel - _channels)); - memset(&channel.dataptr, 0, sizeof(Channel) - ((char*)&channel.dataptr - (char*)&channel)); - - channel.tempo = 0xFF; - channel.priority = 0; - // normally here are nullfuncs but we set 0 for now - channel.primaryEffect = 0; - channel.secondaryEffect = 0; - channel.spacing1 = 1; -} - -void AdlibDriver::noteOff(Channel &channel) { - debugC(9, kDebugLevelSound, "noteOff(%lu)", (long)(&channel - _channels)); - - // The control channel has no corresponding Adlib channel - - if (_curChannel >= 9) - return; - - // When the rhythm section is enabled, channels 6, 7 and 8 are special. - - if (_rhythmSectionBits && _curChannel >= 6) - return; - - // This means the "Key On" bit will always be 0 - channel.regBx &= 0xDF; - - // Octave / F-Number / Key-On - writeOPL(0xB0 + _curChannel, channel.regBx); -} - -void AdlibDriver::unkOutput2(uint8 chan) { - debugC(9, kDebugLevelSound, "unkOutput2(%d)", chan); - - // The control channel has no corresponding Adlib channel - - if (chan >= 9) - return; - - // I believe this has to do with channels 6, 7, and 8 being special - // when Adlib's rhythm section is enabled. - - if (_rhythmSectionBits && chan >= 6) - return; - - uint8 offset = _regOffset[chan]; - - // The channel is cleared: First the attack/delay rate, then the - // sustain level/release rate, and finally the note is turned off. - - writeOPL(0x60 + offset, 0xFF); - writeOPL(0x63 + offset, 0xFF); - - writeOPL(0x80 + offset, 0xFF); - writeOPL(0x83 + offset, 0xFF); - - writeOPL(0xB0 + chan, 0x00); - - // ...and then the note is turned on again, with whatever value is - // still lurking in the A0 + chan register, but everything else - - // including the two most significant frequency bit, and the octave - - // set to zero. - // - // This is very strange behaviour, and causes problems with the ancient - // FMOPL code we borrowed from AdPlug. I've added a workaround. See - // fmopl.cpp for more details. - // - // More recent versions of the MAME FMOPL don't seem to have this - // problem, but cannot currently be used because of licensing and - // performance issues. - // - // Ken Silverman's Adlib emulator (which can be found on his Web page - - // http://www.advsys.net/ken - and as part of AdPlug) also seems to be - // immune, but is apparently not as feature complete as MAME's. - - writeOPL(0xB0 + chan, 0x20); -} - -// I believe this is a random number generator. It actually does seem to -// generate an even distribution of almost all numbers from 0 through 65535, -// though in my tests some numbers were never generated. - -uint16 AdlibDriver::getRandomNr() { - _rnd += 0x9248; - uint16 lowBits = _rnd & 7; - _rnd >>= 3; - _rnd |= (lowBits << 13); - return _rnd; -} - -void AdlibDriver::setupDuration(uint8 duration, Channel &channel) { - debugC(9, kDebugLevelSound, "setupDuration(%d, %lu)", duration, (long)(&channel - _channels)); - if (channel.durationRandomness) { - channel.duration = duration + (getRandomNr() & channel.durationRandomness); - return; - } - if (channel.fractionalSpacing) { - channel.spacing2 = (duration >> 3) * channel.fractionalSpacing; - } - channel.duration = duration; -} - -// This function may or may not play the note. It's usually followed by a call -// to noteOn(), which will always play the current note. - -void AdlibDriver::setupNote(uint8 rawNote, Channel &channel, bool flag) { - debugC(9, kDebugLevelSound, "setupNote(%d, %lu)", rawNote, (long)(&channel - _channels)); - - channel.rawNote = rawNote; - - int8 note = (rawNote & 0x0F) + channel.baseNote; - int8 octave = ((rawNote + channel.baseOctave) >> 4) & 0x0F; - - // There are only twelve notes. If we go outside that, we have to - // adjust the note and octave. - - if (note >= 12) { - note -= 12; - octave++; - } else if (note < 0) { - note += 12; - octave--; - } - - // The calculation of frequency looks quite different from the original - // disassembly at a first glance, but when you consider that the - // largest possible value would be 0x0246 + 0xFF + 0x47 (and that's if - // baseFreq is unsigned), freq is still a 10-bit value, just as it - // should be to fit in the Ax and Bx registers. - // - // If it were larger than that, it could have overflowed into the - // octave bits, and that could possibly have been used in some sound. - // But as it is now, I can't see any way it would happen. - - uint16 freq = _unkTable[note] + channel.baseFreq; - - // When called from callback 41, the behaviour is slightly different: - // We adjust the frequency, even when channel.unk16 is 0. - - if (channel.unk16 || flag) { - const uint8 *table; - - if (channel.unk16 >= 0) { - table = _unkTables[(channel.rawNote & 0x0F) + 2]; - freq += table[channel.unk16]; - } else { - table = _unkTables[channel.rawNote & 0x0F]; - freq -= table[-channel.unk16]; - } - } - - channel.regAx = freq & 0xFF; - channel.regBx = (channel.regBx & 0x20) | (octave << 2) | ((freq >> 8) & 0x03); - - // Keep the note on or off - writeOPL(0xA0 + _curChannel, channel.regAx); - writeOPL(0xB0 + _curChannel, channel.regBx); -} - -void AdlibDriver::setupInstrument(uint8 regOffset, uint8 *dataptr, Channel &channel) { - debugC(9, kDebugLevelSound, "setupInstrument(%d, %p, %lu)", regOffset, (const void *)dataptr, (long)(&channel - _channels)); - // Amplitude Modulation / Vibrato / Envelope Generator Type / - // Keyboard Scaling Rate / Modulator Frequency Multiple - writeOPL(0x20 + regOffset, *dataptr++); - writeOPL(0x23 + regOffset, *dataptr++); - - uint8 temp = *dataptr++; - - // Feedback / Algorithm - - // It is very likely that _curChannel really does refer to the same - // channel as regOffset, but there's only one Cx register per channel. - - writeOPL(0xC0 + _curChannel, temp); - - // The algorithm bit. I don't pretend to understand this fully, but - // "If set to 0, operator 1 modulates operator 2. In this case, - // operator 2 is the only one producing sound. If set to 1, both - // operators produce sound directly. Complex sounds are more easily - // created if the algorithm is set to 0." - - channel.twoChan = temp & 1; - - // Waveform Select - writeOPL(0xE0 + regOffset, *dataptr++); - writeOPL(0xE3 + regOffset, *dataptr++); - - channel.opLevel1 = *dataptr++; - channel.opLevel2 = *dataptr++; - - // Level Key Scaling / Total Level - writeOPL(0x40 + regOffset, calculateOpLevel1(channel)); - writeOPL(0x43 + regOffset, calculateOpLevel2(channel)); - - // Attack Rate / Decay Rate - writeOPL(0x60 + regOffset, *dataptr++); - writeOPL(0x63 + regOffset, *dataptr++); - - // Sustain Level / Release Rate - writeOPL(0x80 + regOffset, *dataptr++); - writeOPL(0x83 + regOffset, *dataptr++); -} - -// Apart from playing the note, this function also updates the variables for -// primary effect 2. - -void AdlibDriver::noteOn(Channel &channel) { - debugC(9, kDebugLevelSound, "noteOn(%lu)", (long)(&channel - _channels)); - - // The "note on" bit is set, and the current note is played. - - channel.regBx |= 0x20; - writeOPL(0xB0 + _curChannel, channel.regBx); - - int8 shift = 9 - channel.unk33; - uint16 temp = channel.regAx | (channel.regBx << 8); - channel.unk37 = ((temp & 0x3FF) >> shift) & 0xFF; - channel.unk38 = channel.unk36; -} - -void AdlibDriver::adjustVolume(Channel &channel) { - debugC(9, kDebugLevelSound, "adjustVolume(%lu)", (long)(&channel - _channels)); - // Level Key Scaling / Total Level - - writeOPL(0x43 + _regOffset[_curChannel], calculateOpLevel2(channel)); - if (channel.twoChan) - writeOPL(0x40 + _regOffset[_curChannel], calculateOpLevel1(channel)); -} - -// This is presumably only used for some sound effects, e.g. Malcolm blowing up -// the trees in the intro (but not the effect where he "booby-traps" the big -// tree) and turning Kallak to stone. Related functions and variables: -// -// update_setupPrimaryEffect1() -// - Initialises unk29, unk30 and unk31 -// - unk29 is not further modified -// - unk30 is not further modified, except by update_removePrimaryEffect1() -// -// update_removePrimaryEffect1() -// - Deinitialises unk30 -// -// unk29 - determines how often the notes are played -// unk30 - modifies the frequency -// unk31 - determines how often the notes are played - -void AdlibDriver::primaryEffect1(Channel &channel) { - debugC(9, kDebugLevelSound, "Calling primaryEffect1 (channel: %d)", _curChannel); - uint8 temp = channel.unk31; - channel.unk31 += channel.unk29; - if (channel.unk31 >= temp) - return; - - // Initialise unk1 to the current frequency - uint16 unk1 = ((channel.regBx & 3) << 8) | channel.regAx; - - // This is presumably to shift the "note on" bit so far to the left - // that it won't be affected by any of the calculations below. - uint16 unk2 = ((channel.regBx & 0x20) << 8) | (channel.regBx & 0x1C); - - int16 unk3 = (int16)channel.unk30; - - if (unk3 >= 0) { - unk1 += unk3; - if (unk1 >= 734) { - // The new frequency is too high. Shift it down and go - // up one octave. - unk1 >>= 1; - if (!(unk1 & 0x3FF)) - ++unk1; - unk2 = (unk2 & 0xFF00) | ((unk2 + 4) & 0xFF); - unk2 &= 0xFF1C; - } - } else { - unk1 += unk3; - if (unk1 < 388) { - // The new frequency is too low. Shift it up and go - // down one octave. - unk1 <<= 1; - if (!(unk1 & 0x3FF)) - --unk1; - unk2 = (unk2 & 0xFF00) | ((unk2 - 4) & 0xFF); - unk2 &= 0xFF1C; - } - } - - // Make sure that the new frequency is still a 10-bit value. - unk1 &= 0x3FF; - - writeOPL(0xA0 + _curChannel, unk1 & 0xFF); - channel.regAx = unk1 & 0xFF; - - // Shift down the "note on" bit again. - uint8 value = unk1 >> 8; - value |= (unk2 >> 8) & 0xFF; - value |= unk2 & 0xFF; - - writeOPL(0xB0 + _curChannel, value); - channel.regBx = value; -} - -// This is presumably only used for some sound effects, e.g. Malcolm entering -// and leaving Kallak's hut. Related functions and variables: -// -// update_setupPrimaryEffect2() -// - Initialises unk32, unk33, unk34, unk35 and unk36 -// - unk32 is not further modified -// - unk33 is not further modified -// - unk34 is a countdown that gets reinitialised to unk35 on zero -// - unk35 is based on unk34 and not further modified -// - unk36 is not further modified -// -// noteOn() -// - Plays the current note -// - Updates unk37 with a new (lower?) frequency -// - Copies unk36 to unk38. The unk38 variable is a countdown. -// -// unk32 - determines how often the notes are played -// unk33 - modifies the frequency -// unk34 - countdown, updates frequency on zero -// unk35 - initialiser for unk34 countdown -// unk36 - initialiser for unk38 countdown -// unk37 - frequency -// unk38 - countdown, begins playing on zero -// unk41 - determines how often the notes are played -// -// Note that unk41 is never initialised. Not that it should matter much, but it -// is a bit sloppy. - -void AdlibDriver::primaryEffect2(Channel &channel) { - debugC(9, kDebugLevelSound, "Calling primaryEffect2 (channel: %d)", _curChannel); - if (channel.unk38) { - --channel.unk38; - return; - } - - uint8 temp = channel.unk41; - channel.unk41 += channel.unk32; - if (channel.unk41 < temp) { - uint16 unk1 = channel.unk37; - if (!(--channel.unk34)) { - unk1 ^= 0xFFFF; - ++unk1; - channel.unk37 = unk1; - channel.unk34 = channel.unk35; - } - - uint16 unk2 = (channel.regAx | (channel.regBx << 8)) & 0x3FF; - unk2 += unk1; - - channel.regAx = unk2 & 0xFF; - channel.regBx = (channel.regBx & 0xFC) | (unk2 >> 8); - - // Octave / F-Number / Key-On - writeOPL(0xA0 + _curChannel, channel.regAx); - writeOPL(0xB0 + _curChannel, channel.regBx); - } -} - -// I don't know where this is used. The same operation is performed several -// times on the current channel, using a chunk of the _soundData[] buffer for -// parameters. The parameters are used starting at the end of the chunk. -// -// Since we use _curRegOffset to specify the final register, it's quite -// unlikely that this function is ever used to play notes. It's probably only -// used to modify the sound. Another thing that supports this idea is that it -// can be combined with any of the effects callbacks above. -// -// Related functions and variables: -// -// update_setupSecondaryEffect1() -// - Initialies unk18, unk19, unk20, unk21, unk22 and offset -// - unk19 is not further modified -// - unk20 is not further modified -// - unk22 is not further modified -// - offset is not further modified -// -// unk18 - determines how often the operation is performed -// unk19 - determines how often the operation is performed -// unk20 - the start index into the data chunk -// unk21 - the current index into the data chunk -// unk22 - the operation to perform -// offset - the offset to the data chunk - -void AdlibDriver::secondaryEffect1(Channel &channel) { - debugC(9, kDebugLevelSound, "Calling secondaryEffect1 (channel: %d)", _curChannel); - uint8 temp = channel.unk18; - channel.unk18 += channel.unk19; - if (channel.unk18 < temp) { - if (--channel.unk21 < 0) { - channel.unk21 = channel.unk20; - } - writeOPL(channel.unk22 + _curRegOffset, _soundData[channel.offset + channel.unk21]); - } -} - -uint8 AdlibDriver::calculateOpLevel1(Channel &channel) { - int8 value = channel.opLevel1 & 0x3F; - - if (channel.twoChan) { - value += channel.opExtraLevel1; - value += channel.opExtraLevel2; - value += channel.opExtraLevel3; - } - - // Preserve the scaling level bits from opLevel1 - - return checkValue(value) | (channel.opLevel1 & 0xC0); -} - -uint8 AdlibDriver::calculateOpLevel2(Channel &channel) { - int8 value = channel.opLevel2 & 0x3F; - - value += channel.opExtraLevel1; - value += channel.opExtraLevel2; - value += channel.opExtraLevel3; - - // Preserve the scaling level bits from opLevel2 - - return checkValue(value) | (channel.opLevel2 & 0xC0); -} - -// parser opcodes - -int AdlibDriver::update_setRepeat(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.repeatCounter = value; - return 0; -} - -int AdlibDriver::update_checkRepeat(uint8 *&dataptr, Channel &channel, uint8 value) { - ++dataptr; - if (--channel.repeatCounter) { - int16 add = READ_LE_UINT16(dataptr - 2); - dataptr += add; - } - return 0; -} - -int AdlibDriver::update_setupProgram(uint8 *&dataptr, Channel &channel, uint8 value) { - if (value == 0xFF) - return 0; - - uint8 *ptr = getProgram(value); - uint8 chan = *ptr++; - uint8 priority = *ptr++; - - Channel &channel2 = _channels[chan]; - - if (priority >= channel2.priority) { - _flagTrigger = 1; - _flags |= 8; - initChannel(channel2); - channel2.priority = priority; - channel2.dataptr = ptr; - channel2.tempo = 0xFF; - channel2.position = 0xFF; - channel2.duration = 1; - unkOutput2(chan); - } - - return 0; -} - -int AdlibDriver::update_setNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.spacing1 = value; - return 0; -} - -int AdlibDriver::update_jump(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - int16 add = READ_LE_UINT16(dataptr); dataptr += 2; - dataptr += add; - return 0; -} - -int AdlibDriver::update_jumpToSubroutine(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - int16 add = READ_LE_UINT16(dataptr); dataptr += 2; - channel.dataptrStack[channel.dataptrStackPos++] = dataptr; - dataptr += add; - return 0; -} - -int AdlibDriver::update_returnFromSubroutine(uint8 *&dataptr, Channel &channel, uint8 value) { - dataptr = channel.dataptrStack[--channel.dataptrStackPos]; - return 0; -} - -int AdlibDriver::update_setBaseOctave(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.baseOctave = value; - return 0; -} - -int AdlibDriver::update_stopChannel(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.priority = 0; - if (_curChannel != 9) { - noteOff(channel); - } - dataptr = 0; - return 2; -} - -int AdlibDriver::update_playRest(uint8 *&dataptr, Channel &channel, uint8 value) { - setupDuration(value, channel); - noteOff(channel); - return (value != 0); -} - -int AdlibDriver::update_writeAdlib(uint8 *&dataptr, Channel &channel, uint8 value) { - writeOPL(value, *dataptr++); - return 0; -} - -int AdlibDriver::update_setupNoteAndDuration(uint8 *&dataptr, Channel &channel, uint8 value) { - setupNote(value, channel); - value = *dataptr++; - setupDuration(value, channel); - return (value != 0); -} - -int AdlibDriver::update_setBaseNote(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.baseNote = value; - return 0; -} - -int AdlibDriver::update_setupSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.unk18 = value; - channel.unk19 = value; - channel.unk20 = channel.unk21 = *dataptr++; - channel.unk22 = *dataptr++; - channel.offset = READ_LE_UINT16(dataptr); dataptr += 2; - channel.secondaryEffect = &AdlibDriver::secondaryEffect1; - return 0; -} - -int AdlibDriver::update_stopOtherChannel(uint8 *&dataptr, Channel &channel, uint8 value) { - Channel &channel2 = _channels[value]; - channel2.duration = 0; - channel2.priority = 0; - channel2.dataptr = 0; - return 0; -} - -int AdlibDriver::update_waitForEndOfProgram(uint8 *&dataptr, Channel &channel, uint8 value) { - uint8 *ptr = getProgram(value); - uint8 chan = *ptr; - - if (!_channels[chan].dataptr) { - return 0; - } - - dataptr -= 2; - return 2; -} - -int AdlibDriver::update_setupInstrument(uint8 *&dataptr, Channel &channel, uint8 value) { - setupInstrument(_curRegOffset, getInstrument(value), channel); - return 0; -} - -int AdlibDriver::update_setupPrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.unk29 = value; - channel.unk30 = READ_BE_UINT16(dataptr); - dataptr += 2; - channel.primaryEffect = &AdlibDriver::primaryEffect1; - channel.unk31 = 0xFF; - return 0; -} - -int AdlibDriver::update_removePrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - channel.primaryEffect = 0; - channel.unk30 = 0; - return 0; -} - -int AdlibDriver::update_setBaseFreq(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.baseFreq = value; - return 0; -} - -int AdlibDriver::update_setupPrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.unk32 = value; - channel.unk33 = *dataptr++; - uint8 temp = *dataptr++; - channel.unk34 = temp + 1; - channel.unk35 = temp << 1; - channel.unk36 = *dataptr++; - channel.primaryEffect = &AdlibDriver::primaryEffect2; - return 0; -} - -int AdlibDriver::update_setPriority(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.priority = value; - return 0; -} - -int AdlibDriver::updateCallback23(uint8 *&dataptr, Channel &channel, uint8 value) { - value >>= 1; - _unkValue1 = _unkValue2 = value; - _unkValue3 = 0xFF; - _unkValue4 = _unkValue5 = 0; - return 0; -} - -int AdlibDriver::updateCallback24(uint8 *&dataptr, Channel &channel, uint8 value) { - if (_unkValue5) { - if (_unkValue4 & value) { - _unkValue5 = 0; - return 0; - } - } - - if (!(value & _unkValue4)) { - ++_unkValue5; - } - - dataptr -= 2; - channel.duration = 1; - return 2; -} - -int AdlibDriver::update_setExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.opExtraLevel1 = value; - adjustVolume(channel); - return 0; -} - -int AdlibDriver::update_setupDuration(uint8 *&dataptr, Channel &channel, uint8 value) { - setupDuration(value, channel); - return (value != 0); -} - -int AdlibDriver::update_playNote(uint8 *&dataptr, Channel &channel, uint8 value) { - setupDuration(value, channel); - noteOn(channel); - return (value != 0); -} - -int AdlibDriver::update_setFractionalNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.fractionalSpacing = value & 7; - return 0; -} - -int AdlibDriver::update_setTempo(uint8 *&dataptr, Channel &channel, uint8 value) { - _tempo = value; - return 0; -} - -int AdlibDriver::update_removeSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - channel.secondaryEffect = 0; - return 0; -} - -int AdlibDriver::update_setChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.tempo = value; - return 0; -} - -int AdlibDriver::update_setExtraLevel3(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.opExtraLevel3 = value; - return 0; -} - -int AdlibDriver::update_setExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value) { - int channelBackUp = _curChannel; - - _curChannel = value; - Channel &channel2 = _channels[value]; - channel2.opExtraLevel2 = *dataptr++; - adjustVolume(channel2); - - _curChannel = channelBackUp; - return 0; -} - -int AdlibDriver::update_changeExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value) { - int channelBackUp = _curChannel; - - _curChannel = value; - Channel &channel2 = _channels[value]; - channel2.opExtraLevel2 += *dataptr++; - adjustVolume(channel2); - - _curChannel = channelBackUp; - return 0; -} - -// Apart from initialising to zero, these two functions are the only ones that -// modify _vibratoAndAMDepthBits. - -int AdlibDriver::update_setAMDepth(uint8 *&dataptr, Channel &channel, uint8 value) { - if (value & 1) - _vibratoAndAMDepthBits |= 0x80; - else - _vibratoAndAMDepthBits &= 0x7F; - - writeOPL(0xBD, _vibratoAndAMDepthBits); - return 0; -} - -int AdlibDriver::update_setVibratoDepth(uint8 *&dataptr, Channel &channel, uint8 value) { - if (value & 1) - _vibratoAndAMDepthBits |= 0x40; - else - _vibratoAndAMDepthBits &= 0xBF; - - writeOPL(0xBD, _vibratoAndAMDepthBits); - return 0; -} - -int AdlibDriver::update_changeExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.opExtraLevel1 += value; - adjustVolume(channel); - return 0; -} - -int AdlibDriver::updateCallback38(uint8 *&dataptr, Channel &channel, uint8 value) { - int channelBackUp = _curChannel; - - _curChannel = value; - Channel &channel2 = _channels[value]; - channel2.duration = channel2.priority = 0; - channel2.dataptr = 0; - channel2.opExtraLevel2 = 0; - - if (value != 9) { - uint8 outValue = _regOffset[value]; - - // Feedback strength / Connection type - writeOPL(0xC0 + _curChannel, 0x00); - - // Key scaling level / Operator output level - writeOPL(0x43 + outValue, 0x3F); - - // Sustain Level / Release Rate - writeOPL(0x83 + outValue, 0xFF); - - // Key On / Octave / Frequency - writeOPL(0xB0 + _curChannel, 0x00); - } - - _curChannel = channelBackUp; - return 0; -} - -int AdlibDriver::updateCallback39(uint8 *&dataptr, Channel &channel, uint8 value) { - uint16 unk = *dataptr++; - unk |= value << 8; - unk &= getRandomNr(); - - uint16 unk2 = ((channel.regBx & 0x1F) << 8) | channel.regAx; - unk2 += unk; - unk2 |= ((channel.regBx & 0x20) << 8); - - // Frequency - writeOPL(0xA0 + _curChannel, unk2 & 0xFF); - - // Key On / Octave / Frequency - writeOPL(0xB0 + _curChannel, (unk2 & 0xFF00) >> 8); - - return 0; -} - -int AdlibDriver::update_removePrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - channel.primaryEffect = 0; - return 0; -} - -int AdlibDriver::updateCallback41(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.unk16 = value; - setupNote(channel.rawNote, channel, true); - return 0; -} - -int AdlibDriver::update_resetToGlobalTempo(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - channel.tempo = _tempo; - return 0; -} - -int AdlibDriver::update_nop1(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - return 0; -} - -int AdlibDriver::update_setDurationRandomness(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.durationRandomness = value; - return 0; -} - -int AdlibDriver::update_changeChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value) { - int tempo = channel.tempo + (int8)value; - - if (tempo <= 0) - tempo = 1; - else if (tempo > 255) - tempo = 255; - - channel.tempo = tempo; - return 0; -} - -int AdlibDriver::updateCallback46(uint8 *&dataptr, Channel &channel, uint8 value) { - uint8 entry = *dataptr++; - _tablePtr1 = _unkTable2[entry++]; - _tablePtr2 = _unkTable2[entry]; - if (value == 2) { - // Frequency - writeOPL(0xA0, _tablePtr2[0]); - } - return 0; -} - -// TODO: This is really the same as update_nop1(), so they should be combined -// into one single update_nop(). - -int AdlibDriver::update_nop2(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - return 0; -} - -int AdlibDriver::update_setupRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) { - int channelBackUp = _curChannel; - int regOffsetBackUp = _curRegOffset; - - _curChannel = 6; - _curRegOffset = _regOffset[6]; - - setupInstrument(_curRegOffset, getInstrument(value), channel); - _unkValue6 = channel.opLevel2; - - _curChannel = 7; - _curRegOffset = _regOffset[7]; - - setupInstrument(_curRegOffset, getInstrument(*dataptr++), channel); - _unkValue7 = channel.opLevel1; - _unkValue8 = channel.opLevel2; - - _curChannel = 8; - _curRegOffset = _regOffset[8]; - - setupInstrument(_curRegOffset, getInstrument(*dataptr++), channel); - _unkValue9 = channel.opLevel1; - _unkValue10 = channel.opLevel2; - - // Octave / F-Number / Key-On for channels 6, 7 and 8 - - _channels[6].regBx = *dataptr++ & 0x2F; - writeOPL(0xB6, _channels[6].regBx); - writeOPL(0xA6, *dataptr++); - - _channels[7].regBx = *dataptr++ & 0x2F; - writeOPL(0xB7, _channels[7].regBx); - writeOPL(0xA7, *dataptr++); - - _channels[8].regBx = *dataptr++ & 0x2F; - writeOPL(0xB8, _channels[8].regBx); - writeOPL(0xA8, *dataptr++); - - _rhythmSectionBits = 0x20; - - _curRegOffset = regOffsetBackUp; - _curChannel = channelBackUp; - return 0; -} - -int AdlibDriver::update_playRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) { - // Any instrument that we want to play, and which was already playing, - // is temporarily keyed off. Instruments that were off already, or - // which we don't want to play, retain their old on/off status. This is - // probably so that the instrument's envelope is played from its - // beginning again... - - writeOPL(0xBD, (_rhythmSectionBits & ~(value & 0x1F)) | 0x20); - - // ...but since we only set the rhythm instrument bits, and never clear - // them (until the entire rhythm section is disabled), I'm not sure how - // useful the cleverness above is. We could perhaps simply turn off all - // the rhythm instruments instead. - - _rhythmSectionBits |= value; - - writeOPL(0xBD, _vibratoAndAMDepthBits | 0x20 | _rhythmSectionBits); - return 0; -} - -int AdlibDriver::update_removeRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - _rhythmSectionBits = 0; - - // All the rhythm bits are cleared. The AM and Vibrato depth bits - // remain unchanged. - - writeOPL(0xBD, _vibratoAndAMDepthBits); - return 0; -} - -int AdlibDriver::updateCallback51(uint8 *&dataptr, Channel &channel, uint8 value) { - uint8 value2 = *dataptr++; - - if (value & 1) { - _unkValue12 = value2; - - // Channel 7, op1: Level Key Scaling / Total Level - writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue11 + _unkValue12)); - } - - if (value & 2) { - _unkValue14 = value2; - - // Channel 8, op2: Level Key Scaling / Total Level - writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14)); - } - - if (value & 4) { - _unkValue15 = value2; - - // Channel 8, op1: Level Key Scaling / Total Level - writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15)); - } - - if (value & 8) { - _unkValue18 = value2; - - // Channel 7, op2: Level Key Scaling / Total Level - writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18)); - } - - if (value & 16) { - _unkValue20 = value2; - - // Channel 6, op2: Level Key Scaling / Total Level - writeOPL(0x53, checkValue(value2 + _unkValue6 + _unkValue19 + _unkValue20)); - } - - return 0; -} - -int AdlibDriver::updateCallback52(uint8 *&dataptr, Channel &channel, uint8 value) { - uint8 value2 = *dataptr++; - - if (value & 1) { - _unkValue11 = checkValue(value2 + _unkValue7 + _unkValue11 + _unkValue12); - - // Channel 7, op1: Level Key Scaling / Total Level - writeOPL(0x51, _unkValue11); - } - - if (value & 2) { - _unkValue13 = checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14); - - // Channel 8, op2: Level Key Scaling / Total Level - writeOPL(0x55, _unkValue13); - } - - if (value & 4) { - _unkValue16 = checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15); - - // Channel 8, op1: Level Key Scaling / Total Level - writeOPL(0x52, _unkValue16); - } - - if (value & 8) { - _unkValue17 = checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18); - - // Channel 7, op2: Level Key Scaling / Total Level - writeOPL(0x54, _unkValue17); - } - - if (value & 16) { - _unkValue19 = checkValue(value2 + _unkValue6 + _unkValue19 + _unkValue20); - - // Channel 6, op2: Level Key Scaling / Total Level - writeOPL(0x53, _unkValue19); - } - - return 0; -} - -int AdlibDriver::updateCallback53(uint8 *&dataptr, Channel &channel, uint8 value) { - uint8 value2 = *dataptr++; - - if (value & 1) { - _unkValue11 = value2; - - // Channel 7, op1: Level Key Scaling / Total Level - writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue12)); - } - - if (value & 2) { - _unkValue13 = value2; - - // Channel 8, op2: Level Key Scaling / Total Level - writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue14)); - } - - if (value & 4) { - _unkValue16 = value2; - - // Channel 8, op1: Level Key Scaling / Total Level - writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue15)); - } - - if (value & 8) { - _unkValue17 = value2; - - // Channel 7, op2: Level Key Scaling / Total Level - writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue18)); - } - - if (value & 16) { - _unkValue19 = value2; - - // Channel 6, op2: Level Key Scaling / Total Level - writeOPL(0x53, checkValue(value2 + _unkValue6 + _unkValue20)); - } - - return 0; -} - -int AdlibDriver::update_setSoundTrigger(uint8 *&dataptr, Channel &channel, uint8 value) { - _soundTrigger = value; - return 0; -} - -int AdlibDriver::update_setTempoReset(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.tempoReset = value; - return 0; -} - -int AdlibDriver::updateCallback56(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.unk39 = value; - channel.unk40 = *dataptr++; - return 0; -} - -// static res - -#define COMMAND(x) { &AdlibDriver::x, #x } - -void AdlibDriver::setupOpcodeList() { - static const OpcodeEntry opcodeList[] = { - COMMAND(snd_ret0x100), - COMMAND(snd_ret0x1983), - COMMAND(snd_initDriver), - COMMAND(snd_deinitDriver), - COMMAND(snd_setSoundData), - COMMAND(snd_unkOpcode1), - COMMAND(snd_startSong), - COMMAND(snd_unkOpcode2), - COMMAND(snd_unkOpcode3), - COMMAND(snd_readByte), - COMMAND(snd_writeByte), - COMMAND(snd_getSoundTrigger), - COMMAND(snd_unkOpcode4), - COMMAND(snd_dummy), - COMMAND(snd_getNullvar4), - COMMAND(snd_setNullvar3), - COMMAND(snd_setFlag), - COMMAND(snd_clearFlag) - }; - - _opcodeList = opcodeList; - _opcodesEntries = ARRAYSIZE(opcodeList); -} - -void AdlibDriver::setupParserOpcodeTable() { - static const ParserOpcode parserOpcodeTable[] = { - // 0 - COMMAND(update_setRepeat), - COMMAND(update_checkRepeat), - COMMAND(update_setupProgram), - COMMAND(update_setNoteSpacing), - - // 4 - COMMAND(update_jump), - COMMAND(update_jumpToSubroutine), - COMMAND(update_returnFromSubroutine), - COMMAND(update_setBaseOctave), - - // 8 - COMMAND(update_stopChannel), - COMMAND(update_playRest), - COMMAND(update_writeAdlib), - COMMAND(update_setupNoteAndDuration), - - // 12 - COMMAND(update_setBaseNote), - COMMAND(update_setupSecondaryEffect1), - COMMAND(update_stopOtherChannel), - COMMAND(update_waitForEndOfProgram), - - // 16 - COMMAND(update_setupInstrument), - COMMAND(update_setupPrimaryEffect1), - COMMAND(update_removePrimaryEffect1), - COMMAND(update_setBaseFreq), - - // 20 - COMMAND(update_stopChannel), - COMMAND(update_setupPrimaryEffect2), - COMMAND(update_stopChannel), - COMMAND(update_stopChannel), - - // 24 - COMMAND(update_stopChannel), - COMMAND(update_stopChannel), - COMMAND(update_setPriority), - COMMAND(update_stopChannel), - - // 28 - COMMAND(updateCallback23), - COMMAND(updateCallback24), - COMMAND(update_setExtraLevel1), - COMMAND(update_stopChannel), - - // 32 - COMMAND(update_setupDuration), - COMMAND(update_playNote), - COMMAND(update_stopChannel), - COMMAND(update_stopChannel), - - // 36 - COMMAND(update_setFractionalNoteSpacing), - COMMAND(update_stopChannel), - COMMAND(update_setTempo), - COMMAND(update_removeSecondaryEffect1), - - // 40 - COMMAND(update_stopChannel), - COMMAND(update_setChannelTempo), - COMMAND(update_stopChannel), - COMMAND(update_setExtraLevel3), - - // 44 - COMMAND(update_setExtraLevel2), - COMMAND(update_changeExtraLevel2), - COMMAND(update_setAMDepth), - COMMAND(update_setVibratoDepth), - - // 48 - COMMAND(update_changeExtraLevel1), - COMMAND(update_stopChannel), - COMMAND(update_stopChannel), - COMMAND(updateCallback38), - - // 52 - COMMAND(update_stopChannel), - COMMAND(updateCallback39), - COMMAND(update_removePrimaryEffect2), - COMMAND(update_stopChannel), - - // 56 - COMMAND(update_stopChannel), - COMMAND(updateCallback41), - COMMAND(update_resetToGlobalTempo), - COMMAND(update_nop1), - - // 60 - COMMAND(update_setDurationRandomness), - COMMAND(update_changeChannelTempo), - COMMAND(update_stopChannel), - COMMAND(updateCallback46), - - // 64 - COMMAND(update_nop2), - COMMAND(update_setupRhythmSection), - COMMAND(update_playRhythmSection), - COMMAND(update_removeRhythmSection), - - // 68 - COMMAND(updateCallback51), - COMMAND(updateCallback52), - COMMAND(updateCallback53), - COMMAND(update_setSoundTrigger), - - // 72 - COMMAND(update_setTempoReset), - COMMAND(updateCallback56), - COMMAND(update_stopChannel) - }; - - _parserOpcodeTable = parserOpcodeTable; - _parserOpcodeTableSize = ARRAYSIZE(parserOpcodeTable); -} -#undef COMMAND - -// This table holds the register offset for operator 1 for each of the nine -// channels. To get the register offset for operator 2, simply add 3. - -const uint8 AdlibDriver::_regOffset[] = { - 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, - 0x12 -}; - -// Given the size of this table, and the range of its values, it's probably the -// F-Numbers (10 bits) for the notes of the 12-tone scale. However, it does not -// match the table in the Adlib documentation I've seen. - -const uint16 AdlibDriver::_unkTable[] = { - 0x0134, 0x0147, 0x015A, 0x016F, 0x0184, 0x019C, 0x01B4, 0x01CE, 0x01E9, - 0x0207, 0x0225, 0x0246 -}; - -// These tables are currently only used by updateCallback46(), which only ever -// uses the first element of one of the sub-tables. - -const uint8 *AdlibDriver::_unkTable2[] = { - AdlibDriver::_unkTable2_1, - AdlibDriver::_unkTable2_2, - AdlibDriver::_unkTable2_1, - AdlibDriver::_unkTable2_2, - AdlibDriver::_unkTable2_3, - AdlibDriver::_unkTable2_2 -}; - -const uint8 AdlibDriver::_unkTable2_1[] = { - 0x50, 0x50, 0x4F, 0x4F, 0x4E, 0x4E, 0x4D, 0x4D, - 0x4C, 0x4C, 0x4B, 0x4B, 0x4A, 0x4A, 0x49, 0x49, - 0x48, 0x48, 0x47, 0x47, 0x46, 0x46, 0x45, 0x45, - 0x44, 0x44, 0x43, 0x43, 0x42, 0x42, 0x41, 0x41, - 0x40, 0x40, 0x3F, 0x3F, 0x3E, 0x3E, 0x3D, 0x3D, - 0x3C, 0x3C, 0x3B, 0x3B, 0x3A, 0x3A, 0x39, 0x39, - 0x38, 0x38, 0x37, 0x37, 0x36, 0x36, 0x35, 0x35, - 0x34, 0x34, 0x33, 0x33, 0x32, 0x32, 0x31, 0x31, - 0x30, 0x30, 0x2F, 0x2F, 0x2E, 0x2E, 0x2D, 0x2D, - 0x2C, 0x2C, 0x2B, 0x2B, 0x2A, 0x2A, 0x29, 0x29, - 0x28, 0x28, 0x27, 0x27, 0x26, 0x26, 0x25, 0x25, - 0x24, 0x24, 0x23, 0x23, 0x22, 0x22, 0x21, 0x21, - 0x20, 0x20, 0x1F, 0x1F, 0x1E, 0x1E, 0x1D, 0x1D, - 0x1C, 0x1C, 0x1B, 0x1B, 0x1A, 0x1A, 0x19, 0x19, - 0x18, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, - 0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, - 0x10, 0x10 -}; - -// no don't ask me WHY this table exsits! -const uint8 AdlibDriver::_unkTable2_2[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, - 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x6F, - 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F -}; - -const uint8 AdlibDriver::_unkTable2_3[] = { - 0x40, 0x40, 0x40, 0x3F, 0x3F, 0x3F, 0x3E, 0x3E, - 0x3E, 0x3D, 0x3D, 0x3D, 0x3C, 0x3C, 0x3C, 0x3B, - 0x3B, 0x3B, 0x3A, 0x3A, 0x3A, 0x39, 0x39, 0x39, - 0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x36, 0x36, - 0x36, 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x33, - 0x33, 0x33, 0x32, 0x32, 0x32, 0x31, 0x31, 0x31, - 0x30, 0x30, 0x30, 0x2F, 0x2F, 0x2F, 0x2E, 0x2E, - 0x2E, 0x2D, 0x2D, 0x2D, 0x2C, 0x2C, 0x2C, 0x2B, - 0x2B, 0x2B, 0x2A, 0x2A, 0x2A, 0x29, 0x29, 0x29, - 0x28, 0x28, 0x28, 0x27, 0x27, 0x27, 0x26, 0x26, - 0x26, 0x25, 0x25, 0x25, 0x24, 0x24, 0x24, 0x23, - 0x23, 0x23, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21, - 0x20, 0x20, 0x20, 0x1F, 0x1F, 0x1F, 0x1E, 0x1E, - 0x1E, 0x1D, 0x1D, 0x1D, 0x1C, 0x1C, 0x1C, 0x1B, - 0x1B, 0x1B, 0x1A, 0x1A, 0x1A, 0x19, 0x19, 0x19, - 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x16, 0x16, - 0x16, 0x15 -}; - -// This table is used to modify the frequency of the notes, depending on the -// note value and unk16. In theory, we could very well try to access memory -// outside this table, but in reality that probably won't happen. -// -// This could be some sort of pitch bend, but I have yet to see it used for -// anything so it's hard to say. - -const uint8 AdlibDriver::_unkTables[][32] = { - // 0 - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, - 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x19, - 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21 }, - // 1 - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x07, 0x09, - 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, - 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x1A, - 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x22, 0x24 }, - // 2 - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x09, - 0x0A, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13, - 0x14, 0x15, 0x16, 0x17, 0x19, 0x1A, 0x1C, 0x1D, - 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26 }, - // 3 - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A, - 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13, - 0x14, 0x15, 0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1D, - 0x1E, 0x1F, 0x20, 0x21, 0x23, 0x25, 0x27, 0x28 }, - // 4 - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A, - 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x13, 0x15, - 0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20, - 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x28, 0x2A }, - // 5 - { 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B, - 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, - 0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20, - 0x21, 0x22, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D }, - // 6 - { 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B, - 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, - 0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1E, 0x21, 0x24, - 0x25, 0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30 }, - // 7 - { 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, - 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, 0x18, - 0x19, 0x1A, 0x1C, 0x1D, 0x1F, 0x21, 0x23, 0x25, - 0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30, 0x32 }, - // 8 - { 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0D, - 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x14, 0x17, 0x1A, - 0x19, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x25, 0x28, - 0x29, 0x2A, 0x2B, 0x2D, 0x2F, 0x31, 0x33, 0x35 }, - // 9 - { 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E, - 0x0F, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1B, - 0x1C, 0x1D, 0x1E, 0x20, 0x22, 0x24, 0x26, 0x29, - 0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x39 }, - // 10 - { 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E, - 0x0F, 0x10, 0x12, 0x14, 0x16, 0x19, 0x1B, 0x1E, - 0x1F, 0x21, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D, - 0x2E, 0x2F, 0x31, 0x32, 0x34, 0x36, 0x39, 0x3C }, - // 11 - { 0x00, 0x01, 0x03, 0x05, 0x07, 0x0A, 0x0C, 0x0F, - 0x10, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1E, - 0x1F, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2B, 0x2E, - 0x2F, 0x30, 0x32, 0x34, 0x36, 0x39, 0x3C, 0x3F }, - // 12 - { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x10, - 0x11, 0x12, 0x14, 0x16, 0x18, 0x1B, 0x1E, 0x21, - 0x22, 0x23, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32, - 0x33, 0x34, 0x36, 0x38, 0x3B, 0x34, 0x41, 0x44 }, - // 13 - { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x11, - 0x12, 0x13, 0x15, 0x17, 0x1A, 0x1D, 0x20, 0x23, - 0x24, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32, 0x35, - 0x36, 0x37, 0x39, 0x3B, 0x3E, 0x41, 0x44, 0x47 } -}; - -// #pragma mark - - -// At the time of writing, the only known case where Kyra 1 uses sound triggers -// is in the castle, to cycle between three different songs. - -const int CadlPlayer::_kyra1SoundTriggers[] = { - 0, 4, 5, 3 -}; - -const int CadlPlayer::_kyra1NumSoundTriggers = ARRAYSIZE(CadlPlayer::_kyra1SoundTriggers); - -CadlPlayer::CadlPlayer(Copl *newopl) - : CPlayer(newopl), numsubsongs(0), _trackEntries(), _soundDataPtr(0) -{ - memset(_trackEntries, 0, sizeof(_trackEntries)); - _driver = new AdlibDriver(newopl); - assert(_driver); - - _sfxPlayingSound = -1; - // _soundFileLoaded = ""; - - _soundTriggers = _kyra1SoundTriggers; - _numSoundTriggers = _kyra1NumSoundTriggers; - - init(); -} - -CadlPlayer::~CadlPlayer() { - delete [] _soundDataPtr; - delete _driver; -} - -bool CadlPlayer::init() { - _driver->callback(2); - _driver->callback(16, int(4)); - return true; -} - -void CadlPlayer::process() { - uint8 trigger = _driver->callback(11); - - if (trigger < _numSoundTriggers) { - int soundId = _soundTriggers[trigger]; - - if (soundId) { - playTrack(soundId); - } - } else { - warning("Unknown sound trigger %d", trigger); - // TODO: At this point, we really want to clear the trigger... - } -} - -// void CadlPlayer::setVolume(int volume) { -// } - -// int CadlPlayer::getVolume() { -// return 0; -// } - -// void CadlPlayer::loadMusicFile(const char *file) { -// loadSoundFile(file); -// } - -void CadlPlayer::playTrack(uint8 track) { - play(track); -} - -// void CadlPlayer::haltTrack() { -// unk1(); -// unk2(); -// //_engine->_system->delayMillis(3 * 60); -// } - -void CadlPlayer::playSoundEffect(uint8_t track) { - play(track); -} - -void CadlPlayer::play(uint8_t track) { - uint8 soundId = _trackEntries[track]; - if ((int8)soundId == -1 || !_soundDataPtr) - return; - soundId &= 0xFF; - _driver->callback(16, 0); - // while ((_driver->callback(16, 0) & 8)) { - // We call the system delay and not the game delay to avoid concurrency issues. - // _engine->_system->delayMillis(10); - // } - if (_sfxPlayingSound != -1) { - // Restore the sounds's normal values. - _driver->callback(10, _sfxPlayingSound, int(1), int(_sfxPriority)); - _driver->callback(10, _sfxPlayingSound, int(3), int(_sfxFourthByteOfSong)); - _sfxPlayingSound = -1; - } - - int chan = _driver->callback(9, soundId, int(0)); - - if (chan != 9) { - _sfxPlayingSound = soundId; - _sfxPriority = _driver->callback(9, soundId, int(1)); - _sfxFourthByteOfSong = _driver->callback(9, soundId, int(3)); - - // In the cases I've seen, the mysterious fourth byte has been - // the parameter for the update_setExtraLevel3() callback. - // - // The extra level is part of the channels "total level", which - // is a six-bit value where larger values means softer volume. - // - // So what seems to be happening here is that sounds which are - // started by this function are given a slightly lower priority - // and a slightly higher (i.e. softer) extra level 3 than they - // would have if they were started from anywhere else. Strange. - - int newVal = ((((-_sfxFourthByteOfSong) + 63) * 0xFF) >> 8) & 0xFF; - newVal = -newVal + 63; - _driver->callback(10, soundId, int(3), newVal); - newVal = ((_sfxPriority * 0xFF) >> 8) & 0xFF; - _driver->callback(10, soundId, int(1), newVal); - } - - _driver->callback(6, soundId); -} - -// void CadlPlayer::beginFadeOut() { -// playSoundEffect(1); -// } - -bool CadlPlayer::load(const std::string &filename, const CFileProvider &fp) -{ - binistream *f = fp.open(filename); - - // file validation section - if(!f || !fp.extension(filename, ".adl")) { - fp.close(f); - return false; - } - - // if (_soundFileLoaded == file) - // return; - - // if (_soundDataPtr) { - // haltTrack(); - // } - - uint8 *file_data = 0; uint32 file_size = 0; - - // char filename[25]; - // sprintf(filename, "%s.ADL", file); - - // file_data = _engine->resource()->fileData(filename, &file_size); - // if (!file_data) { - // warning("Couldn't find music file: '%s'", filename); - // return; - // } - - unk2(); - unk1(); - - file_size = fp.filesize(f); - file_data = new uint8 [file_size]; - f->readString((char *)file_data, file_size); - - _driver->callback(8, int(-1)); - _soundDataPtr = 0; - - uint8 *p = file_data; - memcpy(_trackEntries, p, 120*sizeof(uint8)); - p += 120; - - int soundDataSize = file_size - 120; - - _soundDataPtr = new uint8[soundDataSize]; - assert(_soundDataPtr); - - memcpy(_soundDataPtr, p, soundDataSize*sizeof(uint8)); - - delete [] file_data; - file_data = p = 0; - file_size = 0; - - _driver->callback(4, _soundDataPtr); - - // _soundFileLoaded = file; - - // find last subsong - for(int i = 119; i >= 0; i--) - if(_trackEntries[i] != 0xff) { - numsubsongs = i + 1; - break; - } - - fp.close(f); - cursubsong = 2; - rewind(); - return true; -} - -void CadlPlayer::rewind(int subsong) -{ - if(subsong == -1) subsong = cursubsong; - opl->init(); - opl->write(1,32); - playSoundEffect(subsong); - cursubsong = subsong; - update(); -} - -unsigned int CadlPlayer::getsubsongs() -{ - return numsubsongs; -} - -bool CadlPlayer::update() -{ - bool songend = true; - -// if(_trackEntries[cursubsong] == 0xff) -// return false; - - _driver->callback(); - - for(int i = 0; i < 10; i++) - if(_driver->_channels[i].dataptr != NULL) - songend = false; - - return !songend; -} - -void CadlPlayer::unk1() { - playSoundEffect(0); - //_engine->_system->delayMillis(5 * 60); -} - -void CadlPlayer::unk2() { - playSoundEffect(0); -} - -CPlayer *CadlPlayer::factory(Copl *newopl) -{ - return new CadlPlayer(newopl); -} |