# HG changeset patch # User chainsaw # Date 1150586271 25200 # Node ID 6ad7eb96dd26d049fb4f6cc5e9506b761b706d11 # Parent 01d2aa561b537f3ef660f90d5ecab16fcc957abf [svn] Sync with upstream. This adds Westwood ADL format support. diff -r 01d2aa561b53 -r 6ad7eb96dd26 ChangeLog --- a/ChangeLog Fri Jun 16 20:55:52 2006 -0700 +++ b/ChangeLog Sat Jun 17 16:17:51 2006 -0700 @@ -1,3 +1,14 @@ +2006-06-17 03:55:52 +0000 William Pitcock + revision [1472] + - libaac: use unified fileinfo requester, since functionality is adequate (cannot save data, only read it at present) + + + Changes: Modified: + +1 -1 trunk/Plugins/Input/aac/src/Makefile.in + +0 -449 trunk/Plugins/Input/aac/src/fileinfo.c + +1 -1 trunk/Plugins/Input/aac/src/libmp4.c + + 2006-06-16 13:13:21 +0000 Yoshiki Yazawa revision [1470] - fix for alsa_mutex deadlock diff -r 01d2aa561b53 -r 6ad7eb96dd26 Plugins/Input/adplug/core/Makefile.in --- a/Plugins/Input/adplug/core/Makefile.in Fri Jun 16 20:55:52 2006 -0700 +++ b/Plugins/Input/adplug/core/Makefile.in Sat Jun 17 16:17:51 2006 -0700 @@ -8,14 +8,15 @@ a2m.cpp adtrack.cpp amd.cpp bam.cpp d00.cpp dfm.cpp dmo.cpp hsp.cpp ksm.cpp \ mad.cpp mid.cpp mkj.cpp cff.cpp dtm.cpp fmc.cpp mtk.cpp rad.cpp raw.cpp \ sa2.cpp s3m.cpp xad.cpp flash.cpp bmf.cpp hybrid.cpp hyp.cpp psi.cpp rat.cpp \ -u6m.cpp rol.cpp xsm.cpp adlibemu.c dro.cpp lds.cpp temuopl.cpp msc.cpp rix.cpp +u6m.cpp rol.cpp xsm.cpp adlibemu.c dro.cpp lds.cpp temuopl.cpp msc.cpp rix.cpp \ +adl.cpp noinst_HEADERS = adplug.h emuopl.h fmopl.h silentopl.h opl.h diskopl.h \ a2m.h amd.h bam.h d00.h dfm.h hsc.h hsp.h imf.h ksm.h lds.h mid.h mkj.h mtk.h \ protrack.h rad.h raw.h sa2.h sng.h u6m.h player.h fmc.h mad.h xad.h bmf.h \ flash.h hyp.h psi.h rat.h hybrid.h rol.h adtrack.h cff.h dtm.h fprovide.h \ database.h players.h xsm.h adlibemu.h kemuopl.h dro.h dmo.h s3m.h temuopl.h \ -msc.h rix.h +msc.h rix.h adl.h CXXFLAGS += -fPIC -DPIC $(BINIO_CFLAGS) -I../../../../intl -I../../../.. -Dstricmp=strcasecmp CFLAGS += -fPIC -DPIC $(BINIO_CFLAGS) -I../../../../intl -I../../../.. -Dstricmp=strcasecmp diff -r 01d2aa561b53 -r 6ad7eb96dd26 Plugins/Input/adplug/core/adl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/adplug/core/adl.cpp Sat Jun 17 16:17:51 2006 -0700 @@ -0,0 +1,2422 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2006 Simon Peter, , et al. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * adl.cpp - ADL player adaption by Simon Peter + * + * Original ADL player by Torbjorn Andersson and Johannes Schickel + * 'lordhoto' of the ScummVM project. + */ + +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 The ScummVM project + * + * 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. + */ + +#include +#include +#include + +#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]; + _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; + + for(int i = 0; i < 200; i++) + if(_trackEntries[i] != 0xff) + numsubsongs = i + 1; + + fp.close(f); + return true; +} + +void CadlPlayer::rewind(int subsong) +{ + 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); +} diff -r 01d2aa561b53 -r 6ad7eb96dd26 Plugins/Input/adplug/core/adl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/adplug/core/adl.h Sat Jun 17 16:17:51 2006 -0700 @@ -0,0 +1,79 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2006 Simon Peter, , et al. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * adl.h - ADL player adaption by Simon Peter + */ + +#ifndef H_ADPLUG_ADLPLAYER +#define H_ADPLUG_ADLPLAYER + +#include + +#include "player.h" + +class AdlibDriver; + +class CadlPlayer: public CPlayer +{ + public: + static CPlayer *factory(Copl *newopl); + + CadlPlayer(Copl *newopl); + ~CadlPlayer(); + + bool load(const std::string &filename, const CFileProvider &fp); + bool update(); + void rewind(int subsong); + + // refresh rate is fixed at 72Hz + float getrefresh() + { + return 72.0f; + } + + unsigned int getsubsongs(); + std::string gettype() { return std::string("Westwood ADL"); } + + private: + int numsubsongs, cursubsong; + + AdlibDriver *_driver; + + uint8_t _trackEntries[120]; + uint8_t *_soundDataPtr; + int _sfxPlayingSound; + + uint8_t _sfxPriority; + uint8_t _sfxFourthByteOfSong; + + int _numSoundTriggers; + const int *_soundTriggers; + + static const int _kyra1NumSoundTriggers; + static const int _kyra1SoundTriggers[]; + + bool init(); + void process(); + void playTrack(uint8_t track); + void playSoundEffect(uint8_t track); + void play(uint8_t track); + void unk1(); + void unk2(); +}; + +#endif diff -r 01d2aa561b53 -r 6ad7eb96dd26 Plugins/Input/adplug/core/adplug.cpp --- a/Plugins/Input/adplug/core/adplug.cpp Fri Jun 16 20:55:52 2006 -0700 +++ b/Plugins/Input/adplug/core/adplug.cpp Sat Jun 17 16:17:51 2006 -0700 @@ -63,6 +63,7 @@ #include "dro.h" #include "msc.h" #include "rix.h" +#include "adl.h" /***** Defines *****/ @@ -108,6 +109,7 @@ CPlayerDesc(CdroPlayer::factory, "DOSBox Raw OPL", ".dro\0"), CPlayerDesc(CmscPlayer::factory, "Adlib MSC Player", ".msc\0"), CPlayerDesc(CrixPlayer::factory, "Softstar RIX OPL Music", ".rix\0"), + CPlayerDesc(CadlPlayer::factory, "Westwood ADL", ".adl\0"), CPlayerDesc() }; diff -r 01d2aa561b53 -r 6ad7eb96dd26 Plugins/Input/adplug/core/amd.cpp --- a/Plugins/Input/adplug/core/amd.cpp Fri Jun 16 20:55:52 2006 -0700 +++ b/Plugins/Input/adplug/core/amd.cpp Sat Jun 17 16:17:51 2006 -0700 @@ -1,6 +1,6 @@ /* * Adplug - Replayer for many OPL2/OPL3 audio file formats. - * Copyright (C) 1999 - 2003 Simon Peter, , et al. + * Copyright (C) 1999 - 2006 Simon Peter, , et al. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -36,7 +36,7 @@ char id[9]; unsigned char version; } header; - int i, j, k, t, numtrax; + int i, j, k, t, numtrax, maxi = 0; unsigned char buf, buf2, buf3; const unsigned char convfx[10] = {0,1,2,9,17,11,13,18,3,14}; @@ -60,6 +60,7 @@ for(i=0;i<128;i++) order[i] = f->readInt(1); f->seek(10, binio::Add); if(header.version == 0x10) { // unpacked module + maxi = nop * 9; for(i=0;i<64*9;i++) trackord[i/9][i%9] = i+1; t = 0; @@ -89,6 +90,7 @@ for(k=0;kreadInt(2); if(i > 575) i = 575; // fix corrupted modules + maxi = (i + 1 > maxi ? i + 1 : maxi); j = 0; do { buf = f->readInt(1); @@ -144,15 +146,17 @@ if(instname[i][j] == '\xff') instname[i][j] = '\x20'; } - for(i=0;i, et al. + * Copyright (C) 1999 - 2003, 2006 Simon Peter, , et al. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -251,7 +251,9 @@ { memcpy(&event, &bmf.streams[i][bmf.channel[i].stream_position], sizeof(bmf_event)); #ifdef DEBUG - AdPlug_LogWrite("%02X %02X %02X %02X %02X %02X\n",event.note,event.delay,event.volume,event.instrument,event.cmd,event.cmd_data ); + AdPlug_LogWrite("%02X %02X %02X %02X %02X %02X\n", + event.note,event.delay,event.volume,event.instrument, + event.cmd,event.cmd_data); #endif if (event.cmd == 0xFF) diff -r 01d2aa561b53 -r 6ad7eb96dd26 Plugins/Input/adplug/core/d00.cpp --- a/Plugins/Input/adplug/core/d00.cpp Fri Jun 16 20:55:52 2006 -0700 +++ b/Plugins/Input/adplug/core/d00.cpp Sat Jun 17 16:17:51 2006 -0700 @@ -1,6 +1,6 @@ /* * Adplug - Replayer for many OPL2/OPL3 audio file formats. - * Copyright (C) 1999 - 2003 Simon Peter, , et al. + * Copyright (C) 1999 - 2006 Simon Peter, , et al. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -29,6 +29,7 @@ #include #include +#include #include "debug.h" #include "d00.h" @@ -37,7 +38,13 @@ #define LOBYTE(val) (val & 0xff) static const unsigned short notetable[12] = // D00 note table - {340,363,385,408,432,458,485,514,544,577,611,647}; + {340,363,385,408,432,458,485,514,544,577,611,647}; + +static inline uint16_t LE_WORD(const uint16_t *val) +{ + const uint8_t *b = (const uint8_t *)val; + return (b[1] << 8) + b[0]; +} /*** public methods *************************************/ @@ -48,480 +55,484 @@ bool Cd00Player::load(const std::string &filename, const CFileProvider &fp) { - binistream *f = fp.open(filename); if(!f) return false; - d00header *checkhead; - d00header1 *ch; - unsigned long filesize; - int i,ver1=0; - char *str; + binistream *f = fp.open(filename); if(!f) return false; + d00header *checkhead; + d00header1 *ch; + unsigned long filesize; + int i,ver1=0; + char *str; - // file validation section - checkhead = new d00header; - f->readString((char *)checkhead, sizeof(d00header)); + // file validation section + checkhead = new d00header; + f->readString((char *)checkhead, sizeof(d00header)); - // Check for version 2-4 header - if(strncmp(checkhead->id,"JCH\x26\x02\x66",6) || checkhead->type || - !checkhead->subsongs || checkhead->soundcard) { - // Check for version 0 or 1 header (and .d00 file extension) - delete checkhead; - if(!fp.extension(filename, ".d00")) { fp.close(f); return false; } - ch = new d00header1; - f->seek(0); f->readString((char *)ch, sizeof(d00header1)); - if(ch->version > 1 || !ch->subsongs) - { delete ch; fp.close(f); return false; } - delete ch; - ver1 = 1; - } else - delete checkhead; + // Check for version 2-4 header + if(strncmp(checkhead->id,"JCH\x26\x02\x66",6) || checkhead->type || + !checkhead->subsongs || checkhead->soundcard) { + // Check for version 0 or 1 header (and .d00 file extension) + delete checkhead; + if(!fp.extension(filename, ".d00")) { fp.close(f); return false; } + ch = new d00header1; + f->seek(0); f->readString((char *)ch, sizeof(d00header1)); + if(ch->version > 1 || !ch->subsongs) + { delete ch; fp.close(f); return false; } + delete ch; + ver1 = 1; + } else + delete checkhead; - AdPlug_LogWrite("Cd00Player::load(f,\"%s\"): %s format D00 file detected!\n", - filename.c_str(), ver1 ? "Old" : "New"); + AdPlug_LogWrite("Cd00Player::load(f,\"%s\"): %s format D00 file detected!\n", + filename.c_str(), ver1 ? "Old" : "New"); - // load section - filesize = fp.filesize(f); f->seek(0); - filedata = new char [filesize + 1]; // 1 byte is needed for old-style DataInfo block - f->readString((char *)filedata, filesize); - fp.close(f); - if(!ver1) { // version 2 and above - header = (struct d00header *)filedata; - version = header->version; - datainfo = (char *)filedata + header->infoptr; - inst = (struct Sinsts *)((char *)filedata + header->instptr); - seqptr = (unsigned short *)((char *)filedata + header->seqptr); - for(i=31;i>=0;i--) // erase whitespace - if(header->songname[i] == ' ') - header->songname[i] = '\0'; - else - break; - for(i=31;i>=0;i--) - if(header->author[i] == ' ') - header->author[i] = '\0'; - else - break; - } else { // version 1 - header1 = (struct d00header1 *)filedata; - version = header1->version; - datainfo = (char *)filedata + header1->infoptr; - inst = (struct Sinsts *)((char *)filedata + header1->instptr); - seqptr = (unsigned short *)((char *)filedata + header1->seqptr); - } - switch(version) { - case 0: - levpuls = 0; - spfx = 0; - header1->speed = 70; // v0 files default to 70Hz - break; - case 1: - levpuls = (struct Slevpuls *)((char *)filedata + header1->lpulptr); - spfx = 0; - break; - case 2: - levpuls = (struct Slevpuls *)((char *)filedata + header->spfxptr); - spfx = 0; - break; - case 3: - spfx = 0; - levpuls = 0; - break; - case 4: - spfx = (struct Sspfx *)((char *)filedata + header->spfxptr); - levpuls = 0; - break; - } - if((str = strstr(datainfo,"\xff\xff"))) - while((*str == '\xff' || *str == ' ') && str >= datainfo) { - *str = '\0'; str--; - } - else // old-style block - memset((char *)filedata+filesize,0,1); + // load section + filesize = fp.filesize(f); f->seek(0); + filedata = new char [filesize + 1]; // 1 byte is needed for old-style DataInfo block + f->readString((char *)filedata, filesize); + fp.close(f); + if(!ver1) { // version 2 and above + header = (struct d00header *)filedata; + version = header->version; + datainfo = (char *)filedata + LE_WORD(&header->infoptr); + inst = (struct Sinsts *)((char *)filedata + LE_WORD(&header->instptr)); + seqptr = (unsigned short *)((char *)filedata + LE_WORD(&header->seqptr)); + for(i=31;i>=0;i--) // erase whitespace + if(header->songname[i] == ' ') + header->songname[i] = '\0'; + else + break; + for(i=31;i>=0;i--) + if(header->author[i] == ' ') + header->author[i] = '\0'; + else + break; + } else { // version 1 + header1 = (struct d00header1 *)filedata; + version = header1->version; + datainfo = (char *)filedata + LE_WORD(&header1->infoptr); + inst = (struct Sinsts *)((char *)filedata + LE_WORD(&header1->instptr)); + seqptr = (unsigned short *)((char *)filedata + LE_WORD(&header1->seqptr)); + } + switch(version) { + case 0: + levpuls = 0; + spfx = 0; + header1->speed = 70; // v0 files default to 70Hz + break; + case 1: + levpuls = (struct Slevpuls *)((char *)filedata + LE_WORD(&header1->lpulptr)); + spfx = 0; + break; + case 2: + levpuls = (struct Slevpuls *)((char *)filedata + LE_WORD(&header->spfxptr)); + spfx = 0; + break; + case 3: + spfx = 0; + levpuls = 0; + break; + case 4: + spfx = (struct Sspfx *)((char *)filedata + LE_WORD(&header->spfxptr)); + levpuls = 0; + break; + } + if((str = strstr(datainfo,"\xff\xff"))) + while((*str == '\xff' || *str == ' ') && str >= datainfo) { + *str = '\0'; str--; + } + else // old-style block + memset((char *)filedata+filesize,0,1); - rewind(0); - return true; + rewind(0); + return true; } bool Cd00Player::update() { - unsigned char c,cnt,trackend=0,fx,note; - unsigned short ord,*patt,buf,fxop; + unsigned char c,cnt,trackend=0,fx,note; + unsigned short ord,*patt,buf,fxop,pattpos; - // effect handling (timer dependant) - for(c=0;c<9;c++) { - channel[c].slideval += channel[c].slide; setfreq(c); // sliding - vibrato(c); // vibrato + // effect handling (timer dependant) + for(c=0;c<9;c++) { + channel[c].slideval += channel[c].slide; setfreq(c); // sliding + vibrato(c); // vibrato - if(channel[c].spfx != 0xffff) { // SpFX - if(channel[c].fxdel) - channel[c].fxdel--; - else { - channel[c].spfx = spfx[channel[c].spfx].ptr; - channel[c].fxdel = spfx[channel[c].spfx].duration; - channel[c].inst = spfx[channel[c].spfx].instnr & 0xfff; - if(spfx[channel[c].spfx].modlev != 0xff) - channel[c].modvol = spfx[channel[c].spfx].modlev; - setinst(c); - if(spfx[channel[c].spfx].instnr & 0x8000) // locked frequency - note = spfx[channel[c].spfx].halfnote; - else // unlocked frequency - note = spfx[channel[c].spfx].halfnote + channel[c].note; - channel[c].freq = notetable[note%12] + ((note/12) << 10); - setfreq(c); - } - channel[c].modvol += spfx[channel[c].spfx].modlevadd; channel[c].modvol &= 63; - setvolume(c); - } + if(channel[c].spfx != 0xffff) { // SpFX + if(channel[c].fxdel) + channel[c].fxdel--; + else { + channel[c].spfx = LE_WORD(&spfx[channel[c].spfx].ptr); + channel[c].fxdel = spfx[channel[c].spfx].duration; + channel[c].inst = LE_WORD(&spfx[channel[c].spfx].instnr) & 0xfff; + if(spfx[channel[c].spfx].modlev != 0xff) + channel[c].modvol = spfx[channel[c].spfx].modlev; + setinst(c); + if(LE_WORD(&spfx[channel[c].spfx].instnr) & 0x8000) // locked frequency + note = spfx[channel[c].spfx].halfnote; + else // unlocked frequency + note = spfx[channel[c].spfx].halfnote + channel[c].note; + channel[c].freq = notetable[note%12] + ((note/12) << 10); + setfreq(c); + } + channel[c].modvol += spfx[channel[c].spfx].modlevadd; channel[c].modvol &= 63; + setvolume(c); + } - if(channel[c].levpuls != 0xff) // Levelpuls - if(channel[c].frameskip) - channel[c].frameskip--; - else { - channel[c].frameskip = inst[channel[c].inst].timer; - if(channel[c].fxdel) - channel[c].fxdel--; - else { - channel[c].levpuls = levpuls[channel[c].levpuls].ptr - 1; - channel[c].fxdel = levpuls[channel[c].levpuls].duration; - if(levpuls[channel[c].levpuls].level != 0xff) - channel[c].modvol = levpuls[channel[c].levpuls].level; - } - channel[c].modvol += levpuls[channel[c].levpuls].voladd; channel[c].modvol &= 63; - setvolume(c); - } + if(channel[c].levpuls != 0xff) // Levelpuls + if(channel[c].frameskip) + channel[c].frameskip--; + else { + channel[c].frameskip = inst[channel[c].inst].timer; + if(channel[c].fxdel) + channel[c].fxdel--; + else { + channel[c].levpuls = levpuls[channel[c].levpuls].ptr - 1; + channel[c].fxdel = levpuls[channel[c].levpuls].duration; + if(levpuls[channel[c].levpuls].level != 0xff) + channel[c].modvol = levpuls[channel[c].levpuls].level; } + channel[c].modvol += levpuls[channel[c].levpuls].voladd; channel[c].modvol &= 63; + setvolume(c); + } + } - // song handling - for(c=0;c<9;c++) - if(version < 3 ? channel[c].del : channel[c].del <= 0x7f) { - if(version == 4) // v4: hard restart SR - if(channel[c].del == inst[channel[c].inst].timer) - if(channel[c].nextnote) - opl->write(0x83 + op_table[c], inst[channel[c].inst].sr); - if(version < 3) - channel[c].del--; - else - if(channel[c].speed) - channel[c].del += channel[c].speed; - else { - channel[c].seqend = 1; - continue; - } - } else { - if(channel[c].speed) { - if(version < 3) - channel[c].del = channel[c].speed; - else { - channel[c].del &= 0x7f; - channel[c].del += channel[c].speed; - } - } else { - channel[c].seqend = 1; - continue; - } - if(channel[c].rhcnt) { // process pending REST/HOLD events - channel[c].rhcnt--; - continue; - } -readorder: // process arrangement (orderlist) - ord = channel[c].order[channel[c].ordpos]; - switch(ord) { - case 0xfffe: channel[c].seqend = 1; continue; // end of arrangement stream - case 0xffff: // jump to order - channel[c].ordpos = channel[c].order[channel[c].ordpos+1]; - channel[c].seqend = 1; - goto readorder; - default: - if(ord >= 0x9000) { // set speed - channel[c].speed = ord & 0xff; - ord = channel[c].order[channel[c].ordpos - 1]; - channel[c].ordpos++; - } else - if(ord >= 0x8000) { // transpose track - channel[c].transpose = ord & 0xff; - if(ord & 0x100) - channel[c].transpose = -channel[c].transpose; - ord = channel[c].order[++channel[c].ordpos]; - } - patt = (unsigned short *)((char *)filedata + seqptr[ord]); - break; - } -readseq: // process sequence (pattern) - if(!version) // v0: always initialize rhcnt - channel[c].rhcnt = channel[c].irhcnt; - if(patt[channel[c].pattpos] == 0xffff) { // pattern ended? - channel[c].pattpos = 0; - channel[c].ordpos++; - goto readorder; - } - cnt = HIBYTE(patt[channel[c].pattpos]); - note = LOBYTE(patt[channel[c].pattpos]); - fx = patt[channel[c].pattpos] >> 12; - fxop = patt[channel[c].pattpos] & 0x0fff; - channel[c].pattpos++; - channel[c].nextnote = LOBYTE(patt[channel[c].pattpos]) & 0x7f; - if(version ? cnt < 0x40 : !fx) { // note event - switch(note) { - case 0: // REST event - case 0x80: - if(!note || version) { - channel[c].key = 0; - setfreq(c); - } - // fall through... - case 0x7e: // HOLD event - if(version) - channel[c].rhcnt = cnt; - channel[c].nextnote = 0; - break; - default: // play note - channel[c].slideval = 0; channel[c].slide = 0; channel[c].vibdepth = 0; // restart fx + // song handling + for(c=0;c<9;c++) + if(version < 3 ? channel[c].del : channel[c].del <= 0x7f) { + if(version == 4) // v4: hard restart SR + if(channel[c].del == inst[channel[c].inst].timer) + if(channel[c].nextnote) + opl->write(0x83 + op_table[c], inst[channel[c].inst].sr); + if(version < 3) + channel[c].del--; + else + if(channel[c].speed) + channel[c].del += channel[c].speed; + else { + channel[c].seqend = 1; + continue; + } + } else { + if(channel[c].speed) { + if(version < 3) + channel[c].del = channel[c].speed; + else { + channel[c].del &= 0x7f; + channel[c].del += channel[c].speed; + } + } else { + channel[c].seqend = 1; + continue; + } + if(channel[c].rhcnt) { // process pending REST/HOLD events + channel[c].rhcnt--; + continue; + } + readorder: // process arrangement (orderlist) + ord = LE_WORD(&channel[c].order[channel[c].ordpos]); + switch(ord) { + case 0xfffe: channel[c].seqend = 1; continue; // end of arrangement stream + case 0xffff: // jump to order + channel[c].ordpos = LE_WORD(&channel[c].order[channel[c].ordpos + 1]); + channel[c].seqend = 1; + goto readorder; + default: + if(ord >= 0x9000) { // set speed + channel[c].speed = ord & 0xff; + ord = LE_WORD(&channel[c].order[channel[c].ordpos - 1]); + channel[c].ordpos++; + } else + if(ord >= 0x8000) { // transpose track + channel[c].transpose = ord & 0xff; + if(ord & 0x100) + channel[c].transpose = -channel[c].transpose; + ord = LE_WORD(&channel[c].order[++channel[c].ordpos]); + } + patt = (unsigned short *)((char *)filedata + LE_WORD(&seqptr[ord])); + break; + } + readseq: // process sequence (pattern) + if(!version) // v0: always initialize rhcnt + channel[c].rhcnt = channel[c].irhcnt; + pattpos = LE_WORD(&patt[channel[c].pattpos]); + if(pattpos == 0xffff) { // pattern ended? + channel[c].pattpos = 0; + channel[c].ordpos++; + goto readorder; + } + cnt = HIBYTE(pattpos); + note = LOBYTE(pattpos); + fx = pattpos >> 12; + fxop = pattpos & 0x0fff; + channel[c].pattpos++; pattpos = LE_WORD(&patt[channel[c].pattpos]); + channel[c].nextnote = LOBYTE(pattpos) & 0x7f; + if(version ? cnt < 0x40 : !fx) { // note event + switch(note) { + case 0: // REST event + case 0x80: + if(!note || version) { + channel[c].key = 0; + setfreq(c); + } + // fall through... + case 0x7e: // HOLD event + if(version) + channel[c].rhcnt = cnt; + channel[c].nextnote = 0; + break; + default: // play note + // restart fx + channel[c].slideval = 0; channel[c].slide = 0; channel[c].vibdepth = 0; - if(version) { // note handling for v1 and above - if(note > 0x80) // locked note (no channel transpose) - note -= 0x80; - else // unlocked note - note += channel[c].transpose; - channel[c].note = note; // remember note for SpFX + if(version) { // note handling for v1 and above + if(note > 0x80) // locked note (no channel transpose) + note -= 0x80; + else // unlocked note + note += channel[c].transpose; + channel[c].note = note; // remember note for SpFX - if(channel[c].ispfx != 0xffff && cnt < 0x20) { // reset SpFX - channel[c].spfx = channel[c].ispfx; - if(spfx[channel[c].spfx].instnr & 0x8000) // locked frequency - note = spfx[channel[c].spfx].halfnote; - else // unlocked frequency - note += spfx[channel[c].spfx].halfnote; - channel[c].inst = spfx[channel[c].spfx].instnr & 0xfff; - channel[c].fxdel = spfx[channel[c].spfx].duration; - if(spfx[channel[c].spfx].modlev != 0xff) - channel[c].modvol = spfx[channel[c].spfx].modlev; - else - channel[c].modvol = inst[channel[c].inst].data[7] & 63; - } + if(channel[c].ispfx != 0xffff && cnt < 0x20) { // reset SpFX + channel[c].spfx = channel[c].ispfx; + if(LE_WORD(&spfx[channel[c].spfx].instnr) & 0x8000) // locked frequency + note = spfx[channel[c].spfx].halfnote; + else // unlocked frequency + note += spfx[channel[c].spfx].halfnote; + channel[c].inst = LE_WORD(&spfx[channel[c].spfx].instnr) & 0xfff; + channel[c].fxdel = spfx[channel[c].spfx].duration; + if(spfx[channel[c].spfx].modlev != 0xff) + channel[c].modvol = spfx[channel[c].spfx].modlev; + else + channel[c].modvol = inst[channel[c].inst].data[7] & 63; + } - if(channel[c].ilevpuls != 0xff && cnt < 0x20) { // reset LevelPuls - channel[c].levpuls = channel[c].ilevpuls; - channel[c].fxdel = levpuls[channel[c].levpuls].duration; - channel[c].frameskip = inst[channel[c].inst].timer; - if(levpuls[channel[c].levpuls].level != 0xff) - channel[c].modvol = levpuls[channel[c].levpuls].level; - else - channel[c].modvol = inst[channel[c].inst].data[7] & 63; - } + if(channel[c].ilevpuls != 0xff && cnt < 0x20) { // reset LevelPuls + channel[c].levpuls = channel[c].ilevpuls; + channel[c].fxdel = levpuls[channel[c].levpuls].duration; + channel[c].frameskip = inst[channel[c].inst].timer; + if(levpuls[channel[c].levpuls].level != 0xff) + channel[c].modvol = levpuls[channel[c].levpuls].level; + else + channel[c].modvol = inst[channel[c].inst].data[7] & 63; + } - channel[c].freq = notetable[note%12] + ((note/12) << 10); - if(cnt < 0x20) // normal note - playnote(c); - else { // tienote - setfreq(c); - cnt -= 0x20; // make count proper - } - channel[c].rhcnt = cnt; - } else { // note handling for v0 - if(cnt < 2) // unlocked note - note += channel[c].transpose; - channel[c].note = note; + channel[c].freq = notetable[note%12] + ((note/12) << 10); + if(cnt < 0x20) // normal note + playnote(c); + else { // tienote + setfreq(c); + cnt -= 0x20; // make count proper + } + channel[c].rhcnt = cnt; + } else { // note handling for v0 + if(cnt < 2) // unlocked note + note += channel[c].transpose; + channel[c].note = note; - channel[c].freq = notetable[note%12] + ((note/12) << 10); - if(cnt == 1) // tienote - setfreq(c); - else // normal note - playnote(c); - } - break; - } - continue; // event is complete - } else { // effect event - switch(fx) { - case 6: // Cut/Stop Voice - buf = channel[c].inst; - channel[c].inst = 0; - playnote(c); - channel[c].inst = buf; - channel[c].rhcnt = fxop; - continue; // no note follows this event - case 7: // Vibrato - channel[c].vibspeed = fxop & 0xff; - channel[c].vibdepth = fxop >> 8; - channel[c].trigger = fxop >> 9; - break; - case 8: // v0: Duration - if(!version) - channel[c].irhcnt = fxop; - break; - case 9: // New Level - channel[c].vol = fxop & 63; - if(channel[c].vol + channel[c].cvol < 63) // apply channel volume - channel[c].vol += channel[c].cvol; - else - channel[c].vol = 63; - setvolume(c); - break; - case 0xb: // v4: Set SpFX - if(version == 4) - channel[c].ispfx = fxop; - break; - case 0xc: // Set Instrument - channel[c].ispfx = 0xffff; - channel[c].spfx = 0xffff; - channel[c].inst = fxop; - channel[c].modvol = inst[fxop].data[7] & 63; - if(version < 3 && version && inst[fxop].tunelev) // Set LevelPuls - channel[c].ilevpuls = inst[fxop].tunelev - 1; - else { - channel[c].ilevpuls = 0xff; - channel[c].levpuls = 0xff; - } - break; - case 0xd: // Slide up - channel[c].slide = fxop; - break; - case 0xe: // Slide down - channel[c].slide = -fxop; - break; - } - goto readseq; // event is incomplete, note follows - } - } + channel[c].freq = notetable[note%12] + ((note/12) << 10); + if(cnt == 1) // tienote + setfreq(c); + else // normal note + playnote(c); + } + break; + } + continue; // event is complete + } else { // effect event + switch(fx) { + case 6: // Cut/Stop Voice + buf = channel[c].inst; + channel[c].inst = 0; + playnote(c); + channel[c].inst = buf; + channel[c].rhcnt = fxop; + continue; // no note follows this event + case 7: // Vibrato + channel[c].vibspeed = fxop & 0xff; + channel[c].vibdepth = fxop >> 8; + channel[c].trigger = fxop >> 9; + break; + case 8: // v0: Duration + if(!version) + channel[c].irhcnt = fxop; + break; + case 9: // New Level + channel[c].vol = fxop & 63; + if(channel[c].vol + channel[c].cvol < 63) // apply channel volume + channel[c].vol += channel[c].cvol; + else + channel[c].vol = 63; + setvolume(c); + break; + case 0xb: // v4: Set SpFX + if(version == 4) + channel[c].ispfx = fxop; + break; + case 0xc: // Set Instrument + channel[c].ispfx = 0xffff; + channel[c].spfx = 0xffff; + channel[c].inst = fxop; + channel[c].modvol = inst[fxop].data[7] & 63; + if(version < 3 && version && inst[fxop].tunelev) // Set LevelPuls + channel[c].ilevpuls = inst[fxop].tunelev - 1; + else { + channel[c].ilevpuls = 0xff; + channel[c].levpuls = 0xff; + } + break; + case 0xd: // Slide up + channel[c].slide = fxop; + break; + case 0xe: // Slide down + channel[c].slide = -fxop; + break; + } + goto readseq; // event is incomplete, note follows + } + } - for(c=0;c<9;c++) - if(channel[c].seqend) - trackend++; - if(trackend == 9) - songend = 1; + for(c=0;c<9;c++) + if(channel[c].seqend) + trackend++; + if(trackend == 9) + songend = 1; - return !songend; + return !songend; } void Cd00Player::rewind(int subsong) { - struct Stpoin { - unsigned short ptr[9]; - unsigned char volume[9],dummy[5]; - } *tpoin; - int i; + struct Stpoin { + unsigned short ptr[9]; + unsigned char volume[9],dummy[5]; + } *tpoin; + int i; - if(version > 1) { // do nothing if subsong > number of subsongs - if(subsong >= header->subsongs) - return; - } else - if(subsong >= header1->subsongs) - return; + if(version > 1) { // do nothing if subsong > number of subsongs + if(subsong >= header->subsongs) + return; + } else + if(subsong >= header1->subsongs) + return; - memset(channel,0,sizeof(channel)); - if(version > 1) - tpoin = (struct Stpoin *)((char *)filedata + header->tpoin); - else - tpoin = (struct Stpoin *)((char *)filedata + header1->tpoin); - for(i=0;i<9;i++) { - if(tpoin[subsong].ptr[i]) { // track enabled - channel[i].speed = *((unsigned short *)((char *)filedata + tpoin[subsong].ptr[i])); - channel[i].order = (unsigned short *)((char *)filedata + tpoin[subsong].ptr[i] + 2); - } else { // track disabled - channel[i].speed = 0; - channel[i].order = 0; - } - channel[i].ispfx = 0xffff; channel[i].spfx = 0xffff; // no SpFX - channel[i].ilevpuls = 0xff; channel[i].levpuls = 0xff; // no LevelPuls - channel[i].cvol = tpoin[subsong].volume[i] & 0x7f; // our player may savely ignore bit 7 - channel[i].vol = channel[i].cvol; // initialize volume - } - songend = 0; - opl->init(); opl->write(1,32); // reset OPL chip + memset(channel,0,sizeof(channel)); + if(version > 1) + tpoin = (struct Stpoin *)((char *)filedata + LE_WORD(&header->tpoin)); + else + tpoin = (struct Stpoin *)((char *)filedata + LE_WORD(&header1->tpoin)); + for(i=0;i<9;i++) { + if(LE_WORD(&tpoin[subsong].ptr[i])) { // track enabled + channel[i].speed = LE_WORD((unsigned short *) + ((char *)filedata + LE_WORD(&tpoin[subsong].ptr[i]))); + channel[i].order = (unsigned short *) + ((char *)filedata + LE_WORD(&tpoin[subsong].ptr[i]) + 2); + } else { // track disabled + channel[i].speed = 0; + channel[i].order = 0; + } + channel[i].ispfx = 0xffff; channel[i].spfx = 0xffff; // no SpFX + channel[i].ilevpuls = 0xff; channel[i].levpuls = 0xff; // no LevelPuls + channel[i].cvol = tpoin[subsong].volume[i] & 0x7f; // our player may savely ignore bit 7 + channel[i].vol = channel[i].cvol; // initialize volume + } + songend = 0; + opl->init(); opl->write(1,32); // reset OPL chip } std::string Cd00Player::gettype() { - char tmpstr[40]; + char tmpstr[40]; - sprintf(tmpstr,"EdLib packed (version %d)",version > 1 ? header->version : header1->version); - return std::string(tmpstr); + sprintf(tmpstr,"EdLib packed (version %d)",version > 1 ? header->version : header1->version); + return std::string(tmpstr); } float Cd00Player::getrefresh() { - if(version > 1) - return header->speed; - else - return header1->speed; + if(version > 1) + return header->speed; + else + return header1->speed; } unsigned int Cd00Player::getsubsongs() { - if(version <= 1) // return number of subsongs - return header1->subsongs; - else - return header->subsongs; + if(version <= 1) // return number of subsongs + return header1->subsongs; + else + return header->subsongs; } /*** private methods *************************************/ void Cd00Player::setvolume(unsigned char chan) { - unsigned char op = op_table[chan]; - unsigned short insnr = channel[chan].inst; + unsigned char op = op_table[chan]; + unsigned short insnr = channel[chan].inst; - opl->write(0x43 + op,(int)(63-((63-(inst[insnr].data[2] & 63))/63.0)*(63-channel[chan].vol)) + - (inst[insnr].data[2] & 192)); - if(inst[insnr].data[10] & 1) - opl->write(0x40 + op,(int)(63-((63-channel[chan].modvol)/63.0)*(63-channel[chan].vol)) + - (inst[insnr].data[7] & 192)); - else - opl->write(0x40 + op,channel[chan].modvol + (inst[insnr].data[7] & 192)); + opl->write(0x43 + op,(int)(63-((63-(inst[insnr].data[2] & 63))/63.0)*(63-channel[chan].vol)) + + (inst[insnr].data[2] & 192)); + if(inst[insnr].data[10] & 1) + opl->write(0x40 + op,(int)(63-((63-channel[chan].modvol)/63.0)*(63-channel[chan].vol)) + + (inst[insnr].data[7] & 192)); + else + opl->write(0x40 + op,channel[chan].modvol + (inst[insnr].data[7] & 192)); } void Cd00Player::setfreq(unsigned char chan) { - unsigned short freq = channel[chan].freq; + unsigned short freq = channel[chan].freq; - if(version == 4) // v4: apply instrument finetune - freq += inst[channel[chan].inst].tunelev; + if(version == 4) // v4: apply instrument finetune + freq += inst[channel[chan].inst].tunelev; - freq += channel[chan].slideval; - opl->write(0xa0 + chan, freq & 255); - if(channel[chan].key) - opl->write(0xb0 + chan, ((freq >> 8) & 31) | 32); - else - opl->write(0xb0 + chan, (freq >> 8) & 31); + freq += channel[chan].slideval; + opl->write(0xa0 + chan, freq & 255); + if(channel[chan].key) + opl->write(0xb0 + chan, ((freq >> 8) & 31) | 32); + else + opl->write(0xb0 + chan, (freq >> 8) & 31); } void Cd00Player::setinst(unsigned char chan) { - unsigned char op = op_table[chan]; - unsigned short insnr = channel[chan].inst; + unsigned char op = op_table[chan]; + unsigned short insnr = channel[chan].inst; - // set instrument data - opl->write(0x63 + op, inst[insnr].data[0]); - opl->write(0x83 + op, inst[insnr].data[1]); - opl->write(0x23 + op, inst[insnr].data[3]); - opl->write(0xe3 + op, inst[insnr].data[4]); - opl->write(0x60 + op, inst[insnr].data[5]); - opl->write(0x80 + op, inst[insnr].data[6]); - opl->write(0x20 + op, inst[insnr].data[8]); - opl->write(0xe0 + op, inst[insnr].data[9]); - if(version) - opl->write(0xc0 + chan, inst[insnr].data[10]); - else - opl->write(0xc0 + chan, (inst[insnr].data[10] << 1) + (inst[insnr].tunelev & 1)); + // set instrument data + opl->write(0x63 + op, inst[insnr].data[0]); + opl->write(0x83 + op, inst[insnr].data[1]); + opl->write(0x23 + op, inst[insnr].data[3]); + opl->write(0xe3 + op, inst[insnr].data[4]); + opl->write(0x60 + op, inst[insnr].data[5]); + opl->write(0x80 + op, inst[insnr].data[6]); + opl->write(0x20 + op, inst[insnr].data[8]); + opl->write(0xe0 + op, inst[insnr].data[9]); + if(version) + opl->write(0xc0 + chan, inst[insnr].data[10]); + else + opl->write(0xc0 + chan, (inst[insnr].data[10] << 1) + (inst[insnr].tunelev & 1)); } void Cd00Player::playnote(unsigned char chan) { - // set misc vars & play - opl->write(0xb0 + chan, 0); // stop old note - setinst(chan); - channel[chan].key = 1; - setfreq(chan); - setvolume(chan); + // set misc vars & play + opl->write(0xb0 + chan, 0); // stop old note + setinst(chan); + channel[chan].key = 1; + setfreq(chan); + setvolume(chan); } void Cd00Player::vibrato(unsigned char chan) { - if(!channel[chan].vibdepth) - return; + if(!channel[chan].vibdepth) + return; - if(channel[chan].trigger) - channel[chan].trigger--; - else { - channel[chan].trigger = channel[chan].vibdepth; - channel[chan].vibspeed = -channel[chan].vibspeed; - } - channel[chan].freq += channel[chan].vibspeed; - setfreq(chan); + if(channel[chan].trigger) + channel[chan].trigger--; + else { + channel[chan].trigger = channel[chan].vibdepth; + channel[chan].vibspeed = -channel[chan].vibspeed; + } + channel[chan].freq += channel[chan].vibspeed; + setfreq(chan); } diff -r 01d2aa561b53 -r 6ad7eb96dd26 Plugins/Input/adplug/core/d00.h --- a/Plugins/Input/adplug/core/d00.h Fri Jun 16 20:55:52 2006 -0700 +++ b/Plugins/Input/adplug/core/d00.h Sat Jun 17 16:17:51 2006 -0700 @@ -1,6 +1,6 @@ /* * AdPlug - Replayer for many OPL2/OPL3 audio file formats. - * Copyright (C) 1999 - 2003 Simon Peter, , et al. + * Copyright (C) 1999 - 2006 Simon Peter, , et al. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -26,7 +26,7 @@ class Cd00Player: public CPlayer { -public: + public: static CPlayer *factory(Copl *newopl); Cd00Player(Copl *newopl) @@ -49,7 +49,7 @@ { if(*datainfo) return std::string(datainfo); else return std::string(); }; unsigned int getsubsongs(); -protected: + protected: #pragma pack(1) struct d00header { char id[6]; @@ -96,7 +96,7 @@ d00header1 *header1; char *filedata; -private: + private: void setvolume(unsigned char chan); void setfreq(unsigned char chan); void setinst(unsigned char chan); diff -r 01d2aa561b53 -r 6ad7eb96dd26 Plugins/Input/adplug/core/ksm.cpp --- a/Plugins/Input/adplug/core/ksm.cpp Fri Jun 16 20:55:52 2006 -0700 +++ b/Plugins/Input/adplug/core/ksm.cpp Sat Jun 17 16:17:51 2006 -0700 @@ -1,6 +1,6 @@ /* * Adplug - Replayer for many OPL2/OPL3 audio file formats. - * Copyright (C) 1999 - 2003 Simon Peter, , et al. + * Copyright (C) 1999 - 2006 Simon Peter, , et al. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -50,7 +50,6 @@ if(!fp.extension(filename, ".ksm")) { AdPlug_LogWrite("CksmPlayer::load(,\"%s\"): File doesn't have '.ksm' " "extension! Rejected!\n", filename.c_str()); - delete [] fn; return false; } AdPlug_LogWrite("*** CksmPlayer::load(,\"%s\") ***\n", filename.c_str()); @@ -98,7 +97,7 @@ bool CksmPlayer::update() { - int quanter,chan=0,drumnum=0,freq,track,volevel,volval; + int quanter,chan,drumnum,freq,track,volevel,volval; unsigned int i,j,bufnum; unsigned long temp,templong; @@ -113,7 +112,10 @@ if ((templong&192) == 0) { i = 0; - while (((chanfreq[i] != (templong&63)) || (chantrack[i] != ((templong>>8)&15))) && (i < numchans)) + + while ((i < numchans) && + ((chanfreq[i] != (templong&63)) || + (chantrack[i] != ((templong>>8)&15)))) i++; if (i < numchans) { diff -r 01d2aa561b53 -r 6ad7eb96dd26 Plugins/Input/adplug/core/msc.cpp --- a/Plugins/Input/adplug/core/msc.cpp Fri Jun 16 20:55:52 2006 -0700 +++ b/Plugins/Input/adplug/core/msc.cpp Sat Jun 17 16:17:51 2006 -0700 @@ -1,6 +1,6 @@ /* * Adplug - Replayer for many OPL2/OPL3 audio file formats. - * Copyright (C) 1999 - 2005 Simon Peter, , et al. + * Copyright (C) 1999 - 2006 Simon Peter, , et al. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,30 +22,28 @@ #include #include "msc.h" +#include "debug.h" const unsigned char CmscPlayer::msc_signature [MSC_SIGN_LEN] = { 'C', 'e', 'r', 'e', 's', ' ', '\x13', ' ', 'M', 'S', 'C', 'p', 'l', 'a', 'y', ' ' }; - /*** public methods *************************************/ -CPlayer * -CmscPlayer::factory (Copl * newopl) +CPlayer *CmscPlayer::factory (Copl * newopl) { return new CmscPlayer (newopl); } - -CmscPlayer::CmscPlayer (Copl * newopl) : CPlayer (newopl) { +CmscPlayer::CmscPlayer(Copl * newopl) : CPlayer (newopl) +{ desc = NULL; msc_data = NULL; raw_data = NULL; nr_blocks = 0; } - -CmscPlayer::~CmscPlayer () +CmscPlayer::~CmscPlayer() { if (raw_data != NULL) delete [] raw_data; @@ -64,9 +62,7 @@ delete [] desc; } - -bool -CmscPlayer::load (const std::string & filename, const CFileProvider & fp) +bool CmscPlayer::load(const std::string & filename, const CFileProvider & fp) { binistream * bf; msc_header hdr; @@ -115,9 +111,7 @@ return true; } - -bool -CmscPlayer::update () +bool CmscPlayer::update() { // output data while (! delay) { @@ -156,9 +150,7 @@ return true; } - -void -CmscPlayer::rewind (int subsong) +void CmscPlayer::rewind(int subsong) { // reset state dec_prefix = 0; @@ -169,32 +161,27 @@ delay = 0; // init the OPL chip and go to OPL2 mode - opl->init (); - opl->write (1, 32); + opl->init(); + opl->write(1, 32); } - -float -CmscPlayer::getrefresh () +float CmscPlayer::getrefresh() { // PC timer oscillator frequency / wait register return 1193180 / (float) (timer_div ? timer_div : 0xffff); } -std::string -CmscPlayer::gettype () +std::string CmscPlayer::gettype() { char vstr [40]; - snprintf (vstr, sizeof (vstr), "AdLib MSCplay (version %d)", version); + sprintf(vstr, "AdLib MSCplay (version %d)", version); return std::string (vstr); } - /*** private methods *************************************/ -bool -CmscPlayer::load_header (binistream * bf, msc_header * hdr) +bool CmscPlayer::load_header(binistream * bf, msc_header * hdr) { // check signature bf->readString ((char *) hdr->mh_sign, sizeof (hdr->mh_sign)); @@ -213,9 +200,7 @@ return true; } - -bool -CmscPlayer::decode_octet (u8 * output) +bool CmscPlayer::decode_octet(u8 * output) { msc_block blk; // compressed data block @@ -225,7 +210,7 @@ blk = msc_data [block_num]; while (1) { u8 octet; // decoded octet - u8 len_corr = 0; // length correction + u8 len_corr; // length correction // advance to next block if necessary if (block_pos >= blk.mb_length && dec_len == 0) { @@ -288,7 +273,12 @@ // prefix copy mode case 255: + if((int)raw_pos >= dec_dist) octet = raw_data [raw_pos - dec_dist]; + else { + AdPlug_LogWrite("error! read before raw_data buffer.\n"); + octet = 0; + } dec_len--; if (dec_len == 0) { diff -r 01d2aa561b53 -r 6ad7eb96dd26 Plugins/Input/adplug/core/protrack.cpp --- a/Plugins/Input/adplug/core/protrack.cpp Fri Jun 16 20:55:52 2006 -0700 +++ b/Plugins/Input/adplug/core/protrack.cpp Sat Jun 17 16:17:51 2006 -0700 @@ -1,6 +1,6 @@ /* * Adplug - Replayer for many OPL2/OPL3 audio file formats. - * Copyright (C) 1999 - 2002 Simon Peter, , et al. + * Copyright (C) 1999 - 2006 Simon Peter, , et al. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,16 +17,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * protrack.cpp - Generic Protracker Player - * Copyright (C) 2000 - 2002 Simon Peter * * NOTES: * This is a generic Protracker-based formats player. It offers all Protracker * features, plus a good set of extensions to be compatible to other Protracker * derivatives. It is derived from the original SA2 player by me. If you got a * Protracker-like format, this is most certainly the player you want to use. - * - * USAGE: - * Read the file 'Protracker.txt' in the 'doc' subdirectory. */ #include "protrack.h" @@ -37,11 +33,11 @@ // SA2 compatible adlib note table const unsigned short CmodPlayer::sa2_notetable[12] = -{340,363,385,408,432,458,485,514,544,577,611,647}; + {340,363,385,408,432,458,485,514,544,577,611,647}; // SA2 compatible vibrato rate table const unsigned char CmodPlayer::vibratotab[32] = -{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}; + {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}; /*** public methods *************************************/ @@ -337,6 +333,7 @@ if(channel[chan].vol2 > 63) channel[chan].vol2 = 63; } + setvolume(chan); break; case 18: // AMD set speed diff -r 01d2aa561b53 -r 6ad7eb96dd26 Plugins/Input/adplug/core/rix.cpp --- a/Plugins/Input/adplug/core/rix.cpp Fri Jun 16 20:55:52 2006 -0700 +++ b/Plugins/Input/adplug/core/rix.cpp Sat Jun 17 16:17:51 2006 -0700 @@ -21,7 +21,7 @@ */ #include "rix.h" -#include +#include "debug.h" const unsigned char CrixPlayer::adflag[] = {0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1}; const unsigned char CrixPlayer::reg_data[] = {0,1,2,3,4,5,8,9,10,11,12,13,16,17,18,19,20,21}; @@ -53,12 +53,62 @@ } CrixPlayer::CrixPlayer(Copl *newopl) - : CPlayer(newopl),I(0),T(0),mus_block(0),ins_block(0),rhythm(0),mutex(0), - music_on(0),pause_flag(0),band(0),band_low(0),e0_reg_flag(0),bd_modify(0), - sustain(0),dro_end(0), mstotal(0), opl3_mode(0) + : CPlayer(newopl), buf_addr(0) +{ +} + +CrixPlayer::~CrixPlayer() +{ + if(buf_addr) + delete [] buf_addr; +} + +bool CrixPlayer::load(const std::string &filename, const CFileProvider &fp) { - memset(dro, 0, 128000); - memset(buf_addr, 0, 327680); + binistream *f = fp.open(filename); if(!f) return false; + unsigned long i=0; + + if(f->readInt(2)!=0x55aa) { fp.close(f);return false; } + buf_addr = new unsigned char [fp.filesize(f) + 1]; + buf_addr[i++]=0xaa;buf_addr[i++]=0x55; + while(!f->eof()) + buf_addr[i++]=f->readInt(1); + length=i; + fp.close(f); + + rewind(0); + return true; +} + +bool CrixPlayer::update() +{ + if (delay>100) { + delay-=100; + return true; + } else delay=1; + + int_08h_entry(); + return !dro_end; +} + +void CrixPlayer::rewind(int subsong) +{ + I = 0; T = 0; + mus_block = 0; + ins_block = 0; + rhythm = 0; + mutex = 0; + music_on = 0; + pause_flag = 0; + band = 0; + band_low = 0; + e0_reg_flag = 0; + bd_modify = 0; + delay = 1; + sustain = 0; + dro_end = 0; + pos = index = 0; + memset(buffer, 0, sizeof(unsigned short) * 300); memset(a0b0_data2, 0, sizeof(unsigned short) * 11); memset(a0b0_data3, 0, 18); @@ -68,93 +118,17 @@ memset(insbuf, 0, 28 * sizeof(unsigned short)); memset(displace, 0, 11 * sizeof(unsigned short)); memset(reg_bufs, 0, 18 * sizeof(ADDT)); - memset(for40reg, 0, 18); - if(opl->gettype() == Copl::TYPE_OPL2) - opl3_mode = 0; - else - opl3_mode = 1; -}; - -bool CrixPlayer::load(const std::string &filename, const CFileProvider &fp) -{ - binistream *f = fp.open(filename); if(!f) return false; - unsigned long i=0; - - if(f->readInt(2)!=0x55aa) {fp.close(f);return false; } - buf_addr[i++]=0xaa;buf_addr[i++]=0x55; - while(!f->eof()) - buf_addr[i++]=f->readInt(1); - length=i; - fp.close(f); + opl->init(); + opl->write(1,32); // go to OPL2 mode set_new_int(); data_initial(); - while(!dro_end) - int_08h_entry(); - - length=T; - mode = (OplMode)1; // Type of opl data this can contain - - // binofstream *g=new binofstream(filename+string(".dro")); - // g->writeString("DBRAWOPL",8); - // g->writeInt(mstotal,4); - // g->writeInt(length+1,4); - // g->writeInt(1,1); - // for(int t=0;twriteInt(dro[t],1); - // g->close(); - // delete g; - - rewind(0); - return true; -} - -bool CrixPlayer::update() -{ - if (delay>500) { - delay-=500; - return true; - } else delay=1; - while (pos < length) - { - unsigned char cmd = dro[pos++]; - switch(cmd) { - case 0: - delay = 1 + dro[pos++]; - return true; - case 1: - delay = 1 + dro[pos] + (dro[pos+1]<<8); - pos+=2; - return true; - case 2: - index = 0; - opl->setchip(0); - break; - case 3: - index = 1; - opl->setchip(1); - break; - default: - if(index == 0 || opl3_mode) - opl->write(cmd,dro[pos++]); - break; - } - } - return posinit(); - opl->write(1,32); // go to OPL2 mode } float CrixPlayer::getrefresh() { - if (delay > 500) return 1000 / 500; - else return 1000 / (double)delay; + if (delay > 100) return 1000 / 100; + else return 1000.0 / (double)delay; } /*------------------Implemention----------------------------*/ @@ -239,7 +213,9 @@ /*----------------------------------------------------------*/ inline void CrixPlayer::ad_bop(unsigned short reg,unsigned short value) { - dro[T++]=reg;dro[T++]=value; + if(reg == 2 || reg == 3) + AdPlug_LogWrite("switch OPL2/3 mode!\n"); + opl->write(reg & 0xff, value & 0xff); } /*------------------------------------------------------*/ inline unsigned short CrixPlayer::ad_test() /* Test the SoundCard */ @@ -254,7 +230,7 @@ } /*--------------------------------------------------------------*/ inline void CrixPlayer::int_08h_entry() -{ + { unsigned short band_sus = 1; while(band_sus) { @@ -263,11 +239,7 @@ mutex++; band_sus = rix_proc(); if(band_sus) sustain += band_sus; - mstotal+=sustain; - dro[T++]=(sustain>=0x100?1:0); - dro[T++]=sustain&0xff; - if(sustain>=0x100) - dro[T++]=(sustain>>8)&0xff; + delay=sustain; mutex--; if(band_sus == 0) { @@ -277,11 +249,11 @@ } else { - if(band_sus) sustain -= 1; /* aging */ + if(band_sus) sustain -= 9; /* aging */ break; } } -} + } /*--------------------------------------------------------------*/ inline unsigned short CrixPlayer::rix_proc() { @@ -312,7 +284,13 @@ /*--------------------------------------------------------------*/ inline void CrixPlayer::rix_get_ins() { - memcpy(insbuf,(&buf_addr[ins_block])+(band_low<<6),56); + int i; + unsigned char *baddr = (&buf_addr[ins_block])+(band_low<<6); + + for(i = 0; i < 28; i++) + insbuf[i] = (baddr[i * 2 + 1] << 8) + baddr[i * 2]; + +// memcpy(insbuf,(&buf_addr[ins_block])+(band_low<<6),56); } /*--------------------------------------------------------------*/ inline void CrixPlayer::rix_90_pro(unsigned short ctrl_l) diff -r 01d2aa561b53 -r 6ad7eb96dd26 Plugins/Input/adplug/core/rix.h --- a/Plugins/Input/adplug/core/rix.h Fri Jun 16 20:55:52 2006 -0700 +++ b/Plugins/Input/adplug/core/rix.h Sat Jun 17 16:17:51 2006 -0700 @@ -28,7 +28,7 @@ static CPlayer *factory(Copl *newopl); CrixPlayer(Copl *newopl); - ~CrixPlayer() {}; + ~CrixPlayer(); bool load(const std::string &filename, const CFileProvider &fp); bool update(); @@ -57,7 +57,7 @@ unsigned char bd_modify; int sustain; int dro_end; - unsigned char buf_addr[327680]; /* rix files' buffer */ + unsigned char *buf_addr; /* rix files' buffer */ unsigned short buffer[300]; unsigned short a0b0_data2[11]; unsigned char a0b0_data3[18]; @@ -68,12 +68,8 @@ unsigned short displace[11]; ADDT reg_bufs[18]; unsigned long pos,length; - unsigned long msdone,mstotal; + unsigned char index; unsigned short delay; - unsigned char index, opl3_mode; - enum OplMode { - ModeOPL2,ModeOPL3,ModeDUALOPL2 - } mode; static const unsigned char adflag[18]; static const unsigned char reg_data[18]; diff -r 01d2aa561b53 -r 6ad7eb96dd26 Plugins/Input/adplug/core/u6m.cpp --- a/Plugins/Input/adplug/core/u6m.cpp Fri Jun 16 20:55:52 2006 -0700 +++ b/Plugins/Input/adplug/core/u6m.cpp Sat Jun 17 16:17:51 2006 -0700 @@ -1,6 +1,6 @@ /* * Adplug - Replayer for many OPL2/OPL3 audio file formats. - * Copyright (C) 1999 - 2003 Simon Peter, , et al. + * Copyright (C) 1999 - 2006 Simon Peter, , et al. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -60,7 +60,7 @@ // load section song_data = new unsigned char[decompressed_filesize]; - unsigned char* compressed_song_data = new unsigned char[filesize-4]; + unsigned char* compressed_song_data = new unsigned char[filesize-3]; f->seek(4); f->readString((char *)compressed_song_data, filesize - 4); @@ -891,6 +891,11 @@ } +Cu6mPlayer::MyDict::~MyDict() +{ + delete [] dictionary; +} + // re-initializes the dictionary void Cu6mPlayer::MyDict::reset() { diff -r 01d2aa561b53 -r 6ad7eb96dd26 Plugins/Input/adplug/core/u6m.h --- a/Plugins/Input/adplug/core/u6m.h Fri Jun 16 20:55:52 2006 -0700 +++ b/Plugins/Input/adplug/core/u6m.h Sat Jun 17 16:17:51 2006 -0700 @@ -1,6 +1,6 @@ /* * Adplug - Replayer for many OPL2/OPL3 audio file formats. - * Copyright (C) 1999 - 2003 Simon Peter, , et al. + * Copyright (C) 1999 - 2006 Simon Peter, , et al. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -52,7 +52,6 @@ return std::string("Ultima 6 Music"); }; - protected: struct byte_pair @@ -93,6 +92,7 @@ public: MyDict(); // use dictionary size of 4096 MyDict(int); // let the caller specify a dictionary size + ~MyDict(); void reset(); // re-initializes the dictionary void add(unsigned char, int); unsigned char get_root(int);