Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,316 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "common/textconsole.h"
#include "audio/fmopl.h"
#include "sky/music/adlibchannel.h"
#include "sky/sky.h"
namespace Sky {
AdLibChannel::AdLibChannel(OPL::OPL *opl, uint8 *pMusicData, uint16 startOfData) {
_opl = opl;
_musicData = pMusicData;
_channelData.loopPoint = startOfData;
_channelData.eventDataPtr = startOfData;
_channelData.channelActive = true;
_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;
_musicVolume = 128;
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);
}
AdLibChannel::~AdLibChannel() {
stopNote();
}
bool AdLibChannel::isActive() {
return _channelData.channelActive;
}
void AdLibChannel::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 AdLibChannel::setRegister(uint8 regNum, uint8 value) {
if (_adlibRegMirror[regNum] != value) {
_opl->writeReg(regNum, value);
_adlibRegMirror[regNum] = value;
}
}
void AdLibChannel::stopNote() {
if (_channelData.note & 0x20) {
_channelData.note &= ~0x20;
setRegister(0xB0 | _channelData.adlibChannelNumber, _channelData.note);
}
}
int32 AdLibChannel::getNextEventTime() {
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) {
return -1; // should never happen
} 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_loopMusic(); break;
case 9: com90_keyOff(); break;
case 12: com90_setLoopPoint(); break;
default:
error("AdLibChannel: Unknown music opcode 0x%02X", opcode);
break;
}
} else {
// new adlib channel assignment
_channelData.adlibChannelNumber = opcode & 0xF;
_channelData.adlibReg1 = _registerTable[((opcode & 0xF) << 1) | 0];
_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 << 1;
resVol >>= 16;
assert(resVol < 0x81);
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 << 1;
resVol >>= 16;
} else
resVol = _channelData.instrumentData->totOutLev_Op1;
assert(resVol < 0x81);
resultOp = ((_channelData.instrumentData->scalingLevel << 2) & 0xC0) | _opOutputTable[resVol];
setRegister(0x40 | _channelData.adlibReg1, resultOp);
}
void AdLibChannel::adlibSetupInstrument() {
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);
}
uint16 AdLibChannel::getNextNote(uint8 param) {
int16 freqIndex = ((int16)_channelData.freqOffset) - 0x40;
if (freqIndex >= 0x3F)
freqIndex++;
freqIndex *= 2;
freqIndex += param << 6;
uint16 freqData = FROM_LE_16(_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() {
if (_musicData[_channelData.eventDataPtr] == _channelData.lastCommand)
stopNote();
_channelData.eventDataPtr++;
}
void AdLibChannel::com90_stopChannel() {
stopNote();
_channelData.channelActive = false;
}
void AdLibChannel::com90_setupInstrument() {
_channelData.channelVolume = 0x7F;
_channelData.freqOffset = 0x40;
_channelData.assignedInstrument = _musicData[_channelData.eventDataPtr];
_channelData.eventDataPtr++;
_channelData.instrumentData = _instruments + _channelData.assignedInstrument;
adlibSetupInstrument();
}
uint8 AdLibChannel::com90_updateTempo() {
return _musicData[_channelData.eventDataPtr++];
}
void AdLibChannel::com90_getFreqOffset() {
_channelData.freqOffset = _musicData[_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() {
_channelData.channelVolume = _musicData[_channelData.eventDataPtr++];
}
void AdLibChannel::com90_getTremoVibro() {
_channelData.tremoVibro = _musicData[_channelData.eventDataPtr++];
}
void AdLibChannel::com90_loopMusic() {
_channelData.eventDataPtr = _channelData.loopPoint;
}
void AdLibChannel::com90_keyOff() {
stopNote();
}
void AdLibChannel::com90_setLoopPoint() {
_channelData.loopPoint = _channelData.eventDataPtr;
}
} // End of namespace Sky

View File

@@ -0,0 +1,110 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SKY_MUSIC_ADLIBCHANNEL_H
#define SKY_MUSIC_ADLIBCHANNEL_H
#include "sky/music/musicbase.h"
namespace OPL {
class OPL;
}
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 loopPoint;
uint8 adlibChannelNumber;
uint8 lastCommand;
bool channelActive;
uint8 note;
uint8 adlibReg1, adlibReg2;
InstrumentStruct *instrumentData;
uint8 assignedInstrument;
uint8 channelVolume;
uint8 padding; // field_12 / not used by original driver
uint8 tremoVibro;
uint8 freqOffset;
uint16 frequency;
} AdLibChannelType;
class AdLibChannel : public ChannelBase {
public:
AdLibChannel (OPL::OPL *opl, uint8 *pMusicData, uint16 startOfData);
~AdLibChannel() override;
uint8 process(uint16 aktTime) override;
void updateVolume(uint16 pVolume) override;
bool isActive() override;
private:
OPL::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();
uint16 getNextNote(uint8 param);
void adlibSetupInstrument();
void setupInstrument(uint8 opcode);
void setupChannelVolume(uint8 volume);
void stopNote();
// Streamfunctions from Command90hTable
void com90_caseNoteOff(); // 0
void com90_stopChannel(); // 1
void com90_setupInstrument(); // 2
uint8 com90_updateTempo(); // 3
//void com90_dummy(); // 4
void com90_getFreqOffset(); // 5
void com90_getChannelVolume(); // 6
void com90_getTremoVibro(); // 7
void com90_loopMusic(); // 8
void com90_keyOff(); // 9
//void com90_error(); // 10
//void com90_doLodsb(); // 11
void com90_setLoopPoint(); // 12
//void com90_do_two_Lodsb(); // 13
};
} // End of namespace Sky
#endif //ADLIBCHANNEL_H

View File

@@ -0,0 +1,98 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "common/textconsole.h"
#include "sky/music/adlibmusic.h"
#include "sky/music/adlibchannel.h"
#include "audio/fmopl.h"
#include "sky/sky.h"
namespace Audio {
class Mixer;
}
namespace Sky {
AdLibMusic::AdLibMusic(Audio::Mixer *pMixer, Disk *pDisk) : MusicBase(pMixer, pDisk) {
_driverFileBase = 60202;
_opl = OPL::Config::create();
if (!_opl || !_opl->init())
error("Failed to create OPL");
_opl->start(new Common::Functor0Mem<void, AdLibMusic>(this, &AdLibMusic::onTimer), 50);
}
AdLibMusic::~AdLibMusic() {
delete _opl;
}
void AdLibMusic::onTimer() {
if (_musicData != NULL)
pollMusic();
}
void AdLibMusic::setupPointers() {
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;
}
}
void AdLibMusic::setupChannels(uint8 *channelData) {
_numberOfChannels = channelData[0];
channelData++;
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) {
uint16 chDataStart = READ_LE_UINT16((uint16 *)channelData + cnt) + _musicDataLoc;
_channels[cnt] = new AdLibChannel(_opl, _musicData, chDataStart);
// also set proper volume - Fixes bug where intro music plays at full volume
_channels[cnt]->updateVolume(_musicVolume);
}
}
void AdLibMusic::startDriver() {
uint16 cnt = 0;
while (_initSequence[cnt] || _initSequence[cnt + 1]) {
_opl->writeReg(_initSequence[cnt], _initSequence[cnt + 1]);
cnt += 2;
}
}
void AdLibMusic::setVolume(uint16 param) {
_musicVolume = param;
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++)
_channels[cnt]->updateVolume(_musicVolume);
}
} // End of namespace Sky

View File

@@ -0,0 +1,60 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SKY_MUSIC_ADLIBMUSIC_H
#define SKY_MUSIC_ADLIBMUSIC_H
#include "sky/music/musicbase.h"
namespace Audio {
class Mixer;
}
namespace OPL {
class OPL;
}
namespace Sky {
class AdLibMusic : public MusicBase {
public:
AdLibMusic(Audio::Mixer *pMixer, Disk *pDisk);
~AdLibMusic() override;
// AudioStream API
void setVolume(uint16 param) override;
private:
OPL::OPL *_opl;
uint8 *_initSequence;
uint32 _sampleRate;
void setupPointers() override;
void setupChannels(uint8 *channelData) override;
void startDriver() override;
void premixerCall(int16 *buf, uint len);
void onTimer();
};
} // End of namespace Sky
#endif //ADLIBMUSIC_H

View File

@@ -0,0 +1,195 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "gmchannel.h"
#include "common/util.h"
#include "common/textconsole.h"
#include "audio/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.loopPoint = startOfData;
_channelData.eventDataPtr = startOfData;
_channelData.channelActive = true;
_channelData.nextEventTime = getNextEventTime();
_instMap = pInstMap;
_veloTab = veloTab;
_musicVolume = 0x7F;
_currentChannelVolume = 0x7F;
}
GmChannel::~GmChannel() {
stopNote();
}
bool GmChannel::isActive() {
return _channelData.channelActive;
}
void GmChannel::updateVolume(uint16 pVolume) {
_musicVolume = pVolume;
if (_musicVolume > 0)
_musicVolume = (_musicVolume * 2) / 3 + 43;
uint8 newVol = (_currentChannelVolume * _musicVolume) >> 7;
_midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x700 | (newVol << 16));
}
void GmChannel::stopNote() {
// All Notes Off
_midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x7B00 | 0 | 0x79000000);
// Reset the Pitch Wheel. See bug #1742.
_midiDrv->send((0xE0 | _channelData.midiChannelNumber) | 0x400000);
}
int32 GmChannel::getNextEventTime() {
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_loopMusic(); break;
case 9: com90_keyOff(); break;
case 11: com90_getChannelPanValue(); break;
case 12: com90_setLoopPoint(); break;
case 13: com90_getChannelControl(); break;
default:
error("GmChannel: Unknown music opcode 0x%02X", opcode);
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() {
_midiDrv->send((0x90 | _channelData.midiChannelNumber) | (_musicData[_channelData.eventDataPtr] << 8));
_channelData.eventDataPtr++;
}
void GmChannel::com90_stopChannel() {
stopNote();
_channelData.channelActive = false;
}
void GmChannel::com90_setupInstrument() {
byte instrument = _musicData[_channelData.eventDataPtr];
if (_instMap)
instrument = _instMap[instrument];
_midiDrv->send((0xC0 | _channelData.midiChannelNumber) | (instrument << 8));
_channelData.eventDataPtr++;
}
uint8 GmChannel::com90_updateTempo() {
return _musicData[_channelData.eventDataPtr++];
}
void GmChannel::com90_getPitch() {
_midiDrv->send((0xE0 | _channelData.midiChannelNumber) | 0 | (_musicData[_channelData.eventDataPtr] << 16));
_channelData.eventDataPtr++;
}
void GmChannel::com90_getChannelVolume() {
_currentChannelVolume = _musicData[_channelData.eventDataPtr++];
uint8 newVol = (uint8)((_currentChannelVolume * _musicVolume) >> 7);
_midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x700 | (newVol << 16));
}
void GmChannel::com90_loopMusic() {
_channelData.eventDataPtr = _channelData.loopPoint;
}
void GmChannel::com90_keyOff() {
_midiDrv->send((0x90 | _channelData.midiChannelNumber) | (_channelData.note << 8) | 0);
}
void GmChannel::com90_setLoopPoint() {
_channelData.loopPoint = _channelData.eventDataPtr;
}
void GmChannel::com90_getChannelPanValue() {
_midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x0A00 | (_musicData[_channelData.eventDataPtr] << 16));
_channelData.eventDataPtr++;
}
void GmChannel::com90_getChannelControl() {
uint8 conNum = _musicData[_channelData.eventDataPtr++];
uint8 conDat = _musicData[_channelData.eventDataPtr++];
_midiDrv->send((0xB0 | _channelData.midiChannelNumber) | (conNum << 8) | (conDat << 16));
}
} // End of namespace Sky

View File

@@ -0,0 +1,82 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SKY_MUSIC_GMCHANNEL_H
#define SKY_MUSIC_GMCHANNEL_H
#include "sky/music/musicbase.h"
class MidiDriver;
namespace Sky {
typedef struct {
uint16 eventDataPtr;
int32 nextEventTime;
uint16 loopPoint;
uint8 midiChannelNumber;
uint8 note;
bool channelActive;
} MidiChannelType;
class GmChannel : public ChannelBase {
public:
GmChannel(uint8 *pMusicData, uint16 startOfData, MidiDriver *pMidiDrv, const byte *pInstMap, const byte *veloTab);
~GmChannel() override;
virtual void stopNote();
uint8 process(uint16 aktTime) override;
void updateVolume(uint16 pVolume) override;
bool isActive() override;
private:
const byte *_instMap;
const byte *_veloTab;
MidiDriver *_midiDrv;
uint8 *_musicData;
uint16 _musicVolume;
MidiChannelType _channelData;
uint8 _currentChannelVolume;
//- normal subs
void setRegister(uint8 regNum, uint8 value);
int32 getNextEventTime();
uint16 getNextNote(uint8 param);
void adlibSetupInstrument();
void setupInstrument(uint8 opcode);
void setupChannelVolume(uint8 volume);
//- Streamfunctions from Command90hTable
void com90_caseNoteOff(); // 0
void com90_stopChannel(); // 1
void com90_setupInstrument(); // 2
uint8 com90_updateTempo(); // 3
//void com90_dummy(); // 4
void com90_getPitch(); // 5
void com90_getChannelVolume(); // 6
//void com90_skipTremoVibro(); // 7
void com90_loopMusic(); // 8
void com90_keyOff(); // 9
//void com90_error(); // 10
void com90_getChannelPanValue(); // 11
void com90_setLoopPoint(); // 12
void com90_getChannelControl(); // 13
};
} // End of namespace Sky
#endif //SKYGMCHANNEL_H

View File

@@ -0,0 +1,120 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "sky/music/gmmusic.h"
#include "sky/music/gmchannel.h"
#include "sky/sky.h"
#include "common/util.h"
#include "common/endian.h"
#include "common/textconsole.h"
#include "audio/mididrv.h"
namespace Sky {
void GmMusic::passTimerFunc(void *param) {
((GmMusic *)param)->timerCall();
}
GmMusic::GmMusic(MidiDriver *pMidiDrv, Audio::Mixer *pMixer, Disk *pDisk) : MusicBase(pMixer, 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);
_midiDrv->sendGMReset();
}
GmMusic::~GmMusic() {
_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::setVolume(uint16 param) {
_musicVolume = param;
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++)
_channels[cnt]->updateVolume(_musicVolume);
}
void GmMusic::timerCall() {
_timerCount += _midiDrv->getBaseTempo();
if (_timerCount > (1000 * 1000 / 50)) {
// call pollMusic() 50 times per second
_timerCount -= 1000 * 1000 / 50;
if (_musicData != NULL)
pollMusic();
}
}
void GmMusic::setupPointers() {
if (SkyEngine::_systemVars->gameVersion == 109) {
_musicDataLoc = READ_LE_UINT16(_musicData + 0x79B);
_sysExSequence = _musicData + 0x1EF2;
} else {
_musicDataLoc = READ_LE_UINT16(_musicData + 0x7DC);
_sysExSequence = READ_LE_UINT16(_musicData + 0x7E0) + _musicData;
}
}
void GmMusic::setupChannels(uint8 *channelData) {
_numberOfChannels = channelData[0];
channelData++;
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) {
uint16 chDataStart = READ_LE_UINT16((uint16 *)channelData + cnt) + _musicDataLoc;
_channels[cnt] = new GmChannel(_musicData, chDataStart, _midiDrv, MidiDriver::_mt32ToGm, _veloTab);
_channels[cnt]->updateVolume(_musicVolume);
}
}
void GmMusic::startDriver() {
// Send GM System On to reset channel parameters etc.
uint8 sysEx[] = { 0x7e, 0x7f, 0x09, 0x01 };
_midiDrv->sysEx(sysEx, sizeof(sysEx));
//_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

View File

@@ -0,0 +1,52 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SKY_MUSIC_GMMUSIC_H
#define SKY_MUSIC_GMMUSIC_H
#include "sky/music/musicbase.h"
class MidiDriver;
namespace Sky {
class GmMusic : public MusicBase {
public:
GmMusic(MidiDriver *pMidiDrv, Audio::Mixer *pMixer, Disk *pDisk);
~GmMusic() override;
void setVolume(uint16 param) override;
private:
static void passTimerFunc(void *param);
void timerCall();
uint32 _timerCount;
uint8 *_sysExSequence;
MidiDriver *_midiDrv;
static const byte _veloTab[128];
void setupPointers() override;
void setupChannels(uint8 *channelData) override;
void startDriver() override;
};
} // End of namespace Sky
#endif //GMMUSIC_H

View File

@@ -0,0 +1,176 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "sky/music/mt32music.h"
#include "sky/music/gmchannel.h"
#include "common/util.h"
#include "common/system.h"
#include "common/endian.h"
#include "common/textconsole.h"
#include "audio/mididrv.h"
namespace Sky {
void MT32Music::passTimerFunc(void *param) {
((MT32Music *)param)->timerCall();
}
MT32Music::MT32Music(MidiDriver *pMidiDrv, Audio::Mixer *pMixer, Disk *pDisk) : MusicBase(pMixer, 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);
_midiDrv->sendMT32Reset();
}
MT32Music::~MT32Music() {
_midiDrv->close();
_midiDrv->setTimerCallback(NULL, NULL);
delete _midiDrv;
}
void MT32Music::timerCall() {
_timerCount += _midiDrv->getBaseTempo();
if (_timerCount > (1000000 / 50)) {
// call pollMusic() 50 times per second
_timerCount -= 1000000 / 50;
if (_musicData != NULL)
pollMusic();
}
}
void MT32Music::setVolume(uint16 volume) {
uint8 sysEx[10] = "\x41\x10\x16\x12\x10\x00\x16\x00\x00";
_musicVolume = volume;
sysEx[7] = (volume > 100) ? 100 : (uint8)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() {
_musicDataLoc = READ_LE_UINT16(_musicData + 0x7DC);
_sysExSequence = READ_LE_UINT16(_musicData + 0x7E0) + _musicData;
}
void MT32Music::setupChannels(uint8 *channelData) {
_numberOfChannels = channelData[0];
channelData++;
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) {
uint16 chDataStart = READ_LE_UINT16((uint16 *)channelData + cnt) + _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);
// We delay the time it takes to send the sysEx plus an
// additional 40ms, which is required for MT-32 rev00,
// to assure no buffer overflow or missing bytes
g_system->delayMillis(17 * 1000 / 3125 + 40);
return true;
}
void MT32Music::startDriver() {
// 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);
// We delay the time it takes to send the sysEx plus an
// additional 40ms, which is required for MT-32 rev00,
// to assure no buffer overflow or missing bytes
g_system->delayMillis((len + 2) * 1000 / 3125 + 40);
}
while (processPatchSysEx(sysExData))
sysExData += 5;
}
} // End of namespace Sky

View File

@@ -0,0 +1,52 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SKY_MUSIC_MT32MUSIC_H
#define SKY_MUSIC_MT32MUSIC_H
#include "sky/music/musicbase.h"
class MidiDriver;
namespace Sky {
class MT32Music : public MusicBase {
public:
MT32Music(MidiDriver *pMidiDrv, Audio::Mixer *pMixer, Disk *pDisk);
~MT32Music() override;
private:
static void passTimerFunc(void *param);
void timerCall();
bool processPatchSysEx(uint8 *sysExData);
void setVolume(uint16 volume) override;
uint32 _timerCount;
uint8 *_sysExSequence;
MidiDriver *_midiDrv;
void setupPointers() override;
void setupChannels(uint8 *channelData) override;
void startDriver() override;
};
} // End of namespace Sky
#endif //MT32MUSIC_H

View File

@@ -0,0 +1,197 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "sky/music/musicbase.h"
#include "sky/disk.h"
#include "common/util.h"
#include "common/endian.h"
#include "common/textconsole.h"
#include "audio/audiostream.h"
namespace Sky {
MusicBase::MusicBase(Audio::Mixer *pMixer, Disk *pDisk) {
_musicData = NULL;
_mixer = pMixer;
_skyDisk = pDisk;
_currentMusic = 0;
_musicVolume = 127;
_numberOfChannels = _currentMusic = 0;
}
MusicBase::~MusicBase() {
stopMusic();
free(_musicData);
}
void MusicBase::loadSection(uint8 pSection) {
if (_currentMusic)
stopMusicInternal();
Common::StackLock lock(_mutex);
free(_musicData);
_currentSection = pSection;
_musicData = _skyDisk->loadFile(_driverFileBase + FILES_PER_SECTION * pSection);
_musicTempo0 = 0x78; // init constants taken from idb file, area ~0x1060
_musicTempo1 = 0xC0;
_onNextPoll.musicToProcess = 0;
_onNextPoll.stream = nullptr;
_tempo = _aktTime = 0x10001;
_numberOfChannels = _currentMusic = 0;
setupPointers();
startDriver();
}
bool MusicBase::musicIsPlaying() {
if (_mixer->isSoundHandleActive(_musicHandle))
return true;
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++)
if (_channels[cnt]->isActive())
return true;
return false;
}
void MusicBase::stopMusic() {
stopMusicInternal();
}
void MusicBase::stopMusicInternal() {
_mixer->stopHandle(_musicHandle);
Common::StackLock lock(_mutex);
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++)
delete _channels[cnt];
_numberOfChannels = 0;
}
void MusicBase::updateTempo() {
uint16 tempoMul = _musicTempo0 * _musicTempo1;
uint16 divisor = 0x4446390/ 23864;
_tempo = (tempoMul / divisor) << 16;
_tempo |= (((tempoMul % divisor) << 16) | (tempoMul / divisor)) / divisor;
}
void MusicBase::loadNewMusic() {
uint16 musicPos;
if (_onNextPoll.musicToProcess > _musicData[_musicDataLoc]) {
error("Music %d requested but doesn't exist in file.", _onNextPoll.musicToProcess);
return;
}
if (_currentMusic != 0)
stopMusicInternal();
_currentMusic = _onNextPoll.musicToProcess;
if (_currentMusic == 0)
return;
// Try playing digital audio first (from the Music Enhancement Project).
// TODO: This always prefers digital music over the MIDI music types!
Audio::SeekableAudioStream *stream = _onNextPoll.stream;
if (stream) {
_onNextPoll.stream = nullptr;
// not all tracks should loop
bool loops = true;
uint8 section = _currentSection;
uint8 song = _currentMusic;
if ((section == 0 && song == 1)
|| (section == 1 && song == 1) || (section == 1 && song == 4)
|| (section == 2 && song == 1) || (section == 2 && song == 4)
|| (section == 4 && song == 2) || (section == 4 && song == 3)
|| (section == 4 && song == 5) || (section == 4 && song == 6)
|| (section == 4 && song == 11) || (section == 5 && song == 1)
|| (section == 5 && song == 3) || (section == 5 && song == 4))
loops = false;
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, Audio::makeLoopingAudioStream(stream, loops ? 0 : 1));
return;
}
// no digital audio, resort to MIDI playback
musicPos = READ_LE_UINT16(_musicData + _musicDataLoc + 1);
musicPos += _musicDataLoc + ((_currentMusic - 1) << 1);
musicPos = READ_LE_UINT16(_musicData + musicPos) + _musicDataLoc;
_musicTempo0 = _musicData[musicPos];
_musicTempo1 = _musicData[musicPos+1];
setupChannels(_musicData + musicPos + 2);
updateTempo();
}
void MusicBase::pollMusic() {
Common::StackLock lock(_mutex);
uint8 newTempo;
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();
}
}
_aktTime &= 0xFFFF;
}
void MusicBase::startMusic(uint16 param) {
uint8 song = param & 0xF;
_onNextPoll.musicToProcess = song;
delete _onNextPoll.stream;
_onNextPoll.stream = nullptr;
if (song == 0)
return;
// Load digital audio now if available
// This avoids doing it in the audio thread
uint8 section = _currentSection;
// handle duplicates
if ((section == 2 && song == 1) || (section == 5 && song == 1)) {
section = 1;
song = 1;
} else if ((section == 2 && song == 4) || (section == 5 && song == 4)) {
section = 1;
song = 4;
} else if (section == 5 && song == 6) {
section = 4;
song = 4;
}
Common::Path trackName(Common::String::format("music_%d%02d", section, song));
_onNextPoll.stream = Audio::SeekableAudioStream::openStreamFile(trackName);
}
uint8 MusicBase::giveVolume() {
return (uint8)_musicVolume;
}
uint8 MusicBase::giveCurrentMusic() {
return _currentMusic;
}
} // End of namespace Sky

View File

@@ -0,0 +1,100 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SKY_MUSIC_MUSICBASE_H
#define SKY_MUSIC_MUSICBASE_H
#include "common/scummsys.h"
#include "common/mutex.h"
#include "audio/mixer.h"
namespace Audio {
class SeekableAudioStream;
}
namespace Sky {
class Disk;
#define FILES_PER_SECTION 4
typedef struct {
uint8 musicToProcess;
Audio::SeekableAudioStream *stream;
} Actions;
class ChannelBase {
public:
virtual ~ChannelBase() {}
virtual uint8 process(uint16 aktTime) = 0;
virtual void updateVolume(uint16 pVolume) = 0;
virtual bool isActive() = 0;
private:
};
class MusicBase {
public:
MusicBase(Audio::Mixer *pMixer, Disk *pDisk);
virtual ~MusicBase();
void loadSection(uint8 pSection);
void startMusic(uint16 param);
void stopMusic();
bool musicIsPlaying();
uint8 giveVolume();
uint8 giveCurrentMusic();
virtual void setVolume(uint16 param) = 0;
protected:
Audio::Mixer *_mixer;
Disk *_skyDisk;
uint8 *_musicData;
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;
Audio::SoundHandle _musicHandle;
virtual void setupPointers() = 0;
virtual void setupChannels(uint8 *channelData) = 0;
virtual void startDriver() = 0;
void updateTempo();
void loadNewMusic();
void pollMusic();
void stopMusicInternal();
};
} // End of namespace Sky
#endif //MUSICBASE_H