diff options
Diffstat (limited to 'sky/music')
-rw-r--r-- | sky/music/adlibchannel.cpp | 323 | ||||
-rw-r--r-- | sky/music/adlibchannel.h | 101 | ||||
-rw-r--r-- | sky/music/adlibmusic.cpp | 108 | ||||
-rw-r--r-- | sky/music/adlibmusic.h | 47 | ||||
-rw-r--r-- | sky/music/gmchannel.cpp | 203 | ||||
-rw-r--r-- | sky/music/gmchannel.h | 76 | ||||
-rw-r--r-- | sky/music/gmmusic.cpp | 146 | ||||
-rw-r--r-- | sky/music/gmmusic.h | 49 | ||||
-rw-r--r-- | sky/music/mt32music.cpp | 182 | ||||
-rw-r--r-- | sky/music/mt32music.h | 50 | ||||
-rw-r--r-- | sky/music/musicbase.cpp | 164 | ||||
-rw-r--r-- | sky/music/musicbase.h | 85 |
12 files changed, 1534 insertions, 0 deletions
diff --git a/sky/music/adlibchannel.cpp b/sky/music/adlibchannel.cpp new file mode 100644 index 0000000000..26ad970814 --- /dev/null +++ b/sky/music/adlibchannel.cpp @@ -0,0 +1,323 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include "adlibchannel.h" +#include "sound/fmopl.h" + +SkyAdlibChannel::SkyAdlibChannel(uint8 *pMusicData, uint16 startOfData, uint32 version) +{ + _gameVersion = version; + _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 (_gameVersion == 267) { + instrumentDataLoc = (_musicData[0x11FC] << 8) | _musicData[0x11FB]; + _frequenceTable = (uint16*)(_musicData+0x7F4); + _registerTable = _musicData+0xDF4; + _opOutputTable = _musicData+0xE06; + _adlibRegMirror = _musicData+0xF55; + } else { + instrumentDataLoc = (_musicData[0x1206] << 8) | _musicData[0x1205]; + _frequenceTable = (uint16*)(_musicData+0x7FE); + _registerTable = _musicData+0xDFE; + _opOutputTable = _musicData+0xE10; + _adlibRegMirror = _musicData+0xF5F; + } + + _instrumentMap = _musicData+instrumentDataLoc; + _instruments = (InstrumentStruct*)(_instrumentMap+0x80); + + _musicVolume = 0x100; +} + +void SkyAdlibChannel::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 SkyAdlibChannel::setRegister(uint8 regNum, uint8 value) { + + if (_adlibRegMirror[regNum] != value) { + YM3812Write(0, 0, regNum); + YM3812Write(0, 1, value); + _adlibRegMirror[regNum] = value; + } +} + +void SkyAdlibChannel::stopNote(void) { + + if (_channelData.note & 0x20) { + _channelData.note &= ~0x20; + setRegister(0xB0 | _channelData.adlibChannelNumber, _channelData.note); + } +} + +int32 SkyAdlibChannel::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 SkyAdlibChannel::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("SkyChannel: dummy music routine 0x%02X was called",opcode); + _channelData.channelActive = 0; + break; + default: + // these opcodes aren't implemented in original music driver + error("SkyChannel: Not existant 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 SkyAdlibChannel::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 SkyAdlibChannel::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 SkyAdlibChannel::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 SkyAdlibChannel::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 SkyAdlibChannel::com90_caseNoteOff(void) { + + if (_musicData[_channelData.eventDataPtr] == _channelData.lastCommand) + stopNote(); + _channelData.eventDataPtr++; +} + +void SkyAdlibChannel::com90_stopChannel(void) { + + stopNote(); + _channelData.channelActive = 0; +} + +void SkyAdlibChannel::com90_setupInstrument(void) { + + _channelData.channelVolume = 0x7F; + _channelData.freqOffset = 0x40; + _channelData.assignedInstrument = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; + _channelData.instrumentData = _instruments + _channelData.assignedInstrument; + adlibSetupInstrument(); +} + +uint8 SkyAdlibChannel::com90_updateTempo(void) { + + uint8 retV = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; + return retV; +} + +void SkyAdlibChannel::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 SkyAdlibChannel::com90_getChannelVolume(void) { + + _channelData.channelVolume = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; +} + +void SkyAdlibChannel::com90_getTremoVibro(void) { + + _channelData.tremoVibro = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; +} + +void SkyAdlibChannel::com90_rewindMusic(void) { + + _channelData.eventDataPtr = _channelData.startOfData; +} + +void SkyAdlibChannel::com90_keyOff(void) { + + stopNote(); +} + +void SkyAdlibChannel::com90_setStartOfData(void) { + + _channelData.startOfData = _channelData.eventDataPtr; +} diff --git a/sky/music/adlibchannel.h b/sky/music/adlibchannel.h new file mode 100644 index 0000000000..a9b43c3f9b --- /dev/null +++ b/sky/music/adlibchannel.h @@ -0,0 +1,101 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#ifndef ADLIBCHANNEL_H +#define ADLIBCHANNEL_H + +#include "stdafx.h" +#include "common/engine.h" +#include "sky/music/musicbase.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; +} AdlibChannelType; + +class SkyAdlibChannel : public SkyChannelBase { +public: + SkyAdlibChannel(uint8 *pMusicData, uint16 startOfData, uint32 version); + virtual void stopNote(void); + virtual uint8 process(uint16 aktTime); + virtual void updateVolume(uint16 pVolume); +private: + uint32 _gameVersion; + 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 +}; + +#endif //ADLIBCHANNEL_H diff --git a/sky/music/adlibmusic.cpp b/sky/music/adlibmusic.cpp new file mode 100644 index 0000000000..b23afd879c --- /dev/null +++ b/sky/music/adlibmusic.cpp @@ -0,0 +1,108 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include "sky/music/adlibmusic.h" +#include "sound/fmopl.h" + +void SkyAdlibMusic::passMixerFunc(void *param, int16 *buf, uint len) { + + ((SkyAdlibMusic*)param)->premixerCall(buf, len); +} + +SkyAdlibMusic::SkyAdlibMusic(SoundMixer *pMixer, SkyDisk *pSkyDisk, uint32 version) + : SkyMusicBase(pSkyDisk, version) { + + _driverFileBase = 60202; + _mixer = pMixer; + _sampleRate = g_system->property(OSystem::PROP_GET_SAMPLE_RATE, 0); + if (0 != YM3812Init(1, 3579545, _sampleRate)) + error("Error initialising YM3812 sound chip emulation"); + _mixer->setupPremix(this, passMixerFunc); +} + +SkyAdlibMusic::~SkyAdlibMusic(void) { + + _mixer->setupPremix(NULL, NULL); + YM3812Shutdown(); +} + +void SkyAdlibMusic::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(0, buf, render); + buf += render; + if (_nextMusicPoll == 0) { + pollMusic(); + _nextMusicPoll = _sampleRate/50; + } + } +} + +void SkyAdlibMusic::setupPointers(void) { + + printf("game version: %d\n",_gameVersion); + if (_gameVersion == 267) { + // disk demo uses a different adlib driver version, some offsets have changed + _musicDataLoc = (_musicData[0x11F8] << 8) | _musicData[0x11F7]; + _initSequence = _musicData + 0xE87; + } else { + _musicDataLoc = (_musicData[0x1202] << 8) | _musicData[0x1201]; + _initSequence = _musicData + 0xE91; + } + _nextMusicPoll = 0; +} + +void SkyAdlibMusic::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 SkyAdlibChannel(_musicData, chDataStart, _gameVersion); + } +} + +void SkyAdlibMusic::startDriver(void) { + + uint16 cnt = 0; + while (_initSequence[cnt] || _initSequence[cnt+1]) { + YM3812Write(0, 0, _initSequence[cnt]); + YM3812Write(0, 1, _initSequence[cnt+1]); + cnt += 2; + } + _allowedCommands = 0xD; +} diff --git a/sky/music/adlibmusic.h b/sky/music/adlibmusic.h new file mode 100644 index 0000000000..dcde24150c --- /dev/null +++ b/sky/music/adlibmusic.h @@ -0,0 +1,47 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#ifndef ADLIBMUSIC_H +#define ADLIBMUSIC_H + +#include "stdafx.h" +#include "sound/mixer.h" +#include "common/engine.h" +#include "adlibchannel.h" +#include "musicbase.h" + +class SkyAdlibMusic : public SkyMusicBase { +public: + SkyAdlibMusic(SoundMixer *pMixer, SkyDisk *pSkyDisk, uint32 version); + ~SkyAdlibMusic(void); +private: + SoundMixer *_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); + static void passMixerFunc(void *param, int16 *buf, uint len); +}; + +#endif //ADLIBMUSIC_H diff --git a/sky/music/gmchannel.cpp b/sky/music/gmchannel.cpp new file mode 100644 index 0000000000..5db1256388 --- /dev/null +++ b/sky/music/gmchannel.cpp @@ -0,0 +1,203 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include "gmchannel.h" + +// the MT32 is a lot more sensitive to velocities than most general midi devices, +// so we need to boost them a little +uint8 SkyGmChannel::_veloTab[128] = { +0x00, 0x30, 0x31, 0x31, 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x36, 0x36, 0x37, +0x38, 0x38, 0x39, 0x39, 0x3A, 0x3B, 0x3B, 0x3C, 0x3D, 0x3D, 0x3E, 0x3E, 0x3F, +0x40, 0x40, 0x41, 0x42, 0x42, 0x43, 0x43, 0x44, 0x45, 0x45, 0x46, 0x47, 0x47, +0x48, 0x48, 0x49, 0x4A, 0x4A, 0x4B, 0x4C, 0x4C, 0x4D, 0x4D, 0x4E, 0x4F, 0x4F, +0x50, 0x50, 0x51, 0x52, 0x52, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56, 0x57, 0x57, +0x58, 0x59, 0x59, 0x5A, 0x5A, 0x5B, 0x5C, 0x5C, 0x5D, 0x5E, 0x5E, 0x5F, 0x5F, +0x60, 0x61, 0x61, 0x62, 0x63, 0x63, 0x64, 0x64, 0x65, 0x66, 0x66, 0x67, 0x68, +0x68, 0x69, 0x69, 0x6A, 0x6B, 0x6B, 0x6C, 0x6C, 0x6D, 0x6E, 0x6E, 0x6F, 0x70, +0x70, 0x71, 0x71, 0x72, 0x73, 0x73, 0x74, 0x75, 0x75, 0x76, 0x76, 0x77, 0x78, +0x78, 0x79, 0x7A, 0x7A, 0x7B, 0x7B, 0x7C, 0x7D, 0x7D, 0x7E, 0x7F }; + +SkyGmChannel::SkyGmChannel(uint8 *pMusicData, uint16 startOfData, MidiDriver *pMidiDrv, byte *pInstMap) +{ + _musicData = pMusicData; + _midiDrv = pMidiDrv; + _channelData.startOfData = startOfData; + _channelData.eventDataPtr = startOfData; + _channelData.channelActive = 1; + _channelData.nextEventTime = getNextEventTime(); + _mt32_to_gm = pInstMap; + + _musicVolume = 0x100; +} + +void SkyGmChannel::updateVolume(uint16 pVolume) { + + _musicVolume = pVolume; +} + +void SkyGmChannel::stopNote(void) { + + _midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x7B00 | 0 | 0x79000000); + _midiDrv->send(0); +} + +int32 SkyGmChannel::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 SkyGmChannel::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("SkyChannel: dummy music routine 0x%02X was called",opcode); + _channelData.channelActive = 0; + break; + default: + // these opcodes aren't implemented in original music driver + error("SkyChannel: Not existant routine 0x%02X was called",opcode); + _channelData.channelActive = 0; + break; + } + } else { + // new midi channel assignment + _channelData.midiChannelNumber = opcode&0xF; + } + } else { + _channelData.note = opcode; + uint8 velocity = _veloTab[_musicData[_channelData.eventDataPtr]]; + _channelData.eventDataPtr++; + _midiDrv->send((0x90 | _channelData.midiChannelNumber) | (opcode << 8) | (velocity << 16)); + } + if (_channelData.channelActive) + _channelData.nextEventTime += getNextEventTime(); + } + return returnVal; +} + +//- command 90h routines + +void SkyGmChannel::com90_caseNoteOff(void) { + + _midiDrv->send((0x90 | _channelData.midiChannelNumber) | (_musicData[_channelData.eventDataPtr] << 8)); + _channelData.eventDataPtr++; +} + +void SkyGmChannel::com90_stopChannel(void) { + + stopNote(); + _channelData.channelActive = 0; +} + +void SkyGmChannel::com90_setupInstrument(void) { + + _midiDrv->send((0xC0 | _channelData.midiChannelNumber) | (_mt32_to_gm[_musicData[_channelData.eventDataPtr]] << 8)); + _channelData.eventDataPtr++; +} + +uint8 SkyGmChannel::com90_updateTempo(void) { + + uint8 retV = _musicData[_channelData.eventDataPtr]; + _channelData.eventDataPtr++; + return retV; +} + +void SkyGmChannel::com90_getPitch(void) { + + _midiDrv->send((0xE0 | _channelData.midiChannelNumber) | 0 | (_musicData[_channelData.eventDataPtr] << 16)); + _channelData.eventDataPtr++; +} + +void SkyGmChannel::com90_getChannelVolume(void) { + + _midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x700 | (_musicData[_channelData.eventDataPtr] << 16)); + _channelData.eventDataPtr++; +} + +void SkyGmChannel::com90_rewindMusic(void) { + + _channelData.eventDataPtr = _channelData.startOfData; +} + +void SkyGmChannel::com90_keyOff(void) { + + _midiDrv->send((0x90 | _channelData.midiChannelNumber) | (_channelData.note << 8) | 0); +} + +void SkyGmChannel::com90_setStartOfData(void) { + + _channelData.startOfData = _channelData.eventDataPtr; +} + +void SkyGmChannel::com90_getChannelPanValue(void) { + + _midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x0A00 | (_musicData[_channelData.eventDataPtr] << 16)); + _channelData.eventDataPtr++; +} + +void SkyGmChannel::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)); +} diff --git a/sky/music/gmchannel.h b/sky/music/gmchannel.h new file mode 100644 index 0000000000..693eabf3d0 --- /dev/null +++ b/sky/music/gmchannel.h @@ -0,0 +1,76 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#ifndef SKYGMCHANNEL_H +#define SKYGMCHANNEL_H + +#include "stdafx.h" +#include "sound/mididrv.h" +#include "common/engine.h" +#include "sky/music/musicbase.h" + +typedef struct { + uint16 eventDataPtr; + int32 nextEventTime; + uint16 startOfData; + uint8 midiChannelNumber; + uint8 note; + uint8 channelActive; +} MidiChannelType; + +class SkyGmChannel : public SkyChannelBase { +public: + SkyGmChannel(uint8 *pMusicData, uint16 startOfData, MidiDriver *pMidiDrv, byte *pInstMap); + virtual void stopNote(void); + virtual uint8 process(uint16 aktTime); + virtual void updateVolume(uint16 pVolume); +private: + byte *_mt32_to_gm; + static uint8 _veloTab[128]; + MidiDriver *_midiDrv; + uint8 *_musicData; + uint16 _musicVolume; + MidiChannelType _channelData; + //- 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 +}; + +#endif //SKYGMCHANNEL_H diff --git a/sky/music/gmmusic.cpp b/sky/music/gmmusic.cpp new file mode 100644 index 0000000000..3d2ddf947d --- /dev/null +++ b/sky/music/gmmusic.cpp @@ -0,0 +1,146 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include "sky/gmmusic.h" + +void SkyGmMusic::passTimerFunc(void *param) { + + ((SkyGmMusic*)param)->timerCall(); +} + +SkyGmMusic::SkyGmMusic(MidiDriver *pMidiDrv, SkyDisk *pSkyDisk, uint32 version) + : SkyMusicBase(pSkyDisk, version) { + + _driverFileBase = 60200; + _midiDrv = pMidiDrv; + int midiRes = _midiDrv->open(); + if (midiRes != 0) { + error("Can't open midi device. Errorcode: %d\n",midiRes); + } + _midiDrv->setTimerCallback(this, passTimerFunc); + _ignoreNextPoll = false; +} + +SkyGmMusic::~SkyGmMusic(void) { + + _midiDrv->setTimerCallback(NULL, NULL); + if (_currentMusic) stopMusic(); + _midiDrv->close(); + delete _midiDrv; +} + +void SkyGmMusic::timerCall(void) { + + // midi driver polls hundred times per sec. We only want 50 times. + _ignoreNextPoll = !_ignoreNextPoll; + if (!_ignoreNextPoll) return; + + if (_musicData != NULL) + pollMusic(); +} + +void SkyGmMusic::setupPointers(void) { + + _musicDataLoc = (_musicData[0x7DD] << 8) | _musicData[0x7DC]; + _sysExSequence = ((_musicData[0x7E1] << 8) | _musicData[0x7E0]) + _musicData; +} + +void SkyGmMusic::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 SkyGmChannel(_musicData, chDataStart, _midiDrv, _mt32_to_gm + 128 * _currentSection); + } +} + +void SkyGmMusic::startDriver(void) { + + //_midiDrv->send(0xFF); //ALSA can't handle this. + // skip all sysEx as it can't be handled anyways. +} + +// not sure about these tables. Now it's just 6 copies of jamiesons table in the +// instruments.cpp, but I still think that there must be something to be done to them. + +byte SkyGmMusic::_mt32_to_gm[6*128] = { +// Section 0: +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 0, 2, 4, 4, 5, 3, 16, 17, 18, 16, 16, 19, 20, 21, // 0x + 6, 6, 6, 7, 7, 7, 8, 112, 62, 62, 63, 63, 38, 38, 39, 39, // 1x + 88, 95, 52, 98, 97, 99, 14, 54, 102, 96, 53, 102, 81, 100, 14, 80, // 2x + 48, 48, 49, 45, 41, 40, 42, 42, 43, 46, 45, 24, 25, 28, 27, 104, // 3x + 32, 32, 34, 33, 36, 37, 35, 35, 79, 73, 72, 72, 74, 75, 64, 65, // 4x + 66, 67, 71, 71, 68, 69, 70, 22, 56, 59, 57, 57, 60, 60, 58, 61, // 5x + 61, 11, 11, 98, 14, 9, 14, 13, 12, 107, 107, 77, 78, 78, 76, 76, // 6x + 47, 117, 127, 118, 118, 116, 115, 119, 115, 112, 55, 124, 123, 0, 14, 117, // 7x +// Section 1: +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 0, 2, 4, 4, 5, 3, 16, 17, 18, 16, 16, 19, 20, 21, // 0x + 6, 6, 6, 7, 7, 7, 8, 112, 62, 62, 63, 63, 38, 38, 39, 39, // 1x + 88, 95, 52, 98, 97, 99, 14, 54, 102, 96, 53, 102, 81, 100, 14, 80, // 2x + 48, 48, 49, 45, 41, 40, 42, 42, 43, 46, 45, 24, 25, 28, 27, 104, // 3x + 32, 32, 34, 33, 36, 37, 35, 35, 79, 73, 72, 72, 74, 75, 64, 65, // 4x + 66, 67, 71, 71, 68, 69, 70, 22, 56, 59, 57, 57, 60, 60, 58, 61, // 5x + 61, 11, 11, 98, 14, 9, 14, 13, 12, 107, 107, 77, 78, 78, 76, 76, // 6x + 47, 117, 127, 118, 118, 116, 115, 119, 115, 112, 55, 124, 123, 0, 14, 117, // 7x +// Section 2: +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 0, 2, 4, 4, 5, 3, 16, 17, 18, 16, 16, 19, 20, 21, // 0x + 6, 6, 6, 7, 7, 7, 8, 112, 62, 62, 63, 63, 38, 38, 39, 39, // 1x + 88, 95, 52, 98, 97, 99, 14, 54, 102, 96, 53, 102, 81, 100, 14, 80, // 2x + 48, 48, 49, 45, 41, 40, 42, 42, 43, 46, 45, 24, 25, 28, 27, 104, // 3x + 32, 32, 34, 33, 36, 37, 35, 35, 79, 73, 72, 72, 74, 75, 64, 65, // 4x + 66, 67, 71, 71, 68, 69, 70, 22, 56, 59, 57, 57, 60, 60, 58, 61, // 5x + 61, 11, 11, 98, 14, 9, 14, 13, 12, 107, 107, 77, 78, 78, 76, 76, // 6x + 47, 117, 127, 118, 118, 116, 115, 119, 115, 112, 55, 124, 123, 0, 14, 117, // 7x +// Section 3: +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 0, 2, 4, 4, 5, 3, 16, 17, 18, 16, 16, 19, 20, 21, // 0x + 6, 6, 6, 7, 7, 7, 8, 112, 62, 62, 63, 63, 38, 38, 39, 39, // 1x + 88, 95, 52, 98, 97, 99, 14, 54, 102, 96, 53, 102, 81, 100, 14, 80, // 2x + 48, 48, 49, 45, 41, 40, 42, 42, 43, 46, 45, 24, 25, 28, 27, 104, // 3x + 32, 32, 34, 33, 36, 37, 35, 35, 79, 73, 72, 72, 74, 75, 64, 65, // 4x + 66, 67, 71, 71, 68, 69, 70, 22, 56, 59, 57, 57, 60, 60, 58, 61, // 5x + 61, 11, 11, 98, 14, 9, 14, 13, 12, 107, 107, 77, 78, 78, 76, 76, // 6x + 47, 117, 127, 118, 118, 116, 115, 119, 115, 112, 55, 124, 123, 0, 14, 117, // 7x +// Section 4: +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 0, 2, 4, 4, 5, 3, 16, 17, 18, 16, 16, 19, 20, 21, // 0x + 6, 6, 6, 7, 7, 7, 8, 112, 62, 62, 63, 63, 38, 38, 39, 39, // 1x + 88, 95, 52, 98, 97, 99, 14, 54, 102, 96, 53, 102, 81, 100, 14, 80, // 2x + 48, 48, 49, 45, 41, 40, 42, 42, 43, 46, 45, 24, 25, 28, 27, 104, // 3x + 32, 32, 34, 33, 36, 37, 35, 35, 79, 73, 72, 72, 74, 75, 64, 65, // 4x + 66, 67, 71, 71, 68, 69, 70, 22, 56, 59, 57, 57, 60, 60, 58, 61, // 5x + 61, 11, 11, 98, 14, 9, 14, 13, 12, 107, 107, 77, 78, 78, 76, 76, // 6x + 47, 117, 127, 118, 118, 116, 115, 119, 115, 112, 55, 124, 123, 0, 14, 117, // 7x +// Section 5: +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 0, 2, 4, 4, 5, 3, 16, 17, 18, 16, 16, 19, 20, 21, // 0x + 6, 6, 6, 7, 7, 7, 8, 112, 62, 62, 63, 63, 38, 38, 39, 39, // 1x + 88, 95, 52, 98, 97, 99, 14, 54, 102, 96, 53, 102, 81, 100, 14, 80, // 2x + 48, 48, 49, 45, 41, 40, 42, 42, 43, 46, 45, 24, 25, 28, 27, 104, // 3x + 32, 32, 34, 33, 36, 37, 35, 35, 79, 73, 72, 72, 74, 75, 64, 65, // 4x + 66, 67, 71, 71, 68, 69, 70, 22, 56, 59, 57, 57, 60, 60, 58, 61, // 5x + 61, 11, 11, 98, 14, 9, 14, 13, 12, 107, 107, 77, 78, 78, 76, 76, // 6x + 47, 117, 127, 118, 118, 116, 115, 119, 115, 112, 55, 124, 123, 0, 14, 117 // 7x +}; diff --git a/sky/music/gmmusic.h b/sky/music/gmmusic.h new file mode 100644 index 0000000000..be26bf26fd --- /dev/null +++ b/sky/music/gmmusic.h @@ -0,0 +1,49 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#ifndef GMMUSIC_H +#define GMMUSIC_H + +#include "stdafx.h" +#include "common/engine.h" +#include "musicbase.h" +#include "sound/mididrv.h" +#include "gmchannel.h" + +class SkyGmMusic : public SkyMusicBase { +public: + SkyGmMusic(MidiDriver *pMidiDrv, SkyDisk *pSkyDisk, uint32 version); + ~SkyGmMusic(void); +private: + static void passTimerFunc(void *param); + void timerCall(void); + + bool _ignoreNextPoll; + uint8 *_sysExSequence; + MidiDriver *_midiDrv; + static byte _mt32_to_gm[6*128]; + + virtual void setupPointers(void); + virtual void setupChannels(uint8 *channelData); + virtual void startDriver(void); +}; + +#endif //GMMUSIC_H diff --git a/sky/music/mt32music.cpp b/sky/music/mt32music.cpp new file mode 100644 index 0000000000..53765f9941 --- /dev/null +++ b/sky/music/mt32music.cpp @@ -0,0 +1,182 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include "sky/mt32music.h" + +void SkyMT32Music::passTimerFunc(void *param) { + + ((SkyMT32Music*)param)->timerCall(); +} + +SkyMT32Music::SkyMT32Music(MidiDriver *pMidiDrv, SkyDisk *pSkyDisk, uint32 version) + : SkyMusicBase(pSkyDisk, version) { + + _driverFileBase = 60200; + _midiDrv = pMidiDrv; + int midiRes = _midiDrv->open(); + if (midiRes != 0) { + error("Can't open midi device. Errorcode: %d\n",midiRes); + } + _midiDrv->setTimerCallback(this, passTimerFunc); + _ignoreNextPoll = false; + for (uint8 cnt = 0; cnt < 128; cnt++) + _dummyMap[cnt] = cnt; +} + +SkyMT32Music::~SkyMT32Music(void) { + + _midiDrv->close(); + _midiDrv->setTimerCallback(NULL, NULL); + delete _midiDrv; +} + +void SkyMT32Music::timerCall(void) { + + // midi driver polls hundred times per sec. We only want 50 times. + _ignoreNextPoll = !_ignoreNextPoll; + if (!_ignoreNextPoll) return; + + if (_musicData != NULL) + pollMusic(); +} + +void SkyMT32Music::setupPointers(void) { + + _musicDataLoc = (_musicData[0x7DD] << 8) | _musicData[0x7DC]; + _sysExSequence = ((_musicData[0x7E1] << 8) | _musicData[0x7E0]) + _musicData; +} + +void SkyMT32Music::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 SkyGmChannel(_musicData, chDataStart, _midiDrv, _dummyMap); + } +} + +#define MIDI_PACK(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24)) + +bool SkyMT32Music::processPatchSysEx(uint8 *sysExData) { + + uint8 crc = 0; + if (sysExData[0] & 0x80) return false; + uint8 patchNum = sysExData[0]; + sysExData++; + + uint8 timbreGroup = sysExData[0] >> 6; + uint8 timbreNumber = sysExData[0] & 0x3F; + uint8 keyShift = sysExData[1] & 0x3F; + uint8 fineTune = sysExData[2] & 0x7F; + uint8 benderRange = sysExData[3] & 0x7F; + uint8 assignMode = sysExData[1] >> 6; + uint8 reverbSwitch = sysExData[2] >> 7; + + _midiDrv->send(MIDI_PACK(0xF0, 0x41, 0x10, 0x16)); + _midiDrv->send(MIDI_PACK(0x12, 5, patchNum >> 4, (patchNum & 0xF) << 3)); + + crc -= 5 + (patchNum >> 4) + ((patchNum & 0xF) << 3); + crc -= timbreGroup + timbreNumber + keyShift + fineTune; + crc -= benderRange + assignMode + reverbSwitch; + + _midiDrv->send(MIDI_PACK(timbreGroup, timbreNumber, keyShift, fineTune)); + _midiDrv->send(MIDI_PACK(benderRange, assignMode, reverbSwitch, crc)); + _midiDrv->send(0xF7); + + debug(3," Patch %02X:\n",patchNum); + debug(3," Timbre Group: %d\n",timbreGroup); + debug(3," Timbre Number: %d\n",timbreNumber); + debug(3," Key Shift: %d\n",keyShift); + debug(3," Fine Tune: %d\n",fineTune); + debug(3," Bender Range: %d\n",benderRange); + debug(3," Assign Mode: %d\n",assignMode); + debug(3," Reverb Switch: %d\n\n",reverbSwitch); + return true; +} + +void SkyMT32Music::startDriver(void) { + + _midiDrv->send(0xFF); // reset midi device + + // setup timbres and patches using SysEx data + uint8* sysExData = _sysExSequence; + uint8 timbreNum = sysExData[0]; + uint8 cnt, crc; + uint32 sysComb; + sysExData++; + for (cnt = 0; cnt < timbreNum; cnt++) { + crc = 0; + _midiDrv->send(MIDI_PACK(0xF0, 0x41, 0x10, 0x16)); + //- sendTimbreAddress + sysComb = (0x2 << 16) | (sysExData[0] << 8) | 0xA; + sysExData++; + uint8 sysByte1 = (uint8)(sysComb >> 14); + uint8 sysByte2 = (uint8)((sysComb & 0x3FFF) >> 7); + uint8 sysByte3 = (uint8)(sysComb & 0x7F); + _midiDrv->send(MIDI_PACK(0x12, sysByte1, sysByte2, sysByte3)); + debug(3,"InitBySysEx: Timbre address: %02X:%02X:%02X (%02X)\n",sysByte1,sysByte2,sysByte3,(sysExData-1)[0]); + crc -= sysByte1 + sysByte2 + sysByte3; + //- sendTimbreData + uint8 dataLen = sysExData[0]; + debug(3,"[%02X]",dataLen); + sysExData++; + uint32 nextSend = 0; + uint8 bytesInSend = 0; + debug(3," Timbre Data:"); + do { + uint8 rlVal = 1; + uint8 codeVal = sysExData[0]; + sysExData++; + + if (codeVal & 0x80) { + codeVal &= 0x7F; + rlVal = sysExData[0]; + sysExData++; + dataLen--; + } + for (uint8 cnt = 0; cnt < rlVal; cnt++) { + nextSend |= codeVal << (bytesInSend << 3); + crc -= codeVal; + debug(3," %02X",codeVal); + bytesInSend++; + if (bytesInSend == 4) { + _midiDrv->send(nextSend); + nextSend = bytesInSend = 0; + } + } + dataLen--; + } while (dataLen > 0); + crc &= 0x7F; + debug(3," %02X F7\n",crc); + nextSend |= crc << (bytesInSend << 3); + bytesInSend++; + if (bytesInSend == 4) { + _midiDrv->send(nextSend); + nextSend = bytesInSend = 0; + } + nextSend |= 0xF7 << (bytesInSend << 3); + _midiDrv->send(nextSend); + } + + while (processPatchSysEx(sysExData)) + sysExData += 5; +} diff --git a/sky/music/mt32music.h b/sky/music/mt32music.h new file mode 100644 index 0000000000..9b396b649d --- /dev/null +++ b/sky/music/mt32music.h @@ -0,0 +1,50 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#ifndef MT32MUSIC_H +#define MT32MUSIC_H + +#include "stdafx.h" +#include "common/engine.h" +#include "musicbase.h" +#include "sound/mididrv.h" +#include "gmchannel.h" + +class SkyMT32Music : public SkyMusicBase { +public: + SkyMT32Music(MidiDriver *pMidiDrv, SkyDisk *pSkyDisk, uint32 version); + ~SkyMT32Music(void); +private: + static void passTimerFunc(void *param); + void timerCall(void); + bool processPatchSysEx(uint8 *sysExData); + + bool _ignoreNextPoll; + uint8 *_sysExSequence; + MidiDriver *_midiDrv; + uint8 _dummyMap[128]; + + virtual void setupPointers(void); + virtual void setupChannels(uint8 *channelData); + virtual void startDriver(void); +}; + +#endif //MT32MUSIC_H diff --git a/sky/music/musicbase.cpp b/sky/music/musicbase.cpp new file mode 100644 index 0000000000..7d930dae18 --- /dev/null +++ b/sky/music/musicbase.cpp @@ -0,0 +1,164 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include "musicbase.h" + +SkyMusicBase::SkyMusicBase(SkyDisk *pSkyDisk, uint32 version) { + + _gameVersion = version; + _musicData = NULL; + _allowedCommands = 0; + _skyDisk = pSkyDisk; + _currentMusic = 0; +} + +SkyMusicBase::~SkyMusicBase(void) +{ + if (_musicData) free(_musicData); +} + +void SkyMusicBase::loadSection(uint8 pSection) +{ + if (_currentMusic) stopMusic(); + if (_musicData) free(_musicData); + _currentSection = pSection; + _musicData = _skyDisk->loadFile(_driverFileBase + FILES_PER_SECTION * pSection, NULL); + _allowedCommands = 0; + _musicTempo0 = 0x78; // init constants taken from idb file, area ~0x1060 + _musicTempo1 = 0xC0; + _musicVolume = 0x100; + _onNextPoll.doReInit = false; + _onNextPoll.doStopMusic = false; + _onNextPoll.musicToProcess = 0; + _tempo = _aktTime = 0x10001; + _numberOfChannels = _currentMusic = 0; + setupPointers(); + startDriver(); +} + +void SkyMusicBase::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: + debug(1,"SkyMusic: got call to startAdlibDriver(). Not necessary in this implementation.\n"); + break; + case 1: + 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 SkyMusicBase::setFMVolume(uint16 param) +{ + _musicVolume = param; + for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) + _channels[cnt]->updateVolume(_musicVolume); +} + +void SkyMusicBase::stopMusic(void) +{ + for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) { + _channels[cnt]->stopNote(); + delete _channels[cnt]; + } + _numberOfChannels = 0; +} + +void SkyMusicBase::updateTempo(void) +{ + uint16 tempoMul = _musicTempo0*_musicTempo1; + uint16 divisor = 0x4446390/23864; + _tempo = (tempoMul / divisor)<<16; + _tempo |= (((tempoMul%divisor)<<16) | (tempoMul/divisor)) / divisor; +} + +void SkyMusicBase::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; + + _musicTempo0 = _musicData[musicPos]; + _musicTempo1 = _musicData[musicPos+1]; + + setupChannels(_musicData + musicPos + 2); + + updateTempo(); + } +} + +void SkyMusicBase::pollMusic(void) +{ + uint8 newTempo; + if (_onNextPoll.doReInit) startDriver(); + 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 --git a/sky/music/musicbase.h b/sky/music/musicbase.h new file mode 100644 index 0000000000..306cfaa0db --- /dev/null +++ b/sky/music/musicbase.h @@ -0,0 +1,85 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#ifndef MUSICBASE_H +#define MUSICBASE_H + +#include "stdafx.h" +#include "common/engine.h" +#include "sky/disk.h" + +#define FILES_PER_SECTION 4 + +typedef struct { + bool doReInit, doStopMusic; + uint8 musicToProcess; +} Actions; + +class SkyChannelBase { +public: + virtual void stopNote(void) = 0; + virtual uint8 process(uint16 aktTime) = 0; + virtual void updateVolume(uint16 pVolume) = 0; +private: +}; + +class SkyMusicBase { +public: + SkyMusicBase(SkyDisk *pSkyDisk, uint32 version); + virtual ~SkyMusicBase(void); + void loadSection(uint8 pSection); + void musicCommand(uint16 command); + void startMusic(uint16 param) { _onNextPoll.musicToProcess = param & 0xF; }; // 4 + +protected: + uint32 _gameVersion; + + SkyDisk *_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; + SkyChannelBase *_channels[10]; + + virtual void setupPointers(void) = 0; + virtual void setupChannels(uint8 *channelData) = 0; + + void updateTempo(void); + void loadNewMusic(void); + //- functions from CommandTable @0x90 (the main interface) + virtual void startDriver(void) = 0; // 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 //MUSICBASE_H |