diff -Nu sky/cd_intro.cpp sky/cd_intro.cpp --- sky/cd_intro.cpp 2003-04-21 20:00:12.000000000 +0200 +++ sky/cd_intro.cpp 2003-04-21 20:00:33.000000000 +0200 @@ -822,7 +822,7 @@ cd2_seq_data_1 = _skyDisk->loadFile(cd_104, NULL); WAIT_SEQUENCE; //103 - //fn_start_music(2); + _music->startMusic(2); fnFadeDown(0); COPY_SCREEN; showScreen(); diff -Nu sky/intro.cpp sky/intro.cpp --- sky/intro.cpp 2003-04-21 20:00:12.000000000 +0200 +++ sky/intro.cpp 2003-04-21 20:00:33.000000000 +0200 @@ -237,12 +237,12 @@ _workScreen = _skyDisk->loadFile(60112, NULL); //while virgin screen is up, load rev screen _tempPal = _skyDisk->loadFile(60113, NULL); - //loadSectionMusic(0); + _music->loadSectionMusic(0); delay(3000); //keep virgin screen up for 3 seconds //if (!isCDVersion(_gameVersion)) - // fn_start_music(); + // _music->startMusic(1); delay(3000); //and another 3 seconds. fnFadeDown(0); //remove virgin screen diff -Nu sky/skychannel.cpp sky/skychannel.cpp --- sky/skychannel.cpp 1970-01-01 01:00:00.000000000 +0100 +++ sky/skychannel.cpp 2003-04-21 20:00:33.000000000 +0200 @@ -0,0 +1,288 @@ +#include "skychannel.h" +#include "sound/fmopl.h" + +SkyChannel::SkyChannel(uint8 *pMusicData, uint16 startOfData, FM_OPL *pOpl) +{ + _musicData = pMusicData; + _opl = pOpl; + _channelData.startOfData = startOfData; + _channelData.eventDataPtr = startOfData; + _channelData.channelActive = 1; + _channelData.freqDataSize = 2; + _channelData.tremoVibro = 0; + _channelData.assignedInstrument = 0xFF; + _channelData.channelVolume = 0x7F; + _channelData.nextEventTime = getNextEventTime(); + + _channelData.adlibChannelNumber = _channelData.lastCommand = _channelData.note = + _channelData.adlibReg1 = _channelData.adlibReg2 = _channelData.freqOffset = 0; + _channelData.frequency = 0; + _channelData.instrumentData = NULL; + + uint16 instrumentDataLoc = (_musicData[0x1206] << 8) | _musicData[0x1205]; + _instrumentMap = _musicData+instrumentDataLoc; + _instruments = (InstrumentStruct*)(_instrumentMap+0x80); + + _frequenceTable = (uint16*)(_musicData+0x7FE); + _registerTable = _musicData+0xDFE; + _opOutputTable = _musicData+0xE10; + _adlibRegMirror = _musicData+0xF5F; + _musicVolume = 0x100; +} + +void SkyChannel::updateVolume(uint16 pVolume) { + + _musicVolume = pVolume; +} + +/* This class uses the same area for the register mirror as the original + asm driver did (_musicData[0xF5F..0x105E]), so the cache is indeed shared + by all instances of the class. +*/ +void SkyChannel::setRegister(uint8 regNum, uint8 value) { + + if (_adlibRegMirror[regNum] != value) { + OPLWriteReg(_opl,regNum,value); + _adlibRegMirror[regNum] = value; + } +} + +void SkyChannel::stopNote(void) { + + if (_channelData.note & 0x20) { + _channelData.note &= ~0x20; + setRegister(0xB0 | _channelData.adlibChannelNumber, _channelData.note); + } +} + +int32 SkyChannel::getNextEventTime(void) +{ + int32 retV = 0; + uint8 cnt, lVal; + for (cnt = 0; cnt < 4; cnt++) { + lVal = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; + retV = (retV << 7) | (lVal & 0x7F); + if (!(lVal & 0x80)) break; + } + if (lVal & 0x80) { // should never happen + return -1; + } else return retV; + +} + +uint8 SkyChannel::process(uint16 aktTime) { + + if (!_channelData.channelActive) { + return 0; + } + + uint8 returnVal = 0; + + _channelData.nextEventTime -= aktTime; + uint8 opcode; + while ((_channelData.nextEventTime < 0) && (_channelData.channelActive)) { + opcode = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; + if (opcode&0x80) { + if (opcode == 0xFF) { + // dummy opcode + } else if (opcode > 0x9D) { + + } else if (opcode >= 0x90) { + switch (opcode&0xF) { + case 0: com90_caseNoteOff(); break; + case 1: com90_stopChannel(); break; + case 2: com90_setupInstrument(); break; + case 3: + returnVal = com90_updateTempo(); + break; + case 5: com90_getFreqOffset(); break; + case 6: com90_getChannelVolume(); break; + case 7: com90_getTremoVibro(); break; + case 8: com90_rewindMusic(); break; + case 9: com90_keyOff(); break; + case 12: com90_setStartOfData(); break; + case 4: //com90_dummy(); + case 10: //com90_error(); + case 11: //com90_doLodsb(); + case 13: //com90_do_two_Lodsb(); + error("SkyChannel: dummy music routine 0x%02X was called\n",opcode); + _channelData.channelActive = 0; + break; + default: + // these opcodes aren't implemented in original music driver + error("SkyChannel: Not existant routine 0x%02X was called\n",opcode); + _channelData.channelActive = 0; + break; + } + } else { + // new adlib channel assignment + _channelData.adlibChannelNumber = opcode&0xF; + _channelData.adlibReg1 = _registerTable[(opcode&0xF)<<1]; + _channelData.adlibReg2 = _registerTable[((opcode&0xF)<<1)|1]; + } + } else { + _channelData.lastCommand = opcode; + stopNote(); + setupInstrument(opcode); + + opcode = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; + setupChannelVolume(opcode); + } + if (_channelData.channelActive) + _channelData.nextEventTime += getNextEventTime(); + } + return returnVal; +} + +void SkyChannel::setupInstrument(uint8 opcode) { + + uint16 nextNote; + if (_channelData.tremoVibro) { + uint8 newInstrument = _instrumentMap[opcode]; + if (newInstrument != _channelData.assignedInstrument) { + _channelData.assignedInstrument = newInstrument; + _channelData.instrumentData = _instruments + newInstrument; + adlibSetupInstrument(); + } + _channelData.lastCommand = _channelData.instrumentData->bindedEffect; + nextNote = getNextNote(_channelData.lastCommand); + } else { + nextNote = getNextNote(opcode - 0x18 + _channelData.instrumentData->bindedEffect); + } + _channelData.frequency = nextNote; + setRegister(0xA0 | _channelData.adlibChannelNumber, (uint8)nextNote); + setRegister(0xB0 | _channelData.adlibChannelNumber, (uint8)((nextNote >> 8) | 0x20)); + _channelData.note = (uint8)((nextNote >> 8) | 0x20); +} + +void SkyChannel::setupChannelVolume(uint8 volume) { + + uint8 resultOp; + uint32 resVol = ((volume + 1) * (_channelData.instrumentData->totOutLev_Op2 + 1)) << 1; + resVol &= 0xFFFF; + resVol *= (_channelData.channelVolume+1)<<1; + resVol >>= 8; + resVol *= _musicVolume; + resVol >>= 16; + resultOp = ((_channelData.instrumentData->scalingLevel << 6) & 0xC0) | _opOutputTable[resVol]; + setRegister(0x40 | _channelData.adlibReg2, resultOp); + if (_channelData.instrumentData->feedBack & 1) { + resVol = ((volume + 1) * (_channelData.instrumentData->totOutLev_Op1 + 1)) << 1; + resVol &= 0xFFFF; + resVol *= (_channelData.channelVolume + 1)<<1; + resVol >>= 8; + resVol *= (_musicVolume & 0xFF); + resVol >>= 16; + } else resVol = _channelData.instrumentData->totOutLev_Op1; + resultOp = ((_channelData.instrumentData->scalingLevel << 2) & 0xC0) | _opOutputTable[resVol]; + setRegister(0x40 | _channelData.adlibReg1, resultOp); +} + +void SkyChannel::adlibSetupInstrument(void) { + + setRegister(0x60 | _channelData.adlibReg1, _channelData.instrumentData->ad_Op1); + setRegister(0x60 | _channelData.adlibReg2, _channelData.instrumentData->ad_Op2); + setRegister(0x80 | _channelData.adlibReg1, _channelData.instrumentData->sr_Op1); + setRegister(0x80 | _channelData.adlibReg2, _channelData.instrumentData->sr_Op2); + setRegister(0xE0 | _channelData.adlibReg1, _channelData.instrumentData->waveSelect_Op1); + setRegister(0xE0 | _channelData.adlibReg2, _channelData.instrumentData->waveSelect_Op2); + setRegister(0xC0 | _channelData.adlibChannelNumber, _channelData.instrumentData->feedBack); + setRegister(0x20 | _channelData.adlibReg1, _channelData.instrumentData->ampMod_Op1); + setRegister(0x20 | _channelData.adlibReg2, _channelData.instrumentData->ampMod_Op2); +} + +#ifdef SCUMM_BIG_ENDIAN +#define ENDIAN16(x) ((x>>8)|((x&0xFF)<<8)) +#else +#define ENDIAN16(x) (x) +#endif + +uint16 SkyChannel::getNextNote(uint8 param) { + + int16 freqIndex = ((int16)_channelData.freqOffset)-0x40; + if (freqIndex >= 0x3F) freqIndex++; + freqIndex *= _channelData.freqDataSize; + freqIndex += param<<6; + uint16 freqData = ENDIAN16(_frequenceTable[freqIndex%0x300]); + if ((freqIndex%0x300 >= 0x1C0) || (freqIndex/0x300 > 0)) { + return (((freqIndex/0x300)-1)<<10)+(freqData&0x7FF); + } else { + // looks like a bug. dunno why. It's what the ASM code says. + return (uint16)(((int16)freqData) >> 1); + } +} + +//- command 90h routines + +void SkyChannel::com90_caseNoteOff(void) { + + if (_musicData[_channelData.eventDataPtr] == _channelData.lastCommand) + stopNote(); + _channelData.eventDataPtr++; +} + +void SkyChannel::com90_stopChannel(void) { + + stopNote(); + _channelData.channelActive = 0; +} + +void SkyChannel::com90_setupInstrument(void) { + + _channelData.channelVolume = 0x7F; + _channelData.freqOffset = 0x40; + _channelData.assignedInstrument = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; + _channelData.instrumentData = _instruments + _channelData.assignedInstrument; + adlibSetupInstrument(); +} + +uint8 SkyChannel::com90_updateTempo(void) { + + uint8 retV = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; + return retV; +} + +void SkyChannel::com90_getFreqOffset(void) { + + _channelData.freqOffset = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; + if (_channelData.note) { + uint16 nextNote = getNextNote( + _channelData.lastCommand - 0x18 + _channelData.instrumentData->bindedEffect); + setRegister(0xA0|_channelData.adlibChannelNumber, (uint8)nextNote); + setRegister(0xB0|_channelData.adlibChannelNumber, (uint8)((nextNote>>8)|0x20)); + _channelData.note = (uint8)(nextNote>>8)|0x20; + } +} + +void SkyChannel::com90_getChannelVolume(void) { + + _channelData.channelVolume = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; +} + +void SkyChannel::com90_getTremoVibro(void) { + + _channelData.tremoVibro = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; +} + +void SkyChannel::com90_rewindMusic(void) { + + _channelData.eventDataPtr = _channelData.startOfData; +} + +void SkyChannel::com90_keyOff(void) { + + stopNote(); +} + +void SkyChannel::com90_setStartOfData(void) { + + _channelData.startOfData = _channelData.eventDataPtr; +} diff -Nu sky/skychannel.h sky/skychannel.h --- sky/skychannel.h 1970-01-01 01:00:00.000000000 +0100 +++ sky/skychannel.h 2003-04-21 20:00:33.000000000 +0200 @@ -0,0 +1,85 @@ +/* + * C++ implementation of "Tony William's Sound Images Generation 2"-drivers + * by Robert "LavosSpawn" Göffringmann + */ + +#ifndef __SkyChannel__ +#define __SkyChannel__ + +#include "stdafx.h" +#include "sound/fmopl.h" +#include "common/engine.h" + +typedef struct { + uint8 ad_Op1, ad_Op2; + uint8 sr_Op1, sr_Op2; + uint8 ampMod_Op1, ampMod_Op2; + uint8 waveSelect_Op1, waveSelect_Op2; + uint8 bindedEffect; + uint8 feedBack; + uint8 totOutLev_Op1, totOutLev_Op2; + uint8 scalingLevel; + uint8 pad1, pad2, pad3; +} InstrumentStruct; + +typedef struct { + uint16 eventDataPtr; + int32 nextEventTime; + uint16 startOfData; + uint8 adlibChannelNumber; + uint8 lastCommand; + uint8 channelActive; + uint8 note; + uint8 adlibReg1, adlibReg2; + InstrumentStruct *instrumentData; + uint8 assignedInstrument; + uint8 channelVolume; + uint8 padding; // field_12 / not used by original driver + uint8 tremoVibro; + uint8 freqDataSize; + uint8 freqOffset; + uint16 frequency; +} ChannelType; + +class SkyChannel { +public: + SkyChannel(uint8 *pMusicData, uint16 startOfData, FM_OPL *pOpl); + void stopNote(void); + uint8 process(uint16 aktTime); + void updateVolume(uint16 pVolume); +private: + uint8 *_musicData; + uint16 _musicVolume; + FM_OPL *_opl; + ChannelType _channelData; + //- + InstrumentStruct *_instruments; + uint16 *_frequenceTable; + uint8 *_instrumentMap; + uint8 *_registerTable, *_opOutputTable; + uint8 *_adlibRegMirror; + //- normal subs + void setRegister(uint8 regNum, uint8 value); + int32 getNextEventTime(void); + uint16 getNextNote(uint8 param); + void adlibSetupInstrument(void); + void setupInstrument(uint8 opcode); + void setupChannelVolume(uint8 volume); + //- Streamfunctions from Command90hTable + void com90_caseNoteOff(void); // 0 + void com90_stopChannel(void); // 1 + void com90_setupInstrument(void); // 2 + uint8 com90_updateTempo(void); // 3 + //void com90_dummy(void); // 4 + void com90_getFreqOffset(void); // 5 + void com90_getChannelVolume(void); // 6 + void com90_getTremoVibro(void); // 7 + void com90_rewindMusic(void); // 8 + void com90_keyOff(void); // 9 + //void com90_error(void); // 10 + //void com90_doLodsb(void); // 11 + void com90_setStartOfData(void); // 12 + //void com90_do_two_Lodsb(void); // 13 +}; + +#endif //__SkyChannel__ diff -Nu sky/sky.cpp sky/sky.cpp --- sky/sky.cpp 2003-04-21 20:00:12.000000000 +0200 +++ sky/sky.cpp 2003-04-21 20:10:52.000000000 +0200 @@ -83,6 +83,7 @@ _dump_file = stdout; initialise(); + if (!isDemo(_gameVersion) || isCDVersion(_gameVersion)) intro(); @@ -95,11 +96,10 @@ //initialise_memory(); initTimer(); - //init_music(); _sound = new SkySound(_mixer); - _skyDisk = new SkyDisk(_gameDataPath); + _music = new SkyMusic(_mixer,_skyDisk); _gameVersion = _skyDisk->determineGameVersion(); _skyText = getSkyText(); diff -Nu sky/sky.h sky/sky.h --- sky/sky.h 2003-04-21 20:00:12.000000000 +0200 +++ sky/sky.h 2003-04-21 20:05:28.000000000 +0200 @@ -30,6 +30,7 @@ #include "sky/sound.h" #include "sky/text.h" #include "sky/disk.h" +#include "sky/skymusic.h" class SkyState : public Engine { void errorString(const char *buf_input, char *buf_output); @@ -67,6 +68,7 @@ SkySound *_sound; SkyDisk *_skyDisk; + SkyMusic *_music; byte *_workScreen; byte *_backScreen; diff -Nu sky/skymusic.cpp sky/skymusic.cpp --- sky/skymusic.cpp 1970-01-01 01:00:00.000000000 +0100 +++ sky/skymusic.cpp 2003-04-21 20:02:21.000000000 +0200 @@ -0,0 +1,219 @@ +#include "skymusic.h" + +void SkyMusic::passMixerFunc(void *param, int16 *buf, uint len) { + + ((SkyMusic*)param)->premixerCall(buf, len); +} + +void SkyMusic::premixerCall(int16 *buf, uint len) { + + if (_musicData == NULL) { + // no music loaded + memset(buf, 0, len * sizeof(int16)); + return; + } else if ((_currentMusic == 0) || (_numberOfChannels == 0)) { + // music loaded but not played as of yet + memset(buf, 0, len * sizeof(int16)); + // poll anyways as pollMusic() can activate the music + pollMusic(); + _nextMusicPoll = _sampleRate/50; + return; + } + uint32 render; + while (len) { + render = (len > _nextMusicPoll) ? (_nextMusicPoll) : (len); + len -= render; + _nextMusicPoll -= render; + YM3812UpdateOne(_opl, buf, render); + buf += render; + if (_nextMusicPoll == 0) { + pollMusic(); + _nextMusicPoll = _sampleRate/50; + } + } +} + +SkyMusic::SkyMusic(SoundMixer *mixer, SkyDisk *pSkyDisk) { + + _musicData = NULL; + _allowedCommands = 0; + _mixer = mixer; + _sampleRate = g_system->property(OSystem::PROP_GET_SAMPLE_RATE, 0); + int env_bits = g_system->property(OSystem::PROP_GET_FMOPL_ENV_BITS, NULL); + int eg_ent = g_system->property(OSystem::PROP_GET_FMOPL_EG_ENT, NULL); + OPLBuildTables((env_bits ? env_bits : FMOPL_ENV_BITS_HQ), (eg_ent ? eg_ent : FMOPL_EG_ENT_HQ)); + _opl = OPLCreate(OPL_TYPE_YM3812, 3579545, _sampleRate); + _mixer->setupPremix(this, passMixerFunc); + _skyDisk = pSkyDisk; +} + +SkyMusic::~SkyMusic(void) +{ + if (_currentMusic) stopMusic(); + _mixer->setupPremix(NULL, NULL); + if (_musicData) free(_musicData); + OPLDestroy(_opl); +} + +void SkyMusic::loadSectionMusic(uint8 pSection) +{ + if (_currentMusic) stopMusic(); + if (_musicData) free(_musicData); + _musicData = _skyDisk->loadFile(MUSIC_BASE_FILE + FILES_PER_SECTION*pSection,NULL); + _allowedCommands = 0; + _musicTempo0 = 0x78; // init constants taken from idb file, area ~0x1060 + _musicTempo1 = 0xC0; + _musicVolume = 0x100; + _numberOfChannels = _currentMusic = 0; + _musicDataLoc = (_musicData[0x1202]<<8)|_musicData[0x1201]; + _initSequence = _musicData+0xE91; + _onNextPoll.doReInit = false; + _onNextPoll.doStopMusic = false; + _onNextPoll.musicToProcess = 0; + _tempo = _aktTime = 0x10001; + _nextMusicPoll = 0; + startAdlibDriver(); +} + +/*void SkyMusic::loadData(uint8 *pMusicData) +{ + if (_currentMusic) stopMusic(); + if (_musicData) free(_musicData); + _musicData = pMusicData; + _allowedCommands = 0; + _musicTempo0 = 0x78; // init constants taken from idb file, area ~0x1060 + _musicTempo1 = 0xC0; + _musicVolume = 0x100; + _numberOfChannels = _currentMusic = 0; + _musicDataLoc = (_musicData[0x1202]<<8)|_musicData[0x1201]; + _initSequence = _musicData+0xE91; + _onNextPoll.doReInit = false; + _onNextPoll.doStopMusic = false; + _onNextPoll.musicToProcess = 0; + _tempo = _aktTime = 0x10001; + _nextMusicPoll = 0; +}*/ + +void SkyMusic::musicCommand(uint16 command) +{ + if (_musicData == NULL) { + debug(1,"Got music command but driver is not yet loaded.\n"); + return ; + } + if ((command >> 8) > _allowedCommands) { + debug(1,"got musicCommand %d while expecting <= %d\n", command >> 8, _allowedCommands); + return ; + } + switch(command >> 8) { + case 0: + //startAdlibDriver(); break; + debug(1,"SkyMusic: got call to startAdlibDriver(). Not necessary in this implementation.\n"); + break; + case 1: + //StopDriver(command&0xFF); break; + debug(1,"SkyMusic: got call to stopDriver(). Not necessary in this implementation.\n"); + break; + case 2: + debug(1,"SkyMusic: got call to SetTempo(). Tempo is fixed in this implementation.\n"); + break; + case 3: + debug(1,"SkyMusic: ignored direct call to driverPoll().\n"); + break; + case 4: + startMusic(command&0xFF); + break; + case 6: + reinitFM(); + break; + case 7: + stopMusic(); + break; + case 13: + setFMVolume(command&0xFF); + break; + default: + debug(1,"musicCommand %d ignored.\n",command>>8); + } +} + +void SkyMusic::setFMVolume(uint16 param) +{ + _musicVolume = param; + for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) + _channels[cnt]->updateVolume(_musicVolume); +} + +void SkyMusic::startAdlibDriver(void) +{ + uint16 cnt = 0; + while (_initSequence[cnt] || _initSequence[cnt+1]) { + OPLWriteReg(_opl, _initSequence[cnt], _initSequence[cnt+1]); + cnt += 2; + } + _allowedCommands = 0xD; +} + +void SkyMusic::stopMusic(void) +{ + for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) { + _channels[cnt]->stopNote(); + delete _channels[cnt]; + } + _numberOfChannels = 0; +} + +void SkyMusic::updateTempo(void) +{ + uint16 tempoMul = _musicTempo0*_musicTempo1; + uint16 divisor = 0x4446390/23864; + _tempo = (tempoMul / divisor)<<16; + _tempo |= (((tempoMul%divisor)<<16) | (tempoMul/divisor)) / divisor; +} + +void SkyMusic::loadNewMusic(void) +{ + uint16 musicPos; + if (_onNextPoll.musicToProcess > _musicData[_musicDataLoc]) { + error("Music %d requested but doesn't exist in file.\n", _onNextPoll.musicToProcess); + return; + } + if (_currentMusic != 0) stopMusic(); + + _currentMusic = _onNextPoll.musicToProcess; + _onNextPoll.musicToProcess = 0; + + if (_currentMusic != 0) { + musicPos = (_musicData[_musicDataLoc+2]<<8) | _musicData[_musicDataLoc+1]; + musicPos += _musicDataLoc+((_currentMusic-1)<<1); + musicPos = ((_musicData[musicPos+1]<<8) | _musicData[musicPos]) + _musicDataLoc; + + _musicTempo1 = _musicData[musicPos+1]; + _musicTempo0 = _musicData[musicPos]; + _numberOfChannels = _musicData[musicPos+2]; + musicPos += 3; + + for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) { + uint16 chDataStart = ((_musicData[musicPos+1]<<8) | _musicData[musicPos]) + _musicDataLoc; + _channels[cnt] = new SkyChannel(_musicData, chDataStart, _opl); + musicPos += 2; + } + updateTempo(); + } +} + +void SkyMusic::pollMusic(void) +{ + uint8 newTempo; + if (_onNextPoll.doReInit) startAdlibDriver(); + if (_onNextPoll.doStopMusic) stopMusic(); + if (_onNextPoll.musicToProcess) loadNewMusic(); + _aktTime += _tempo; + for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) { + newTempo = _channels[cnt]->process((uint16)(_aktTime >> 16)); + if (newTempo) { + _musicTempo1 = newTempo; + updateTempo(); + } + } + _aktTime &= 0xFFFF; +} diff -Nu sky/skymusic.h sky/skymusic.h --- sky/skymusic.h 1970-01-01 01:00:00.000000000 +0100 +++ sky/skymusic.h 2003-04-21 20:02:29.000000000 +0200 @@ -0,0 +1,66 @@ +/* + * C++ implementation of "Tony William's Sound Images Generation 2"-drivers + * by Robert "LavosSpawn" Göffringmann + */ + +#ifndef __SkyMusicDriver__ +#define __SkyMusicDriver__ + +#include "stdafx.h" +#include "sound/fmopl.h" +#include "sound/mixer.h" +#include "common/engine.h" +#include "skychannel.h" +#include "disk.h" + +#define MUSIC_BASE_FILE 60202 // usually 60200 ( + 0 for Roland and +2 for Adlib) +#define FILES_PER_SECTION 4 + +typedef struct { + bool doReInit, doStopMusic; + uint8 musicToProcess; +} Actions; + +class SkyMusic { +public: + SkyMusic(SoundMixer *mixer, SkyDisk *pSkyDisk); + ~SkyMusic(void); + //void loadData(uint8 *pMusicData); + void loadSectionMusic(uint8 pSection); + void musicCommand(uint16 command); + void startMusic(uint16 param) { _onNextPoll.musicToProcess = param & 0xF; }; // 4 + +private: + SoundMixer *_mixer; + SkyDisk *_skyDisk; + FM_OPL *_opl; + uint8 *_musicData; + uint8 *_initSequence; + uint8 _allowedCommands; + uint16 _musicDataLoc; + SkyChannel *_channels[10]; + + uint16 _musicVolume, _numberOfChannels; + uint8 _currentMusic; + uint8 _musicTempo0; // can be changed by music stream + uint8 _musicTempo1; // given once per music + uint32 _tempo; // calculated from musicTempo0 and musicTempo1 + uint32 _aktTime; + uint32 _sampleRate, _nextMusicPoll; + Actions _onNextPoll; + + void premixerCall(int16 *buf, uint len); + static void passMixerFunc(void *param, int16 *buf, uint len); + void updateTempo(void); + void loadNewMusic(void); + //- functions from CommandTable @0x90 (the main interface) + void startAdlibDriver(void); // 0 + void StopDriver(void); // 1 + void setTempo(uint16 newTempo); // 2 + void pollMusic(); // 3 + void reinitFM(void) { _onNextPoll.doReInit = true; }; // 6 + void stopMusic(); // 7 + void setFMVolume(uint16 param); // 13 +}; + +#endif //__SkyMusicDriver__