/* 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 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "common/endian.h" #include "common/file.h" #include "common/memstream.h" #include "common/system.h" #include "common/textconsole.h" #include "common/timer.h" #include "common/mutex.h" #include "common/config-manager.h" #include "cine/cine.h" #include "cine/sound.h" #include "backends/audiocd/audiocd.h" #include "audio/audiostream.h" #include "audio/fmopl.h" #include "audio/mididrv.h" #include "audio/decoders/raw.h" #include "audio/mods/soundfx.h" namespace Cine { class PCSoundDriver { public: typedef void (*UpdateCallback)(void *); virtual ~PCSoundDriver() {} virtual void setupChannel(int channel, const byte *data, int instrument, int volume) = 0; virtual void setChannelFrequency(int channel, int frequency) = 0; virtual void stopChannel(int channel) = 0; virtual void playSample(const byte *data, int size, int channel, int volume) = 0; virtual void stopAll() = 0; virtual const char *getInstrumentExtension() const { return ""; } virtual void notifyInstrumentLoad(const byte *data, int size, int channel) {} virtual void setUpdateCallback(UpdateCallback upCb, void *ref) = 0; void resetChannel(int channel); void findNote(int freq, int *note, int *oct) const; protected: static const int _noteTable[]; static const int _noteTableCount; }; const int PCSoundDriver::_noteTable[] = { 0xEEE, 0xE17, 0xD4D, 0xC8C, 0xBD9, 0xB2F, 0xA8E, 0x9F7, 0x967, 0x8E0, 0x861, 0x7E8, 0x777, 0x70B, 0x6A6, 0x647, 0x5EC, 0x597, 0x547, 0x4FB, 0x4B3, 0x470, 0x430, 0x3F4, 0x3BB, 0x385, 0x353, 0x323, 0x2F6, 0x2CB, 0x2A3, 0x27D, 0x259, 0x238, 0x218, 0x1FA, 0x1DD, 0x1C2, 0x1A9, 0x191, 0x17B, 0x165, 0x151, 0x13E, 0x12C, 0x11C, 0x10C, 0x0FD, 0x0EE, 0x0E1, 0x0D4, 0x0C8, 0x0BD, 0x0B2, 0x0A8, 0x09F, 0x096, 0x08E, 0x086, 0x07E, 0x077, 0x070, 0x06A, 0x064, 0x05E, 0x059, 0x054, 0x04F, 0x04B, 0x047, 0x043, 0x03F, 0x03B, 0x038, 0x035, 0x032, 0x02F, 0x02C, 0x02A, 0x027, 0x025, 0x023, 0x021, 0x01F, 0x01D, 0x01C, 0x01A, 0x019, 0x017, 0x016, 0x015, 0x013, 0x012, 0x011, 0x010, 0x00F }; const int PCSoundDriver::_noteTableCount = ARRAYSIZE(_noteTable); struct AdLibRegisterSoundInstrument { uint8 vibrato; uint8 attackDecay; uint8 sustainRelease; uint8 feedbackStrength; uint8 keyScaling; uint8 outputLevel; uint8 freqMod; }; struct AdLibSoundInstrument { byte mode; byte channel; AdLibRegisterSoundInstrument regMod; AdLibRegisterSoundInstrument regCar; byte waveSelectMod; byte waveSelectCar; byte amDepth; }; class AdLibSoundDriver : public PCSoundDriver { public: AdLibSoundDriver(Audio::Mixer *mixer); virtual ~AdLibSoundDriver(); // PCSoundDriver interface virtual void setUpdateCallback(UpdateCallback upCb, void *ref); virtual void setupChannel(int channel, const byte *data, int instrument, int volume); virtual void stopChannel(int channel); virtual void stopAll(); void initCard(); void onTimer(); void setupInstrument(const byte *data, int channel); void loadRegisterInstrument(const byte *data, AdLibRegisterSoundInstrument *reg); virtual void loadInstrument(const byte *data, AdLibSoundInstrument *asi) = 0; protected: UpdateCallback _upCb; void *_upRef; OPL::OPL *_opl; Audio::Mixer *_mixer; byte _vibrato; int _channelsVolumeTable[4]; AdLibSoundInstrument _instrumentsTable[4]; static const int _freqTable[]; static const int _freqTableCount; static const int _operatorsTable[]; static const int _operatorsTableCount; static const int _voiceOperatorsTable[]; static const int _voiceOperatorsTableCount; }; const int AdLibSoundDriver::_freqTable[] = { 0x157, 0x16C, 0x181, 0x198, 0x1B1, 0x1CB, 0x1E6, 0x203, 0x222, 0x243, 0x266, 0x28A }; const int AdLibSoundDriver::_freqTableCount = ARRAYSIZE(_freqTable); const int AdLibSoundDriver::_operatorsTable[] = { 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21 }; const int AdLibSoundDriver::_operatorsTableCount = ARRAYSIZE(_operatorsTable); const int AdLibSoundDriver::_voiceOperatorsTable[] = { 0, 3, 1, 4, 2, 5, 6, 9, 7, 10, 8, 11, 12, 15, 16, 16, 14, 14, 17, 17, 13, 13 }; const int AdLibSoundDriver::_voiceOperatorsTableCount = ARRAYSIZE(_voiceOperatorsTable); // Future Wars AdLib driver class AdLibSoundDriverINS : public AdLibSoundDriver { public: AdLibSoundDriverINS(Audio::Mixer *mixer) : AdLibSoundDriver(mixer) {} virtual const char *getInstrumentExtension() const { return ".INS"; } virtual void loadInstrument(const byte *data, AdLibSoundInstrument *asi); virtual void setChannelFrequency(int channel, int frequency); virtual void playSample(const byte *data, int size, int channel, int volume); }; // Operation Stealth AdLib driver class AdLibSoundDriverADL : public AdLibSoundDriver { public: AdLibSoundDriverADL(Audio::Mixer *mixer) : AdLibSoundDriver(mixer) {} virtual const char *getInstrumentExtension() const { return ".ADL"; } virtual void loadInstrument(const byte *data, AdLibSoundInstrument *asi); virtual void setChannelFrequency(int channel, int frequency); virtual void playSample(const byte *data, int size, int channel, int volume); }; // (Future Wars) MIDI driver class MidiSoundDriverH32 : public PCSoundDriver { public: MidiSoundDriverH32(MidiDriver *output); ~MidiSoundDriverH32(); virtual void setUpdateCallback(UpdateCallback upCb, void *ref); virtual void setupChannel(int channel, const byte *data, int instrument, int volume); virtual void setChannelFrequency(int channel, int frequency); virtual void stopChannel(int channel); virtual void playSample(const byte *data, int size, int channel, int volume); virtual void stopAll() {} virtual const char *getInstrumentExtension() const { return ".H32"; } virtual void notifyInstrumentLoad(const byte *data, int size, int channel); private: MidiDriver *_output; UpdateCallback _callback; Common::Mutex _mutex; void writeInstrument(int offset, const byte *data, int size); void selectInstrument(int channel, int timbreGroup, int timbreNumber, int volume); }; class PCSoundFxPlayer { public: PCSoundFxPlayer(PCSoundDriver *driver); ~PCSoundFxPlayer(); bool load(const char *song); void play(); void stop(); void fadeOut(); static void updateCallback(void *ref); enum { NUM_INSTRUMENTS = 15, NUM_CHANNELS = 4 }; private: void update(); void handleEvents(); void handlePattern(int channel, const byte *patternData); void unload(); bool _playing; int _currentPos; int _currentOrder; int _numOrders; int _eventsDelay; int _fadeOutCounter; int _updateTicksCounter; int _instrumentsChannelTable[NUM_CHANNELS]; byte *_sfxData; byte *_instrumentsData[NUM_INSTRUMENTS]; PCSoundDriver *_driver; Common::Mutex _mutex; }; void PCSoundDriver::findNote(int freq, int *note, int *oct) const { if (freq > 0x777) *oct = 0; else if (freq > 0x3BB) *oct = 1; else if (freq > 0x1DD) *oct = 2; else if (freq > 0x0EE) *oct = 3; else if (freq > 0x077) *oct = 4; else if (freq > 0x03B) *oct = 5; else if (freq > 0x01D) *oct = 6; else *oct = 7; *note = 11; for (int i = 0; i < 12; ++i) { if (_noteTable[*oct * 12 + i] <= freq) { *note = i; break; } } } void PCSoundDriver::resetChannel(int channel) { stopChannel(channel); stopAll(); } AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer) : _upCb(0), _upRef(0), _mixer(mixer) { _opl = OPL::Config::create(); if (!_opl || !_opl->init()) error("Failed to create OPL"); memset(_channelsVolumeTable, 0, sizeof(_channelsVolumeTable)); memset(_instrumentsTable, 0, sizeof(_instrumentsTable)); initCard(); _opl->start(new Common::Functor0Mem(this, &AdLibSoundDriver::onTimer), 50); } AdLibSoundDriver::~AdLibSoundDriver() { delete _opl; } void AdLibSoundDriver::setUpdateCallback(UpdateCallback upCb, void *ref) { _upCb = upCb; _upRef = ref; } void AdLibSoundDriver::setupChannel(int channel, const byte *data, int instrument, int volume) { assert(channel < 4); if (data) { if (volume > 80) { volume = 80; } else if (volume < 0) { volume = 0; } volume += volume / 4; _channelsVolumeTable[channel] = volume; setupInstrument(data, channel); } } void AdLibSoundDriver::stopChannel(int channel) { assert(channel < 4); AdLibSoundInstrument *ins = &_instrumentsTable[channel]; if (ins->mode != 0 && ins->channel == 6) { channel = 6; } if (ins->mode == 0 || channel == 6) { _opl->writeReg(0xB0 | channel, 0); } if (ins->mode != 0) { _vibrato &= ~(1 << (10 - ins->channel)); _opl->writeReg(0xBD, _vibrato); } } void AdLibSoundDriver::stopAll() { int i; for (i = 0; i < 18; ++i) { _opl->writeReg(0x40 | _operatorsTable[i], 63); } for (i = 0; i < 9; ++i) { _opl->writeReg(0xB0 | i, 0); } _opl->writeReg(0xBD, 0); } void AdLibSoundDriver::initCard() { _vibrato = 0x20; _opl->writeReg(0xBD, _vibrato); _opl->writeReg(0x08, 0x40); static const int oplRegs[] = { 0x40, 0x60, 0x80, 0x20, 0xE0 }; for (int i = 0; i < 9; ++i) { _opl->writeReg(0xB0 | i, 0); } for (int i = 0; i < 9; ++i) { _opl->writeReg(0xC0 | i, 0); } for (int j = 0; j < 5; j++) { for (int i = 0; i < 18; ++i) { _opl->writeReg(oplRegs[j] | _operatorsTable[i], 0); } } _opl->writeReg(1, 0x20); _opl->writeReg(1, 0); } void AdLibSoundDriver::onTimer() { if (_upCb) { (*_upCb)(_upRef); } } void AdLibSoundDriver::setupInstrument(const byte *data, int channel) { assert(channel < 4); AdLibSoundInstrument *ins = &_instrumentsTable[channel]; loadInstrument(data, ins); int mod, car, tmp; const AdLibRegisterSoundInstrument *reg; if (ins->mode != 0) { mod = _operatorsTable[_voiceOperatorsTable[2 * ins->channel + 0]]; car = _operatorsTable[_voiceOperatorsTable[2 * ins->channel + 1]]; } else { mod = _operatorsTable[_voiceOperatorsTable[2 * channel + 0]]; car = _operatorsTable[_voiceOperatorsTable[2 * channel + 1]]; } if (ins->mode == 0 || ins->channel == 6) { reg = &ins->regMod; _opl->writeReg(0x20 | mod, reg->vibrato); if (reg->freqMod) { tmp = reg->outputLevel & 0x3F; } else { tmp = (63 - (reg->outputLevel & 0x3F)) * _channelsVolumeTable[channel]; tmp = 63 - (2 * tmp + 127) / (2 * 127); } _opl->writeReg(0x40 | mod, tmp | (reg->keyScaling << 6)); _opl->writeReg(0x60 | mod, reg->attackDecay); _opl->writeReg(0x80 | mod, reg->sustainRelease); if (ins->mode != 0) { _opl->writeReg(0xC0 | ins->channel, reg->feedbackStrength); } else { _opl->writeReg(0xC0 | channel, reg->feedbackStrength); } _opl->writeReg(0xE0 | mod, ins->waveSelectMod); } reg = &ins->regCar; _opl->writeReg(0x20 | car, reg->vibrato); tmp = (63 - (reg->outputLevel & 0x3F)) * _channelsVolumeTable[channel]; tmp = 63 - (2 * tmp + 127) / (2 * 127); _opl->writeReg(0x40 | car, tmp | (reg->keyScaling << 6)); _opl->writeReg(0x60 | car, reg->attackDecay); _opl->writeReg(0x80 | car, reg->sustainRelease); _opl->writeReg(0xE0 | car, ins->waveSelectCar); } void AdLibSoundDriver::loadRegisterInstrument(const byte *data, AdLibRegisterSoundInstrument *reg) { reg->vibrato = 0; if (READ_LE_UINT16(data + 18)) { // amplitude vibrato reg->vibrato |= 0x80; } if (READ_LE_UINT16(data + 20)) { // frequency vibrato reg->vibrato |= 0x40; } if (READ_LE_UINT16(data + 10)) { // sustaining sound reg->vibrato |= 0x20; } if (READ_LE_UINT16(data + 22)) { // envelope scaling reg->vibrato |= 0x10; } reg->vibrato |= READ_LE_UINT16(data + 2) & 0xF; // frequency multiplier reg->attackDecay = READ_LE_UINT16(data + 6) << 4; // attack rate reg->attackDecay |= READ_LE_UINT16(data + 12) & 0xF; // decay rate reg->sustainRelease = READ_LE_UINT16(data + 8) << 4; // sustain level reg->sustainRelease |= READ_LE_UINT16(data + 14) & 0xF; // release rate reg->feedbackStrength = READ_LE_UINT16(data + 4) << 1; // feedback if (READ_LE_UINT16(data + 24) == 0) { // frequency modulation reg->feedbackStrength |= 1; } reg->keyScaling = READ_LE_UINT16(data); reg->outputLevel = READ_LE_UINT16(data + 16); reg->freqMod = READ_LE_UINT16(data + 24); } void AdLibSoundDriverINS::loadInstrument(const byte *data, AdLibSoundInstrument *asi) { asi->mode = *data++; asi->channel = *data++; loadRegisterInstrument(data, &asi->regMod); data += 26; loadRegisterInstrument(data, &asi->regCar); data += 26; asi->waveSelectMod = data[0] & 3; data += 2; asi->waveSelectCar = data[0] & 3; data += 2; asi->amDepth = data[0]; data += 2; } void AdLibSoundDriverINS::setChannelFrequency(int channel, int frequency) { assert(channel < 4); AdLibSoundInstrument *ins = &_instrumentsTable[channel]; if (ins->mode != 0 && ins->channel == 6) { channel = 6; } if (ins->mode == 0 || ins->channel == 6) { int freq, note, oct; findNote(frequency, ¬e, &oct); if (channel == 6) oct = 0; freq = _freqTable[note % 12]; _opl->writeReg(0xA0 | channel, freq); freq = (oct << 2) | ((freq & 0x300) >> 8); if (ins->mode == 0) { freq |= 0x20; } _opl->writeReg(0xB0 | channel, freq); } if (ins->mode != 0) { _vibrato |= 1 << (10 - ins->channel); _opl->writeReg(0xBD, _vibrato); } } void AdLibSoundDriverINS::playSample(const byte *data, int size, int channel, int volume) { assert(channel < 4); _channelsVolumeTable[channel] = 127; resetChannel(channel); setupInstrument(data + 257, channel); AdLibSoundInstrument *ins = &_instrumentsTable[channel]; if (ins->mode != 0 && ins->channel == 6) { channel = 6; } if (ins->mode == 0 || channel == 6) { uint16 note = 12; int freq = _freqTable[note % 12]; _opl->writeReg(0xA0 | channel, freq); freq = ((note / 12) << 2) | ((freq & 0x300) >> 8); if (ins->mode == 0) { freq |= 0x20; } _opl->writeReg(0xB0 | channel, freq); } if (ins->mode != 0) { _vibrato |= 1 << (10 - ins->channel); _opl->writeReg(0xBD, _vibrato); } } void AdLibSoundDriverADL::loadInstrument(const byte *data, AdLibSoundInstrument *asi) { asi->mode = *data++; asi->channel = *data++; asi->waveSelectMod = *data++ & 3; asi->waveSelectCar = *data++ & 3; asi->amDepth = *data++; ++data; loadRegisterInstrument(data, &asi->regMod); data += 26; loadRegisterInstrument(data, &asi->regCar); data += 26; } void AdLibSoundDriverADL::setChannelFrequency(int channel, int frequency) { assert(channel < 4); AdLibSoundInstrument *ins = &_instrumentsTable[channel]; if (ins->mode != 0) { channel = ins->channel; if (channel == 9) { channel = 8; } else if (channel == 10) { channel = 7; } } int freq, note, oct; findNote(frequency, ¬e, &oct); if (ins->amDepth) { note = ins->amDepth; oct = note / 12; } if (note < 0) { note = 0; oct = 0; } freq = _freqTable[note % 12]; _opl->writeReg(0xA0 | channel, freq); freq = (oct << 2) | ((freq & 0x300) >> 8); if (ins->mode == 0) { freq |= 0x20; } _opl->writeReg(0xB0 | channel, freq); if (ins->mode != 0) { _vibrato |= 1 << (10 - channel); _opl->writeReg(0xBD, _vibrato); } } void AdLibSoundDriverADL::playSample(const byte *data, int size, int channel, int volume) { assert(channel < 4); _channelsVolumeTable[channel] = 127; setupInstrument(data, channel); AdLibSoundInstrument *ins = &_instrumentsTable[channel]; if (ins->mode != 0 && ins->channel == 6) { _opl->writeReg(0xB0 | channel, 0); } if (ins->mode != 0) { _vibrato &= ~(1 << (10 - ins->channel)); _opl->writeReg(0xBD, _vibrato); } if (ins->mode != 0) { channel = ins->channel; if (channel == 9) { channel = 8; } else if (channel == 10) { channel = 7; } } uint16 note = 48; if (ins->amDepth) { note = ins->amDepth; } int freq = _freqTable[note % 12]; _opl->writeReg(0xA0 | channel, freq); freq = ((note / 12) << 2) | ((freq & 0x300) >> 8); if (ins->mode == 0) { freq |= 0x20; } _opl->writeReg(0xB0 | channel, freq); if (ins->mode != 0) { _vibrato |= 1 << (10 - channel); _opl->writeReg(0xBD, _vibrato); } } MidiSoundDriverH32::MidiSoundDriverH32(MidiDriver *output) : _output(output), _callback(0), _mutex() { } MidiSoundDriverH32::~MidiSoundDriverH32() { if (_callback) g_system->getTimerManager()->removeTimerProc(_callback); _output->close(); delete _output; } void MidiSoundDriverH32::setUpdateCallback(UpdateCallback upCb, void *ref) { Common::StackLock lock(_mutex); Common::TimerManager *timer = g_system->getTimerManager(); assert(timer); if (_callback) timer->removeTimerProc(_callback); _callback = upCb; if (_callback) timer->installTimerProc(_callback, 1000000 / 50, ref, "MidiSoundDriverH32"); } void MidiSoundDriverH32::setupChannel(int channel, const byte *data, int instrument, int volume) { Common::StackLock lock(_mutex); if (volume < 0 || volume > 100) volume = 0; if (!data) selectInstrument(channel, 0, 0, volume); // In case the instrument is a builtin instrument select it directly. else if (data[0] < 0x80) selectInstrument(channel, data[0] / 0x40, data[0] % 0x40, volume); // In case we use a custom instrument we need to specify the timbre group // 2, which means it's a timbre from the timbre memory area. else selectInstrument(channel, 2, instrument, volume); } void MidiSoundDriverH32::setChannelFrequency(int channel, int frequency) { Common::StackLock lock(_mutex); int note, oct; findNote(frequency, ¬e, &oct); note %= 12; note = oct * 12 + note + 12; _output->send(0x91 + channel, note, 0x7F); } void MidiSoundDriverH32::stopChannel(int channel) { Common::StackLock lock(_mutex); _output->send(0xB1 + channel, 0x7B, 0x00); } void MidiSoundDriverH32::playSample(const byte *data, int size, int channel, int volume) { Common::StackLock lock(_mutex); stopChannel(channel); volume = volume * 8 / 5; if (data[0] < 0x80) { selectInstrument(channel, data[0] / 0x40, data[0] % 0x40, volume); } else { writeInstrument(channel * 512 + 0x80000, data + 1, 256); selectInstrument(channel, 2, channel, volume); } _output->send(0x91 + channel, 12, 0x7F); } void MidiSoundDriverH32::notifyInstrumentLoad(const byte *data, int size, int channel) { Common::StackLock lock(_mutex); // In case we specify a standard instrument or standard rhythm instrument // do not do anything here. It might be noteworthy that the instrument // selection client code does not support rhythm instruments! if (data[0] < 0x80 || data[0] > 0xC0) return; writeInstrument(channel * 512 + 0x80000, data + 1, size - 1); } void MidiSoundDriverH32::writeInstrument(int offset, const byte *data, int size) { byte sysEx[254]; sysEx[0] = 0x41; sysEx[1] = 0x10; sysEx[2] = 0x16; sysEx[3] = 0x12; sysEx[4] = (offset >> 16) & 0xFF; sysEx[5] = (offset >> 8) & 0xFF; sysEx[6] = (offset >> 0) & 0xFF; int copySize = MIN(246, size); memcpy(&sysEx[7], data, copySize); byte checkSum = 0; for (int i = 0; i < copySize + 3; ++i) checkSum += sysEx[4 + i]; sysEx[7 + copySize] = 0x80 - (checkSum & 0x7F); _output->sysEx(sysEx, copySize + 8); } void MidiSoundDriverH32::selectInstrument(int channel, int timbreGroup, int timbreNumber, int volume) { const int offset = channel * 16 + 0x30000; // 0x30000 is the start of the patch temp area byte sysEx[24] = { 0x41, 0x10, 0x16, 0x12, 0x00, 0x00, 0x00, // offset 0x00, // Timbre group _ timbreGroup * 64 + timbreNumber should be the 0x00, // Timbre number / MT-32 instrument in case timbreGroup is 0 or 1. 0x18, // Key shift (= 0) 0x32, // Fine tune (= 0) 0x0C, // Bender Range 0x03, // Assign Mode 0x01, // Reverb Switch (= enabled) 0x00, // dummy 0x00, // Output level 0x07, // Panpot (= balanced) 0x00, // dummy 0x00, // dummy 0x00, // dummy 0x00, // dummy 0x00, // dummy 0x00, // dummy 0x00 // checksum }; sysEx[4] = (offset >> 16) & 0xFF; sysEx[5] = (offset >> 8) & 0xFF; sysEx[6] = (offset >> 0) & 0xFF; sysEx[7] = timbreGroup; sysEx[8] = timbreNumber; sysEx[15] = volume; byte checkSum = 0; for (int i = 4; i < 23; ++i) checkSum += sysEx[i]; sysEx[23] = 0x80 - (checkSum & 0x7F); _output->sysEx(sysEx, 24); } PCSoundFxPlayer::PCSoundFxPlayer(PCSoundDriver *driver) : _playing(false), _driver(driver), _mutex() { memset(_instrumentsData, 0, sizeof(_instrumentsData)); _sfxData = NULL; _fadeOutCounter = 0; _driver->setUpdateCallback(updateCallback, this); } PCSoundFxPlayer::~PCSoundFxPlayer() { Common::StackLock lock(_mutex); _driver->setUpdateCallback(NULL, NULL); stop(); } bool PCSoundFxPlayer::load(const char *song) { debug(9, "PCSoundFxPlayer::load('%s')", song); /* stop (w/ fade out) the previous song */ while (_fadeOutCounter != 0 && _fadeOutCounter < 100) { g_system->delayMillis(50); } _fadeOutCounter = 0; Common::StackLock lock(_mutex); stop(); _sfxData = readBundleSoundFile(song); if (!_sfxData) { warning("Unable to load soundfx module '%s'", song); return 0; } for (int i = 0; i < NUM_INSTRUMENTS; ++i) { _instrumentsData[i] = NULL; char instrument[64]; memset(instrument, 0, 64); // Clear the data first memcpy(instrument, _sfxData + 20 + i * 30, 12); instrument[63] = '\0'; if (instrument[0] != '\0') { char *dot = strrchr(instrument, '.'); if (dot) { *dot = '\0'; } Common::strlcat(instrument, _driver->getInstrumentExtension(), sizeof(instrument)); uint32 instrumentSize; _instrumentsData[i] = readBundleSoundFile(instrument, &instrumentSize); if (!_instrumentsData[i]) { warning("Unable to load soundfx instrument '%s'", instrument); } else { _driver->notifyInstrumentLoad(_instrumentsData[i], instrumentSize, i); } } } return 1; } void PCSoundFxPlayer::play() { debug(9, "PCSoundFxPlayer::play()"); Common::StackLock lock(_mutex); if (_sfxData) { for (int i = 0; i < NUM_CHANNELS; ++i) { _instrumentsChannelTable[i] = -1; } _currentPos = 0; _currentOrder = 0; _numOrders = _sfxData[470]; _eventsDelay = (252 - _sfxData[471]) * 50 / 1060; _updateTicksCounter = 0; _playing = true; } } void PCSoundFxPlayer::stop() { Common::StackLock lock(_mutex); if (_playing || _fadeOutCounter != 0) { _fadeOutCounter = 0; _playing = false; for (int i = 0; i < NUM_CHANNELS; ++i) { _driver->stopChannel(i); } _driver->stopAll(); } unload(); } void PCSoundFxPlayer::fadeOut() { Common::StackLock lock(_mutex); if (_playing) { _fadeOutCounter = 1; _playing = false; } } void PCSoundFxPlayer::updateCallback(void *ref) { ((PCSoundFxPlayer *)ref)->update(); } void PCSoundFxPlayer::update() { Common::StackLock lock(_mutex); if (_playing || (_fadeOutCounter != 0 && _fadeOutCounter < 100)) { ++_updateTicksCounter; if (_updateTicksCounter > _eventsDelay) { handleEvents(); _updateTicksCounter = 0; } } } void PCSoundFxPlayer::handleEvents() { const byte *patternData = _sfxData + 600; const byte *orderTable = _sfxData + 472; uint16 patternNum = orderTable[_currentOrder] * 1024; for (int i = 0; i < 4; ++i) { handlePattern(i, patternData + patternNum + _currentPos); patternData += 4; } if (_fadeOutCounter != 0 && _fadeOutCounter < 100) { _fadeOutCounter += 2; } _currentPos += 16; if (_currentPos >= 1024) { _currentPos = 0; ++_currentOrder; if (_currentOrder == _numOrders) { _currentOrder = 0; } } debug(7, "_currentOrder=%d/%d _currentPos=%d", _currentOrder, _numOrders, _currentPos); } void PCSoundFxPlayer::handlePattern(int channel, const byte *patternData) { int instrument = patternData[2] >> 4; if (instrument != 0) { --instrument; if (_instrumentsChannelTable[channel] != instrument || _fadeOutCounter != 0) { _instrumentsChannelTable[channel] = instrument; const int volume = _sfxData[instrument] - _fadeOutCounter; _driver->setupChannel(channel, _instrumentsData[instrument], instrument, volume); } } int16 freq = (int16)READ_BE_UINT16(patternData); if (freq > 0) { _driver->stopChannel(channel); _driver->setChannelFrequency(channel, freq); } } void PCSoundFxPlayer::unload() { for (int i = 0; i < NUM_INSTRUMENTS; ++i) { free(_instrumentsData[i]); _instrumentsData[i] = NULL; } free(_sfxData); _sfxData = NULL; } PCSound::PCSound(Audio::Mixer *mixer, CineEngine *vm) : Sound(mixer, vm), _soundDriver(0) { _currentMusic = 0; _currentMusicStatus = 0; _currentBgSlot = 0; const MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB); const MusicType musicType = MidiDriver::getMusicType(dev); if (musicType == MT_MT32 || musicType == MT_GM) { const bool isMT32 = (musicType == MT_MT32 || ConfMan.getBool("native_mt32")); if (isMT32) { MidiDriver *driver = MidiDriver::createMidi(dev); if (driver && driver->open() == 0) { driver->sendMT32Reset(); _soundDriver = new MidiSoundDriverH32(driver); } else { warning("Could not create MIDI output, falling back to AdLib"); } } else { warning("General MIDI output devices are not supported, falling back to AdLib"); } } if (!_soundDriver) { if (_vm->getGameType() == GType_FW) { _soundDriver = new AdLibSoundDriverINS(_mixer); } else { _soundDriver = new AdLibSoundDriverADL(_mixer); } } _player = new PCSoundFxPlayer(_soundDriver); // Ensure the CD is open if (_vm->getGameType() == GType_FW && (_vm->getFeatures() & GF_CD)) g_system->getAudioCDManager()->open(); } PCSound::~PCSound() { delete _player; delete _soundDriver; } static const char *const musicFileNames[11] = { "DUGGER.DAT", "SUITE21.DAT", "FWARS.DAT", "SUITE23.DAT", "SUITE22.DAT", "ESCAL", "MOINES.DAT", "MEDIAVAL.DAT", "SFUTUR", "ALIENS", "TELESONG.DAT", }; static uint8 musicCDTracks[11] = { 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 22, }; void PCSound::loadMusic(const char *name) { debugC(5, kCineDebugSound, "PCSound::loadMusic('%s')", name); if (_vm->getGameType() == GType_FW && (_vm->getFeatures() & GF_CD)) { _currentMusic = 0; _currentMusicStatus = 0; for (int i = 0; i < 11; i++) { if (!strcmp((const char *)name, musicFileNames[i])) { _currentMusic = musicCDTracks[i]; _currentMusicStatus = musicCDTracks[i]; } } } else { _player->load(name); } } void PCSound::playMusic() { debugC(5, kCineDebugSound, "PCSound::playMusic()"); if (_vm->getGameType() == GType_FW && (_vm->getFeatures() & GF_CD)) { g_system->getAudioCDManager()->stop(); g_system->getAudioCDManager()->play(_currentMusic - 1, -1, 0, 0); } else { _player->play(); } } static uint8 bgCDTracks[49] = { 0, 21, 21, 23, 0, 29, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 22, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; void PCSound::setBgMusic(int num) { debugC(5, kCineDebugSound, "PCSound::setBgMusic(%d)", num); _currentBgSlot = num; if (!bgCDTracks[_currentBgSlot]) return; if ((_currentBgSlot == 1) || (_currentMusicStatus == 0 && _currentMusic != bgCDTracks[_currentBgSlot])) { _currentMusic = bgCDTracks[_currentBgSlot]; g_system->getAudioCDManager()->stop(); g_system->getAudioCDManager()->play(bgCDTracks[_currentBgSlot] - 1, -1, 0, 0); } } void PCSound::stopMusic() { debugC(5, kCineDebugSound, "PCSound::stopMusic()"); if (_vm->getGameType() == GType_FW && (_vm->getFeatures() & GF_CD)) { if (_currentBgSlot != 1) g_system->getAudioCDManager()->stop(); } _player->stop(); } void PCSound::fadeOutMusic() { debugC(5, kCineDebugSound, "PCSound::fadeOutMusic()"); if (_vm->getGameType() == GType_FW && (_vm->getFeatures() & GF_CD)) { if (_currentMusicStatus) { if (_currentBgSlot == 1) { _currentMusicStatus = 0; } else { _currentMusic = 0; _currentMusicStatus = 0; g_system->getAudioCDManager()->stop(); if (bgCDTracks[_currentBgSlot]) { g_system->getAudioCDManager()->play(_currentBgSlot - 1, -1, 0, 0); } } } } _player->fadeOut(); } void PCSound::playSound(int channel, int frequency, const uint8 *data, int size, int volumeStep, int stepCount, int volume, int repeat) { debugC(5, kCineDebugSound, "PCSound::playSound() channel %d size %d", channel, size); _soundDriver->playSample(data, size, channel, volume); } void PCSound::stopSound(int channel) { debugC(5, kCineDebugSound, "PCSound::stopSound() channel %d", channel); _soundDriver->resetChannel(channel); } PaulaSound::PaulaSound(Audio::Mixer *mixer, CineEngine *vm) : Sound(mixer, vm), _sfxTimer(0), _musicTimer(0), _musicFadeTimer(0) { _moduleStream = 0; // The original is using the following timer frequency: // 0.709379Mhz / 8000 = 88.672375Hz // 1000000 / 88.672375Hz = 11277.46944863us g_system->getTimerManager()->installTimerProc(&PaulaSound::sfxTimerProc, 11277, this, "PaulaSound::sfxTimerProc"); // The original is using the following timer frequency: // 0.709379Mhz / 14565 = 48.704359Hz // 1000000 / 48.704359Hz = 20532.04313806us g_system->getTimerManager()->installTimerProc(&PaulaSound::musicTimerProc, 20532, this, "PaulaSound::musicTimerProc"); } PaulaSound::~PaulaSound() { Common::StackLock sfxLock(_sfxMutex); g_system->getTimerManager()->removeTimerProc(&PaulaSound::sfxTimerProc); for (int i = 0; i < NUM_CHANNELS; ++i) { stopSound(i); } Common::StackLock musicLock(_musicMutex); g_system->getTimerManager()->removeTimerProc(&PaulaSound::musicTimerProc); stopMusic(); } void PaulaSound::loadMusic(const char *name) { debugC(5, kCineDebugSound, "PaulaSound::loadMusic('%s')", name); for (int i = 0; i < NUM_CHANNELS; ++i) { stopSound(i); } // Fade music out when there is music playing. _musicMutex.lock(); if (_mixer->isSoundHandleActive(_moduleHandle)) { // Only start fade out when it is not in progress. if (!_musicFadeTimer) { _musicFadeTimer = 1; } _musicMutex.unlock(); while (_musicFadeTimer != 64) { g_system->delayMillis(50); } } else { _musicMutex.unlock(); } Common::StackLock lock(_musicMutex); assert(!_mixer->isSoundHandleActive(_moduleHandle)); if (_vm->getGameType() == GType_FW) { // look for separate files Common::File f; if (f.open(name)) { _moduleStream = Audio::makeSoundFxStream(&f, 0, _mixer->getOutputRate()); } } else { // look in bundle files uint32 size; byte *buf = readBundleSoundFile(name, &size); if (buf) { Common::MemoryReadStream s(buf, size); _moduleStream = Audio::makeSoundFxStream(&s, readBundleSoundFile, _mixer->getOutputRate()); free(buf); } } } void PaulaSound::playMusic() { debugC(5, kCineDebugSound, "PaulaSound::playMusic()"); Common::StackLock lock(_musicMutex); _mixer->stopHandle(_moduleHandle); if (_moduleStream) { _musicFadeTimer = 0; _mixer->playStream(Audio::Mixer::kMusicSoundType, &_moduleHandle, _moduleStream); } } void PaulaSound::stopMusic() { debugC(5, kCineDebugSound, "PaulaSound::stopMusic()"); Common::StackLock lock(_musicMutex); _mixer->stopHandle(_moduleHandle); } void PaulaSound::setBgMusic(int num) { } void PaulaSound::fadeOutMusic() { debugC(5, kCineDebugSound, "PaulaSound::fadeOutMusic()"); Common::StackLock lock(_musicMutex); _musicFadeTimer = 1; } void PaulaSound::playSound(int channel, int frequency, const uint8 *data, int size, int volumeStep, int stepCount, int volume, int repeat) { debugC(5, kCineDebugSound, "PaulaSound::playSound() channel %d size %d", channel, size); Common::StackLock lock(_sfxMutex); assert(frequency > 0); stopSound(channel); if (size > 0) { byte *sound = (byte *)malloc(size); if (sound) { // Create the audio stream memcpy(sound, data, size); // Clear the first and last 16 bits like in the original. sound[0] = sound[1] = sound[size - 2] = sound[size - 1] = 0; Audio::SeekableAudioStream *stream = Audio::makeRawStream(sound, size, PAULA_FREQ / frequency, 0); // Initialize the volume control _channelsTable[channel].initialize(volume, volumeStep, stepCount); // Start the sfx _mixer->playStream(Audio::Mixer::kSFXSoundType, &_channelsTable[channel].handle, Audio::makeLoopingAudioStream(stream, repeat ? 0 : 1), -1, volume * Audio::Mixer::kMaxChannelVolume / 63, _channelBalance[channel]); } } } void PaulaSound::stopSound(int channel) { debugC(5, kCineDebugSound, "PaulaSound::stopSound() channel %d", channel); Common::StackLock lock(_sfxMutex); _mixer->stopHandle(_channelsTable[channel].handle); } void PaulaSound::sfxTimerProc(void *param) { PaulaSound *sound = (PaulaSound *)param; sound->sfxTimerCallback(); } void PaulaSound::sfxTimerCallback() { Common::StackLock lock(_sfxMutex); if (_sfxTimer < 6) { ++_sfxTimer; for (int i = 0; i < NUM_CHANNELS; ++i) { // Only process active channels if (!_mixer->isSoundHandleActive(_channelsTable[i].handle)) { continue; } if (_channelsTable[i].curStep) { --_channelsTable[i].curStep; } else { _channelsTable[i].curStep = _channelsTable[i].stepCount; const int volume = CLIP(_channelsTable[i].volume + _channelsTable[i].volumeStep, 0, 63); _channelsTable[i].volume = volume; // Unlike the original we stop silent sounds if (volume) { _mixer->setChannelVolume(_channelsTable[i].handle, volume * Audio::Mixer::kMaxChannelVolume / 63); } else { _mixer->stopHandle(_channelsTable[i].handle); } } } } else { _sfxTimer = 0; // Possible TODO: The original only ever started sounds here. This // should not be noticable though. So we do not do it for now. } } void PaulaSound::musicTimerProc(void *param) { PaulaSound *sound = (PaulaSound *)param; sound->musicTimerCallback(); } void PaulaSound::musicTimerCallback() { Common::StackLock lock(_musicMutex); ++_musicTimer; if (_musicTimer == 6) { _musicTimer = 0; if (_musicFadeTimer) { ++_musicFadeTimer; if (_musicFadeTimer == 64) { stopMusic(); } else { if (_mixer->isSoundHandleActive(_moduleHandle)) { _mixer->setChannelVolume(_moduleHandle, (64 - _musicFadeTimer) * Audio::Mixer::kMaxChannelVolume / 64); } } } } } const int PaulaSound::_channelBalance[NUM_CHANNELS] = { // L/R/R/L This is according to the Hardware Reference Manual. // TODO: It seems the order is swapped for some Amiga models: // http://www.amiga.org/forums/archive/index.php/t-7862.html // Maybe we should consider using R/L/L/R to match Amiga 500? // This also is a bit more drastic to what WineUAE defaults, // which is only 70% of full panning. -127, 127, 127, -127 }; } // End of namespace Cine