diff options
43 files changed, 1308 insertions, 848 deletions
diff --git a/audio/softsynth/adlib.cpp b/audio/adlib.cpp index 98519343b4..f609164495 100644 --- a/audio/softsynth/adlib.cpp +++ b/audio/adlib.cpp @@ -927,18 +927,20 @@ static void createLookupTable() { // //////////////////////////////////////// -class MidiDriver_ADLIB : public MidiDriver_Emulated { +class MidiDriver_ADLIB : public MidiDriver { friend class AdLibPart; friend class AdLibPercussionChannel; public: - MidiDriver_ADLIB(Audio::Mixer *mixer); + MidiDriver_ADLIB(); int open(); void close(); void send(uint32 b); void send(byte channel, uint32 b); // Supports higher than channel 15 uint32 property(int prop, uint32 param); + bool isOpen() const { return _isOpen; } + uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; } void setPitchBendRange(byte channel, uint range); void sysEx_customInstrument(byte channel, uint32 type, const byte *instr); @@ -946,10 +948,7 @@ public: MidiChannel *allocateChannel(); MidiChannel *getPercussionChannel() { return &_percussion; } // Percussion partially supported - - // AudioStream API - bool isStereo() const { return _opl->isStereo(); } - int getRate() const { return _mixer->getOutputRate(); } + virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc); private: bool _scummSmallHeader; // FIXME: This flag controls a special mode for SCUMM V3 games @@ -963,6 +962,9 @@ private: byte *_regCacheSecondary; #endif + Common::TimerManager::TimerProc _adlibTimerProc; + void *_adlibTimerParam; + int _timerCounter; uint16 _channelTable2[9]; @@ -974,7 +976,8 @@ private: AdLibPart _parts[32]; AdLibPercussionChannel _percussion; - void generateSamples(int16 *buf, int len); + bool _isOpen; + void onTimer(); void partKeyOn(AdLibPart *part, const AdLibInstrument *instr, byte note, byte velocity, const AdLibInstrument *second, byte pan); void partKeyOff(AdLibPart *part, byte note); @@ -1376,8 +1379,7 @@ void AdLibPercussionChannel::sysEx_customInstrument(uint32 type, const byte *ins // MidiDriver method implementations -MidiDriver_ADLIB::MidiDriver_ADLIB(Audio::Mixer *mixer) - : MidiDriver_Emulated(mixer) { +MidiDriver_ADLIB::MidiDriver_ADLIB() { uint i; _scummSmallHeader = false; @@ -1403,13 +1405,16 @@ MidiDriver_ADLIB::MidiDriver_ADLIB(Audio::Mixer *mixer) _timerIncrease = 0xD69; _timerThreshold = 0x411B; _opl = 0; + _adlibTimerProc = 0; + _adlibTimerParam = 0; + _isOpen = false; } int MidiDriver_ADLIB::open() { if (_isOpen) return MERR_ALREADY_OPEN; - MidiDriver_Emulated::open(); + _isOpen = true; int i; AdLibVoice *voice; @@ -1434,7 +1439,7 @@ int MidiDriver_ADLIB::open() { _opl3Mode = false; } #endif - _opl->init(getRate()); + _opl->init(); _regCache = (byte *)calloc(256, 1); @@ -1452,8 +1457,7 @@ int MidiDriver_ADLIB::open() { } #endif - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); - + _opl->start(new Common::Functor0Mem<void, MidiDriver_ADLIB>(this, &MidiDriver_ADLIB::onTimer)); return 0; } @@ -1462,7 +1466,8 @@ void MidiDriver_ADLIB::close() { return; _isOpen = false; - _mixer->stopHandle(_mixerSoundHandle); + // Stop the OPL timer + _opl->stop(); uint i; for (i = 0; i < ARRAYSIZE(_voices); ++i) { @@ -1616,14 +1621,10 @@ void MidiDriver_ADLIB::adlibWriteSecondary(byte reg, byte value) { } #endif -void MidiDriver_ADLIB::generateSamples(int16 *data, int len) { - if (_opl->isStereo()) { - len *= 2; - } - _opl->readBuffer(data, len); -} - void MidiDriver_ADLIB::onTimer() { + if (_adlibTimerProc) + (*_adlibTimerProc)(_adlibTimerParam); + _timerCounter += _timerIncrease; while (_timerCounter >= _timerThreshold) { _timerCounter -= _timerThreshold; @@ -1655,6 +1656,11 @@ void MidiDriver_ADLIB::onTimer() { } } +void MidiDriver_ADLIB::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) { + _adlibTimerProc = timerProc; + _adlibTimerParam = timerParam; +} + void MidiDriver_ADLIB::mcOff(AdLibVoice *voice) { AdLibVoice *tmp; @@ -2300,7 +2306,7 @@ MusicDevices AdLibEmuMusicPlugin::getDevices() const { } Common::Error AdLibEmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const { - *mididriver = new MidiDriver_ADLIB(g_system->getMixer()); + *mididriver = new MidiDriver_ADLIB(); return Common::kNoError; } diff --git a/audio/alsa_opl.cpp b/audio/alsa_opl.cpp new file mode 100644 index 0000000000..6b9e48e987 --- /dev/null +++ b/audio/alsa_opl.cpp @@ -0,0 +1,349 @@ +/* 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. + * + */ + +/* OPL implementation for hardware OPL using ALSA Direct FM API. + * + * Caveats and limitations: + * - Pretends to be a softsynth (emitting silence). + * - Dual OPL2 mode requires OPL3 hardware. + * - Every register write leads to a series of register writes on the hardware, + * due to the lack of direct register access in the ALSA Direct FM API. + * - No timers + */ + +#define FORBIDDEN_SYMBOL_ALLOW_ALL +#include "common/scummsys.h" + +#include "common/debug.h" +#include "common/str.h" +#include "audio/fmopl.h" + +#include <sys/ioctl.h> +#include <alsa/asoundlib.h> +#include <sound/asound_fm.h> + +namespace OPL { +namespace ALSA { + +class OPL : public ::OPL::RealOPL { +private: + enum { + kOpl2Voices = 9, + kVoices = 18, + kOpl2Operators = 18, + kOperators = 36 + }; + + Config::OplType _type; + int _iface; + snd_hwdep_t *_opl; + snd_dm_fm_voice _oper[kOperators]; + snd_dm_fm_note _voice[kVoices]; + snd_dm_fm_params _params; + int index[2]; + static const int voiceToOper0[kVoices]; + static const int regOffsetToOper[0x20]; + + void writeOplReg(int c, int r, int v); + void clear(); + +public: + OPL(Config::OplType type); + ~OPL(); + + bool init(); + void reset(); + + void write(int a, int v); + byte read(int a); + + void writeReg(int r, int v); +}; + +const int OPL::voiceToOper0[OPL::kVoices] = + { 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 }; + +const int OPL::regOffsetToOper[0x20] = + { 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + +OPL::OPL(Config::OplType type) : _type(type), _opl(nullptr), _iface(0) { +} + +OPL::~OPL() { + stop(); + + if (_opl) { + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr); + snd_hwdep_close(_opl); + } +} + +void OPL::clear() { + index[0] = index[1] = 0; + + memset(_oper, 0, sizeof(_oper)); + memset(_voice, 0, sizeof(_voice)); + memset(&_params, 0, sizeof(_params)); + + for (int i = 0; i < kOperators; ++i) { + _oper[i].op = (i / 3) % 2; + _oper[i].voice = (i / 6) * 3 + (i % 3); + } + + for (int i = 0; i < kVoices; ++i) + _voice[i].voice = i; + + // For OPL3 hardware we need to set up the panning in OPL2 modes + if (_iface == SND_HWDEP_IFACE_OPL3) { + if (_type == Config::kDualOpl2) { + for (int i = 0; i < kOpl2Operators; ++i) + _oper[i].left = 1; // FIXME below + for (int i = kOpl2Operators; i < kOperators; ++i) + _oper[i].right = 1; + } else if (_type == Config::kOpl2) { + for (int i = 0; i < kOpl2Operators; ++i) { + _oper[i].left = 1; + _oper[i].right = 1; + } + } + } +} + +bool OPL::init() { + clear(); + + int card = -1; + snd_ctl_t *ctl; + snd_hwdep_info_t *info; + snd_hwdep_info_alloca(&info); + + int iface = SND_HWDEP_IFACE_OPL3; + if (_type == Config::kOpl2) + iface = SND_HWDEP_IFACE_OPL2; + + // Look for OPL hwdep interface + while (!snd_card_next(&card) && card >= 0) { + int dev = -1; + Common::String name = Common::String::format("hw:%d", card); + + if (snd_ctl_open(&ctl, name.c_str(), 0) < 0) + continue; + + while (!snd_ctl_hwdep_next_device(ctl, &dev) && dev >= 0) { + name = Common::String::format("hw:%d,%d", card, dev); + + if (snd_hwdep_open(&_opl, name.c_str(), SND_HWDEP_OPEN_WRITE) < 0) + continue; + + if (!snd_hwdep_info(_opl, info)) { + int found = snd_hwdep_info_get_iface(info); + // OPL3 can be used for (Dual) OPL2 mode + if (found == iface || found == SND_HWDEP_IFACE_OPL3) { + snd_ctl_close(ctl); + _iface = found; + reset(); + return true; + } + } + + // Wrong interface, try next device + snd_hwdep_close(_opl); + _opl = nullptr; + } + + snd_ctl_close(ctl); + } + + return false; +} + +void OPL::reset() { + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr); + if (_iface == SND_HWDEP_IFACE_OPL3) + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_MODE, (void *)SNDRV_DM_FM_MODE_OPL3); + + clear(); + + // Sync up with the hardware + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params); + for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kVoices : kOpl2Voices); ++i) + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[i]); + for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kOperators : kOpl2Operators); ++i) + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[i]); +} + +void OPL::write(int port, int val) { + val &= 0xff; + int chip = (port & 2) >> 1; + + if (port & 1) { + switch(_type) { + case Config::kOpl2: + writeOplReg(0, index[0], val); + break; + case Config::kDualOpl2: + if (port & 8) { + writeOplReg(0, index[0], val); + writeOplReg(1, index[1], val); + } else + writeOplReg(chip, index[chip], val); + break; + case Config::kOpl3: + writeOplReg(chip, index[chip], val); + } + } else { + switch(_type) { + case Config::kOpl2: + index[0] = val; + break; + case Config::kDualOpl2: + if (port & 8) { + index[0] = val; + index[1] = val; + } else + index[chip] = val; + break; + case Config::kOpl3: + index[chip] = val; + } + } +} + +byte OPL::read(int port) { + return 0; +} + +void OPL::writeReg(int r, int v) { + switch (_type) { + case Config::kOpl2: + writeOplReg(0, r, v); + break; + case Config::kDualOpl2: + writeOplReg(0, r, v); + writeOplReg(1, r, v); + break; + case Config::kOpl3: + writeOplReg(r >= 0x100, r & 0xff, v); + } +} + +void OPL::writeOplReg(int c, int r, int v) { + if (r == 0x04 && c == 1 && _type == Config::kOpl3) { + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_CONNECTION, reinterpret_cast<void *>(v & 0x3f)); + } else if (r == 0x08 && c == 0) { + _params.kbd_split = (v >> 6) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params); + } else if (r == 0xbd && c == 0) { + _params.hihat = v & 0x1; + _params.cymbal = (v >> 1) & 0x1; + _params.tomtom = (v >> 2) & 0x1; + _params.snare = (v >> 3) & 0x1; + _params.bass = (v >> 4) & 0x1; + _params.rhythm = (v >> 5) & 0x1; + _params.vib_depth = (v >> 6) & 0x1; + _params.am_depth = (v >> 7) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params); + } else if (r < 0xa0 || r >= 0xe0) { + // Operator + int idx = regOffsetToOper[r & 0x1f]; + + if (idx == -1) + return; + + if (c == 1) + idx += kOpl2Operators; + + switch (r & 0xf0) { + case 0x20: + case 0x30: + _oper[idx].harmonic = v & 0xf; + _oper[idx].kbd_scale = (v >> 4) & 0x1; + _oper[idx].do_sustain = (v >> 5) & 0x1; + _oper[idx].vibrato = (v >> 6) & 0x1; + _oper[idx].am = (v >> 7) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0x40: + case 0x50: + _oper[idx].volume = ~v & 0x3f; + _oper[idx].scale_level = (v >> 6) & 0x3; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0x60: + case 0x70: + _oper[idx].decay = v & 0xf; + _oper[idx].attack = (v >> 4) & 0xf; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0x80: + case 0x90: + _oper[idx].release = v & 0xf; + _oper[idx].sustain = (v >> 4) & 0xf; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + break; + case 0xe0: + case 0xf0: + _oper[idx].waveform = v & (_type == Config::kOpl3 ? 0x7 : 0x3); + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]); + } + } else { + // Voice + int idx = r & 0xf; + + if (idx >= kOpl2Voices) + return; + + if (c == 1) + idx += kOpl2Voices; + + int opIdx = voiceToOper0[idx]; + + switch (r & 0xf0) { + case 0xa0: + _voice[idx].fnum = (_voice[idx].fnum & 0x300) | (v & 0xff); + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]); + break; + case 0xb0: + _voice[idx].fnum = ((v << 8) & 0x300) | (_voice[idx].fnum & 0xff); + _voice[idx].octave = (v >> 2) & 0x7; + _voice[idx].key_on = (v >> 5) & 0x1; + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]); + break; + case 0xc0: + _oper[opIdx].connection = _oper[opIdx + 3].connection = v & 0x1; + _oper[opIdx].feedback = _oper[opIdx + 3].feedback = (v >> 1) & 0x7; + if (_type == Config::kOpl3) { + _oper[opIdx].left = _oper[opIdx + 3].left = (v >> 4) & 0x1; + _oper[opIdx].right = _oper[opIdx + 3].right = (v >> 5) & 0x1; + } + snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[opIdx]); + } + } +} + +OPL *create(Config::OplType type) { + return new OPL(type); +} + +} // End of namespace ALSA +} // End of namespace OPL diff --git a/audio/fmopl.cpp b/audio/fmopl.cpp index 30229ea6bf..cc00ace264 100644 --- a/audio/fmopl.cpp +++ b/audio/fmopl.cpp @@ -22,21 +22,33 @@ #include "audio/fmopl.h" +#include "audio/mixer.h" #include "audio/softsynth/opl/dosbox.h" #include "audio/softsynth/opl/mame.h" #include "common/config-manager.h" +#include "common/system.h" #include "common/textconsole.h" +#include "common/timer.h" #include "common/translation.h" namespace OPL { +// Factory functions + +#ifdef USE_ALSA +namespace ALSA { + OPL *create(Config::OplType type); +} // End of namespace ALSA +#endif // USE_ALSA + // Config implementation enum OplEmulator { kAuto = 0, kMame = 1, - kDOSBox = 2 + kDOSBox = 2, + kALSA = 3 }; OPL::OPL() { @@ -51,6 +63,9 @@ const Config::EmulatorDescription Config::_drivers[] = { #ifndef DISABLE_DOSBOX_OPL { "db", _s("DOSBox OPL emulator"), kDOSBox, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 }, #endif +#ifdef USE_ALSA + { "alsa", _s("ALSA Direct FM"), kALSA, kFlagOpl2 | kFlagDualOpl2 | kFlagOpl3 }, +#endif { 0, 0, 0, 0 } }; @@ -63,6 +78,15 @@ Config::DriverId Config::parse(const Common::String &name) { return -1; } +const Config::EmulatorDescription *Config::findDriver(DriverId id) { + for (int i = 0; _drivers[i].name; ++i) { + if (_drivers[i].id == id) + return &_drivers[i]; + } + + return 0; +} + Config::DriverId Config::detect(OplType type) { uint32 flags = 0; switch (type) { @@ -90,8 +114,11 @@ Config::DriverId Config::detect(OplType type) { // When a valid driver is selected, check whether it supports // the requested OPL chip. if (drv != -1 && drv != kAuto) { + const EmulatorDescription *driverDesc = findDriver(drv); // If the chip is supported, just use the driver. - if ((flags & _drivers[drv].flags)) { + if (!driverDesc) { + warning("The selected OPL driver %d could not be found", drv); + } else if ((flags & driverDesc->flags)) { return drv; } else { // Else we will output a warning and just @@ -151,6 +178,11 @@ OPL *Config::create(DriverId driver, OplType type) { return new DOSBox::OPL(type); #endif +#ifdef USE_ALSA + case kALSA: + return ALSA::create(type); +#endif + default: warning("Unsupported OPL emulator %d", driver); // TODO: Maybe we should add some dummy emulator too, which just outputs @@ -159,43 +191,143 @@ OPL *Config::create(DriverId driver, OplType type) { } } +void OPL::start(TimerCallback *callback, int timerFrequency) { + _callback.reset(callback); + startCallbacks(timerFrequency); +} + +void OPL::stop() { + stopCallbacks(); + _callback.reset(); +} + bool OPL::_hasInstance = false; -} // End of namespace OPL +RealOPL::RealOPL() : _baseFreq(0), _remainingTicks(0) { +} + +RealOPL::~RealOPL() { + // Stop callbacks, just in case. If it's still playing at this + // point, there's probably a bigger issue, though. The subclass + // needs to call stop() or the pointer can still use be used in + // the mixer thread at the same time. + stop(); +} + +void RealOPL::setCallbackFrequency(int timerFrequency) { + stopCallbacks(); + startCallbacks(timerFrequency); +} + +void RealOPL::startCallbacks(int timerFrequency) { + _baseFreq = timerFrequency; + assert(_baseFreq > 0); + + // We can't request more a timer faster than 100Hz. We'll handle this by calling + // the proc multiple times in onTimer() later on. + if (timerFrequency > kMaxFreq) + timerFrequency = kMaxFreq; -void OPLDestroy(FM_OPL *OPL) { - delete OPL; + _remainingTicks = 0; + g_system->getTimerManager()->installTimerProc(timerProc, 1000000 / timerFrequency, this, "RealOPL"); } -void OPLResetChip(FM_OPL *OPL) { - OPL->reset(); +void RealOPL::stopCallbacks() { + g_system->getTimerManager()->removeTimerProc(timerProc); + _baseFreq = 0; + _remainingTicks = 0; } -void OPLWrite(FM_OPL *OPL, int a, int v) { - OPL->write(a, v); +void RealOPL::timerProc(void *refCon) { + static_cast<RealOPL *>(refCon)->onTimer(); } -unsigned char OPLRead(FM_OPL *OPL, int a) { - return OPL->read(a); +void RealOPL::onTimer() { + uint callbacks = 1; + + if (_baseFreq > kMaxFreq) { + // We run faster than our max, so run the callback multiple + // times to approximate the actual timer callback frequency. + uint totalTicks = _baseFreq + _remainingTicks; + callbacks = totalTicks / kMaxFreq; + _remainingTicks = totalTicks % kMaxFreq; + } + + // Call the callback multiple times. The if is on the inside of the + // loop in case the callback removes itself. + for (uint i = 0; i < callbacks; i++) + if (_callback && _callback->isValid()) + (*_callback)(); } -void OPLWriteReg(FM_OPL *OPL, int r, int v) { - OPL->writeReg(r, v); +EmulatedOPL::EmulatedOPL() : + _nextTick(0), + _samplesPerTick(0), + _baseFreq(0), + _handle(new Audio::SoundHandle()) { } -void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length) { - OPL->readBuffer(buffer, length); +EmulatedOPL::~EmulatedOPL() { + // Stop callbacks, just in case. If it's still playing at this + // point, there's probably a bigger issue, though. The subclass + // needs to call stop() or the pointer can still use be used in + // the mixer thread at the same time. + stop(); + + delete _handle; } -FM_OPL *makeAdLibOPL(int rate) { - FM_OPL *opl = OPL::Config::create(); +int EmulatedOPL::readBuffer(int16 *buffer, const int numSamples) { + const int stereoFactor = isStereo() ? 2 : 1; + int len = numSamples / stereoFactor; + int step; + + do { + step = len; + if (step > (_nextTick >> FIXP_SHIFT)) + step = (_nextTick >> FIXP_SHIFT); + + generateSamples(buffer, step * stereoFactor); - if (opl) { - if (!opl->init(rate)) { - delete opl; - opl = 0; + _nextTick -= step << FIXP_SHIFT; + if (!(_nextTick >> FIXP_SHIFT)) { + if (_callback && _callback->isValid()) + (*_callback)(); + + _nextTick += _samplesPerTick; } - } - return opl; + buffer += step * stereoFactor; + len -= step; + } while (len); + + return numSamples; +} + +int EmulatedOPL::getRate() const { + return g_system->getMixer()->getOutputRate(); } + +void EmulatedOPL::startCallbacks(int timerFrequency) { + setCallbackFrequency(timerFrequency); + g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); +} + +void EmulatedOPL::stopCallbacks() { + g_system->getMixer()->stopHandle(*_handle); +} + +void EmulatedOPL::setCallbackFrequency(int timerFrequency) { + _baseFreq = timerFrequency; + assert(_baseFreq != 0); + + int d = getRate() / _baseFreq; + int r = getRate() % _baseFreq; + + // This is equivalent to (getRate() << FIXP_SHIFT) / BASE_FREQ + // but less prone to arithmetic overflow. + + _samplesPerTick = (d << FIXP_SHIFT) + (r << FIXP_SHIFT) / _baseFreq; +} + +} // End of namespace OPL diff --git a/audio/fmopl.h b/audio/fmopl.h index 85ac606c7a..ba0872d87b 100644 --- a/audio/fmopl.h +++ b/audio/fmopl.h @@ -23,8 +23,16 @@ #ifndef AUDIO_FMOPL_H #define AUDIO_FMOPL_H +#include "audio/audiostream.h" + +#include "common/func.h" +#include "common/ptr.h" #include "common/scummsys.h" +namespace Audio { +class SoundHandle; +} + namespace Common { class String; } @@ -71,6 +79,12 @@ public: static DriverId parse(const Common::String &name); /** + * @return The driver description for the given id or 0 in case it is not + * available. + */ + static const EmulatorDescription *findDriver(DriverId id); + + /** * Detects a driver for the specific type. * * @return Returns a valid driver id on success, -1 otherwise. @@ -92,6 +106,14 @@ private: static const EmulatorDescription _drivers[]; }; +/** + * The type of the OPL timer callback functor. + */ +typedef Common::Functor0<void> TimerCallback; + +/** + * A representation of a Yamaha OPL chip. + */ class OPL { private: static bool _hasInstance; @@ -102,10 +124,9 @@ public: /** * Initializes the OPL emulator. * - * @param rate output sample rate * @return true on success, false on failure */ - virtual bool init(int rate) = 0; + virtual bool init() = 0; /** * Reinitializes the OPL emulator @@ -140,6 +161,101 @@ public: virtual void writeReg(int r, int v) = 0; /** + * Start the OPL with callbacks. + */ + void start(TimerCallback *callback, int timerFrequency = kDefaultCallbackFrequency); + + /** + * Stop the OPL + */ + void stop(); + + /** + * Change the callback frequency. This must only be called from a + * timer proc. + */ + virtual void setCallbackFrequency(int timerFrequency) = 0; + + enum { + /** + * The default callback frequency that start() uses + */ + kDefaultCallbackFrequency = 250 + }; + +protected: + /** + * Start the callbacks. + */ + virtual void startCallbacks(int timerFrequency) = 0; + + /** + * Stop the callbacks. + */ + virtual void stopCallbacks() = 0; + + /** + * The functor for callbacks. + */ + Common::ScopedPtr<TimerCallback> _callback; +}; + +/** + * An OPL that represents a real OPL, as opposed to an emulated one. + * + * This will use an actual timer instead of using one calculated from + * the number of samples in an AudioStream::readBuffer call. + */ +class RealOPL : public OPL { +public: + RealOPL(); + virtual ~RealOPL(); + + // OPL API + void setCallbackFrequency(int timerFrequency); + +protected: + // OPL API + void startCallbacks(int timerFrequency); + void stopCallbacks(); + +private: + static void timerProc(void *refCon); + void onTimer(); + + uint _baseFreq; + uint _remainingTicks; + + enum { + kMaxFreq = 100 + }; +}; + +/** + * An OPL that represents an emulated OPL. + * + * This will send callbacks based on the number of samples + * decoded in readBuffer(). + */ +class EmulatedOPL : public OPL, protected Audio::AudioStream { +public: + EmulatedOPL(); + virtual ~EmulatedOPL(); + + // OPL API + void setCallbackFrequency(int timerFrequency); + + // AudioStream API + int readBuffer(int16 *buffer, const int numSamples); + int getRate() const; + bool endOfData() const { return false; } + +protected: + // OPL API + void startCallbacks(int timerFrequency); + void stopCallbacks(); + + /** * Read up to 'length' samples. * * Data will be in native endianess, 16 bit per sample, signed. @@ -149,33 +265,21 @@ public: * So if you request 4 samples from a stereo OPL, you will get * a total of two left channel and two right channel samples. */ - virtual void readBuffer(int16 *buffer, int length) = 0; - - /** - * Returns whether the setup OPL mode is stereo or not - */ - virtual bool isStereo() const = 0; -}; + virtual void generateSamples(int16 *buffer, int numSamples) = 0; -} // End of namespace OPL +private: + int _baseFreq; -// Legacy API -// !You should not write any new code using the legacy API! -typedef OPL::OPL FM_OPL; + enum { + FIXP_SHIFT = 16 + }; -void OPLDestroy(FM_OPL *OPL); + int _nextTick; + int _samplesPerTick; -void OPLResetChip(FM_OPL *OPL); -void OPLWrite(FM_OPL *OPL, int a, int v); -unsigned char OPLRead(FM_OPL *OPL, int a); -void OPLWriteReg(FM_OPL *OPL, int r, int v); -void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length); + Audio::SoundHandle *_handle; +}; -/** - * Legacy factory to create an AdLib (OPL2) chip. - * - * !You should not write any new code using the legacy API! - */ -FM_OPL *makeAdLibOPL(int rate); +} // End of namespace OPL #endif diff --git a/audio/miles_adlib.cpp b/audio/miles_adlib.cpp index 903b0a92be..bf5c9d4a73 100644 --- a/audio/miles_adlib.cpp +++ b/audio/miles_adlib.cpp @@ -116,9 +116,9 @@ uint16 milesAdLibVolumeSensitivityTable[] = { }; -class MidiDriver_Miles_AdLib : public MidiDriver_Emulated { +class MidiDriver_Miles_AdLib : public MidiDriver { public: - MidiDriver_Miles_AdLib(Audio::Mixer *mixer, InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount); + MidiDriver_Miles_AdLib(InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount); virtual ~MidiDriver_Miles_AdLib(); // MidiDriver @@ -128,18 +128,14 @@ public: MidiChannel *allocateChannel() { return NULL; } MidiChannel *getPercussionChannel() { return NULL; } - // AudioStream - bool isStereo() const { return _modeStereo; } - int getRate() const { return _mixer->getOutputRate(); } - int getPolyphony() const { return _modePhysicalFmVoicesCount; } - bool hasRhythmChannel() const { return false; } - - // MidiDriver_Emulated - void generateSamples(int16 *buf, int len); + bool isOpen() const { return _isOpen; } + uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; } void setVolume(byte volume); virtual uint32 property(int prop, uint32 param); + void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc); + private: bool _modeOPL3; byte _modePhysicalFmVoicesCount; @@ -222,6 +218,11 @@ private: OPL::OPL *_opl; int _masterVolume; + Common::TimerManager::TimerProc _adlibTimerProc; + void *_adlibTimerParam; + + bool _isOpen; + // stores information about all MIDI channels (not the actual OPL FM voice channels!) MidiChannelEntry _midiChannels[MILES_MIDI_CHANNEL_COUNT]; @@ -238,10 +239,8 @@ private: bool circularPhysicalAssignment; byte circularPhysicalAssignmentFmVoice; -protected: void onTimer(); -private: void resetData(); void resetAdLib(); void resetAdLibOperatorRegisters(byte baseRegister, byte value); @@ -271,8 +270,9 @@ private: void pitchBendChange(byte MIDIchannel, byte parameter1, byte parameter2); }; -MidiDriver_Miles_AdLib::MidiDriver_Miles_AdLib(Audio::Mixer *mixer, InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount) - : MidiDriver_Emulated(mixer), _masterVolume(15), _opl(0) { +MidiDriver_Miles_AdLib::MidiDriver_Miles_AdLib(InstrumentEntry *instrumentTablePtr, uint16 instrumentTableCount) + : _masterVolume(15), _opl(0), + _adlibTimerProc(0), _adlibTimerParam(0), _isOpen(false) { _instrumentTablePtr = instrumentTablePtr; _instrumentTableCount = instrumentTableCount; @@ -298,8 +298,6 @@ MidiDriver_Miles_AdLib::~MidiDriver_Miles_AdLib() { } int MidiDriver_Miles_AdLib::open() { - int rate = _mixer->getOutputRate(); - if (_modeOPL3) { // Try to create OPL3 first _opl = OPL::Config::create(OPL::Config::kOpl3); @@ -319,11 +317,11 @@ int MidiDriver_Miles_AdLib::open() { return -1; } - _opl->init(rate); + _opl->init(); - MidiDriver_Emulated::open(); + _isOpen = true; - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + _opl->start(new Common::Functor0Mem<void, MidiDriver_Miles_AdLib>(this, &MidiDriver_Miles_AdLib::onTimer)); resetAdLib(); @@ -331,9 +329,8 @@ int MidiDriver_Miles_AdLib::open() { } void MidiDriver_Miles_AdLib::close() { - _mixer->stopHandle(_mixerSoundHandle); - delete _opl; + _isOpen = false; } void MidiDriver_Miles_AdLib::setVolume(byte volume) { @@ -342,6 +339,8 @@ void MidiDriver_Miles_AdLib::setVolume(byte volume) { } void MidiDriver_Miles_AdLib::onTimer() { + if (_adlibTimerProc) + (*_adlibTimerProc)(_adlibTimerParam); } void MidiDriver_Miles_AdLib::resetData() { @@ -438,11 +437,9 @@ void MidiDriver_Miles_AdLib::send(uint32 b) { } } -void MidiDriver_Miles_AdLib::generateSamples(int16 *data, int len) { - if (_modeStereo) - len *= 2; - - _opl->readBuffer(data, len); +void MidiDriver_Miles_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) { + _adlibTimerProc = timerProc; + _adlibTimerParam = timerParam; } int16 MidiDriver_Miles_AdLib::searchFreeVirtualFmVoiceChannel() { @@ -1271,7 +1268,7 @@ MidiDriver *MidiDriver_Miles_AdLib_create(const Common::String &filenameAdLib, c // Free instrument file/stream data delete[] streamDataPtr; - return new MidiDriver_Miles_AdLib(g_system->getMixer(), instrumentTablePtr, instrumentTableCount); + return new MidiDriver_Miles_AdLib(instrumentTablePtr, instrumentTableCount); } } // End of namespace Audio diff --git a/audio/module.mk b/audio/module.mk index abbeed6184..9e002d57ba 100644 --- a/audio/module.mk +++ b/audio/module.mk @@ -1,6 +1,7 @@ MODULE := audio MODULE_OBJS := \ + adlib.o \ audiostream.o \ fmopl.o \ mididrv.o \ @@ -39,7 +40,6 @@ MODULE_OBJS := \ mods/rjp1.o \ mods/soundfx.o \ mods/tfmx.o \ - softsynth/adlib.o \ softsynth/cms.o \ softsynth/opl/dbopl.o \ softsynth/opl/dosbox.o \ @@ -58,6 +58,11 @@ MODULE_OBJS := \ softsynth/sid.o \ softsynth/wave6581.o +ifdef USE_ALSA +MODULE_OBJS += \ + alsa_opl.o +endif + ifndef USE_ARM_SOUND_ASM MODULE_OBJS += \ rate.o diff --git a/audio/softsynth/opl/dosbox.cpp b/audio/softsynth/opl/dosbox.cpp index 5c3d833f54..3d90ec93d0 100644 --- a/audio/softsynth/opl/dosbox.cpp +++ b/audio/softsynth/opl/dosbox.cpp @@ -32,6 +32,7 @@ #include "dosbox.h" #include "dbopl.h" +#include "audio/mixer.h" #include "common/system.h" #include "common/scummsys.h" #include "common/util.h" @@ -148,6 +149,7 @@ OPL::OPL(Config::OplType type) : _type(type), _rate(0), _emulator(0) { } OPL::~OPL() { + stop(); free(); } @@ -156,7 +158,7 @@ void OPL::free() { _emulator = 0; } -bool OPL::init(int rate) { +bool OPL::init() { free(); memset(&_reg, 0, sizeof(_reg)); @@ -167,19 +169,19 @@ bool OPL::init(int rate) { return false; DBOPL::InitTables(); - _emulator->Setup(rate); + _rate = g_system->getMixer()->getOutputRate(); + _emulator->Setup(_rate); if (_type == Config::kDualOpl2) { // Setup opl3 mode in the hander _emulator->WriteReg(0x105, 1); } - _rate = rate; return true; } void OPL::reset() { - init(_rate); + init(); } void OPL::write(int port, int val) { @@ -307,7 +309,7 @@ void OPL::dualWrite(uint8 index, uint8 reg, uint8 val) { _emulator->WriteReg(fullReg, val); } -void OPL::readBuffer(int16 *buffer, int length) { +void OPL::generateSamples(int16 *buffer, int length) { // For stereo OPL cards, we divide the sample count by 2, // to match stereo AudioStream behavior. if (_type != Config::kOpl2) diff --git a/audio/softsynth/opl/dosbox.h b/audio/softsynth/opl/dosbox.h index 513a49f6b8..c52f06761a 100644 --- a/audio/softsynth/opl/dosbox.h +++ b/audio/softsynth/opl/dosbox.h @@ -69,7 +69,7 @@ namespace DBOPL { struct Chip; } // end of namespace DBOPL -class OPL : public ::OPL::OPL { +class OPL : public ::OPL::EmulatedOPL { private: Config::OplType _type; uint _rate; @@ -87,7 +87,7 @@ public: OPL(Config::OplType type); ~OPL(); - bool init(int rate); + bool init(); void reset(); void write(int a, int v); @@ -95,8 +95,10 @@ public: void writeReg(int r, int v); - void readBuffer(int16 *buffer, int length); bool isStereo() const { return _type != Config::kOpl2; } + +protected: + void generateSamples(int16 *buffer, int length); }; } // End of namespace DOSBox diff --git a/audio/softsynth/opl/mame.cpp b/audio/softsynth/opl/mame.cpp index da75ba76ba..696169be09 100644 --- a/audio/softsynth/opl/mame.cpp +++ b/audio/softsynth/opl/mame.cpp @@ -31,6 +31,8 @@ #include "mame.h" +#include "audio/mixer.h" +#include "common/system.h" #include "common/textconsole.h" #include "common/util.h" @@ -46,15 +48,19 @@ namespace OPL { namespace MAME { OPL::~OPL() { + stop(); MAME::OPLDestroy(_opl); _opl = 0; } -bool OPL::init(int rate) { - if (_opl) +bool OPL::init() { + if (_opl) { + stopCallbacks(); MAME::OPLDestroy(_opl); + } + + _opl = MAME::makeAdLibOPL(g_system->getMixer()->getOutputRate()); - _opl = MAME::makeAdLibOPL(rate); return (_opl != 0); } @@ -74,7 +80,7 @@ void OPL::writeReg(int r, int v) { MAME::OPLWriteReg(_opl, r, v); } -void OPL::readBuffer(int16 *buffer, int length) { +void OPL::generateSamples(int16 *buffer, int length) { MAME::YM3812UpdateOne(_opl, buffer, length); } diff --git a/audio/softsynth/opl/mame.h b/audio/softsynth/opl/mame.h index bd479d9e45..67d80bb193 100644 --- a/audio/softsynth/opl/mame.h +++ b/audio/softsynth/opl/mame.h @@ -174,14 +174,14 @@ void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length); FM_OPL *makeAdLibOPL(int rate); // OPL API implementation -class OPL : public ::OPL::OPL { +class OPL : public ::OPL::EmulatedOPL { private: FM_OPL *_opl; public: OPL() : _opl(0) {} ~OPL(); - bool init(int rate); + bool init(); void reset(); void write(int a, int v); @@ -189,8 +189,10 @@ public: void writeReg(int r, int v); - void readBuffer(int16 *buffer, int length); bool isStereo() const { return false; } + +protected: + void generateSamples(int16 *buffer, int length); }; } // End of namespace MAME @@ -3556,7 +3556,7 @@ if test "$_alsa" = yes ; then LIBS="$LIBS $ALSA_LIBS -lasound" INCLUDES="$INCLUDES $ALSA_CFLAGS" fi -define_in_config_h_if_yes "$_alsa" 'USE_ALSA' +define_in_config_if_yes "$_alsa" 'USE_ALSA' echo "$_alsa" # diff --git a/engines/agos/drivers/accolade/adlib.cpp b/engines/agos/drivers/accolade/adlib.cpp index 11edc7c5f7..d22710664d 100644 --- a/engines/agos/drivers/accolade/adlib.cpp +++ b/engines/agos/drivers/accolade/adlib.cpp @@ -112,9 +112,9 @@ const uint16 frequencyLookUpTableMusicDrv[12] = { // // I have currently not implemented dynamic channel allocation. -class MidiDriver_Accolade_AdLib : public MidiDriver_Emulated { +class MidiDriver_Accolade_AdLib : public MidiDriver { public: - MidiDriver_Accolade_AdLib(Audio::Mixer *mixer); + MidiDriver_Accolade_AdLib(); virtual ~MidiDriver_Accolade_AdLib(); // MidiDriver @@ -124,20 +124,16 @@ public: MidiChannel *allocateChannel() { return NULL; } MidiChannel *getPercussionChannel() { return NULL; } - // AudioStream - bool isStereo() const { return false; } - int getRate() const { return _mixer->getOutputRate(); } - int getPolyphony() const { return AGOS_ADLIB_VOICES_COUNT; } - bool hasRhythmChannel() const { return false; } - - // MidiDriver_Emulated - void generateSamples(int16 *buf, int len); + bool isOpen() const { return _isOpen; } + uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; } void setVolume(byte volume); virtual uint32 property(int prop, uint32 param); bool setupInstruments(byte *instrumentData, uint16 instrumentDataSize, bool useMusicDrvFile); + void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc); + private: bool _musicDrvMode; @@ -170,16 +166,19 @@ private: OPL::OPL *_opl; int _masterVolume; + Common::TimerManager::TimerProc _adlibTimerProc; + void *_adlibTimerParam; + + bool _isOpen; + // points to a MIDI channel for each of the new voice channels byte _voiceChannelMapping[AGOS_ADLIB_VOICES_COUNT]; // stores information about all FM voice channels ChannelEntry _channels[AGOS_ADLIB_VOICES_COUNT]; -protected: void onTimer(); -private: void resetAdLib(); void resetAdLibOperatorRegisters(byte baseRegister, byte value); void resetAdLibFMVoiceChannelRegisters(byte baseRegister, byte value); @@ -192,8 +191,9 @@ private: void noteOff(byte FMvoiceChannel, byte note, bool dontCheckNote); }; -MidiDriver_Accolade_AdLib::MidiDriver_Accolade_AdLib(Audio::Mixer *mixer) - : MidiDriver_Emulated(mixer), _masterVolume(15), _opl(0) { +MidiDriver_Accolade_AdLib::MidiDriver_Accolade_AdLib() + : _masterVolume(15), _opl(0), + _adlibTimerProc(0), _adlibTimerParam(0), _isOpen(false) { memset(_channelMapping, 0, sizeof(_channelMapping)); memset(_instrumentMapping, 0, sizeof(_instrumentMapping)); memset(_instrumentVolumeAdjust, 0, sizeof(_instrumentVolumeAdjust)); @@ -213,8 +213,6 @@ MidiDriver_Accolade_AdLib::~MidiDriver_Accolade_AdLib() { } int MidiDriver_Accolade_AdLib::open() { - int rate = _mixer->getOutputRate(); - // debugC(kDebugLevelAdLibDriver, "AdLib: starting driver"); _opl = OPL::Config::create(OPL::Config::kOpl2); @@ -222,11 +220,11 @@ int MidiDriver_Accolade_AdLib::open() { if (!_opl) return -1; - _opl->init(rate); + _opl->init(); - MidiDriver_Emulated::open(); + _isOpen = true; - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO); + _opl->start(new Common::Functor0Mem<void, MidiDriver_Accolade_AdLib>(this, &MidiDriver_Accolade_AdLib::onTimer)); resetAdLib(); @@ -260,9 +258,8 @@ int MidiDriver_Accolade_AdLib::open() { } void MidiDriver_Accolade_AdLib::close() { - _mixer->stopHandle(_mixerSoundHandle); - delete _opl; + _isOpen = false; } void MidiDriver_Accolade_AdLib::setVolume(byte volume) { @@ -271,6 +268,8 @@ void MidiDriver_Accolade_AdLib::setVolume(byte volume) { } void MidiDriver_Accolade_AdLib::onTimer() { + if (_adlibTimerProc) + (*_adlibTimerProc)(_adlibTimerParam); } void MidiDriver_Accolade_AdLib::resetAdLib() { @@ -368,8 +367,9 @@ void MidiDriver_Accolade_AdLib::send(uint32 b) { } } -void MidiDriver_Accolade_AdLib::generateSamples(int16 *data, int len) { - _opl->readBuffer(data, len); +void MidiDriver_Accolade_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) { + _adlibTimerProc = timerProc; + _adlibTimerParam = timerParam; } void MidiDriver_Accolade_AdLib::noteOn(byte FMvoiceChannel, byte note, byte velocity) { @@ -871,7 +871,7 @@ MidiDriver *MidiDriver_Accolade_AdLib_create(Common::String driverFilename) { if (!driverData) error("ACCOLADE-ADLIB: error during readDriver()"); - MidiDriver_Accolade_AdLib *driver = new MidiDriver_Accolade_AdLib(g_system->getMixer()); + MidiDriver_Accolade_AdLib *driver = new MidiDriver_Accolade_AdLib(); if (driver) { if (!driver->setupInstruments(driverData, driverDataSize, isMusicDrvFile)) { delete driver; diff --git a/engines/cine/sound.cpp b/engines/cine/sound.cpp index 069a4787ac..0c788b816c 100644 --- a/engines/cine/sound.cpp +++ b/engines/cine/sound.cpp @@ -101,7 +101,7 @@ struct AdLibSoundInstrument { byte amDepth; }; -class AdLibSoundDriver : public PCSoundDriver, Audio::AudioStream { +class AdLibSoundDriver : public PCSoundDriver { public: AdLibSoundDriver(Audio::Mixer *mixer); virtual ~AdLibSoundDriver(); @@ -112,14 +112,8 @@ public: virtual void stopChannel(int channel); virtual void stopAll(); - // AudioStream interface - virtual int readBuffer(int16 *buffer, const int numSamples); - virtual bool isStereo() const { return false; } - virtual bool endOfData() const { return false; } - virtual int getRate() const { return _sampleRate; } - void initCard(); - void update(int16 *buf, int len); + 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; @@ -128,10 +122,8 @@ protected: UpdateCallback _upCb; void *_upRef; - FM_OPL *_opl; - int _sampleRate; + OPL::OPL *_opl; Audio::Mixer *_mixer; - Audio::SoundHandle _soundHandle; byte _vibrato; int _channelsVolumeTable[4]; @@ -282,17 +274,19 @@ void PCSoundDriver::resetChannel(int channel) { AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer) : _upCb(0), _upRef(0), _mixer(mixer) { - _sampleRate = _mixer->getOutputRate(); - _opl = makeAdLibOPL(_sampleRate); + + _opl = OPL::Config::create(); + if (!_opl || !_opl->init()) + error("Failed to create OPL"); + memset(_channelsVolumeTable, 0, sizeof(_channelsVolumeTable)); memset(_instrumentsTable, 0, sizeof(_instrumentsTable)); initCard(); - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + _opl->start(new Common::Functor0Mem<void, AdLibSoundDriver>(this, &AdLibSoundDriver::onTimer), 50); } AdLibSoundDriver::~AdLibSoundDriver() { - _mixer->stopHandle(_soundHandle); - OPLDestroy(_opl); + delete _opl; } void AdLibSoundDriver::setUpdateCallback(UpdateCallback upCb, void *ref) { @@ -322,71 +316,52 @@ void AdLibSoundDriver::stopChannel(int channel) { channel = 6; } if (ins->mode == 0 || channel == 6) { - OPLWriteReg(_opl, 0xB0 | channel, 0); + _opl->writeReg(0xB0 | channel, 0); } if (ins->mode != 0) { _vibrato &= ~(1 << (10 - ins->channel)); - OPLWriteReg(_opl, 0xBD, _vibrato); + _opl->writeReg(0xBD, _vibrato); } } void AdLibSoundDriver::stopAll() { int i; for (i = 0; i < 18; ++i) { - OPLWriteReg(_opl, 0x40 | _operatorsTable[i], 63); + _opl->writeReg(0x40 | _operatorsTable[i], 63); } for (i = 0; i < 9; ++i) { - OPLWriteReg(_opl, 0xB0 | i, 0); + _opl->writeReg(0xB0 | i, 0); } - OPLWriteReg(_opl, 0xBD, 0); -} - -int AdLibSoundDriver::readBuffer(int16 *buffer, const int numSamples) { - update(buffer, numSamples); - return numSamples; + _opl->writeReg(0xBD, 0); } void AdLibSoundDriver::initCard() { _vibrato = 0x20; - OPLWriteReg(_opl, 0xBD, _vibrato); - OPLWriteReg(_opl, 0x08, 0x40); + _opl->writeReg(0xBD, _vibrato); + _opl->writeReg(0x08, 0x40); static const int oplRegs[] = { 0x40, 0x60, 0x80, 0x20, 0xE0 }; for (int i = 0; i < 9; ++i) { - OPLWriteReg(_opl, 0xB0 | i, 0); + _opl->writeReg(0xB0 | i, 0); } for (int i = 0; i < 9; ++i) { - OPLWriteReg(_opl, 0xC0 | i, 0); + _opl->writeReg(0xC0 | i, 0); } for (int j = 0; j < 5; j++) { for (int i = 0; i < 18; ++i) { - OPLWriteReg(_opl, oplRegs[j] | _operatorsTable[i], 0); + _opl->writeReg(oplRegs[j] | _operatorsTable[i], 0); } } - OPLWriteReg(_opl, 1, 0x20); - OPLWriteReg(_opl, 1, 0); + _opl->writeReg(1, 0x20); + _opl->writeReg(1, 0); } -void AdLibSoundDriver::update(int16 *buf, int len) { - static int samplesLeft = 0; - while (len != 0) { - int count = samplesLeft; - if (count > len) { - count = len; - } - samplesLeft -= count; - len -= count; - YM3812UpdateOne(_opl, buf, count); - if (samplesLeft == 0) { - if (_upCb) { - (*_upCb)(_upRef); - } - samplesLeft = _sampleRate / 50; - } - buf += count; +void AdLibSoundDriver::onTimer() { + if (_upCb) { + (*_upCb)(_upRef); } } @@ -408,32 +383,32 @@ void AdLibSoundDriver::setupInstrument(const byte *data, int channel) { if (ins->mode == 0 || ins->channel == 6) { reg = &ins->regMod; - OPLWriteReg(_opl, 0x20 | mod, reg->vibrato); + _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); } - OPLWriteReg(_opl, 0x40 | mod, tmp | (reg->keyScaling << 6)); - OPLWriteReg(_opl, 0x60 | mod, reg->attackDecay); - OPLWriteReg(_opl, 0x80 | mod, reg->sustainRelease); + _opl->writeReg(0x40 | mod, tmp | (reg->keyScaling << 6)); + _opl->writeReg(0x60 | mod, reg->attackDecay); + _opl->writeReg(0x80 | mod, reg->sustainRelease); if (ins->mode != 0) { - OPLWriteReg(_opl, 0xC0 | ins->channel, reg->feedbackStrength); + _opl->writeReg(0xC0 | ins->channel, reg->feedbackStrength); } else { - OPLWriteReg(_opl, 0xC0 | channel, reg->feedbackStrength); + _opl->writeReg(0xC0 | channel, reg->feedbackStrength); } - OPLWriteReg(_opl, 0xE0 | mod, ins->waveSelectMod); + _opl->writeReg(0xE0 | mod, ins->waveSelectMod); } reg = &ins->regCar; - OPLWriteReg(_opl, 0x20 | car, reg->vibrato); + _opl->writeReg(0x20 | car, reg->vibrato); tmp = (63 - (reg->outputLevel & 0x3F)) * _channelsVolumeTable[channel]; tmp = 63 - (2 * tmp + 127) / (2 * 127); - OPLWriteReg(_opl, 0x40 | car, tmp | (reg->keyScaling << 6)); - OPLWriteReg(_opl, 0x60 | car, reg->attackDecay); - OPLWriteReg(_opl, 0x80 | car, reg->sustainRelease); - OPLWriteReg(_opl, 0xE0 | car, ins->waveSelectCar); + _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) { @@ -490,16 +465,16 @@ void AdLibSoundDriverINS::setChannelFrequency(int channel, int frequency) { if (channel == 6) oct = 0; freq = _freqTable[note % 12]; - OPLWriteReg(_opl, 0xA0 | channel, freq); + _opl->writeReg(0xA0 | channel, freq); freq = (oct << 2) | ((freq & 0x300) >> 8); if (ins->mode == 0) { freq |= 0x20; } - OPLWriteReg(_opl, 0xB0 | channel, freq); + _opl->writeReg(0xB0 | channel, freq); } if (ins->mode != 0) { _vibrato |= 1 << (10 - ins->channel); - OPLWriteReg(_opl, 0xBD, _vibrato); + _opl->writeReg(0xBD, _vibrato); } } @@ -515,16 +490,16 @@ void AdLibSoundDriverINS::playSample(const byte *data, int size, int channel, in if (ins->mode == 0 || channel == 6) { uint16 note = 12; int freq = _freqTable[note % 12]; - OPLWriteReg(_opl, 0xA0 | channel, freq); + _opl->writeReg(0xA0 | channel, freq); freq = ((note / 12) << 2) | ((freq & 0x300) >> 8); if (ins->mode == 0) { freq |= 0x20; } - OPLWriteReg(_opl, 0xB0 | channel, freq); + _opl->writeReg(0xB0 | channel, freq); } if (ins->mode != 0) { _vibrato |= 1 << (10 - ins->channel); - OPLWriteReg(_opl, 0xBD, _vibrato); + _opl->writeReg(0xBD, _vibrato); } } @@ -562,15 +537,15 @@ void AdLibSoundDriverADL::setChannelFrequency(int channel, int frequency) { } freq = _freqTable[note % 12]; - OPLWriteReg(_opl, 0xA0 | channel, freq); + _opl->writeReg(0xA0 | channel, freq); freq = (oct << 2) | ((freq & 0x300) >> 8); if (ins->mode == 0) { freq |= 0x20; } - OPLWriteReg(_opl, 0xB0 | channel, freq); + _opl->writeReg(0xB0 | channel, freq); if (ins->mode != 0) { _vibrato |= 1 << (10 - channel); - OPLWriteReg(_opl, 0xBD, _vibrato); + _opl->writeReg(0xBD, _vibrato); } } @@ -580,11 +555,11 @@ void AdLibSoundDriverADL::playSample(const byte *data, int size, int channel, in setupInstrument(data, channel); AdLibSoundInstrument *ins = &_instrumentsTable[channel]; if (ins->mode != 0 && ins->channel == 6) { - OPLWriteReg(_opl, 0xB0 | channel, 0); + _opl->writeReg(0xB0 | channel, 0); } if (ins->mode != 0) { _vibrato &= ~(1 << (10 - ins->channel)); - OPLWriteReg(_opl, 0xBD, _vibrato); + _opl->writeReg(0xBD, _vibrato); } if (ins->mode != 0) { channel = ins->channel; @@ -599,15 +574,15 @@ void AdLibSoundDriverADL::playSample(const byte *data, int size, int channel, in note = ins->amDepth; } int freq = _freqTable[note % 12]; - OPLWriteReg(_opl, 0xA0 | channel, freq); + _opl->writeReg(0xA0 | channel, freq); freq = ((note / 12) << 2) | ((freq & 0x300) >> 8); if (ins->mode == 0) { freq |= 0x20; } - OPLWriteReg(_opl, 0xB0 | channel, freq); + _opl->writeReg(0xB0 | channel, freq); if (ins->mode != 0) { _vibrato |= 1 << (10 - channel); - OPLWriteReg(_opl, 0xBD, _vibrato); + _opl->writeReg(0xBD, _vibrato); } } diff --git a/engines/cruise/sound.cpp b/engines/cruise/sound.cpp index 0b0fab8c4a..f57435f4f7 100644 --- a/engines/cruise/sound.cpp +++ b/engines/cruise/sound.cpp @@ -108,7 +108,7 @@ struct VolumeEntry { int adjusted; }; -class AdLibSoundDriver : public PCSoundDriver, Audio::AudioStream { +class AdLibSoundDriver : public PCSoundDriver { public: AdLibSoundDriver(Audio::Mixer *mixer); virtual ~AdLibSoundDriver(); @@ -118,14 +118,8 @@ public: virtual void stopChannel(int channel); virtual void stopAll(); - // AudioStream interface - virtual int readBuffer(int16 *buffer, const int numSamples); - virtual bool isStereo() const { return false; } - virtual bool endOfData() const { return false; } - virtual int getRate() const { return _sampleRate; } - void initCard(); - void update(int16 *buf, int len); + void onTimer(); void setupInstrument(const byte *data, int channel); void setupInstrument(const AdLibSoundInstrument *ins, int channel); void loadRegisterInstrument(const byte *data, AdLibRegisterSoundInstrument *reg); @@ -135,10 +129,8 @@ public: void adjustVolume(int channel, int volume); protected: - FM_OPL *_opl; - int _sampleRate; + OPL::OPL *_opl; Audio::Mixer *_mixer; - Audio::SoundHandle _soundHandle; byte _vibrato; VolumeEntry _channelsVolumeTable[5]; @@ -302,8 +294,9 @@ void PCSoundDriver::syncSounds() { AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer) : _mixer(mixer) { - _sampleRate = _mixer->getOutputRate(); - _opl = makeAdLibOPL(_sampleRate); + _opl = OPL::Config::create(); + if (!_opl || !_opl->init()) + error("Failed to create OPL"); for (int i = 0; i < 5; ++i) { _channelsVolumeTable[i].original = 0; @@ -311,15 +304,15 @@ AdLibSoundDriver::AdLibSoundDriver(Audio::Mixer *mixer) } memset(_instrumentsTable, 0, sizeof(_instrumentsTable)); initCard(); - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); _musicVolume = ConfMan.getBool("music_mute") ? 0 : MIN(255, ConfMan.getInt("music_volume")); _sfxVolume = ConfMan.getBool("sfx_mute") ? 0 : MIN(255, ConfMan.getInt("sfx_volume")); + + _opl->start(new Common::Functor0Mem<void, AdLibSoundDriver>(this, &AdLibSoundDriver::onTimer), 50); } AdLibSoundDriver::~AdLibSoundDriver() { - _mixer->stopHandle(_soundHandle); - OPLDestroy(_opl); + delete _opl; } void AdLibSoundDriver::syncSounds() { @@ -368,70 +361,51 @@ void AdLibSoundDriver::stopChannel(int channel) { channel = 6; } if (ins->mode == 0 || channel == 6) { - OPLWriteReg(_opl, 0xB0 | channel, 0); + _opl->writeReg(0xB0 | channel, 0); } if (ins->mode != 0) { _vibrato &= ~(1 << (10 - ins->channel)); - OPLWriteReg(_opl, 0xBD, _vibrato); + _opl->writeReg(0xBD, _vibrato); } } void AdLibSoundDriver::stopAll() { for (int i = 0; i < 18; ++i) - OPLWriteReg(_opl, 0x40 | _operatorsTable[i], 63); + _opl->writeReg(0x40 | _operatorsTable[i], 63); for (int i = 0; i < 9; ++i) - OPLWriteReg(_opl, 0xB0 | i, 0); - - OPLWriteReg(_opl, 0xBD, 0); -} + _opl->writeReg(0xB0 | i, 0); -int AdLibSoundDriver::readBuffer(int16 *buffer, const int numSamples) { - update(buffer, numSamples); - return numSamples; + _opl->writeReg(0xBD, 0); } void AdLibSoundDriver::initCard() { _vibrato = 0x20; - OPLWriteReg(_opl, 0xBD, _vibrato); - OPLWriteReg(_opl, 0x08, 0x40); + _opl->writeReg(0xBD, _vibrato); + _opl->writeReg(0x08, 0x40); static const int oplRegs[] = { 0x40, 0x60, 0x80, 0x20, 0xE0 }; for (int i = 0; i < 9; ++i) { - OPLWriteReg(_opl, 0xB0 | i, 0); + _opl->writeReg(0xB0 | i, 0); } for (int i = 0; i < 9; ++i) { - OPLWriteReg(_opl, 0xC0 | i, 0); + _opl->writeReg(0xC0 | i, 0); } for (int j = 0; j < 5; j++) { for (int i = 0; i < 18; ++i) { - OPLWriteReg(_opl, oplRegs[j] | _operatorsTable[i], 0); + _opl->writeReg(oplRegs[j] | _operatorsTable[i], 0); } } - OPLWriteReg(_opl, 1, 0x20); - OPLWriteReg(_opl, 1, 0); + _opl->writeReg(1, 0x20); + _opl->writeReg(1, 0); } -void AdLibSoundDriver::update(int16 *buf, int len) { - static int samplesLeft = 0; - while (len != 0) { - int count = samplesLeft; - if (count > len) { - count = len; - } - samplesLeft -= count; - len -= count; - YM3812UpdateOne(_opl, buf, count); - if (samplesLeft == 0) { - if (_upCb) { - (*_upCb)(_upRef); - } - samplesLeft = _sampleRate / 50; - } - buf += count; +void AdLibSoundDriver::onTimer() { + if (_upCb) { + (*_upCb)(_upRef); } } @@ -457,32 +431,32 @@ void AdLibSoundDriver::setupInstrument(const AdLibSoundInstrument *ins, int chan if (ins->mode == 0 || ins->channel == 6) { reg = &ins->regMod; - OPLWriteReg(_opl, 0x20 | mod, reg->vibrato); + _opl->writeReg(0x20 | mod, reg->vibrato); if (reg->freqMod) { tmp = reg->outputLevel & 0x3F; } else { tmp = (63 - (reg->outputLevel & 0x3F)) * _channelsVolumeTable[channel].adjusted; tmp = 63 - (2 * tmp + 127) / (2 * 127); } - OPLWriteReg(_opl, 0x40 | mod, tmp | (reg->keyScaling << 6)); - OPLWriteReg(_opl, 0x60 | mod, reg->attackDecay); - OPLWriteReg(_opl, 0x80 | mod, reg->sustainRelease); + _opl->writeReg(0x40 | mod, tmp | (reg->keyScaling << 6)); + _opl->writeReg(0x60 | mod, reg->attackDecay); + _opl->writeReg(0x80 | mod, reg->sustainRelease); if (ins->mode != 0) { - OPLWriteReg(_opl, 0xC0 | ins->channel, reg->feedbackStrength); + _opl->writeReg(0xC0 | ins->channel, reg->feedbackStrength); } else { - OPLWriteReg(_opl, 0xC0 | channel, reg->feedbackStrength); + _opl->writeReg(0xC0 | channel, reg->feedbackStrength); } - OPLWriteReg(_opl, 0xE0 | mod, ins->waveSelectMod); + _opl->writeReg(0xE0 | mod, ins->waveSelectMod); } reg = &ins->regCar; - OPLWriteReg(_opl, 0x20 | car, reg->vibrato); + _opl->writeReg(0x20 | car, reg->vibrato); tmp = (63 - (reg->outputLevel & 0x3F)) * _channelsVolumeTable[channel].adjusted; tmp = 63 - (2 * tmp + 127) / (2 * 127); - OPLWriteReg(_opl, 0x40 | car, tmp | (reg->keyScaling << 6)); - OPLWriteReg(_opl, 0x60 | car, reg->attackDecay); - OPLWriteReg(_opl, 0x80 | car, reg->sustainRelease); - OPLWriteReg(_opl, 0xE0 | car, ins->waveSelectCar); + _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) { @@ -551,15 +525,15 @@ void AdLibSoundDriverADL::setChannelFrequency(int channel, int frequency) { } freq = _freqTable[note % 12]; - OPLWriteReg(_opl, 0xA0 | channel, freq); + _opl->writeReg(0xA0 | channel, freq); freq = ((note / 12) << 2) | ((freq & 0x300) >> 8); if (ins->mode == 0) { freq |= 0x20; } - OPLWriteReg(_opl, 0xB0 | channel, freq); + _opl->writeReg(0xB0 | channel, freq); if (ins->mode != 0) { _vibrato |= 1 << (10 - channel); - OPLWriteReg(_opl, 0xBD, _vibrato); + _opl->writeReg(0xBD, _vibrato); } } @@ -570,11 +544,11 @@ void AdLibSoundDriverADL::playSample(const byte *data, int size, int channel, in setupInstrument(data, channel); AdLibSoundInstrument *ins = &_instrumentsTable[channel]; if (ins->mode != 0 && ins->channel == 6) { - OPLWriteReg(_opl, 0xB0 | channel, 0); + _opl->writeReg(0xB0 | channel, 0); } if (ins->mode != 0) { _vibrato &= ~(1 << (10 - ins->channel)); - OPLWriteReg(_opl, 0xBD, _vibrato); + _opl->writeReg(0xBD, _vibrato); } if (ins->mode != 0) { channel = ins->channel; @@ -589,15 +563,15 @@ void AdLibSoundDriverADL::playSample(const byte *data, int size, int channel, in note = ins->amDepth; } int freq = _freqTable[note % 12]; - OPLWriteReg(_opl, 0xA0 | channel, freq); + _opl->writeReg(0xA0 | channel, freq); freq = ((note / 12) << 2) | ((freq & 0x300) >> 8); if (ins->mode == 0) { freq |= 0x20; } - OPLWriteReg(_opl, 0xB0 | channel, freq); + _opl->writeReg(0xB0 | channel, freq); if (ins->mode != 0) { _vibrato |= 1 << (10 - channel); - OPLWriteReg(_opl, 0xBD, _vibrato); + _opl->writeReg(0xBD, _vibrato); } } diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp index 5ab3271a8f..24bdb858d8 100644 --- a/engines/gob/gob.cpp +++ b/engines/gob/gob.cpp @@ -389,6 +389,9 @@ void GobEngine::syncSoundSettings() { Engine::syncSoundSettings(); _init->updateConfig(); + + if (_sound) + _sound->adlibSyncVolume(); } void GobEngine::pauseGame() { diff --git a/engines/gob/sound/adlib.cpp b/engines/gob/sound/adlib.cpp index 65b43cae7a..995cc48824 100644 --- a/engines/gob/sound/adlib.cpp +++ b/engines/gob/sound/adlib.cpp @@ -37,6 +37,28 @@ static const int kPitchTomToSnare = 7; static const int kPitchSnareDrum = kPitchTom + kPitchTomToSnare; +// Attenuation map for GUI volume slider +// Note: no volume control in the original engine +const uint8 AdLib::kVolumeTable[Audio::Mixer::kMaxMixerVolume + 1] = { + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 62, 61, 59, 57, 56, 55, + 53, 52, 51, 50, 49, 48, 47, 46, 46, 45, 44, 43, 43, 42, 41, 41, + 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 33, + 32, 32, 31, 31, 31, 30, 30, 30, 29, 29, 29, 28, 28, 28, 27, 27, + 27, 26, 26, 26, 26, 25, 25, 25, 24, 24, 24, 24, 23, 23, 23, 23, + 22, 22, 22, 22, 21, 21, 21, 21, 21, 20, 20, 20, 20, 19, 19, 19, + 19, 19, 18, 18, 18, 18, 18, 18, 17, 17, 17, 17, 17, 16, 16, 16, + 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 13, + 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, + 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, + 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0 +}; + // Is the operator a modulator (0) or a carrier (1)? const uint8 AdLib::kOperatorType[kOperatorCount] = { 0, 0, 0, 1, 1, 1, @@ -93,23 +115,20 @@ const uint16 AdLib::kHihatParams [kParamCount] = { 0, 1, 0, 15, 11, 0, 7, 5, 0, 0, 0, 0, 0, 0 }; -AdLib::AdLib(Audio::Mixer &mixer) : _mixer(&mixer), _opl(0), - _toPoll(0), _repCount(0), _first(true), _playing(false), _ended(true) { - - _rate = _mixer->getOutputRate(); +AdLib::AdLib(Audio::Mixer &mixer, int callbackFreq) : _mixer(&mixer), _opl(0), + _toPoll(0), _repCount(0), _first(true), _playing(false), _ended(true), _volume(0) { initFreqs(); createOPL(); initOPL(); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_handle, - this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + syncVolume(); + + _opl->start(new Common::Functor0Mem<void, AdLib>(this, &AdLib::onTimer), callbackFreq); } AdLib::~AdLib() { - _mixer->stopHandle(_handle); - delete _opl; } @@ -136,46 +155,38 @@ void AdLib::createOPL() { } _opl = OPL::Config::create(OPL::Config::parse(oplDriver), OPL::Config::kOpl2); - if (!_opl || !_opl->init(_rate)) { + if (!_opl || !_opl->init()) { delete _opl; error("Could not create an AdLib emulator"); } } -int AdLib::readBuffer(int16 *buffer, const int numSamples) { +void AdLib::onTimer() { Common::StackLock slock(_mutex); - // Nothing to do, fill with silence - if (!_playing) { - memset(buffer, 0, numSamples * sizeof(int16)); - return numSamples; + // Nothing to do + if (!_playing) + return; + + // Check if there's anything to do on this step + // If not, decrease the poll number and move on + if (_toPoll > 0) { + _toPoll--; + return; } - // Read samples from the OPL, polling in more music when necessary - uint32 samples = numSamples; - while (samples && _playing) { - if (_toPoll) { - const uint32 render = MIN(samples, _toPoll); - - _opl->readBuffer(buffer, render); - - buffer += render; - samples -= render; - _toPoll -= render; - - } else { - // Song ended, fill the rest with silence - if (_ended) { - memset(buffer, 0, samples * sizeof(int16)); - samples = 0; - break; - } - - // Poll more music - _toPoll = pollMusic(_first); - _first = false; + // Poll until we have to delay until the next poll + while (_toPoll == 0 && _playing) { + // Song ended, break out + if (_ended) { + _toPoll = 0; + break; } + + // Poll more music + _toPoll = pollMusic(_first); + _first = false; } // Song ended, loop if requested @@ -195,24 +206,6 @@ int AdLib::readBuffer(int16 *buffer, const int numSamples) { } else _playing = false; } - - return numSamples; -} - -bool AdLib::isStereo() const { - return _opl->isStereo(); -} - -bool AdLib::endOfData() const { - return !_playing; -} - -bool AdLib::endOfStream() const { - return false; -} - -int AdLib::getRate() const { - return _rate; } bool AdLib::isPlaying() const { @@ -231,10 +224,6 @@ void AdLib::setRepeating(int32 repCount) { _repCount = repCount; } -uint32 AdLib::getSamplesPerSecond() const { - return _rate * (isStereo() ? 2 : 1); -} - void AdLib::startPlay() { Common::StackLock slock(_mutex); @@ -442,6 +431,13 @@ void AdLib::writeKeyScaleLevelVolume(uint8 oper) { volume = (63 - (_operatorParams[oper][kParamLevel] & 0x3F)) * _operatorVolume[oper]; volume = 63 - ((2 * volume + kMaxVolume) / (2 * kMaxVolume)); + // Adjust carriers for GUI volume slider + if (kOperatorType[oper] == 1) { + volume += kVolumeTable[_volume]; + if (volume > 63) + volume = 63; + } + uint8 keyScale = _operatorParams[oper][kParamKeyScaleLevel] << 6; writeOPL(0x40 + kOperatorOffset[oper], volume | keyScale); @@ -639,4 +635,23 @@ void AdLib::setFreq(uint8 voice, uint16 note, bool on) { writeOPL(0xB0 + voice, value); } +void AdLib::setTimerFrequency(int timerFrequency) { + _opl->setCallbackFrequency(timerFrequency); +} + +void AdLib::syncVolume() { + Common::StackLock slock(_mutex); + + bool mute = false; + if (ConfMan.hasKey("mute")) + mute = ConfMan.getBool("mute"); + + _volume = (mute ? 0 : ConfMan.getInt("music_volume")); + + if (_playing) { + for(int i = 0; i < kOperatorCount; i++) + writeKeyScaleLevelVolume(i); + } +} + } // End of namespace Gob diff --git a/engines/gob/sound/adlib.h b/engines/gob/sound/adlib.h index 8071249374..28ebf9d998 100644 --- a/engines/gob/sound/adlib.h +++ b/engines/gob/sound/adlib.h @@ -35,9 +35,9 @@ namespace OPL { namespace Gob { /** Base class for a player of an AdLib music format. */ -class AdLib : public Audio::AudioStream { +class AdLib { public: - AdLib(Audio::Mixer &mixer); + AdLib(Audio::Mixer &mixer, int callbackFrequency); virtual ~AdLib(); bool isPlaying() const; ///< Are we currently playing? @@ -53,13 +53,7 @@ public: void startPlay(); void stopPlay(); - -// AudioStream API - int readBuffer(int16 *buffer, const int numSamples); - bool isStereo() const; - bool endOfData() const; - bool endOfStream() const; - int getRate() const; + void syncVolume(); protected: enum kVoice { @@ -120,8 +114,6 @@ protected: static const int kOPLMidC = 48; ///< A mid C for the OPL. - /** Return the number of samples per second. */ - uint32 getSamplesPerSecond() const; /** Write a value into an OPL register. */ void writeOPL(byte reg, byte val); @@ -135,7 +127,7 @@ protected: /** The callback function that's called for polling more AdLib commands. * * @param first Is this the first poll since the start of the song? - * @return The number of samples until the next poll. + * @return The number of ticks until the next poll. */ virtual uint32 pollMusic(bool first) = 0; @@ -207,7 +199,14 @@ protected: /** Switch a voice off. */ void noteOff(uint8 voice); + /** + * Set the OPL timer frequency + */ + void setTimerFrequency(int timerFrequency); + private: + static const uint8 kVolumeTable[Audio::Mixer::kMaxMixerVolume + 1]; + static const uint8 kOperatorType [kOperatorCount]; static const uint8 kOperatorOffset[kOperatorCount]; static const uint8 kOperatorVoice [kOperatorCount]; @@ -227,11 +226,12 @@ private: Audio::Mixer *_mixer; - Audio::SoundHandle _handle; OPL::OPL *_opl; Common::Mutex _mutex; + int _volume; + uint32 _rate; uint32 _toPoll; @@ -300,6 +300,11 @@ private: void changePitch(uint8 voice, uint16 pitchBend); void setFreq(uint8 voice, uint16 note, bool on); + + /** + * Callback function for OPL + */ + void onTimer(); }; } // End of namespace Gob diff --git a/engines/gob/sound/adlplayer.cpp b/engines/gob/sound/adlplayer.cpp index 384a928360..e5a276032b 100644 --- a/engines/gob/sound/adlplayer.cpp +++ b/engines/gob/sound/adlplayer.cpp @@ -28,7 +28,7 @@ namespace Gob { -ADLPlayer::ADLPlayer(Audio::Mixer &mixer) : AdLib(mixer), +ADLPlayer::ADLPlayer(Audio::Mixer &mixer) : AdLib(mixer, 1000), _songData(0), _songDataSize(0), _playPos(0) { } @@ -135,14 +135,7 @@ uint32 ADLPlayer::pollMusic(bool first) { if (delay & 0x80) delay = ((delay & 3) << 8) | *_playPos++; - return getSampleDelay(delay); -} - -uint32 ADLPlayer::getSampleDelay(uint16 delay) const { - if (delay == 0) - return 0; - - return ((uint32)delay * getSamplesPerSecond()) / 1000; + return delay; } void ADLPlayer::rewind() { diff --git a/engines/gob/sound/adlplayer.h b/engines/gob/sound/adlplayer.h index 3edd238343..bd43cc091c 100644 --- a/engines/gob/sound/adlplayer.h +++ b/engines/gob/sound/adlplayer.h @@ -76,8 +76,6 @@ private: bool readHeader (Common::SeekableReadStream &adl, int &timbreCount); bool readTimbres (Common::SeekableReadStream &adl, int timbreCount); bool readSongData(Common::SeekableReadStream &adl); - - uint32 getSampleDelay(uint16 delay) const; }; } // End of namespace Gob diff --git a/engines/gob/sound/musplayer.cpp b/engines/gob/sound/musplayer.cpp index 7001a5724b..2c0330e70a 100644 --- a/engines/gob/sound/musplayer.cpp +++ b/engines/gob/sound/musplayer.cpp @@ -27,7 +27,7 @@ namespace Gob { -MUSPlayer::MUSPlayer(Audio::Mixer &mixer) : AdLib(mixer), +MUSPlayer::MUSPlayer(Audio::Mixer &mixer) : AdLib(mixer, 60), _songData(0), _songDataSize(0), _playPos(0), _songID(0) { } @@ -43,15 +43,6 @@ void MUSPlayer::unload() { unloadMUS(); } -uint32 MUSPlayer::getSampleDelay(uint16 delay) const { - if (delay == 0) - return 0; - - uint32 freq = (_ticksPerBeat * _tempo) / 60; - - return ((uint32)delay * getSamplesPerSecond()) / freq; -} - void MUSPlayer::skipToTiming() { while (*_playPos < 0x80) _playPos++; @@ -66,8 +57,12 @@ uint32 MUSPlayer::pollMusic(bool first) { return 0; } - if (first) - return getSampleDelay(*_playPos++); + if (first) { + // Set the timer frequency on first run. + // Do not set it in rewind() for thread safety reasons. + setTimerFrequency((_ticksPerBeat * _tempo) / 60); + return *_playPos++; + } uint16 delay = 0; while (delay == 0) { @@ -100,6 +95,7 @@ uint32 MUSPlayer::pollMusic(bool first) { uint32 denom = *_playPos++; _tempo = _baseTempo * num + ((_baseTempo * denom) >> 7); + setTimerFrequency((_ticksPerBeat * _tempo) / 60); _playPos++; } else { @@ -182,7 +178,7 @@ uint32 MUSPlayer::pollMusic(bool first) { delay += *_playPos++; } - return getSampleDelay(delay); + return delay; } void MUSPlayer::rewind() { diff --git a/engines/gob/sound/musplayer.h b/engines/gob/sound/musplayer.h index c76c5aab38..7c1189b84b 100644 --- a/engines/gob/sound/musplayer.h +++ b/engines/gob/sound/musplayer.h @@ -97,7 +97,6 @@ private: bool readMUSHeader(Common::SeekableReadStream &mus); bool readMUSSong (Common::SeekableReadStream &mus); - uint32 getSampleDelay(uint16 delay) const; void setInstrument(uint8 voice, uint8 instrument); void skipToTiming(); diff --git a/engines/gob/sound/sound.cpp b/engines/gob/sound/sound.cpp index d2b2d3d6e8..9b19b9c52c 100644 --- a/engines/gob/sound/sound.cpp +++ b/engines/gob/sound/sound.cpp @@ -425,6 +425,16 @@ int32 Sound::adlibGetRepeating() const { return false; } +void Sound::adlibSyncVolume() { + if (!_hasAdLib) + return; + + if (_adlPlayer) + _adlPlayer->syncVolume(); + if (_mdyPlayer) + _mdyPlayer->syncVolume(); +} + void Sound::adlibSetRepeating(int32 repCount) { if (!_hasAdLib) return; diff --git a/engines/gob/sound/sound.h b/engines/gob/sound/sound.h index c959959755..6ebc323b18 100644 --- a/engines/gob/sound/sound.h +++ b/engines/gob/sound/sound.h @@ -96,6 +96,7 @@ public: int32 adlibGetRepeating() const; void adlibSetRepeating(int32 repCount); + void adlibSyncVolume(); // Infogrames diff --git a/engines/kyra/sound_adlib.cpp b/engines/kyra/sound_adlib.cpp index c0e0f67b8e..1d741d8bd0 100644 --- a/engines/kyra/sound_adlib.cpp +++ b/engines/kyra/sound_adlib.cpp @@ -55,7 +55,7 @@ namespace Kyra { -class AdLibDriver : public Audio::AudioStream { +class AdLibDriver { public: AdLibDriver(Audio::Mixer *mixer, int version); ~AdLibDriver(); @@ -70,34 +70,6 @@ public: void callback(); - // AudioStream API - int readBuffer(int16 *buffer, const int numSamples) { - int32 samplesLeft = numSamples; - memset(buffer, 0, sizeof(int16) * numSamples); - while (samplesLeft) { - if (!_samplesTillCallback) { - callback(); - _samplesTillCallback = _samplesPerCallback; - _samplesTillCallbackRemainder += _samplesPerCallbackRemainder; - if (_samplesTillCallbackRemainder >= CALLBACKS_PER_SECOND) { - _samplesTillCallback++; - _samplesTillCallbackRemainder -= CALLBACKS_PER_SECOND; - } - } - - int32 render = MIN(samplesLeft, _samplesTillCallback); - samplesLeft -= render; - _samplesTillCallback -= render; - YM3812UpdateOne(_adlib, buffer, render); - buffer += render; - } - return numSamples; - } - - bool isStereo() const { return false; } - bool endOfData() const { return false; } - int getRate() const { return _mixer->getOutputRate(); } - void setSyncJumpMask(uint16 mask) { _syncJumpMask = mask; } void setMusicVolume(uint8 volume); @@ -334,11 +306,6 @@ private: // _unkTable2_2[] - One of the tables in _unkTable2[] // _unkTable2_3[] - One of the tables in _unkTable2[] - int32 _samplesPerCallback; - int32 _samplesPerCallbackRemainder; - int32 _samplesTillCallback; - int32 _samplesTillCallbackRemainder; - int _curChannel; uint8 _soundTrigger; @@ -365,7 +332,7 @@ private: uint8 _unkValue19; uint8 _unkValue20; - FM_OPL *_adlib; + OPL::OPL *_adlib; uint8 *_soundData; uint32 _soundDataSize; @@ -411,7 +378,6 @@ private: Common::Mutex _mutex; Audio::Mixer *_mixer; - Audio::SoundHandle _soundHandle; uint8 _musicVolume, _sfxVolume; @@ -427,8 +393,9 @@ AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) { _mixer = mixer; - _adlib = makeAdLibOPL(getRate()); - assert(_adlib); + _adlib = OPL::Config::create(); + if (!_adlib || !_adlib->init()) + error("Failed to create OPL"); memset(_channels, 0, sizeof(_channels)); _soundData = 0; @@ -451,13 +418,6 @@ AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) { _tablePtr1 = _tablePtr2 = 0; - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); - - _samplesPerCallback = getRate() / CALLBACKS_PER_SECOND; - _samplesPerCallbackRemainder = getRate() % CALLBACKS_PER_SECOND; - _samplesTillCallback = 0; - _samplesTillCallbackRemainder = 0; - _syncJumpMask = 0; _musicVolume = 0; @@ -467,11 +427,12 @@ AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) { _programQueueStart = _programQueueEnd = 0; _retrySounds = false; + + _adlib->start(new Common::Functor0Mem<void, AdLibDriver>(this, &AdLibDriver::callback), CALLBACKS_PER_SECOND); } AdLibDriver::~AdLibDriver() { - _mixer->stopHandle(_soundHandle); - OPLDestroy(_adlib); + delete _adlib; _adlib = 0; } @@ -877,7 +838,7 @@ void AdLibDriver::resetAdLibState() { // New calling style: writeOPL(0xAB, 0xCD) void AdLibDriver::writeOPL(byte reg, byte val) { - OPLWriteReg(_adlib, reg, val); + _adlib->writeReg(reg, val); } void AdLibDriver::initChannel(Channel &channel) { diff --git a/engines/mads/nebular/sound_nebular.cpp b/engines/mads/nebular/sound_nebular.cpp index 240c18f6dc..711f82a05b 100644 --- a/engines/mads/nebular/sound_nebular.cpp +++ b/engines/mads/nebular/sound_nebular.cpp @@ -21,6 +21,7 @@ */ #include "audio/audiostream.h" +#include "audio/fmopl.h" #include "audio/decoders/raw.h" #include "common/algorithm.h" #include "common/debug.h" @@ -156,7 +157,7 @@ AdlibSample::AdlibSample(Common::SeekableReadStream &s) { /*-----------------------------------------------------------------------*/ -ASound::ASound(Audio::Mixer *mixer, FM_OPL *opl, const Common::String &filename, int dataOffset) { +ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::String &filename, int dataOffset) { // Open up the appropriate sound file if (!_soundFile.open(filename)) error("Could not open file - %s", filename.c_str()); @@ -188,11 +189,6 @@ ASound::ASound(Audio::Mixer *mixer, FM_OPL *opl, const Common::String &filename, _randomSeed = 1234; _amDep = _vibDep = _splitPoint = true; - _samplesTillCallback = 0; - _samplesTillCallbackRemainder = 0; - _samplesPerCallback = getRate() / CALLBACKS_PER_SECOND; - _samplesPerCallbackRemainder = getRate() % CALLBACKS_PER_SECOND; - for (int i = 0; i < 11; ++i) { _channelData[i]._field0 = 0; _channelData[i]._freqMask = 0; @@ -210,23 +206,19 @@ ASound::ASound(Audio::Mixer *mixer, FM_OPL *opl, const Common::String &filename, _mixer = mixer; _opl = opl; - _opl->init(getRate()); - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, - Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); - // Initialize the Adlib adlibInit(); // Reset the adlib command0(); + + _opl->start(new Common::Functor0Mem<void, ASound>(this, &ASound::onTimer), CALLBACKS_PER_SECOND); } ASound::~ASound() { Common::List<CachedDataEntry>::iterator i; for (i = _dataCache.begin(); i != _dataCache.end(); ++i) delete[] (*i)._data; - - _mixer->stopHandle(_soundHandle); } void ASound::validate() { @@ -832,32 +824,10 @@ void ASound::updateFNumber() { write2(8, hiReg, val2); } -int ASound::readBuffer(int16 *buffer, const int numSamples) { +void ASound::onTimer() { Common::StackLock slock(_driverMutex); - - int32 samplesLeft = numSamples; - memset(buffer, 0, sizeof(int16) * numSamples); - while (samplesLeft) { - if (!_samplesTillCallback) { - poll(); - flush(); - - _samplesTillCallback = _samplesPerCallback; - _samplesTillCallbackRemainder += _samplesPerCallbackRemainder; - if (_samplesTillCallbackRemainder >= CALLBACKS_PER_SECOND) { - _samplesTillCallback++; - _samplesTillCallbackRemainder -= CALLBACKS_PER_SECOND; - } - } - - int32 render = MIN<int>(samplesLeft, _samplesTillCallback); - samplesLeft -= render; - _samplesTillCallback -= render; - - _opl->readBuffer(buffer, render); - buffer += render; - } - return numSamples; + poll(); + flush(); } void ASound::setVolume(int volume) { @@ -984,7 +954,7 @@ const ASound1::CommandPtr ASound1::_commandList[42] = { &ASound1::command40, &ASound1::command41 }; -ASound1::ASound1(Audio::Mixer *mixer, FM_OPL *opl) +ASound1::ASound1(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.001", 0x1520) { _cmd23Toggle = false; @@ -1285,7 +1255,7 @@ const ASound2::CommandPtr ASound2::_commandList[44] = { &ASound2::command40, &ASound2::command41, &ASound2::command42, &ASound2::command43 }; -ASound2::ASound2(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.002", 0x15E0) { +ASound2::ASound2(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.002", 0x15E0) { _command12Param = 0xFD; // Load sound samples @@ -1656,7 +1626,7 @@ const ASound3::CommandPtr ASound3::_commandList[61] = { &ASound3::command60 }; -ASound3::ASound3(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.003", 0x15B0) { +ASound3::ASound3(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.003", 0x15B0) { _command39Flag = false; // Load sound samples @@ -2060,7 +2030,7 @@ const ASound4::CommandPtr ASound4::_commandList[61] = { &ASound4::command60 }; -ASound4::ASound4(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.004", 0x14F0) { +ASound4::ASound4(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.004", 0x14F0) { // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 210; ++i) @@ -2316,7 +2286,7 @@ const ASound5::CommandPtr ASound5::_commandList[42] = { &ASound5::command40, &ASound5::command41 }; -ASound5::ASound5(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.002", 0x15E0) { +ASound5::ASound5(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.002", 0x15E0) { // Load sound samples _soundFile.seek(_dataOffset + 0x144); for (int i = 0; i < 164; ++i) @@ -2557,7 +2527,7 @@ const ASound6::CommandPtr ASound6::_commandList[30] = { &ASound6::nullCommand, &ASound6::command29 }; -ASound6::ASound6(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.006", 0x1390) { +ASound6::ASound6(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.006", 0x1390) { // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 200; ++i) @@ -2713,7 +2683,7 @@ const ASound7::CommandPtr ASound7::_commandList[38] = { &ASound7::command36, &ASound7::command37 }; -ASound7::ASound7(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.007", 0x1460) { +ASound7::ASound7(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.007", 0x1460) { // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 214; ++i) @@ -2919,7 +2889,7 @@ const ASound8::CommandPtr ASound8::_commandList[38] = { &ASound8::command36, &ASound8::command37 }; -ASound8::ASound8(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.008", 0x1490) { +ASound8::ASound8(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.008", 0x1490) { // Load sound samples _soundFile.seek(_dataOffset + 0x122); for (int i = 0; i < 174; ++i) @@ -3175,7 +3145,7 @@ const ASound9::CommandPtr ASound9::_commandList[52] = { &ASound9::command48, &ASound9::command49, &ASound9::command50, &ASound9::command51 }; -ASound9::ASound9(Audio::Mixer *mixer, FM_OPL *opl) : ASound(mixer, opl, "asound.009", 0x16F0) { +ASound9::ASound9(Audio::Mixer *mixer, OPL::OPL *opl) : ASound(mixer, opl, "asound.009", 0x16F0) { _v1 = _v2 = 0; _soundPtr = nullptr; diff --git a/engines/mads/nebular/sound_nebular.h b/engines/mads/nebular/sound_nebular.h index 9bc1a49458..2b80b08d89 100644 --- a/engines/mads/nebular/sound_nebular.h +++ b/engines/mads/nebular/sound_nebular.h @@ -28,9 +28,12 @@ #include "common/mutex.h" #include "common/queue.h" #include "audio/audiostream.h" -#include "audio/fmopl.h" #include "audio/mixer.h" +namespace OPL { +class OPL; +} + namespace MADS { class SoundManager; @@ -142,7 +145,7 @@ struct CachedDataEntry { /** * Base class for the sound player resource files */ -class ASound : public Audio::AudioStream { +class ASound { private: Common::List<CachedDataEntry> _dataCache; uint16 _randomSeed; @@ -192,6 +195,11 @@ private: void processSample(); void updateFNumber(); + + /** + * Timer function for OPL + */ + void onTimer(); protected: int _commandParam; @@ -273,8 +281,7 @@ protected: int nullCommand() { return 0; } public: Audio::Mixer *_mixer; - FM_OPL *_opl; - Audio::SoundHandle _soundHandle; + OPL::OPL *_opl; AdlibChannel _channels[ADLIB_CHANNEL_COUNT]; AdlibChannel *_activeChannelPtr; AdlibChannelData _channelData[11]; @@ -306,10 +313,6 @@ public: int _activeChannelReg; int _v11; bool _amDep, _vibDep, _splitPoint; - int _samplesPerCallback; - int _samplesPerCallbackRemainder; - int _samplesTillCallback; - int _samplesTillCallbackRemainder; public: /** * Constructor @@ -318,7 +321,7 @@ public: * @param filename Specifies the adlib sound player file to use * @param dataOffset Offset in the file of the data segment */ - ASound(Audio::Mixer *mixer, FM_OPL *opl, const Common::String &filename, int dataOffset); + ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::String &filename, int dataOffset); /** * Destructor @@ -363,27 +366,6 @@ public: */ CachedDataEntry &getCachedData(byte *pData); - // AudioStream interface - /** - * Main buffer read - */ - virtual int readBuffer(int16 *buffer, const int numSamples); - - /** - * Mono sound only - */ - virtual bool isStereo() const { return false; } - - /** - * Data is continuously pushed, so definitive end - */ - virtual bool endOfData() const { return false; } - - /** - * Return sample rate - */ - virtual int getRate() const { return 11025; } - /** * Set the volume */ @@ -433,7 +415,7 @@ private: void command111213(); int command2627293032(); public: - ASound1(Audio::Mixer *mixer, FM_OPL *opl); + ASound1(Audio::Mixer *mixer, OPL::OPL *opl); virtual int command(int commandId, int param); }; @@ -485,7 +467,7 @@ private: void command9Randomize(); void command9Apply(byte *data, int val, int incr); public: - ASound2(Audio::Mixer *mixer, FM_OPL *opl); + ASound2(Audio::Mixer *mixer, OPL::OPL *opl); virtual int command(int commandId, int param); }; @@ -545,7 +527,7 @@ private: void command9Randomize(); void command9Apply(byte *data, int val, int incr); public: - ASound3(Audio::Mixer *mixer, FM_OPL *opl); + ASound3(Audio::Mixer *mixer, OPL::OPL *opl); virtual int command(int commandId, int param); }; @@ -583,7 +565,7 @@ private: void method1(); public: - ASound4(Audio::Mixer *mixer, FM_OPL *opl); + ASound4(Audio::Mixer *mixer, OPL::OPL *opl); virtual int command(int commandId, int param); }; @@ -629,7 +611,7 @@ private: int command42(); int command43(); public: - ASound5(Audio::Mixer *mixer, FM_OPL *opl); + ASound5(Audio::Mixer *mixer, OPL::OPL *opl); virtual int command(int commandId, int param); }; @@ -658,7 +640,7 @@ private: int command25(); int command29(); public: - ASound6(Audio::Mixer *mixer, FM_OPL *opl); + ASound6(Audio::Mixer *mixer, OPL::OPL *opl); virtual int command(int commandId, int param); }; @@ -690,7 +672,7 @@ private: int command36(); int command37(); public: - ASound7(Audio::Mixer *mixer, FM_OPL *opl); + ASound7(Audio::Mixer *mixer, OPL::OPL *opl); virtual int command(int commandId, int param); }; @@ -733,7 +715,7 @@ private: void method1(byte *pData); void adjustRange(byte *pData, byte v, int incr); public: - ASound8(Audio::Mixer *mixer, FM_OPL *opl); + ASound8(Audio::Mixer *mixer, OPL::OPL *opl); virtual int command(int commandId, int param); }; @@ -792,7 +774,7 @@ private: int command59(); int command60(); public: - ASound9(Audio::Mixer *mixer, FM_OPL *opl); + ASound9(Audio::Mixer *mixer, OPL::OPL *opl); virtual int command(int commandId, int param); }; diff --git a/engines/mads/sound.cpp b/engines/mads/sound.cpp index 7b9388eee3..4a35edb80f 100644 --- a/engines/mads/sound.cpp +++ b/engines/mads/sound.cpp @@ -21,6 +21,7 @@ */ #include "audio/audiostream.h" +#include "audio/fmopl.h" #include "audio/decoders/raw.h" #include "common/memstream.h" #include "mads/sound.h" @@ -39,7 +40,7 @@ SoundManager::SoundManager(MADSEngine *vm, Audio::Mixer *mixer) { _masterVolume = 255; _opl = OPL::Config::create(); - _opl->init(11025); + _opl->init(); // Validate sound files switch (_vm->getGameID()) { diff --git a/engines/mads/sound.h b/engines/mads/sound.h index 16128f8284..9882f65e5a 100644 --- a/engines/mads/sound.h +++ b/engines/mads/sound.h @@ -37,7 +37,7 @@ class SoundManager { private: MADSEngine *_vm; Audio::Mixer *_mixer; - FM_OPL *_opl; + OPL::OPL *_opl; Nebular::ASound *_driver; bool _pollSoundEnabled; bool _soundPollFlag; diff --git a/engines/parallaction/adlib.cpp b/engines/parallaction/adlib.cpp index 7c1dd1681f..568ad190aa 100644 --- a/engines/parallaction/adlib.cpp +++ b/engines/parallaction/adlib.cpp @@ -25,7 +25,7 @@ #include "audio/fmopl.h" #include "audio/mpu401.h" -#include "audio/softsynth/emumidi.h" +#include "audio/mididrv.h" namespace Parallaction { @@ -270,11 +270,13 @@ struct MelodicVoice { int8 _octave; }; -class AdLibDriver : public MidiDriver_Emulated { +class AdLibDriver : public MidiDriver { public: - AdLibDriver(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) { + AdLibDriver(Audio::Mixer *mixer) { for (uint i = 0; i < 16; ++i) _channels[i].init(this, i); + + _isOpen = false; } int open(); @@ -282,11 +284,13 @@ public: void send(uint32 b); MidiChannel *allocateChannel(); MidiChannel *getPercussionChannel() { return &_channels[9]; } + bool isOpen() const { return _isOpen; } + uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; } - bool isStereo() const { return false; } - int getRate() const { return _mixer->getOutputRate(); } - - void generateSamples(int16 *buf, int len); + virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) { + _adlibTimerProc = timerProc; + _adlibTimerParam = timerParam; + } protected: OPL::OPL *_opl; @@ -320,6 +324,13 @@ protected: void muteMelodicVoice(uint8 voice); void initVoices(); + +private: + void onTimer(); + + Common::TimerManager::TimerProc _adlibTimerProc; + void *_adlibTimerParam; + bool _isOpen; }; MidiDriver *createAdLibDriver() { @@ -348,10 +359,10 @@ int AdLibDriver::open() { if (_isOpen) return MERR_ALREADY_OPEN; - MidiDriver_Emulated::open(); + _isOpen = true; _opl = OPL::Config::create(); - _opl->init(getRate()); + _opl->init(); _opl->writeReg(0x1, 0x20); // set bit 5 (enable all waveforms) // Reset the OPL registers. @@ -364,7 +375,7 @@ int AdLibDriver::open() { initVoices(); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + _opl->start(new Common::Functor0Mem<void, AdLibDriver>(this, &AdLibDriver::onTimer)); return 0; } @@ -373,7 +384,6 @@ void AdLibDriver::close() { return; _isOpen = false; - _mixer->stopHandle(_mixerSoundHandle); delete _opl; } @@ -777,9 +787,9 @@ MidiChannel *AdLibDriver::allocateChannel() { return NULL; } -void AdLibDriver::generateSamples(int16 *buf, int len) { - memset(buf, 0, sizeof(int16) * len); - _opl->readBuffer(buf, len); +void AdLibDriver::onTimer() { + if (_adlibTimerProc) + (*_adlibTimerProc)(_adlibTimerParam); } void AdLibDriver::initVoices() { diff --git a/engines/queen/midiadlib.cpp b/engines/queen/midiadlib.cpp index 25175c21d7..f5bc0f4d58 100644 --- a/engines/queen/midiadlib.cpp +++ b/engines/queen/midiadlib.cpp @@ -23,118 +23,29 @@ #include "common/endian.h" #include "common/textconsole.h" -#include "audio/fmopl.h" -#include "audio/softsynth/emumidi.h" +#include "engines/queen/midiadlib.h" namespace Queen { -class AdLibMidiDriver : public MidiDriver_Emulated { -public: - - AdLibMidiDriver(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) { _adlibWaveformSelect = 0; } - ~AdLibMidiDriver() {} - - // MidiDriver - int open(); - void close(); - void send(uint32 b); - void metaEvent(byte type, byte *data, uint16 length); - MidiChannel *allocateChannel() { return 0; } - MidiChannel *getPercussionChannel() { return 0; } - - // AudioStream - bool isStereo() const { return false; } - int getRate() const { return _mixer->getOutputRate(); } - - // MidiDriver_Emulated - void generateSamples(int16 *buf, int len); - -private: - - void handleMidiEvent0x90_NoteOn(int channel, int param1, int param2); - void handleSequencerSpecificMetaEvent1(int channel, const uint8 *data); - void handleSequencerSpecificMetaEvent2(uint8 value); - void handleSequencerSpecificMetaEvent3(uint8 value); - - void adlibWrite(uint8 port, uint8 value); - void adlibSetupCard(); - void adlibSetupChannels(int fl); - void adlibResetAmpVibratoRhythm(int am, int vib, int kso); - void adlibResetChannels(); - void adlibSetAmpVibratoRhythm(); - void adlibSetCSMKeyboardSplit(); - void adlibSetNoteMul(int mul); - void adlibSetWaveformSelect(int fl); - void adlibSetPitchBend(int channel, int range); - void adlibPlayNote(int channel); - uint8 adlibPlayNoteHelper(int channel, int note1, int note2, int oct); - void adlibTurnNoteOff(int channel); - void adlibTurnNoteOn(int channel, int note); - void adlibSetupChannelFromSequence(int channel, const uint8 *src, int fl); - void adlibSetupChannel(int channel, const uint16 *src, int fl); - void adlibSetNoteVolume(int channel, int volume); - void adlibSetupChannelHelper(int channel); - void adlibSetChannel0x40(int channel); - void adlibSetChannel0xC0(int channel); - void adlibSetChannel0x60(int channel); - void adlibSetChannel0x80(int channel); - void adlibSetChannel0x20(int channel); - void adlibSetChannel0xE0(int channel); - - FM_OPL *_opl; - int _midiNumberOfChannels; - int _adlibNoteMul; - int _adlibWaveformSelect; - int _adlibAMDepthEq48; - int _adlibVibratoDepthEq14; - int _adlibRhythmEnabled; - int _adlibKeyboardSplitOn; - int _adlibVibratoRhythm; - uint8 _midiChannelsFreqTable[9]; - uint8 _adlibChannelsLevelKeyScalingTable[11]; - uint8 _adlibSetupChannelSequence1[14 * 18]; - uint16 _adlibSetupChannelSequence2[14]; - int16 _midiChannelsNote2Table[9]; - uint8 _midiChannelsNote1Table[9]; - uint8 _midiChannelsOctTable[9]; - uint16 _adlibChannelsVolume[11]; - uint16 _adlibMetaSequenceData[28]; - - static const uint8 _adlibChannelsMappingTable1[]; - static const uint8 _adlibChannelsNoFeedback[]; - static const uint8 _adlibChannelsMappingTable2[]; - static const uint8 _adlibChannelsMappingTable3[]; - static const uint8 _adlibChannelsKeyScalingTable1[]; - static const uint8 _adlibChannelsKeyScalingTable2[]; - static const uint8 _adlibChannelsVolumeTable[]; - static const uint8 _adlibInitSequenceData1[]; - static const uint8 _adlibInitSequenceData2[]; - static const uint8 _adlibInitSequenceData3[]; - static const uint8 _adlibInitSequenceData4[]; - static const uint8 _adlibInitSequenceData5[]; - static const uint8 _adlibInitSequenceData6[]; - static const uint8 _adlibInitSequenceData7[]; - static const uint8 _adlibInitSequenceData8[]; - static const int16 _midiChannelsNoteTable[]; - static const int16 _midiNoteFreqTable[]; -}; - int AdLibMidiDriver::open() { - MidiDriver_Emulated::open(); - _opl = makeAdLibOPL(getRate()); + _isOpen = true; + _opl = OPL::Config::create(); + if (!_opl || !_opl->init()) + error("Failed to create OPL"); + adlibSetupCard(); for (int i = 0; i < 11; ++i) { _adlibChannelsVolume[i] = 0; adlibSetNoteVolume(i, 0); adlibTurnNoteOff(i); } - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + + _opl->start(new Common::Functor0Mem<void, AdLibMidiDriver>(this, &AdLibMidiDriver::onTimer)); return 0; } void AdLibMidiDriver::close() { - _mixer->stopHandle(_mixerSoundHandle); - OPLDestroy(_opl); + delete _opl; } void AdLibMidiDriver::send(uint32 b) { @@ -164,6 +75,11 @@ void AdLibMidiDriver::send(uint32 b) { } } +void AdLibMidiDriver::setVolume(uint32 volume) { + for (int i = 0; i < _midiNumberOfChannels; ++i) + adlibSetChannelVolume(i, volume * 64 / 256 + 64); +} + void AdLibMidiDriver::metaEvent(byte type, byte *data, uint16 length) { int event = 0; if (length > 4 && READ_BE_UINT32(data) == 0x3F00) { @@ -192,9 +108,14 @@ void AdLibMidiDriver::metaEvent(byte type, byte *data, uint16 length) { warning("Unhandled meta event %d len %d", event, length); } -void AdLibMidiDriver::generateSamples(int16 *data, int len) { - memset(data, 0, sizeof(int16) * len); - YM3812UpdateOne(_opl, data, len); +void AdLibMidiDriver::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) { + _adlibTimerProc = timerProc; + _adlibTimerParam = timerParam; +} + +void AdLibMidiDriver::onTimer() { + if (_adlibTimerProc) + (*_adlibTimerProc)(_adlibTimerParam); } void AdLibMidiDriver::handleSequencerSpecificMetaEvent1(int channel, const uint8 *data) { @@ -238,7 +159,7 @@ void AdLibMidiDriver::handleMidiEvent0x90_NoteOn(int channel, int param1, int pa } void AdLibMidiDriver::adlibWrite(uint8 port, uint8 value) { - OPLWriteReg(_opl, port, value); + _opl->writeReg(port, value); } void AdLibMidiDriver::adlibSetupCard() { @@ -253,6 +174,7 @@ void AdLibMidiDriver::adlibSetupCard() { _midiChannelsFreqTable[i] = 0; } memset(_adlibChannelsLevelKeyScalingTable, 127, 11); + memset(_adlibChannelsVolumeTable, 128, 11); adlibSetupChannels(0); adlibResetAmpVibratoRhythm(0, 0, 0); adlibSetNoteMul(1); @@ -448,6 +370,11 @@ void AdLibMidiDriver::adlibSetNoteVolume(int channel, int volume) { } } +void AdLibMidiDriver::adlibSetChannelVolume(int channel, uint8 volume) { + if (channel < (_adlibRhythmEnabled ? 11 : 9)) + _adlibChannelsVolumeTable[channel] = volume; +} + void AdLibMidiDriver::adlibSetupChannelHelper(int channel) { adlibSetAmpVibratoRhythm(); adlibSetCSMKeyboardSplit(); @@ -558,10 +485,6 @@ const uint8 AdLibMidiDriver::_adlibChannelsKeyScalingTable2[] = { 0, 3, 1, 4, 2, 5, 6, 9, 7, 10, 8, 11, 12, 15, 16, 255, 14, 255, 17, 255, 13, 255 }; -const uint8 AdLibMidiDriver::_adlibChannelsVolumeTable[] = { - 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 -}; - const uint8 AdLibMidiDriver::_adlibInitSequenceData1[] = { 1, 1, 3, 15, 5, 0, 1, 3, 15, 0, 0, 0, 1, 0 }; @@ -617,8 +540,4 @@ const int16 AdLibMidiDriver::_midiNoteFreqTable[] = { -363, -361, -359, -356, -354, -351, -349, -347, -344, -342, -339, -337 }; -MidiDriver *C_Player_CreateAdLibMidiDriver(Audio::Mixer *mixer) { - return new AdLibMidiDriver(mixer); -} - } // End of namespace Queen diff --git a/engines/queen/midiadlib.h b/engines/queen/midiadlib.h new file mode 100644 index 0000000000..8692e51840 --- /dev/null +++ b/engines/queen/midiadlib.h @@ -0,0 +1,128 @@ +/* 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 "audio/fmopl.h" +#include "audio/mididrv.h" + +namespace Queen { + +class AdLibMidiDriver : public MidiDriver { +public: + + AdLibMidiDriver() { + _adlibWaveformSelect = 0; + _isOpen = false; + } + + ~AdLibMidiDriver() {} + + // MidiDriver + int open(); + void close(); + void send(uint32 b); + void metaEvent(byte type, byte *data, uint16 length); + MidiChannel *allocateChannel() { return 0; } + MidiChannel *getPercussionChannel() { return 0; } + void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc); + bool isOpen() const { return _isOpen; } + uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; } + + void setVolume(uint32 volume); + +private: + + void handleMidiEvent0x90_NoteOn(int channel, int param1, int param2); + void handleSequencerSpecificMetaEvent1(int channel, const uint8 *data); + void handleSequencerSpecificMetaEvent2(uint8 value); + void handleSequencerSpecificMetaEvent3(uint8 value); + + void adlibWrite(uint8 port, uint8 value); + void adlibSetupCard(); + void adlibSetupChannels(int fl); + void adlibResetAmpVibratoRhythm(int am, int vib, int kso); + void adlibResetChannels(); + void adlibSetAmpVibratoRhythm(); + void adlibSetCSMKeyboardSplit(); + void adlibSetNoteMul(int mul); + void adlibSetWaveformSelect(int fl); + void adlibSetPitchBend(int channel, int range); + void adlibPlayNote(int channel); + uint8 adlibPlayNoteHelper(int channel, int note1, int note2, int oct); + void adlibTurnNoteOff(int channel); + void adlibTurnNoteOn(int channel, int note); + void adlibSetupChannelFromSequence(int channel, const uint8 *src, int fl); + void adlibSetupChannel(int channel, const uint16 *src, int fl); + void adlibSetNoteVolume(int channel, int volume); + void adlibSetChannelVolume(int channel, uint8 volume); + void adlibSetupChannelHelper(int channel); + void adlibSetChannel0x40(int channel); + void adlibSetChannel0xC0(int channel); + void adlibSetChannel0x60(int channel); + void adlibSetChannel0x80(int channel); + void adlibSetChannel0x20(int channel); + void adlibSetChannel0xE0(int channel); + + void onTimer(); + + OPL::OPL *_opl; + int _midiNumberOfChannels; + int _adlibNoteMul; + int _adlibWaveformSelect; + int _adlibAMDepthEq48; + int _adlibVibratoDepthEq14; + int _adlibRhythmEnabled; + int _adlibKeyboardSplitOn; + int _adlibVibratoRhythm; + uint8 _midiChannelsFreqTable[9]; + uint8 _adlibChannelsLevelKeyScalingTable[11]; + uint8 _adlibSetupChannelSequence1[14 * 18]; + uint16 _adlibSetupChannelSequence2[14]; + int16 _midiChannelsNote2Table[9]; + uint8 _midiChannelsNote1Table[9]; + uint8 _midiChannelsOctTable[9]; + uint16 _adlibChannelsVolume[11]; + uint16 _adlibMetaSequenceData[28]; + uint8 _adlibChannelsVolumeTable[11]; + + bool _isOpen; + Common::TimerManager::TimerProc _adlibTimerProc; + void *_adlibTimerParam; + + static const uint8 _adlibChannelsMappingTable1[]; + static const uint8 _adlibChannelsNoFeedback[]; + static const uint8 _adlibChannelsMappingTable2[]; + static const uint8 _adlibChannelsMappingTable3[]; + static const uint8 _adlibChannelsKeyScalingTable1[]; + static const uint8 _adlibChannelsKeyScalingTable2[]; + static const uint8 _adlibInitSequenceData1[]; + static const uint8 _adlibInitSequenceData2[]; + static const uint8 _adlibInitSequenceData3[]; + static const uint8 _adlibInitSequenceData4[]; + static const uint8 _adlibInitSequenceData5[]; + static const uint8 _adlibInitSequenceData6[]; + static const uint8 _adlibInitSequenceData7[]; + static const uint8 _adlibInitSequenceData8[]; + static const int16 _midiChannelsNoteTable[]; + static const int16 _midiNoteFreqTable[]; +}; + +} // End of namespace Queen diff --git a/engines/queen/music.cpp b/engines/queen/music.cpp index 93d6527622..9f74aab915 100644 --- a/engines/queen/music.cpp +++ b/engines/queen/music.cpp @@ -23,6 +23,7 @@ #include "common/config-manager.h" #include "common/events.h" +#include "queen/midiadlib.h" #include "queen/music.h" #include "queen/queen.h" #include "queen/resource.h" @@ -33,8 +34,6 @@ namespace Queen { -extern MidiDriver *C_Player_CreateAdLibMidiDriver(Audio::Mixer *); - MidiMusic::MidiMusic(QueenEngine *vm) : _isPlaying(false), _isLooping(false), _randomLoop(false), _masterVolume(192), @@ -69,7 +68,7 @@ MidiMusic::MidiMusic(QueenEngine *vm) // if (READ_LE_UINT16(_musicData + 2) != infoOffset) { // defaultAdLibVolume = _musicData[infoOffset]; // } - _driver = C_Player_CreateAdLibMidiDriver(vm->_mixer); + _driver = new AdLibMidiDriver(); } else { _driver = MidiDriver::createMidi(dev); if (_nativeMT32) { @@ -117,6 +116,9 @@ void MidiMusic::setVolume(int volume) { if (_channelsTable[i]) _channelsTable[i]->volume(_channelsVolume[i] * _masterVolume / 255); } + + if (_adlib) + static_cast<AdLibMidiDriver*>(_driver)->setVolume(volume); } void MidiMusic::playSong(uint16 songNum) { diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp index fcfda2f532..4f557be95e 100644 --- a/engines/sci/sound/drivers/adlib.cpp +++ b/engines/sci/sound/drivers/adlib.cpp @@ -27,7 +27,7 @@ #include "common/textconsole.h" #include "audio/fmopl.h" -#include "audio/softsynth/emumidi.h" +#include "audio/mididrv.h" #include "sci/resource.h" #include "sci/sound/drivers/mididriver.h" @@ -43,29 +43,30 @@ namespace Sci { // FIXME: We don't seem to be sending the polyphony init data, so disable this for now #define ADLIB_DISABLE_VOICE_MAPPING -class MidiDriver_AdLib : public MidiDriver_Emulated { +class MidiDriver_AdLib : public MidiDriver { public: enum { kVoices = 9, kRhythmKeys = 62 }; - MidiDriver_AdLib(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15), _rhythmKeyMap(0), _opl(0) { } + MidiDriver_AdLib(Audio::Mixer *mixer) :_playSwitch(true), _masterVolume(15), _rhythmKeyMap(0), _opl(0), _isOpen(false) { } virtual ~MidiDriver_AdLib() { } // MidiDriver + int open() { return -1; } // Dummy implementation (use openAdLib) int openAdLib(bool isSCI0); void close(); void send(uint32 b); MidiChannel *allocateChannel() { return NULL; } MidiChannel *getPercussionChannel() { return NULL; } + bool isOpen() const { return _isOpen; } + uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; } - // AudioStream - bool isStereo() const { return _stereo; } - int getRate() const { return _mixer->getOutputRate(); } + // MidiDriver + void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc); - // MidiDriver_Emulated - void generateSamples(int16 *buf, int len); + void onTimer(); void setVolume(byte volume); void playSwitch(bool play); @@ -133,6 +134,7 @@ private: bool _stereo; bool _isSCI0; OPL::OPL *_opl; + bool _isOpen; bool _playSwitch; int _masterVolume; Channel _channels[MIDI_CHANNELS]; @@ -140,6 +142,9 @@ private: byte *_rhythmKeyMap; Common::Array<AdLibPatch> _patches; + Common::TimerManager::TimerProc _adlibTimerProc; + void *_adlibTimerParam; + void loadInstrument(const byte *ins); void voiceOn(int voice, int note, int velocity); void voiceOff(int voice); @@ -215,14 +220,12 @@ static const int ym3812_note[13] = { }; int MidiDriver_AdLib::openAdLib(bool isSCI0) { - int rate = _mixer->getOutputRate(); - _stereo = STEREO; debug(3, "ADLIB: Starting driver in %s mode", (isSCI0 ? "SCI0" : "SCI1")); _isSCI0 = isSCI0; - _opl = OPL::Config::create(isStereo() ? OPL::Config::kDualOpl2 : OPL::Config::kOpl2); + _opl = OPL::Config::create(_stereo ? OPL::Config::kDualOpl2 : OPL::Config::kOpl2); // Try falling back to mono, thus plain OPL2 emualtor, when no Dual OPL2 is available. if (!_opl && _stereo) { @@ -233,22 +236,24 @@ int MidiDriver_AdLib::openAdLib(bool isSCI0) { if (!_opl) return -1; - _opl->init(rate); + if (!_opl->init()) { + delete _opl; + _opl = nullptr; + return -1; + } setRegister(0xBD, 0); setRegister(0x08, 0); setRegister(0x01, 0x20); - MidiDriver_Emulated::open(); + _isOpen = true; - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO); + _opl->start(new Common::Functor0Mem<void, MidiDriver_AdLib>(this, &MidiDriver_AdLib::onTimer)); return 0; } void MidiDriver_AdLib::close() { - _mixer->stopHandle(_mixerSoundHandle); - delete _opl; delete[] _rhythmKeyMap; } @@ -325,10 +330,14 @@ void MidiDriver_AdLib::send(uint32 b) { } } -void MidiDriver_AdLib::generateSamples(int16 *data, int len) { - if (isStereo()) - len <<= 1; - _opl->readBuffer(data, len); +void MidiDriver_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) { + _adlibTimerProc = timerProc; + _adlibTimerParam = timerParam; +} + +void MidiDriver_AdLib::onTimer() { + if (_adlibTimerProc) + (*_adlibTimerProc)(_adlibTimerParam); // Increase the age of the notes for (int i = 0; i < kVoices; i++) { @@ -684,7 +693,7 @@ void MidiDriver_AdLib::setVelocityReg(int regOffset, int velocity, int kbScaleLe if (!_playSwitch) velocity = 0; - if (isStereo()) { + if (_stereo) { int velLeft = velocity; int velRight = velocity; @@ -734,7 +743,7 @@ void MidiDriver_AdLib::setRegister(int reg, int value, int channels) { _opl->write(0x221, value); } - if (isStereo()) { + if (_stereo) { if (channels & kRightChannel) { _opl->write(0x222, reg); _opl->write(0x223, value); diff --git a/engines/scumm/players/player_ad.cpp b/engines/scumm/players/player_ad.cpp index adcda68e10..5c0d443105 100644 --- a/engines/scumm/players/player_ad.cpp +++ b/engines/scumm/players/player_ad.cpp @@ -36,25 +36,18 @@ namespace Scumm { #define AD_CALLBACK_FREQUENCY 472 Player_AD::Player_AD(ScummEngine *scumm, Audio::Mixer *mixer) - : _vm(scumm), _mixer(mixer), _rate(mixer->getOutputRate()) { + : _vm(scumm), _mixer(mixer) { _opl2 = OPL::Config::create(); - if (!_opl2->init(_rate)) { + if (!_opl2->init()) { error("Could not initialize OPL2 emulator"); } - _samplesPerCallback = _rate / AD_CALLBACK_FREQUENCY; - _samplesPerCallbackRemainder = _rate % AD_CALLBACK_FREQUENCY; - _samplesTillCallback = 0; - _samplesTillCallbackRemainder = 0; - memset(_registerBackUpTable, 0, sizeof(_registerBackUpTable)); writeReg(0x01, 0x00); writeReg(0xBD, 0x00); writeReg(0x08, 0x00); writeReg(0x01, 0x20); - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); - _engineMusicTimer = 0; _soundPlaying = -1; @@ -78,11 +71,11 @@ Player_AD::Player_AD(ScummEngine *scumm, Audio::Mixer *mixer) _musicVolume = _sfxVolume = 255; _isSeeking = false; + + _opl2->start(new Common::Functor0Mem<void, Player_AD>(this, &Player_AD::onTimer), AD_CALLBACK_FREQUENCY); } Player_AD::~Player_AD() { - _mixer->stopHandle(_soundHandle); - stopAllSounds(); Common::StackLock lock(_mutex); delete _opl2; @@ -244,36 +237,14 @@ void Player_AD::saveLoadWithSerializer(Serializer *ser) { } } -int Player_AD::readBuffer(int16 *buffer, const int numSamples) { +void Player_AD::onTimer() { Common::StackLock lock(_mutex); - int len = numSamples; - - while (len > 0) { - if (!_samplesTillCallback) { - if (_curOffset) { - updateMusic(); - } - - updateSfx(); - - _samplesTillCallback = _samplesPerCallback; - _samplesTillCallbackRemainder += _samplesPerCallbackRemainder; - if (_samplesTillCallbackRemainder >= AD_CALLBACK_FREQUENCY) { - ++_samplesTillCallback; - _samplesTillCallbackRemainder -= AD_CALLBACK_FREQUENCY; - } - } - - const int samplesToRead = MIN(len, _samplesTillCallback); - _opl2->readBuffer(buffer, samplesToRead); - - buffer += samplesToRead; - len -= samplesToRead; - _samplesTillCallback -= samplesToRead; + if (_curOffset) { + updateMusic(); } - return numSamples; + updateSfx(); } void Player_AD::setupVolume() { diff --git a/engines/scumm/players/player_ad.h b/engines/scumm/players/player_ad.h index 63a8503f47..b8cd8dc359 100644 --- a/engines/scumm/players/player_ad.h +++ b/engines/scumm/players/player_ad.h @@ -41,7 +41,7 @@ class ScummEngine; /** * Sound output for v3/v4 AdLib data. */ -class Player_AD : public MusicEngine, public Audio::AudioStream { +class Player_AD : public MusicEngine { public: Player_AD(ScummEngine *scumm, Audio::Mixer *mixer); virtual ~Player_AD(); @@ -56,18 +56,13 @@ public: virtual void saveLoadWithSerializer(Serializer *ser); - // AudioStream API - virtual int readBuffer(int16 *buffer, const int numSamples); - virtual bool isStereo() const { return false; } - virtual bool endOfData() const { return false; } - virtual int getRate() const { return _rate; } + // Timer callback + void onTimer(); private: ScummEngine *const _vm; Common::Mutex _mutex; Audio::Mixer *const _mixer; - const int _rate; - Audio::SoundHandle _soundHandle; void setupVolume(); int _musicVolume; @@ -75,11 +70,6 @@ private: OPL::OPL *_opl2; - int _samplesPerCallback; - int _samplesPerCallbackRemainder; - int _samplesTillCallback; - int _samplesTillCallbackRemainder; - int _soundPlaying; int32 _engineMusicTimer; diff --git a/engines/sherlock/scalpel/drivers/adlib.cpp b/engines/sherlock/scalpel/drivers/adlib.cpp index 91641fcccd..29a39f0c39 100644 --- a/engines/sherlock/scalpel/drivers/adlib.cpp +++ b/engines/sherlock/scalpel/drivers/adlib.cpp @@ -216,10 +216,11 @@ uint16 frequencyLookUpTable[SHERLOCK_ADLIB_NOTES_COUNT] = { 0x1DE6, 0x1E03, 0x1E22, 0x1E42, 0x1E65, 0x1E89 }; -class MidiDriver_SH_AdLib : public MidiDriver_Emulated { +class MidiDriver_SH_AdLib : public MidiDriver { public: MidiDriver_SH_AdLib(Audio::Mixer *mixer) - : MidiDriver_Emulated(mixer), _masterVolume(15), _opl(0) { + : _masterVolume(15), _opl(0), + _adlibTimerProc(0), _adlibTimerParam(0), _isOpen(false) { memset(_voiceChannelMapping, 0, sizeof(_voiceChannelMapping)); } virtual ~MidiDriver_SH_AdLib() { } @@ -230,15 +231,13 @@ public: void send(uint32 b); MidiChannel *allocateChannel() { return NULL; } MidiChannel *getPercussionChannel() { return NULL; } + bool isOpen() const { return _isOpen; } + uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; } - // AudioStream - bool isStereo() const { return false; } - int getRate() const { return _mixer->getOutputRate(); } int getPolyphony() const { return SHERLOCK_ADLIB_VOICES_COUNT; } bool hasRhythmChannel() const { return false; } - // MidiDriver_Emulated - void generateSamples(int16 *buf, int len); + virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc); void setVolume(byte volume); virtual uint32 property(int prop, uint32 param); @@ -261,16 +260,19 @@ private: OPL::OPL *_opl; int _masterVolume; + Common::TimerManager::TimerProc _adlibTimerProc; + void *_adlibTimerParam; + + bool _isOpen; + // points to a MIDI channel for each of the new voice channels byte _voiceChannelMapping[SHERLOCK_ADLIB_VOICES_COUNT]; // stores information about all FM voice channels adlib_ChannelEntry _channels[SHERLOCK_ADLIB_VOICES_COUNT]; -protected: void onTimer(); -private: void resetAdLib(); void resetAdLibOperatorRegisters(byte baseRegister, byte value); void resetAdLibFMVoiceChannelRegisters(byte baseRegister, byte value); @@ -285,8 +287,6 @@ private: }; int MidiDriver_SH_AdLib::open() { - int rate = _mixer->getOutputRate(); - debugC(kDebugLevelAdLibDriver, "AdLib: starting driver"); _opl = OPL::Config::create(OPL::Config::kOpl2); @@ -294,17 +294,18 @@ int MidiDriver_SH_AdLib::open() { if (!_opl) return -1; - _opl->init(rate); + _opl->init(); - MidiDriver_Emulated::open(); + _isOpen = true; - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO); + _opl->start(new Common::Functor0Mem<void, MidiDriver_SH_AdLib>(this, &MidiDriver_SH_AdLib::onTimer)); return 0; } void MidiDriver_SH_AdLib::close() { - _mixer->stopHandle(_mixerSoundHandle); + // Stop the OPL timer + _opl->stop(); delete _opl; } @@ -318,6 +319,12 @@ void MidiDriver_SH_AdLib::setVolume(byte volume) { // original driver did this before MIDI data processing on each tick // we do it atm after MIDI data processing void MidiDriver_SH_AdLib::onTimer() { + if (_adlibTimerProc) + (*_adlibTimerProc)(_adlibTimerParam); + + // this should/must get called per tick + // original driver did this before MIDI data processing on each tick + // we do it atm after MIDI data processing for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) { if (_channels[FMvoiceChannel].inUse) { _channels[FMvoiceChannel].inUseTimer++; @@ -417,10 +424,6 @@ void MidiDriver_SH_AdLib::send(uint32 b) { } } -void MidiDriver_SH_AdLib::generateSamples(int16 *data, int len) { - _opl->readBuffer(data, len); -} - void MidiDriver_SH_AdLib::noteOn(byte MIDIchannel, byte note, byte velocity) { int16 oldestInUseChannel = -1; uint16 oldestInUseTimer = 0; @@ -627,6 +630,11 @@ uint32 MidiDriver_SH_AdLib::property(int prop, uint32 param) { return 0; } +void MidiDriver_SH_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) { + _adlibTimerProc = timerProc; + _adlibTimerParam = timerParam; +} + MidiDriver *MidiDriver_SH_AdLib_create() { return new MidiDriver_SH_AdLib(g_system->getMixer()); } diff --git a/engines/sky/music/adlibchannel.cpp b/engines/sky/music/adlibchannel.cpp index 8400fef6eb..c7acb9b6c1 100644 --- a/engines/sky/music/adlibchannel.cpp +++ b/engines/sky/music/adlibchannel.cpp @@ -29,7 +29,7 @@ namespace Sky { -AdLibChannel::AdLibChannel(FM_OPL *opl, uint8 *pMusicData, uint16 startOfData) { +AdLibChannel::AdLibChannel(OPL::OPL *opl, uint8 *pMusicData, uint16 startOfData) { _opl = opl; _musicData = pMusicData; _channelData.loopPoint = startOfData; @@ -45,6 +45,8 @@ AdLibChannel::AdLibChannel(FM_OPL *opl, uint8 *pMusicData, uint16 startOfData) { _channelData.frequency = 0; _channelData.instrumentData = NULL; + _musicVolume = 128; + uint16 instrumentDataLoc; if (SkyEngine::_systemVars.gameVersion == 109) { @@ -86,7 +88,7 @@ bool AdLibChannel::isActive() { } void AdLibChannel::updateVolume(uint16 pVolume) { - // Do nothing. The mixer handles the music volume for us. + _musicVolume = pVolume; } /* This class uses the same area for the register mirror as the original @@ -95,7 +97,7 @@ void AdLibChannel::updateVolume(uint16 pVolume) { */ void AdLibChannel::setRegister(uint8 regNum, uint8 value) { if (_adlibRegMirror[regNum] != value) { - OPLWriteReg (_opl, regNum, value); + _opl->writeReg(regNum, value); _adlibRegMirror[regNum] = value; } } @@ -208,6 +210,8 @@ void AdLibChannel::setupChannelVolume(uint8 volume) { 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]; @@ -216,6 +220,8 @@ void AdLibChannel::setupChannelVolume(uint8 volume) { 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; diff --git a/engines/sky/music/adlibchannel.h b/engines/sky/music/adlibchannel.h index 80dae93b2c..4504e3b570 100644 --- a/engines/sky/music/adlibchannel.h +++ b/engines/sky/music/adlibchannel.h @@ -60,14 +60,15 @@ typedef struct { class AdLibChannel : public ChannelBase { public: - AdLibChannel (FM_OPL *opl, uint8 *pMusicData, uint16 startOfData); + AdLibChannel (OPL::OPL *opl, uint8 *pMusicData, uint16 startOfData); virtual ~AdLibChannel(); virtual uint8 process(uint16 aktTime); virtual void updateVolume(uint16 pVolume); virtual bool isActive(); private: - FM_OPL *_opl; + OPL::OPL *_opl; uint8 *_musicData; + uint16 _musicVolume; AdLibChannelType _channelData; InstrumentStruct *_instruments; diff --git a/engines/sky/music/adlibmusic.cpp b/engines/sky/music/adlibmusic.cpp index dd64c5bc81..be5e7b2353 100644 --- a/engines/sky/music/adlibmusic.cpp +++ b/engines/sky/music/adlibmusic.cpp @@ -22,6 +22,7 @@ #include "common/endian.h" +#include "common/textconsole.h" #include "sky/music/adlibmusic.h" #include "sky/music/adlibchannel.h" @@ -32,44 +33,21 @@ namespace Sky { AdLibMusic::AdLibMusic(Audio::Mixer *pMixer, Disk *pDisk) : MusicBase(pMixer, pDisk) { _driverFileBase = 60202; - _sampleRate = pMixer->getOutputRate(); - _opl = makeAdLibOPL(_sampleRate); + _opl = OPL::Config::create(); + if (!_opl || !_opl->init()) + error("Failed to create OPL"); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + _opl->start(new Common::Functor0Mem<void, AdLibMusic>(this, &AdLibMusic::onTimer), 50); } AdLibMusic::~AdLibMusic() { - OPLDestroy(_opl); - _mixer->stopHandle(_soundHandle); + delete _opl; } -int AdLibMusic::readBuffer(int16 *data, const int numSamples) { - if (_musicData == NULL) { - // no music loaded - memset(data, 0, numSamples * sizeof(int16)); - } else if ((_currentMusic == 0) || (_numberOfChannels == 0)) { - // music loaded but not played as of yet - memset(data, 0, numSamples * sizeof(int16)); - // poll anyways as pollMusic() can activate the music +void AdLibMusic::onTimer() { + if (_musicData != NULL) pollMusic(); - _nextMusicPoll = _sampleRate / 50; - } else { - uint32 render; - uint remaining = numSamples; - while (remaining) { - render = (remaining > _nextMusicPoll) ? _nextMusicPoll : remaining; - remaining -= render; - _nextMusicPoll -= render; - YM3812UpdateOne(_opl, data, render); - data += render; - if (_nextMusicPoll == 0) { - pollMusic(); - _nextMusicPoll = _sampleRate / 50; - } - } - } - return numSamples; } void AdLibMusic::setupPointers() { @@ -87,7 +65,6 @@ void AdLibMusic::setupPointers() { _musicDataLoc = READ_LE_UINT16(_musicData + 0x1201); _initSequence = _musicData + 0xE91; } - _nextMusicPoll = 0; } void AdLibMusic::setupChannels(uint8 *channelData) { @@ -102,26 +79,15 @@ void AdLibMusic::setupChannels(uint8 *channelData) { void AdLibMusic::startDriver() { uint16 cnt = 0; while (_initSequence[cnt] || _initSequence[cnt + 1]) { - OPLWriteReg (_opl, _initSequence[cnt], _initSequence[cnt + 1]); + _opl->writeReg(_initSequence[cnt], _initSequence[cnt + 1]); cnt += 2; } } void AdLibMusic::setVolume(uint16 param) { _musicVolume = param; - _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, 2 * param); -} - -bool AdLibMusic::isStereo() const { - return false; -} - -bool AdLibMusic::endOfData() const { - return false; -} - -int AdLibMusic::getRate() const { - return _sampleRate; + for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) + _channels[cnt]->updateVolume(_musicVolume); } } // End of namespace Sky diff --git a/engines/sky/music/adlibmusic.h b/engines/sky/music/adlibmusic.h index 886eef026e..7b51f2d3a0 100644 --- a/engines/sky/music/adlibmusic.h +++ b/engines/sky/music/adlibmusic.h @@ -25,32 +25,32 @@ #include "sky/music/musicbase.h" #include "audio/audiostream.h" -#include "audio/fmopl.h" + +namespace OPL { +class OPL; +} namespace Sky { -class AdLibMusic : public Audio::AudioStream, public MusicBase { +class AdLibMusic : public MusicBase { public: AdLibMusic(Audio::Mixer *pMixer, Disk *pDisk); ~AdLibMusic(); // AudioStream API - int readBuffer(int16 *buffer, const int numSamples); - bool isStereo() const; - bool endOfData() const; - int getRate() const; virtual void setVolume(uint16 param); private: - FM_OPL *_opl; - Audio::SoundHandle _soundHandle; + OPL::OPL *_opl; uint8 *_initSequence; - uint32 _sampleRate, _nextMusicPoll; + uint32 _sampleRate; virtual void setupPointers(); virtual void setupChannels(uint8 *channelData); virtual void startDriver(); void premixerCall(int16 *buf, uint len); + + void onTimer(); }; } // End of namespace Sky diff --git a/engines/tsage/sound.cpp b/engines/tsage/sound.cpp index b95b614f09..0d3fb55dd3 100644 --- a/engines/tsage/sound.cpp +++ b/engines/tsage/sound.cpp @@ -20,9 +20,9 @@ * */ +#include "audio/fmopl.h" #include "audio/decoders/raw.h" #include "common/config-manager.h" -#include "audio/decoders/raw.h" #include "audio/audiostream.h" #include "tsage/core.h" #include "tsage/globals.h" @@ -2743,17 +2743,9 @@ AdlibSoundDriver::AdlibSoundDriver(): SoundDriver() { _groupData._pData = &adlib_group_data[0]; _mixer = g_vm->_mixer; - _sampleRate = _mixer->getOutputRate(); _opl = OPL::Config::create(); assert(_opl); - _opl->init(_sampleRate); - - _samplesTillCallback = 0; - _samplesTillCallbackRemainder = 0; - _samplesPerCallback = getRate() / CALLBACKS_PER_SECOND; - _samplesPerCallbackRemainder = getRate() % CALLBACKS_PER_SECOND; - - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + _opl->init(); Common::fill(_channelVoiced, _channelVoiced + ADLIB_CHANNEL_COUNT, false); memset(_channelVolume, 0, ADLIB_CHANNEL_COUNT * sizeof(int)); @@ -2772,11 +2764,12 @@ AdlibSoundDriver::AdlibSoundDriver(): SoundDriver() { _channelVoiced[i] = false; _pitchBlend[i] = 0; } + + _opl->start(new Common::Functor0Mem<void, AdlibSoundDriver>(this, &AdlibSoundDriver::onTimer), CALLBACKS_PER_SECOND); } AdlibSoundDriver::~AdlibSoundDriver() { DEALLOCATE(_patchData); - _mixer->stopHandle(_soundHandle); delete _opl; } @@ -3019,33 +3012,12 @@ void AdlibSoundDriver::setFrequency(int channel) { ((dataWord >> 8) & 3) | (var2 << 2)); } -int AdlibSoundDriver::readBuffer(int16 *buffer, const int numSamples) { +void AdlibSoundDriver::onTimer() { Common::StackLock slock1(SoundManager::sfManager()._serverDisabledMutex); Common::StackLock slock2(SoundManager::sfManager()._serverSuspendedMutex); - int32 samplesLeft = numSamples; - memset(buffer, 0, sizeof(int16) * numSamples); - while (samplesLeft) { - if (!_samplesTillCallback) { - SoundManager::sfUpdateCallback(NULL); - flush(); - - _samplesTillCallback = _samplesPerCallback; - _samplesTillCallbackRemainder += _samplesPerCallbackRemainder; - if (_samplesTillCallbackRemainder >= CALLBACKS_PER_SECOND) { - _samplesTillCallback++; - _samplesTillCallbackRemainder -= CALLBACKS_PER_SECOND; - } - } - - int32 render = MIN<int>(samplesLeft, _samplesTillCallback); - samplesLeft -= render; - _samplesTillCallback -= render; - - _opl->readBuffer(buffer, render); - buffer += render; - } - return numSamples; + SoundManager::sfUpdateCallback(NULL); + flush(); } /*--------------------------------------------------------------------------*/ diff --git a/engines/tsage/sound.h b/engines/tsage/sound.h index 49558b4bca..68755a48c8 100644 --- a/engines/tsage/sound.h +++ b/engines/tsage/sound.h @@ -27,12 +27,15 @@ #include "common/mutex.h" #include "common/queue.h" #include "audio/audiostream.h" -#include "audio/fmopl.h" #include "audio/mixer.h" #include "common/list.h" #include "tsage/saveload.h" #include "tsage/core.h" +namespace OPL { +class OPL; +} + namespace TsAGE { class Sound; @@ -446,21 +449,15 @@ public: #define ADLIB_CHANNEL_COUNT 9 -class AdlibSoundDriver: public SoundDriver, Audio::AudioStream { +class AdlibSoundDriver: public SoundDriver { private: GroupData _groupData; Audio::Mixer *_mixer; - FM_OPL *_opl; - Audio::SoundHandle _soundHandle; - int _sampleRate; + OPL::OPL *_opl; byte _portContents[256]; const byte *_patchData; int _masterVolume; Common::Queue<RegisterValue> _queue; - int _samplesTillCallback; - int _samplesTillCallbackRemainder; - int _samplesPerCallback; - int _samplesPerCallbackRemainder; bool _channelVoiced[ADLIB_CHANNEL_COUNT]; int _channelVolume[ADLIB_CHANNEL_COUNT]; @@ -495,13 +492,8 @@ public: virtual void proc38(int channel, int cmd, int value); virtual void setPitch(int channel, int pitchBlend); - // AudioStream interface - virtual int readBuffer(int16 *buffer, const int numSamples); - virtual bool isStereo() const { return false; } - virtual bool endOfData() const { return false; } - virtual int getRate() const { return _sampleRate; } - - void update(int16 *buf, int len); +private: + void onTimer(); }; class SoundBlasterDriver: public SoundDriver { diff --git a/gui/options.cpp b/gui/options.cpp index 726b89d437..ba247e5f15 100644 --- a/gui/options.cpp +++ b/gui/options.cpp @@ -445,11 +445,9 @@ void OptionsDialog::close() { if (_oplPopUp) { if (_enableAudioSettings) { - const OPL::Config::EmulatorDescription *ed = OPL::Config::getAvailable(); - while (ed->name && ed->id != (int)_oplPopUp->getSelectedTag()) - ++ed; + const OPL::Config::EmulatorDescription *ed = OPL::Config::findDriver(_oplPopUp->getSelectedTag()); - if (ed->name) + if (ed) ConfMan.set("opl_driver", ed->name, _domain); else ConfMan.removeKey("opl_driver", _domain); |