diff options
author | Max Horn | 2006-02-11 22:45:04 +0000 |
---|---|---|
committer | Max Horn | 2006-02-11 22:45:04 +0000 |
commit | 26ee630756ebdd7c96bccede0881a8c8b98e8f2b (patch) | |
tree | 26e378d5cf990a2b81c2c96e9e683a7f333b62e8 /engines/sky/music | |
parent | 2a9a0d4211b1ea5723f1409d91cb95de8984429e (diff) | |
download | scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.tar.gz scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.tar.bz2 scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.zip |
Moved engines to the new engines/ directory
svn-id: r20582
Diffstat (limited to 'engines/sky/music')
-rw-r--r-- | engines/sky/music/adlibchannel.cpp | 344 | ||||
-rw-r--r-- | engines/sky/music/adlibchannel.h | 106 | ||||
-rw-r--r-- | engines/sky/music/adlibmusic.cpp | 121 | ||||
-rw-r--r-- | engines/sky/music/adlibmusic.h | 64 | ||||
-rw-r--r-- | engines/sky/music/gmchannel.cpp | 217 | ||||
-rw-r--r-- | engines/sky/music/gmchannel.h | 82 | ||||
-rw-r--r-- | engines/sky/music/gmmusic.cpp | 118 | ||||
-rw-r--r-- | engines/sky/music/gmmusic.h | 52 | ||||
-rw-r--r-- | engines/sky/music/mt32music.cpp | 169 | ||||
-rw-r--r-- | engines/sky/music/mt32music.h | 53 | ||||
-rw-r--r-- | engines/sky/music/musicbase.cpp | 148 | ||||
-rw-r--r-- | engines/sky/music/musicbase.h | 92 |
12 files changed, 1566 insertions, 0 deletions
diff --git a/engines/sky/music/adlibchannel.cpp b/engines/sky/music/adlibchannel.cpp new file mode 100644 index 0000000000..9b28f832bb --- /dev/null +++ b/engines/sky/music/adlibchannel.cpp @@ -0,0 +1,344 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "common/util.h" +#include "sky/music/adlibchannel.h" +#include "sky/sky.h" + +namespace Sky { + +AdlibChannel::AdlibChannel(FM_OPL *opl, uint8 *pMusicData, uint16 startOfData) { + _opl = opl; + _musicData = pMusicData; + _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; + + if (SkyEngine::_systemVars.gameVersion == 109) { + //instrumentDataLoc = (_musicData[0x11D0] << 8) | _musicData[0x11CF]; + //_frequenceTable = (uint16*)(_musicData + 0x835); + //_registerTable = _musicData + 0xE35; + //_opOutputTable = _musicData + 0xE47; + //_adlibRegMirror = _musicData + 0xF4A; + + instrumentDataLoc = READ_LE_UINT16(_musicData + 0x1204); + _frequenceTable = (uint16*)(_musicData + 0x868); + _registerTable = _musicData + 0xE68; + _opOutputTable = _musicData + 0xE7A; + _adlibRegMirror = _musicData + 0xF7D; + } else if (SkyEngine::_systemVars.gameVersion == 267) { + instrumentDataLoc = READ_LE_UINT16(_musicData + 0x11FB); + _frequenceTable = (uint16*)(_musicData + 0x7F4); + _registerTable = _musicData + 0xDF4; + _opOutputTable = _musicData + 0xE06; + _adlibRegMirror = _musicData + 0xF55; + } else { + instrumentDataLoc = READ_LE_UINT16(_musicData + 0x1205); + _frequenceTable = (uint16*)(_musicData + 0x7FE); + _registerTable = _musicData + 0xDFE; + _opOutputTable = _musicData + 0xE10; + _adlibRegMirror = _musicData + 0xF5F; + } + + _instrumentMap = _musicData+instrumentDataLoc; + _instruments = (InstrumentStruct*)(_instrumentMap+0x80); + + _musicVolume = 0x100; +} + +bool AdlibChannel::isActive(void) { + + return _channelData.channelActive != 0; +} + +void AdlibChannel::updateVolume(uint16 pVolume) { + + _musicVolume = pVolume * 3; +} + +/* 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 AdlibChannel::setRegister(uint8 regNum, uint8 value) { + + if (_adlibRegMirror[regNum] != value) { + OPLWriteReg (_opl, regNum, value); + _adlibRegMirror[regNum] = value; + } +} + +void AdlibChannel::stopNote(void) { + + if (_channelData.note & 0x20) { + _channelData.note &= ~0x20; + setRegister(0xB0 | _channelData.adlibChannelNumber, _channelData.note); + } +} + +int32 AdlibChannel::getNextEventTime(void) { + int32 retV = 0; + uint8 cnt, lVal = 0; + 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 AdlibChannel::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 >= 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("Channel: dummy music routine 0x%02X was called",opcode); + _channelData.channelActive = 0; + break; + default: + // these opcodes aren't implemented in original music driver + error("Channel: Not existent routine 0x%02X was called",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(); + // not sure why this "if" is necessary...either a bug in my + // code or a bug in the music data (section 1, music 2) + if (_channelData.instrumentData || _channelData.tremoVibro) { + setupInstrument(opcode); + + opcode = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; + setupChannelVolume(opcode); + } else _channelData.eventDataPtr++; + } + if (_channelData.channelActive) + _channelData.nextEventTime += getNextEventTime(); + } + return returnVal; +} + +void AdlibChannel::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 AdlibChannel::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 AdlibChannel::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 AdlibChannel::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 AdlibChannel::com90_caseNoteOff(void) { + + if (_musicData[_channelData.eventDataPtr] == _channelData.lastCommand) + stopNote(); + _channelData.eventDataPtr++; +} + +void AdlibChannel::com90_stopChannel(void) { + + stopNote(); + _channelData.channelActive = 0; +} + +void AdlibChannel::com90_setupInstrument(void) { + + _channelData.channelVolume = 0x7F; + _channelData.freqOffset = 0x40; + _channelData.assignedInstrument = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; + _channelData.instrumentData = _instruments + _channelData.assignedInstrument; + adlibSetupInstrument(); +} + +uint8 AdlibChannel::com90_updateTempo(void) { + + uint8 retV = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; + return retV; +} + +void AdlibChannel::com90_getFreqOffset(void) { + + _channelData.freqOffset = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; + if (_channelData.note & 0x20) { + 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 AdlibChannel::com90_getChannelVolume(void) { + + _channelData.channelVolume = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; +} + +void AdlibChannel::com90_getTremoVibro(void) { + + _channelData.tremoVibro = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; +} + +void AdlibChannel::com90_rewindMusic(void) { + + _channelData.eventDataPtr = _channelData.startOfData; +} + +void AdlibChannel::com90_keyOff(void) { + + stopNote(); +} + +void AdlibChannel::com90_setStartOfData(void) { + + _channelData.startOfData = _channelData.eventDataPtr; +} + +} // End of namespace Sky diff --git a/engines/sky/music/adlibchannel.h b/engines/sky/music/adlibchannel.h new file mode 100644 index 0000000000..661f3a20dc --- /dev/null +++ b/engines/sky/music/adlibchannel.h @@ -0,0 +1,106 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef ADLIBCHANNEL_H +#define ADLIBCHANNEL_H + +#include "sky/music/musicbase.h" +#include "sound/fmopl.h" + +namespace Sky { + +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; +} AdlibChannelType; + +class AdlibChannel : public ChannelBase { +public: + AdlibChannel (FM_OPL *opl, uint8 *pMusicData, uint16 startOfData); + virtual void stopNote(void); + virtual uint8 process(uint16 aktTime); + virtual void updateVolume(uint16 pVolume); + virtual bool isActive(void); +private: + FM_OPL *_opl; + uint8 *_musicData; + uint16 _musicVolume; + AdlibChannelType _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 +}; + +} // End of namespace Sky + +#endif //ADLIBCHANNEL_H diff --git a/engines/sky/music/adlibmusic.cpp b/engines/sky/music/adlibmusic.cpp new file mode 100644 index 0000000000..e7ee4ecb6d --- /dev/null +++ b/engines/sky/music/adlibmusic.cpp @@ -0,0 +1,121 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-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. + * + * $URL$ + * $Id$ + * + */ + +#include "sky/music/adlibmusic.h" +#include "sky/music/adlibchannel.h" +#include "sound/mixer.h" +#include "sky/sky.h" + +namespace Sky { + +AdlibMusic::AdlibMusic(Audio::Mixer *pMixer, Disk *pDisk) + : MusicBase(pDisk) { + + _driverFileBase = 60202; + _mixer = pMixer; + _sampleRate = pMixer->getOutputRate(); + + _opl = makeAdlibOPL(_sampleRate); + + _mixer->setupPremix(this); +} + +AdlibMusic::~AdlibMusic(void) { + + _mixer->setupPremix(0); +} + +void AdlibMusic::premixerCall(int16 *data, uint len) { + + if (_musicData == NULL) { + // no music loaded + memset(data, 0, 2 * len * sizeof(int16)); + } else if ((_currentMusic == 0) || (_numberOfChannels == 0)) { + // music loaded but not played as of yet + memset(data, 0, 2 * len * sizeof(int16)); + // poll anyways as pollMusic() can activate the music + pollMusic(); + _nextMusicPoll = _sampleRate/50; + } else { + uint32 render; + int16 *origData = data; + uint origLen = len; + while (len) { + render = (len > _nextMusicPoll) ? (_nextMusicPoll) : (len); + len -= render; + _nextMusicPoll -= render; + YM3812UpdateOne(_opl, data, render); + data += render; + if (_nextMusicPoll == 0) { + pollMusic(); + _nextMusicPoll = _sampleRate/50; + } + } + + // Convert mono data to stereo + for (int i = (origLen - 1); i >= 0; i--) { + origData[2 * i] = origData[2 * i + 1] = origData[i]; + } + } +} + +void AdlibMusic::setupPointers(void) { + + if (SkyEngine::_systemVars.gameVersion == 109) { + // disk demo uses a different adlib driver version, some offsets have changed + //_musicDataLoc = (_musicData[0x11CC] << 8) | _musicData[0x11CB]; + //_initSequence = _musicData + 0xEC8; + + _musicDataLoc = READ_LE_UINT16(_musicData + 0x1200); + _initSequence = _musicData + 0xEFB; + } else if (SkyEngine::_systemVars.gameVersion == 267) { + _musicDataLoc = READ_LE_UINT16(_musicData + 0x11F7); + _initSequence = _musicData + 0xE87; + } else { + _musicDataLoc = READ_LE_UINT16(_musicData + 0x1201); + _initSequence = _musicData + 0xE91; + } + _nextMusicPoll = 0; +} + +void AdlibMusic::setupChannels(uint8 *channelData) { + + _numberOfChannels = channelData[0]; + channelData++; + for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) { + uint16 chDataStart = ((channelData[(cnt << 1) | 1] << 8) | channelData[cnt << 1]) + _musicDataLoc; + _channels[cnt] = new AdlibChannel(_opl, _musicData, chDataStart); + _channels[cnt]->updateVolume(_musicVolume); + } +} + +void AdlibMusic::startDriver(void) { + + uint16 cnt = 0; + while (_initSequence[cnt] || _initSequence[cnt+1]) { + OPLWriteReg (_opl, _initSequence[cnt], _initSequence[cnt+1]); + cnt += 2; + } + _allowedCommands = 0xD; +} + +} // End of namespace Sky diff --git a/engines/sky/music/adlibmusic.h b/engines/sky/music/adlibmusic.h new file mode 100644 index 0000000000..effa19dc51 --- /dev/null +++ b/engines/sky/music/adlibmusic.h @@ -0,0 +1,64 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef ADLIBMUSIC_H +#define ADLIBMUSIC_H + +#include "sky/music/musicbase.h" +#include "sound/audiostream.h" +#include "sound/fmopl.h" + +namespace Audio { + class Mixer; +} + +namespace Sky { + +class AdlibMusic : public AudioStream, public MusicBase { +public: + AdlibMusic(Audio::Mixer *pMixer, Disk *pDisk); + ~AdlibMusic(void); + + // AudioStream API + int readBuffer(int16 *buffer, const int numSamples) { + premixerCall(buffer, numSamples / 2); + return numSamples; + } + bool isStereo() const { return true; } + bool endOfData() const { return false; } + int getRate() const { return _sampleRate; } + +private: + FM_OPL *_opl; + Audio::Mixer *_mixer; + uint8 *_initSequence; + uint32 _sampleRate, _nextMusicPoll; + virtual void setupPointers(void); + virtual void setupChannels(uint8 *channelData); + virtual void startDriver(void); + + void premixerCall(int16 *buf, uint len); +}; + +} // End of namespace Sky + +#endif //ADLIBMUSIC_H diff --git a/engines/sky/music/gmchannel.cpp b/engines/sky/music/gmchannel.cpp new file mode 100644 index 0000000000..66fbdfb69f --- /dev/null +++ b/engines/sky/music/gmchannel.cpp @@ -0,0 +1,217 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-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. + * + * $URL$ + * $Id$ + * + */ + +#include "gmchannel.h" +#include "common/util.h" +#include "sound/mididrv.h" + +namespace Sky { + +GmChannel::GmChannel(uint8 *pMusicData, uint16 startOfData, MidiDriver *pMidiDrv, const byte *pInstMap, const byte *veloTab) { + + _musicData = pMusicData; + _midiDrv = pMidiDrv; + _channelData.midiChannelNumber = 0; + _channelData.startOfData = startOfData; + _channelData.eventDataPtr = startOfData; + _channelData.channelActive = 1; + _channelData.nextEventTime = getNextEventTime(); + _instMap = pInstMap; + _veloTab = veloTab; + + _musicVolume = 0x7F; + _lastVolume = 0xFF; +} + +bool GmChannel::isActive(void) { + + return _channelData.channelActive != 0; +} + +void GmChannel::updateVolume(uint16 pVolume) { + + _musicVolume = pVolume; + if (_musicVolume > 0) + _musicVolume = (_musicVolume * 2) / 3 + 43; + if (_lastVolume < 0xFF) { + uint8 newVol = (_lastVolume * _musicVolume) >> 7; + _midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x700 | (newVol << 16)); + } +} + +void GmChannel::stopNote(void) { + + // All Notes Off + _midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x7B00 | 0 | 0x79000000); + // Reset the Pitch Wheel. See bug #1016556. + _midiDrv->send((0xE0 | _channelData.midiChannelNumber) | 0x400000); +} + +int32 GmChannel::getNextEventTime(void) { + + int32 retV = 0; + uint8 cnt, lVal = 0; + 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 GmChannel::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 >= 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_getPitch(); break; + case 6: com90_getChannelVolume(); break; + case 8: com90_rewindMusic(); break; + case 9: com90_keyOff(); break; + case 11: com90_getChannelPanValue(); break; + case 12: com90_setStartOfData(); break; + case 13: com90_getChannelControl(); break; + case 4: //com90_dummy(); + case 7: //com90_skipTremoVibro(); + case 10: //com90_error(); + error("Channel: dummy music routine 0x%02X was called",opcode); + _channelData.channelActive = 0; + break; + default: + // these opcodes aren't implemented in original music driver + error("Channel: Not existent routine 0x%02X was called",opcode); + _channelData.channelActive = 0; + break; + } + } else { + // new midi channel assignment + _channelData.midiChannelNumber = opcode&0xF; + } + } else { + _channelData.note = opcode; + byte velocity = _musicData[_channelData.eventDataPtr]; + if (_veloTab) + velocity = _veloTab[velocity]; + _channelData.eventDataPtr++; + _midiDrv->send((0x90 | _channelData.midiChannelNumber) | (opcode << 8) | (velocity << 16)); + } + if (_channelData.channelActive) + _channelData.nextEventTime += getNextEventTime(); + } + return returnVal; +} + +//- command 90h routines + +void GmChannel::com90_caseNoteOff(void) { + + _midiDrv->send((0x90 | _channelData.midiChannelNumber) | (_musicData[_channelData.eventDataPtr] << 8)); + _channelData.eventDataPtr++; +} + +void GmChannel::com90_stopChannel(void) { + + stopNote(); + _channelData.channelActive = 0; +} + +void GmChannel::com90_setupInstrument(void) { + byte instrument = _musicData[_channelData.eventDataPtr]; + if (_instMap) + instrument = _instMap[instrument]; + _midiDrv->send((0xC0 | _channelData.midiChannelNumber) | (instrument << 8)); + _channelData.eventDataPtr++; +} + +uint8 GmChannel::com90_updateTempo(void) { + + uint8 retV = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; + return retV; +} + +void GmChannel::com90_getPitch(void) { + + _midiDrv->send((0xE0 | _channelData.midiChannelNumber) | 0 | (_musicData[_channelData.eventDataPtr] << 16)); + _channelData.eventDataPtr++; +} + +void GmChannel::com90_getChannelVolume(void) { + + _lastVolume = _musicData[_channelData.eventDataPtr]; + uint8 newVol = (uint8)((_musicData[_channelData.eventDataPtr++] * _musicVolume) >> 7); + _midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x700 | (newVol << 16)); +} + +void GmChannel::com90_rewindMusic(void) { + + _channelData.eventDataPtr = _channelData.startOfData; +} + +void GmChannel::com90_keyOff(void) { + + _midiDrv->send((0x90 | _channelData.midiChannelNumber) | (_channelData.note << 8) | 0); +} + +void GmChannel::com90_setStartOfData(void) { + + _channelData.startOfData = _channelData.eventDataPtr; +} + +void GmChannel::com90_getChannelPanValue(void) { + + _midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x0A00 | (_musicData[_channelData.eventDataPtr] << 16)); + _channelData.eventDataPtr++; +} + +void GmChannel::com90_getChannelControl(void) { + + uint8 conNum = _musicData[_channelData.eventDataPtr]; + uint8 conDat = _musicData[_channelData.eventDataPtr + 1]; + _channelData.eventDataPtr += 2; + _midiDrv->send((0xB0 | _channelData.midiChannelNumber) | (conNum << 8) | (conDat << 16)); +} + +} // End of namespace Sky diff --git a/engines/sky/music/gmchannel.h b/engines/sky/music/gmchannel.h new file mode 100644 index 0000000000..051b5d0648 --- /dev/null +++ b/engines/sky/music/gmchannel.h @@ -0,0 +1,82 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef SKYGMCHANNEL_H +#define SKYGMCHANNEL_H + +#include "sky/music/musicbase.h" + +class MidiDriver; + +namespace Sky { + +typedef struct { + uint16 eventDataPtr; + int32 nextEventTime; + uint16 startOfData; + uint8 midiChannelNumber; + uint8 note; + uint8 channelActive; +} MidiChannelType; + +class GmChannel : public ChannelBase { +public: + GmChannel(uint8 *pMusicData, uint16 startOfData, MidiDriver *pMidiDrv, const byte *pInstMap, const byte *veloTab); + virtual void stopNote(void); + virtual uint8 process(uint16 aktTime); + virtual void updateVolume(uint16 pVolume); + virtual bool isActive(void); +private: + const byte *_instMap; + const byte *_veloTab; + MidiDriver *_midiDrv; + uint8 *_musicData; + uint16 _musicVolume; + MidiChannelType _channelData; + uint8 _lastVolume; + //- 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_getPitch(void); // 5 + void com90_getChannelVolume(void); // 6 + //void com90_skipTremoVibro(void); // 7 + void com90_rewindMusic(void); // 8 + void com90_keyOff(void); // 9 + //void com90_error(void); // 10 + void com90_getChannelPanValue(void); // 11 + void com90_setStartOfData(void); // 12 + void com90_getChannelControl(void); // 13 +}; + +} // End of namespace Sky + +#endif //SKYGMCHANNEL_H diff --git a/engines/sky/music/gmmusic.cpp b/engines/sky/music/gmmusic.cpp new file mode 100644 index 0000000000..bedb8acc7c --- /dev/null +++ b/engines/sky/music/gmmusic.cpp @@ -0,0 +1,118 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-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. + * + * $URL$ + * $Id$ + * + */ + +#include "sky/music/gmmusic.h" +#include "sky/music/gmchannel.h" +#include "sky/sky.h" +#include "common/util.h" +#include "sound/mididrv.h" + +namespace Sky { + +void GmMusic::passTimerFunc(void *param) { + + ((GmMusic*)param)->timerCall(); +} + +GmMusic::GmMusic(MidiDriver *pMidiDrv, Disk *pDisk) + : MusicBase(pDisk) { + + _driverFileBase = 60200; + _midiDrv = pMidiDrv; + int midiRes = _midiDrv->open(); + if (midiRes != 0) + error("Can't open midi device. Errorcode: %d", midiRes); + _timerCount = 0; + _midiDrv->setTimerCallback(this, passTimerFunc); +} + +GmMusic::~GmMusic(void) { + + _midiDrv->setTimerCallback(NULL, NULL); + if (_currentMusic) + stopMusic(); + // Send All Sound Off and All Notes Off (for external synths) + for (int i = 0; i < 16; ++i) { + _midiDrv->send ((120 << 8) | 0xB0 | i); + _midiDrv->send ((123 << 8) | 0xB0 | i); + } + _midiDrv->close(); + delete _midiDrv; +} + +void GmMusic::timerCall(void) { + _timerCount += _midiDrv->getBaseTempo(); + if (_timerCount > (1000000 / 50)) { + // call pollMusic() 50 times per second + _timerCount -= 1000000 / 50; + if (_musicData != NULL) + pollMusic(); + } +} + +void GmMusic::setupPointers(void) { + + if (SkyEngine::_systemVars.gameVersion == 109) { + _musicDataLoc = (_musicData[0x79C] << 8) | _musicData[0x79B]; + _sysExSequence = _musicData + 0x1EF2; + } else { + _musicDataLoc = (_musicData[0x7DD] << 8) | _musicData[0x7DC]; + _sysExSequence = ((_musicData[0x7E1] << 8) | _musicData[0x7E0]) + _musicData; + } +} + +void GmMusic::setupChannels(uint8 *channelData) { + + _numberOfChannels = channelData[0]; + channelData++; + for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) { + uint16 chDataStart = ((channelData[(cnt << 1) | 1] << 8) | channelData[cnt << 1]) + _musicDataLoc; + _channels[cnt] = new GmChannel(_musicData, chDataStart, _midiDrv, MidiDriver::_mt32ToGm, _veloTab); + _channels[cnt]->updateVolume(_musicVolume); + } +} + +void GmMusic::startDriver(void) { + // Send GM System On to reset channel parameters on external and capa$ + uint8 sysEx[] = "\xf0\x7e\x7f\x09\x01\xf7"; + _midiDrv->sysEx(sysEx, 6); + //_midiDrv->send(0xFF); //ALSA can't handle this. + // skip all sysEx as it can't be handled anyways. +} + +const byte GmMusic::_veloTab[128] = { + 0x00, 0x40, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, + 0x45, 0x45, 0x46, 0x46, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49, + 0x4A, 0x4A, 0x4B, 0x4B, 0x4C, 0x4C, 0x4D, 0x4D, 0x4E, 0x4E, + 0x4F, 0x4F, 0x50, 0x50, 0x51, 0x51, 0x52, 0x52, 0x53, 0x53, + 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x57, 0x57, 0x58, 0x58, + 0x59, 0x59, 0x5A, 0x5A, 0x5B, 0x5B, 0x5C, 0x5C, 0x5D, 0x5D, + 0x5E, 0x5E, 0x5F, 0x5F, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, + 0x63, 0x63, 0x64, 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, + 0x68, 0x68, 0x69, 0x69, 0x6A, 0x6A, 0x6B, 0x6B, 0x6C, 0x6C, + 0x6D, 0x6D, 0x6E, 0x6E, 0x6F, 0x6F, 0x70, 0x70, 0x71, 0x71, + 0x72, 0x72, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x76, 0x76, + 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7A, 0x7A, 0x7B, 0x7B, + 0x7C, 0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7F, 0x7F +}; + +} // End of namespace Sky diff --git a/engines/sky/music/gmmusic.h b/engines/sky/music/gmmusic.h new file mode 100644 index 0000000000..2623c6b5cd --- /dev/null +++ b/engines/sky/music/gmmusic.h @@ -0,0 +1,52 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef GMMUSIC_H +#define GMMUSIC_H + +#include "sky/music/musicbase.h" + +class MidiDriver; + +namespace Sky { + +class GmMusic : public MusicBase { +public: + GmMusic(MidiDriver *pMidiDrv, Disk *pDisk); + ~GmMusic(void); +private: + static void passTimerFunc(void *param); + void timerCall(void); + + uint32 _timerCount; + uint8 *_sysExSequence; + MidiDriver *_midiDrv; + static const byte _veloTab[128]; + + virtual void setupPointers(void); + virtual void setupChannels(uint8 *channelData); + virtual void startDriver(void); +}; + +} // End of namespace Sky + +#endif //GMMUSIC_H diff --git a/engines/sky/music/mt32music.cpp b/engines/sky/music/mt32music.cpp new file mode 100644 index 0000000000..a6bcbf4d05 --- /dev/null +++ b/engines/sky/music/mt32music.cpp @@ -0,0 +1,169 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-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. + * + * $URL$ + * $Id$ + * + */ + +#include "sky/music/mt32music.h" +#include "sky/music/gmchannel.h" +#include "common/util.h" +#include "common/system.h" +#include "sound/mididrv.h" + +namespace Sky { + +void MT32Music::passTimerFunc(void *param) { + + ((MT32Music*)param)->timerCall(); +} + +MT32Music::MT32Music(MidiDriver *pMidiDrv, Disk *pDisk) + : MusicBase(pDisk) { + + _driverFileBase = 60200; + _midiDrv = pMidiDrv; + int midiRes = _midiDrv->open(); + if (midiRes != 0) + error("Can't open midi device. Errorcode: %d",midiRes); + _timerCount = 0; + _midiDrv->setTimerCallback(this, passTimerFunc); +} + +MT32Music::~MT32Music(void) { + + _midiDrv->close(); + _midiDrv->setTimerCallback(NULL, NULL); + delete _midiDrv; +} + +void MT32Music::timerCall(void) { + _timerCount += _midiDrv->getBaseTempo(); + if (_timerCount > (1000000 / 50)) { + // call pollMusic() 50 times per second + _timerCount -= 1000000 / 50; + if (_musicData != NULL) + pollMusic(); + } +} + +void MT32Music::setVolume(uint8 volume) { + uint8 sysEx[10] = "\x41\x10\x16\x12\x10\x00\x16\x00\x00"; + _musicVolume = volume; + sysEx[7] = (volume > 100) ? 100 : volume; + sysEx[8] = 0x00; + for (uint8 cnt = 4; cnt < 8; cnt++) + sysEx[8] -= sysEx[cnt]; + sysEx[8] &= 0x7F; + _midiDrv->sysEx(sysEx, 9); +} + +void MT32Music::setupPointers(void) { + + _musicDataLoc = (_musicData[0x7DD] << 8) | _musicData[0x7DC]; + _sysExSequence = ((_musicData[0x7E1] << 8) | _musicData[0x7E0]) + _musicData; +} + +void MT32Music::setupChannels(uint8 *channelData) { + + _numberOfChannels = channelData[0]; + channelData++; + for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) { + uint16 chDataStart = ((channelData[(cnt << 1) | 1] << 8) | channelData[cnt << 1]) + _musicDataLoc; + _channels[cnt] = new GmChannel(_musicData, chDataStart, _midiDrv, NULL, NULL); + _channels[cnt]->updateVolume(_musicVolume); + } +} + +bool MT32Music::processPatchSysEx(uint8 *sysExData) { + + uint8 sysExBuf[15]; + uint8 crc = 0; + if (sysExData[0] & 0x80) + return false; + + // decompress data from stream + sysExBuf[0] = 0x41; sysExBuf[1] = 0x10; sysExBuf[2] = 0x16; sysExBuf[3] = 0x12; sysExBuf[4] = 0x5; + sysExBuf[5] = sysExData[0] >> 4; // patch offset part 1 + sysExBuf[6] = (sysExData[0] & 0xF) << 3; // patch offset part 2 + sysExBuf[7] = sysExData[1] >> 6; // timbre group + sysExBuf[8] = sysExData[1] & 0x3F; // timbre num + sysExBuf[9] = sysExData[2] & 0x3F; // key shift + sysExBuf[10] = sysExData[3] & 0x7F; // fine tune + sysExBuf[11] = sysExData[4] & 0x7F; // bender range + sysExBuf[12] = sysExData[2] >> 6; // assign mode + sysExBuf[13] = sysExData[3] >> 7; // reverb switch + for (uint8 cnt = 4; cnt < 14; cnt++) + crc -= sysExBuf[cnt]; + sysExBuf[14] = crc & 0x7F; // crc + _midiDrv->sysEx(sysExBuf, 15); + g_system->delayMillis(5); + return true; +} + +void MT32Music::startDriver(void) { + + // setup timbres and patches using SysEx data + uint8* sysExData = _sysExSequence; + uint8 timbreNum = sysExData[0]; + uint8 cnt, crc; + sysExData++; + uint8 sendBuf[256]; + uint8 len; + sendBuf[0] = 0x41; sendBuf[1] = 0x10; sendBuf[2] = 0x16; sendBuf[3] = 0x12; + for (cnt = 0; cnt < timbreNum; cnt++) { + len = 7; + crc = 0; + // Timbre address + sendBuf[4] = 0x8 | (sysExData[0] >> 6); + sendBuf[5] = (sysExData[0] & 0x3F) << 1; + sendBuf[6] = 0xA; + sysExData++; + crc -= sendBuf[4] + sendBuf[5] + sendBuf[6]; + uint8 dataLen = sysExData[0]; + sysExData++; + // Timbre data: + do { + uint8 rlVal = 1; + uint8 codeVal = sysExData[0]; + sysExData++; + + if (codeVal & 0x80) { + codeVal &= 0x7F; + rlVal = sysExData[0]; + sysExData++; + dataLen--; + } + for (uint8 cnt2 = 0; cnt2 < rlVal; cnt2++) { + sendBuf[len] = codeVal; + len++; + crc -= codeVal; + } + dataLen--; + } while (dataLen > 0); + sendBuf[len] = crc & 0x7F; + len++; + _midiDrv->sysEx(sendBuf, len); + g_system->delayMillis (5); + } + + while (processPatchSysEx(sysExData)) + sysExData += 5; +} + +} // End of namespace Sky diff --git a/engines/sky/music/mt32music.h b/engines/sky/music/mt32music.h new file mode 100644 index 0000000000..a64663a15f --- /dev/null +++ b/engines/sky/music/mt32music.h @@ -0,0 +1,53 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef MT32MUSIC_H +#define MT32MUSIC_H + +#include "sky/music/musicbase.h" + +class MidiDriver; + +namespace Sky { + +class MT32Music : public MusicBase { +public: + MT32Music(MidiDriver *pMidiDrv, Disk *pDisk); + ~MT32Music(void); +private: + static void passTimerFunc(void *param); + void timerCall(void); + bool processPatchSysEx(uint8 *sysExData); + virtual void setVolume(uint8 volume); + + uint32 _timerCount; + uint8 *_sysExSequence; + MidiDriver *_midiDrv; + + virtual void setupPointers(void); + virtual void setupChannels(uint8 *channelData); + virtual void startDriver(void); +}; + +} // End of namespace Sky + +#endif //MT32MUSIC_H diff --git a/engines/sky/music/musicbase.cpp b/engines/sky/music/musicbase.cpp new file mode 100644 index 0000000000..9d308ebc7f --- /dev/null +++ b/engines/sky/music/musicbase.cpp @@ -0,0 +1,148 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-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. + * + * $URL$ + * $Id$ + * + */ + +#include "sky/music/musicbase.h" +#include "sky/disk.h" +#include "common/util.h" + +namespace Sky { + +MusicBase::MusicBase(Disk *pDisk) { + + _musicData = NULL; + _allowedCommands = 0; + _skyDisk = pDisk; + _currentMusic = 0; + _musicVolume = 127; + _numberOfChannels = _currentMusic = 0; +} + +MusicBase::~MusicBase(void) { + + stopMusic(); + if (_musicData) + free(_musicData); +} + +void MusicBase::loadSection(uint8 pSection) { + + _mutex.lock(); + if (_currentMusic) + stopMusic(); + if (_musicData) + free(_musicData); + _currentSection = pSection; + _musicData = _skyDisk->loadFile(_driverFileBase + FILES_PER_SECTION * pSection); + _allowedCommands = 0; + _musicTempo0 = 0x78; // init constants taken from idb file, area ~0x1060 + _musicTempo1 = 0xC0; + _onNextPoll.doReInit = false; + _onNextPoll.doStopMusic = false; + _onNextPoll.musicToProcess = 0; + _tempo = _aktTime = 0x10001; + _numberOfChannels = _currentMusic = 0; + setupPointers(); + startDriver(); + _mutex.unlock(); +} + +bool MusicBase::musicIsPlaying(void) { + + for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) + if (_channels[cnt]->isActive()) + return true; + return false; +} + +void MusicBase::setVolume(uint16 param) { + + _musicVolume = param; + for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) + _channels[cnt]->updateVolume(_musicVolume); +} + +void MusicBase::stopMusic(void) { + + for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) { + _channels[cnt]->stopNote(); + delete _channels[cnt]; + } + _numberOfChannels = 0; +} + +void MusicBase::updateTempo(void) { + + uint16 tempoMul = _musicTempo0 * _musicTempo1; + uint16 divisor = 0x4446390/ 23864; + _tempo = (tempoMul / divisor) << 16; + _tempo |= (((tempoMul%divisor) << 16) | (tempoMul / divisor)) / divisor; +} + +void MusicBase::loadNewMusic(void) { + + uint16 musicPos; + if (_onNextPoll.musicToProcess > _musicData[_musicDataLoc]) { + error("Music %d requested but doesn't exist in file.", _onNextPoll.musicToProcess); + return; + } + if (_currentMusic != 0) + stopMusic(); + + _currentMusic = _onNextPoll.musicToProcess; + + if (_currentMusic != 0) { + musicPos = (_musicData[_musicDataLoc + 2] << 8) | _musicData[_musicDataLoc+1]; + musicPos += _musicDataLoc+((_currentMusic-1) << 1); + musicPos = ((_musicData[musicPos+1] << 8) | _musicData[musicPos]) + _musicDataLoc; + + _musicTempo0 = _musicData[musicPos]; + _musicTempo1 = _musicData[musicPos+1]; + + setupChannels(_musicData + musicPos + 2); + + updateTempo(); + } +} + +void MusicBase::pollMusic(void) { + + _mutex.lock(); + uint8 newTempo; + if (_onNextPoll.doReInit) startDriver(); + if (_onNextPoll.doStopMusic) stopMusic(); + if (_onNextPoll.musicToProcess != _currentMusic) + loadNewMusic(); + + _aktTime += _tempo; + + for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) { + newTempo = _channels[cnt]->process((uint16)(_aktTime >> 16)); + if (newTempo) { + _musicTempo1 = newTempo; + updateTempo(); + } + } + _mutex.unlock(); + _aktTime &= 0xFFFF; +} + +} // End of namespace Sky diff --git a/engines/sky/music/musicbase.h b/engines/sky/music/musicbase.h new file mode 100644 index 0000000000..d9f3e22beb --- /dev/null +++ b/engines/sky/music/musicbase.h @@ -0,0 +1,92 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef MUSICBASE_H +#define MUSICBASE_H + +#include "common/stdafx.h" +#include "common/scummsys.h" +#include "common/mutex.h" + +namespace Sky { + +class Disk; + +#define FILES_PER_SECTION 4 + +typedef struct { + bool doReInit, doStopMusic; + uint8 musicToProcess; +} Actions; + +class ChannelBase { +public: + virtual ~ChannelBase() {}; + virtual void stopNote(void) = 0; + virtual uint8 process(uint16 aktTime) = 0; + virtual void updateVolume(uint16 pVolume) = 0; + virtual bool isActive(void) = 0; +private: +}; + +class MusicBase { +public: + MusicBase(Disk *pDisk); + virtual ~MusicBase(void); + void loadSection(uint8 pSection); + void startMusic(uint16 param) { _onNextPoll.musicToProcess = param & 0xF; }; // 4 + void stopMusic(); // 7 + bool musicIsPlaying(void); + uint8 giveVolume(void) { return (uint8)_musicVolume; }; + uint8 giveCurrentMusic(void) { return _currentMusic; }; + void setVolume(uint16 param); + +protected: + + Disk *_skyDisk; + uint8 *_musicData; + uint8 _allowedCommands; + uint16 _musicDataLoc; + uint16 _driverFileBase; + + uint16 _musicVolume, _numberOfChannels; + uint8 _currentMusic, _currentSection; + uint8 _musicTempo0; // can be changed by music stream + uint8 _musicTempo1; // given once per music + uint32 _tempo; // calculated from musicTempo0 and musicTempo1 + uint32 _aktTime; + Actions _onNextPoll; + ChannelBase *_channels[10]; + Common::Mutex _mutex; + + virtual void setupPointers(void) = 0; + virtual void setupChannels(uint8 *channelData) = 0; + virtual void startDriver(void) = 0; + + void updateTempo(void); + void loadNewMusic(void); + void pollMusic(void); +}; + +} // End of namespace Sky + +#endif //MUSICBASE_H |