diff options
author | Robert Göffringmann | 2003-05-03 02:59:45 +0000 |
---|---|---|
committer | Robert Göffringmann | 2003-05-03 02:59:45 +0000 |
commit | 180582765563cd2c2f0dd81a3e189c865ac0741d (patch) | |
tree | 2f0db1b24d5e651344ed36a738e0e368edfd358d /sky | |
parent | 54a166582f457ff352477637b0b62fe632c301c6 (diff) | |
download | scummvm-rg350-180582765563cd2c2f0dd81a3e189c865ac0741d.tar.gz scummvm-rg350-180582765563cd2c2f0dd81a3e189c865ac0741d.tar.bz2 scummvm-rg350-180582765563cd2c2f0dd81a3e189c865ac0741d.zip |
changed structure of musicdriver, added General Midi music support.
mapping table mt32->gm needs a lot of changes.
svn-id: r7273
Diffstat (limited to 'sky')
-rw-r--r-- | sky/adlibchannel.cpp (renamed from sky/skychannel.cpp) | 45 | ||||
-rw-r--r-- | sky/adlibchannel.h (renamed from sky/skychannel.h) | 21 | ||||
-rw-r--r-- | sky/adlibmusic.cpp | 101 | ||||
-rw-r--r-- | sky/adlibmusic.h | 49 | ||||
-rw-r--r-- | sky/gmchannel.cpp | 201 | ||||
-rw-r--r-- | sky/gmchannel.h | 75 | ||||
-rw-r--r-- | sky/gmmusic.cpp | 78 | ||||
-rw-r--r-- | sky/gmmusic.h | 46 | ||||
-rw-r--r-- | sky/musicbase.cpp (renamed from sky/skymusic.cpp) | 118 | ||||
-rw-r--r-- | sky/musicbase.h (renamed from sky/skymusic.h) | 42 | ||||
-rw-r--r-- | sky/sky.cpp | 20 | ||||
-rw-r--r-- | sky/sky.h | 8 |
12 files changed, 648 insertions, 156 deletions
diff --git a/sky/skychannel.cpp b/sky/adlibchannel.cpp index cba947bd29..60a43ed4f4 100644 --- a/sky/skychannel.cpp +++ b/sky/adlibchannel.cpp @@ -19,10 +19,9 @@ * */ -#include "skychannel.h" -#include "sound/fmopl.h" +#include "adlibchannel.h" -SkyChannel::SkyChannel(uint8 *pMusicData, uint16 startOfData, FM_OPL *pOpl) +SkyAdlibChannel::SkyAdlibChannel(uint8 *pMusicData, uint16 startOfData, FM_OPL *pOpl) { _musicData = pMusicData; _opl = pOpl; @@ -51,7 +50,7 @@ SkyChannel::SkyChannel(uint8 *pMusicData, uint16 startOfData, FM_OPL *pOpl) _musicVolume = 0x100; } -void SkyChannel::updateVolume(uint16 pVolume) { +void SkyAdlibChannel::updateVolume(uint16 pVolume) { _musicVolume = pVolume; } @@ -60,7 +59,7 @@ void SkyChannel::updateVolume(uint16 pVolume) { asm driver did (_musicData[0xF5F..0x105E]), so the cache is indeed shared by all instances of the class. */ -void SkyChannel::setRegister(uint8 regNum, uint8 value) { +void SkyAdlibChannel::setRegister(uint8 regNum, uint8 value) { if (_adlibRegMirror[regNum] != value) { OPLWriteReg(_opl,regNum,value); @@ -68,7 +67,7 @@ void SkyChannel::setRegister(uint8 regNum, uint8 value) { } } -void SkyChannel::stopNote(void) { +void SkyAdlibChannel::stopNote(void) { if (_channelData.note & 0x20) { _channelData.note &= ~0x20; @@ -76,7 +75,7 @@ void SkyChannel::stopNote(void) { } } -int32 SkyChannel::getNextEventTime(void) +int32 SkyAdlibChannel::getNextEventTime(void) { int32 retV = 0; uint8 cnt, lVal; @@ -92,7 +91,7 @@ int32 SkyChannel::getNextEventTime(void) } -uint8 SkyChannel::process(uint16 aktTime) { +uint8 SkyAdlibChannel::process(uint16 aktTime) { if (!_channelData.channelActive) { return 0; @@ -108,8 +107,6 @@ uint8 SkyChannel::process(uint16 aktTime) { if (opcode&0x80) { if (opcode == 0xFF) { // dummy opcode - } else if (opcode > 0x9D) { - } else if (opcode >= 0x90) { switch (opcode&0xF) { case 0: com90_caseNoteOff(); break; @@ -162,7 +159,7 @@ uint8 SkyChannel::process(uint16 aktTime) { return returnVal; } -void SkyChannel::setupInstrument(uint8 opcode) { +void SkyAdlibChannel::setupInstrument(uint8 opcode) { uint16 nextNote; if (_channelData.tremoVibro) { @@ -183,7 +180,7 @@ void SkyChannel::setupInstrument(uint8 opcode) { _channelData.note = (uint8)((nextNote >> 8) | 0x20); } -void SkyChannel::setupChannelVolume(uint8 volume) { +void SkyAdlibChannel::setupChannelVolume(uint8 volume) { uint8 resultOp; uint32 resVol = ((volume + 1) * (_channelData.instrumentData->totOutLev_Op2 + 1)) << 1; @@ -206,7 +203,7 @@ void SkyChannel::setupChannelVolume(uint8 volume) { setRegister(0x40 | _channelData.adlibReg1, resultOp); } -void SkyChannel::adlibSetupInstrument(void) { +void SkyAdlibChannel::adlibSetupInstrument(void) { setRegister(0x60 | _channelData.adlibReg1, _channelData.instrumentData->ad_Op1); setRegister(0x60 | _channelData.adlibReg2, _channelData.instrumentData->ad_Op2); @@ -225,7 +222,7 @@ void SkyChannel::adlibSetupInstrument(void) { #define ENDIAN16(x) (x) #endif -uint16 SkyChannel::getNextNote(uint8 param) { +uint16 SkyAdlibChannel::getNextNote(uint8 param) { int16 freqIndex = ((int16)_channelData.freqOffset) - 0x40; if (freqIndex >= 0x3F) freqIndex++; @@ -242,20 +239,20 @@ uint16 SkyChannel::getNextNote(uint8 param) { //- command 90h routines -void SkyChannel::com90_caseNoteOff(void) { +void SkyAdlibChannel::com90_caseNoteOff(void) { if (_musicData[_channelData.eventDataPtr] == _channelData.lastCommand) stopNote(); _channelData.eventDataPtr++; } -void SkyChannel::com90_stopChannel(void) { +void SkyAdlibChannel::com90_stopChannel(void) { stopNote(); _channelData.channelActive = 0; } -void SkyChannel::com90_setupInstrument(void) { +void SkyAdlibChannel::com90_setupInstrument(void) { _channelData.channelVolume = 0x7F; _channelData.freqOffset = 0x40; @@ -265,14 +262,14 @@ void SkyChannel::com90_setupInstrument(void) { adlibSetupInstrument(); } -uint8 SkyChannel::com90_updateTempo(void) { +uint8 SkyAdlibChannel::com90_updateTempo(void) { uint8 retV = _musicData[_channelData.eventDataPtr]; _channelData.eventDataPtr++; return retV; } -void SkyChannel::com90_getFreqOffset(void) { +void SkyAdlibChannel::com90_getFreqOffset(void) { _channelData.freqOffset = _musicData[_channelData.eventDataPtr]; _channelData.eventDataPtr++; @@ -285,29 +282,29 @@ void SkyChannel::com90_getFreqOffset(void) { } } -void SkyChannel::com90_getChannelVolume(void) { +void SkyAdlibChannel::com90_getChannelVolume(void) { _channelData.channelVolume = _musicData[_channelData.eventDataPtr]; _channelData.eventDataPtr++; } -void SkyChannel::com90_getTremoVibro(void) { +void SkyAdlibChannel::com90_getTremoVibro(void) { _channelData.tremoVibro = _musicData[_channelData.eventDataPtr]; _channelData.eventDataPtr++; } -void SkyChannel::com90_rewindMusic(void) { +void SkyAdlibChannel::com90_rewindMusic(void) { _channelData.eventDataPtr = _channelData.startOfData; } -void SkyChannel::com90_keyOff(void) { +void SkyAdlibChannel::com90_keyOff(void) { stopNote(); } -void SkyChannel::com90_setStartOfData(void) { +void SkyAdlibChannel::com90_setStartOfData(void) { _channelData.startOfData = _channelData.eventDataPtr; } diff --git a/sky/skychannel.h b/sky/adlibchannel.h index 22221e2cea..485a99940a 100644 --- a/sky/skychannel.h +++ b/sky/adlibchannel.h @@ -19,12 +19,13 @@ * */ -#ifndef SKYCHANNEL_H -#define SKYCHANNEL_H +#ifndef ADLIBCHANNEL_H +#define ADLIBCHANNEL_H #include "stdafx.h" #include "sound/fmopl.h" #include "common/engine.h" +#include "sky/musicbase.h" typedef struct { uint8 ad_Op1, ad_Op2; @@ -55,19 +56,19 @@ typedef struct { uint8 freqDataSize; uint8 freqOffset; uint16 frequency; -} ChannelType; +} AdlibChannelType; -class SkyChannel { +class SkyAdlibChannel : public SkyChannelBase { public: - SkyChannel(uint8 *pMusicData, uint16 startOfData, FM_OPL *pOpl); - void stopNote(void); - uint8 process(uint16 aktTime); - void updateVolume(uint16 pVolume); + SkyAdlibChannel(uint8 *pMusicData, uint16 startOfData, FM_OPL *pOpl); + virtual void stopNote(void); + virtual uint8 process(uint16 aktTime); + virtual void updateVolume(uint16 pVolume); private: uint8 *_musicData; uint16 _musicVolume; FM_OPL *_opl; - ChannelType _channelData; + AdlibChannelType _channelData; //- InstrumentStruct *_instruments; uint16 *_frequenceTable; @@ -98,4 +99,4 @@ private: //void com90_do_two_Lodsb(void); // 13 }; -#endif //SKYCHANNEL_H +#endif //ADLIBCHANNEL_H diff --git a/sky/adlibmusic.cpp b/sky/adlibmusic.cpp new file mode 100644 index 0000000000..2e62b9db03 --- /dev/null +++ b/sky/adlibmusic.cpp @@ -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$ + * + */ + +#include "sky/adlibmusic.h" + +void SkyAdlibMusic::passMixerFunc(void *param, int16 *buf, uint len) { + + ((SkyAdlibMusic*)param)->premixerCall(buf, len); +} + +SkyAdlibMusic::SkyAdlibMusic(SoundMixer *pMixer, SkyDisk *pSkyDisk) + : SkyMusicBase(pSkyDisk) { + + _driverFileBase = 60202; + _mixer = pMixer; + _sampleRate = g_system->property(OSystem::PROP_GET_SAMPLE_RATE, 0); + int env_bits = g_system->property(OSystem::PROP_GET_FMOPL_ENV_BITS, NULL); + int eg_ent = g_system->property(OSystem::PROP_GET_FMOPL_EG_ENT, NULL); + OPLBuildTables((env_bits ? env_bits : FMOPL_ENV_BITS_HQ), (eg_ent ? eg_ent : FMOPL_EG_ENT_HQ)); + _opl = OPLCreate(OPL_TYPE_YM3812, 3579545, _sampleRate); + _mixer->setupPremix(this, passMixerFunc); +} + +SkyAdlibMusic::~SkyAdlibMusic(void) { + + _mixer->setupPremix(NULL, NULL); + OPLDestroy(_opl); +} + +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(_opl, buf, render); + buf += render; + if (_nextMusicPoll == 0) { + pollMusic(); + _nextMusicPoll = _sampleRate/50; + } + } +} + +void SkyAdlibMusic::setupPointers(void) { + + _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, _opl); + } +} + +void SkyAdlibMusic::startDriver(void) { + + uint16 cnt = 0; + while (_initSequence[cnt] || _initSequence[cnt+1]) { + OPLWriteReg(_opl, _initSequence[cnt], _initSequence[cnt+1]); + cnt += 2; + } + _allowedCommands = 0xD; +} diff --git a/sky/adlibmusic.h b/sky/adlibmusic.h new file mode 100644 index 0000000000..45cc94219b --- /dev/null +++ b/sky/adlibmusic.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 ADLIBMUSIC_H +#define ADLIBMUSIC_H + +#include "stdafx.h" +#include "sound/fmopl.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); + ~SkyAdlibMusic(void); +private: + SoundMixer *_mixer; + FM_OPL *_opl; + 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/gmchannel.cpp b/sky/gmchannel.cpp new file mode 100644 index 0000000000..6383427c0d --- /dev/null +++ b/sky/gmchannel.cpp @@ -0,0 +1,201 @@ +/* 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" + +// instrument map copied from scumm/instrument.cpp + +const byte SkyGmChannel::_mt32_to_gm[128] = { +// 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 +}; + +SkyGmChannel::SkyGmChannel(uint8 *pMusicData, uint16 startOfData, MidiDriver *pMidiDrv) +{ + _musicData = pMusicData; + _midiDrv = pMidiDrv; + _channelData.startOfData = startOfData; + _channelData.eventDataPtr = startOfData; + _channelData.channelActive = 1; + _channelData.nextEventTime = getNextEventTime(); + + _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; + _midiDrv->send((0x90 | _channelData.midiChannelNumber) | (opcode << 8) | (_musicData[_channelData.eventDataPtr] << 16)); + _channelData.eventDataPtr++; + } + 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/gmchannel.h b/sky/gmchannel.h new file mode 100644 index 0000000000..1f5edede12 --- /dev/null +++ b/sky/gmchannel.h @@ -0,0 +1,75 @@ +/* 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/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); + virtual void stopNote(void); + virtual uint8 process(uint16 aktTime); + virtual void updateVolume(uint16 pVolume); +private: + static const byte _mt32_to_gm[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/gmmusic.cpp b/sky/gmmusic.cpp new file mode 100644 index 0000000000..2b0e5f45ad --- /dev/null +++ b/sky/gmmusic.cpp @@ -0,0 +1,78 @@ +/* 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) + : SkyMusicBase(pSkyDisk) { + + _driverFileBase = 60200; + _midiDrv = pMidiDrv; + int midiRes = _midiDrv->open(); + if (midiRes != 0) { + printf("Error code: %d\n",midiRes); + } + _midiDrv->setTimerCallback(this, passTimerFunc); + ignoreNextPoll = false; +} + +SkyGmMusic::~SkyGmMusic(void) { + + _midiDrv->close(); + _midiDrv->setTimerCallback(NULL, NULL); + 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); + } +} + +void SkyGmMusic::startDriver(void) { + + _midiDrv->send(0xFF); +} diff --git a/sky/gmmusic.h b/sky/gmmusic.h new file mode 100644 index 0000000000..51f1bd81c2 --- /dev/null +++ b/sky/gmmusic.h @@ -0,0 +1,46 @@ +/* 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); + ~SkyGmMusic(void); +private: + static void passTimerFunc(void *param); + void timerCall(void); + bool ignoreNextPoll; + uint8 *_sysExSequence; + MidiDriver *_midiDrv; + virtual void setupPointers(void); + virtual void setupChannels(uint8 *channelData); + virtual void startDriver(void); +}; + +#endif //GMMUSIC_H diff --git a/sky/skymusic.cpp b/sky/musicbase.cpp index 96a5854bef..233cc524d4 100644 --- a/sky/skymusic.cpp +++ b/sky/musicbase.cpp @@ -19,103 +19,40 @@ * */ -#include "skymusic.h" +#include "musicbase.h" -void SkyMusic::passMixerFunc(void *param, int16 *buf, uint len) { - - ((SkyMusic*)param)->premixerCall(buf, len); -} - -void SkyMusic::premixerCall(int16 *buf, uint len) { - - if (_musicData == NULL) { - // no music loaded - memset(buf, 0, len * sizeof(int16)); - return; - } else if ((_currentMusic == 0) || (_numberOfChannels == 0)) { - // music loaded but not played as of yet - memset(buf, 0, len * sizeof(int16)); - // poll anyways as pollMusic() can activate the music - pollMusic(); - _nextMusicPoll = _sampleRate/50; - return; - } - uint32 render; - while (len) { - render = (len > _nextMusicPoll) ? (_nextMusicPoll) : (len); - len -= render; - _nextMusicPoll -= render; - YM3812UpdateOne(_opl, buf, render); - buf += render; - if (_nextMusicPoll == 0) { - pollMusic(); - _nextMusicPoll = _sampleRate/50; - } - } -} - -SkyMusic::SkyMusic(SoundMixer *mixer, SkyDisk *pSkyDisk) { +SkyMusicBase::SkyMusicBase(SkyDisk *pSkyDisk) { _musicData = NULL; _allowedCommands = 0; - _mixer = mixer; - _sampleRate = g_system->property(OSystem::PROP_GET_SAMPLE_RATE, 0); - int env_bits = g_system->property(OSystem::PROP_GET_FMOPL_ENV_BITS, NULL); - int eg_ent = g_system->property(OSystem::PROP_GET_FMOPL_EG_ENT, NULL); - OPLBuildTables((env_bits ? env_bits : FMOPL_ENV_BITS_HQ), (eg_ent ? eg_ent : FMOPL_EG_ENT_HQ)); - _opl = OPLCreate(OPL_TYPE_YM3812, 3579545, _sampleRate); - _mixer->setupPremix(this, passMixerFunc); _skyDisk = pSkyDisk; } -SkyMusic::~SkyMusic(void) +SkyMusicBase::~SkyMusicBase(void) { if (_currentMusic) stopMusic(); - _mixer->setupPremix(NULL, NULL); if (_musicData) free(_musicData); - OPLDestroy(_opl); } -void SkyMusic::loadSectionMusic(uint8 pSection) +void SkyMusicBase::loadSectionMusic(uint8 pSection) { if (_currentMusic) stopMusic(); if (_musicData) free(_musicData); - _musicData = _skyDisk->loadFile(MUSIC_BASE_FILE + FILES_PER_SECTION*pSection,NULL); + _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; - _numberOfChannels = _currentMusic = 0; - _musicDataLoc = (_musicData[0x1202]<<8)|_musicData[0x1201]; - _initSequence = _musicData+0xE91; _onNextPoll.doReInit = false; _onNextPoll.doStopMusic = false; _onNextPoll.musicToProcess = 0; _tempo = _aktTime = 0x10001; - _nextMusicPoll = 0; - startAdlibDriver(); -} - -/*void SkyMusic::loadData(uint8 *pMusicData) -{ - if (_currentMusic) stopMusic(); - if (_musicData) free(_musicData); - _musicData = pMusicData; - _allowedCommands = 0; - _musicTempo0 = 0x78; // init constants taken from idb file, area ~0x1060 - _musicTempo1 = 0xC0; - _musicVolume = 0x100; _numberOfChannels = _currentMusic = 0; - _musicDataLoc = (_musicData[0x1202]<<8)|_musicData[0x1201]; - _initSequence = _musicData+0xE91; - _onNextPoll.doReInit = false; - _onNextPoll.doStopMusic = false; - _onNextPoll.musicToProcess = 0; - _tempo = _aktTime = 0x10001; - _nextMusicPoll = 0; -}*/ + setupPointers(); + startDriver(); +} -void SkyMusic::musicCommand(uint16 command) +void SkyMusicBase::musicCommand(uint16 command) { if (_musicData == NULL) { debug(1,"Got music command but driver is not yet loaded.\n"); @@ -127,11 +64,9 @@ void SkyMusic::musicCommand(uint16 command) } switch(command >> 8) { case 0: - //startAdlibDriver(); break; debug(1,"SkyMusic: got call to startAdlibDriver(). Not necessary in this implementation.\n"); break; case 1: - //StopDriver(command&0xFF); break; debug(1,"SkyMusic: got call to stopDriver(). Not necessary in this implementation.\n"); break; case 2: @@ -157,24 +92,14 @@ void SkyMusic::musicCommand(uint16 command) } } -void SkyMusic::setFMVolume(uint16 param) +void SkyMusicBase::setFMVolume(uint16 param) { _musicVolume = param; for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) _channels[cnt]->updateVolume(_musicVolume); } -void SkyMusic::startAdlibDriver(void) -{ - uint16 cnt = 0; - while (_initSequence[cnt] || _initSequence[cnt+1]) { - OPLWriteReg(_opl, _initSequence[cnt], _initSequence[cnt+1]); - cnt += 2; - } - _allowedCommands = 0xD; -} - -void SkyMusic::stopMusic(void) +void SkyMusicBase::stopMusic(void) { for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) { _channels[cnt]->stopNote(); @@ -183,7 +108,7 @@ void SkyMusic::stopMusic(void) _numberOfChannels = 0; } -void SkyMusic::updateTempo(void) +void SkyMusicBase::updateTempo(void) { uint16 tempoMul = _musicTempo0*_musicTempo1; uint16 divisor = 0x4446390/23864; @@ -191,7 +116,7 @@ void SkyMusic::updateTempo(void) _tempo |= (((tempoMul%divisor)<<16) | (tempoMul/divisor)) / divisor; } -void SkyMusic::loadNewMusic(void) +void SkyMusicBase::loadNewMusic(void) { uint16 musicPos; if (_onNextPoll.musicToProcess > _musicData[_musicDataLoc]) { @@ -208,27 +133,24 @@ void SkyMusic::loadNewMusic(void) musicPos += _musicDataLoc+((_currentMusic-1)<<1); musicPos = ((_musicData[musicPos+1]<<8) | _musicData[musicPos]) + _musicDataLoc; - _musicTempo1 = _musicData[musicPos+1]; _musicTempo0 = _musicData[musicPos]; - _numberOfChannels = _musicData[musicPos+2]; - musicPos += 3; + _musicTempo1 = _musicData[musicPos+1]; + + setupChannels(_musicData + musicPos + 2); - for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) { - uint16 chDataStart = ((_musicData[musicPos+1]<<8) | _musicData[musicPos]) + _musicDataLoc; - _channels[cnt] = new SkyChannel(_musicData, chDataStart, _opl); - musicPos += 2; - } updateTempo(); } } -void SkyMusic::pollMusic(void) +void SkyMusicBase::pollMusic(void) { uint8 newTempo; - if (_onNextPoll.doReInit) startAdlibDriver(); + 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) { diff --git a/sky/skymusic.h b/sky/musicbase.h index cb9bea6dd6..a01a41df78 100644 --- a/sky/skymusic.h +++ b/sky/musicbase.h @@ -19,18 +19,13 @@ * */ - -#ifndef SKYMUSIC_H -#define SKYMUSIC_H +#ifndef MUSICBASE_H +#define MUSICBASE_H #include "stdafx.h" -#include "sound/fmopl.h" -#include "sound/mixer.h" #include "common/engine.h" -#include "skychannel.h" #include "disk.h" -#define MUSIC_BASE_FILE 60202 // usually 60200 ( + 0 for Roland and +2 for Adlib) #define FILES_PER_SECTION 4 typedef struct { @@ -38,24 +33,28 @@ typedef struct { uint8 musicToProcess; } Actions; -class SkyMusic { +class SkyChannelBase { public: - SkyMusic(SoundMixer *mixer, SkyDisk *pSkyDisk); - ~SkyMusic(void); - //void loadData(uint8 *pMusicData); + virtual void stopNote(void) = 0; + virtual uint8 process(uint16 aktTime) = 0; + virtual void updateVolume(uint16 pVolume) = 0; +private: +}; + +class SkyMusicBase { +public: + SkyMusicBase(SkyDisk *pSkyDisk); + ~SkyMusicBase(void); void loadSectionMusic(uint8 pSection); void musicCommand(uint16 command); void startMusic(uint16 param) { _onNextPoll.musicToProcess = param & 0xF; }; // 4 -private: - SoundMixer *_mixer; +protected: SkyDisk *_skyDisk; - FM_OPL *_opl; uint8 *_musicData; - uint8 *_initSequence; uint8 _allowedCommands; uint16 _musicDataLoc; - SkyChannel *_channels[10]; + uint16 _driverFileBase; uint16 _musicVolume, _numberOfChannels; uint8 _currentMusic; @@ -63,15 +62,16 @@ private: uint8 _musicTempo1; // given once per music uint32 _tempo; // calculated from musicTempo0 and musicTempo1 uint32 _aktTime; - uint32 _sampleRate, _nextMusicPoll; Actions _onNextPoll; + SkyChannelBase *_channels[10]; - void premixerCall(int16 *buf, uint len); - static void passMixerFunc(void *param, int16 *buf, uint len); + virtual void setupPointers(void) = 0; + virtual void setupChannels(uint8 *channelData) = 0; + void updateTempo(void); void loadNewMusic(void); //- functions from CommandTable @0x90 (the main interface) - void startAdlibDriver(void); // 0 + virtual void startDriver(void) = 0; // 0 void StopDriver(void); // 1 void setTempo(uint16 newTempo); // 2 void pollMusic(); // 3 @@ -80,4 +80,4 @@ private: void setFMVolume(uint16 param); // 13 }; -#endif //SKYMUSIC_H +#endif //MUSICBASE_H diff --git a/sky/sky.cpp b/sky/sky.cpp index d364351e77..0f311940ff 100644 --- a/sky/sky.cpp +++ b/sky/sky.cpp @@ -66,6 +66,7 @@ SkyState::SkyState(GameDetector *detector, OSystem *syst) _debugMode = detector->_debugMode; _debugLevel = detector->_debugLevel; _language = detector->_language; + _detector = detector; } SkyState::~SkyState() { @@ -107,7 +108,24 @@ void SkyState::initialise(void) { _skySound = new SkySound(_mixer); _skyDisk = new SkyDisk(_gameDataPath); - _skyMusic = new SkyMusic(_mixer, _skyDisk); + + // FIXME: This is *ugly* (and maybe even incorrect?) + // We need to know if we have to use adlib for midi or not. + + if (_detector->_midi_driver == MD_ADLIB) { + _skyMusic = new SkyAdlibMusic(_mixer, _skyDisk); + } else { + if (_detector->_midi_driver == MD_AUTO) { +#if defined (_WIN32_WCE) || defined(UNIX) || defined(X11_BACKEND) + _skyMusic = new SkyAdlibMusic(_mixer, _skyDisk); +#else + _skyMusic = new SkyGmMusic(_detector->createMidi(), _skyDisk); +#endif + } else { + _skyMusic = new SkyGmMusic(_detector->createMidi(), _skyDisk); + } + } + _gameVersion = _skyDisk->determineGameVersion(); _skyText = getSkyText(); @@ -31,8 +31,10 @@ #include "sky/text.h" #include "sky/disk.h" #include "sky/struc.h" -#include "sky/skymusic.h" #include "sky/grid.h" +#include "sky/musicbase.h" +#include "sky/adlibmusic.h" +#include "sky/gmmusic.h" class SkyLogic; class SkyGrid; @@ -87,7 +89,9 @@ protected: SkyText *_skyText; SkyGrid *_skyGrid; SkyLogic *_skyLogic; - SkyMusic *_skyMusic; + + SkyMusicBase *_skyMusic; + GameDetector *_detector; // necessary for music byte *_workScreen; byte *_backScreen; |