[Scummvm-git-logs] scummvm master -> 522d077ad89ef55848002eb4c0fac62ca4701563

athrxx athrxx at scummvm.org
Wed Aug 7 17:06:21 CEST 2019


This automated email contains information about 7 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
8b197e4ec7 SCI: (FM-Towns sound driver) - fix pitchwheel glitch
0e73472207 AUDIO: (FM-Towns/PC98) - cleanup mutex handling
47a2f62c15 SCI: (FB01 sound driver) - several fixes
9ea6c43c97 SCI: error dialog for missing sound patch/driver files
f8c98d76e6 SCI: (ADL driver) - remove unused declaration
09f007fa5e SCI: (ADL driver) - implement/fix voice mapping
522d077ad8 SCI: Fix pitch wheel bug in adlib driver


Commit: 8b197e4ec70a52f91c0774be98acf5b373542bde
    https://github.com/scummvm/scummvm/commit/8b197e4ec70a52f91c0774be98acf5b373542bde
Author: athrxx (athrxx at scummvm.org)
Date: 2019-08-07T16:43:05+02:00

Commit Message:
SCI: (FM-Towns sound driver) - fix pitchwheel glitch

Pitchwheel and channel volume settings need to be updated when the channels get  reassigned.
This bug caused faulty music at least in KQ5 when walking from the dwarves scene into the harp playing tree scene.

Changed paths:
    engines/sci/sound/drivers/fmtowns.cpp


diff --git a/engines/sci/sound/drivers/fmtowns.cpp b/engines/sci/sound/drivers/fmtowns.cpp
index eb2f8aa..4dd74bc 100644
--- a/engines/sci/sound/drivers/fmtowns.cpp
+++ b/engines/sci/sound/drivers/fmtowns.cpp
@@ -327,6 +327,8 @@ void TownsMidiPart::addChannels(int num) {
 
 	_chanMissing += num;
 	programChange(_program);
+	pitchBend(_pitchBend);
+	controlChangeVolume(_volume << 1);
 }
 
 void TownsMidiPart::dropChannels(int num) {


Commit: 0e73472207b18e406f51e8bca9c3450dd908ec38
    https://github.com/scummvm/scummvm/commit/0e73472207b18e406f51e8bca9c3450dd908ec38
Author: athrxx (athrxx at scummvm.org)
Date: 2019-08-07T16:43:06+02:00

Commit Message:
AUDIO: (FM-Towns/PC98) - cleanup mutex handling

Changed paths:
    audio/softsynth/fmtowns_pc98/pc98_audio.cpp
    audio/softsynth/fmtowns_pc98/pc98_audio.h
    audio/softsynth/fmtowns_pc98/towns_audio.cpp
    audio/softsynth/fmtowns_pc98/towns_audio.h
    audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp
    audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h
    engines/kyra/sound/sound_towns_darkmoon.cpp
    engines/sci/sound/drivers/fmtowns.cpp
    engines/sci/sound/drivers/pc9801.cpp
    engines/scumm/imuse/drivers/fmtowns.cpp


diff --git a/audio/softsynth/fmtowns_pc98/pc98_audio.cpp b/audio/softsynth/fmtowns_pc98/pc98_audio.cpp
index 3ad1009..c8d58f6 100644
--- a/audio/softsynth/fmtowns_pc98/pc98_audio.cpp
+++ b/audio/softsynth/fmtowns_pc98/pc98_audio.cpp
@@ -26,11 +26,11 @@
 
 class PC98AudioCoreInternal : public TownsPC98_FmSynth {
 private:
-	PC98AudioCoreInternal(Audio::Mixer *mixer, PC98AudioCore *owner, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type, bool externalMutexHandling = false);
+	PC98AudioCoreInternal(Audio::Mixer *mixer, PC98AudioCore *owner, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type);
 public:
 	~PC98AudioCoreInternal();
 
-	static PC98AudioCoreInternal *addNewRef(Audio::Mixer *mixer, PC98AudioCore *owner, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type, bool externalMutexHandling = false);
+	static PC98AudioCoreInternal *addNewRef(Audio::Mixer *mixer, PC98AudioCore *owner, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type);
 	static void releaseRef(PC98AudioCore *owner);
 
 	bool init();
@@ -69,8 +69,8 @@ private:
 	static int _refCount;
 };
 
-PC98AudioCoreInternal::PC98AudioCoreInternal(Audio::Mixer *mixer, PC98AudioCore *owner, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type, bool externalMutexHandling) :
-	TownsPC98_FmSynth(mixer, (TownsPC98_FmSynth::EmuType)type, externalMutexHandling),
+PC98AudioCoreInternal::PC98AudioCoreInternal(Audio::Mixer *mixer, PC98AudioCore *owner, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type) :
+	TownsPC98_FmSynth(mixer, (TownsPC98_FmSynth::EmuType)type),
 	_drv(driver), _drvOwner(owner),
 	_musicVolume(Audio::Mixer::kMaxMixerVolume), _sfxVolume(Audio::Mixer::kMaxMixerVolume),
 	_port1(type == PC98AudioPluginDriver::kTypeTowns ? 0x4D8 : 0x188), _port2(type == PC98AudioPluginDriver::kTypeTowns ? 0x4DA : 0x18A),
@@ -80,22 +80,22 @@ PC98AudioCoreInternal::PC98AudioCoreInternal(Audio::Mixer *mixer, PC98AudioCore
 }
 
 PC98AudioCoreInternal::~PC98AudioCoreInternal() {
+	Common::StackLock lock(_mutex);
 	_ready = false;
 	deinit();
 
-	Common::StackLock lock(_mutex);
 	/*
 	
 	*/
 }
 
-PC98AudioCoreInternal *PC98AudioCoreInternal::addNewRef(Audio::Mixer *mixer, PC98AudioCore *owner, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type, bool externalMutexHandling) {
+PC98AudioCoreInternal *PC98AudioCoreInternal::addNewRef(Audio::Mixer *mixer, PC98AudioCore *owner, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type) {
 	_refCount++;
 	if (_refCount == 1 && _refInstance == 0)
-		_refInstance = new PC98AudioCoreInternal(mixer, owner, driver, type, externalMutexHandling);
+		_refInstance = new PC98AudioCoreInternal(mixer, owner, driver, type);
 	else if (_refCount < 2 || _refInstance == 0)
 		error("PC98AudioCoreInternal::addNewRef(): Internal reference management failure");
-	else if (!_refInstance->assignPluginDriver(owner, driver, externalMutexHandling))
+	else if (!_refInstance->assignPluginDriver(owner, driver))
 		error("PC98AudioCoreInternal::addNewRef(): Plugin driver conflict");
 
 	return _refInstance;
@@ -139,6 +139,7 @@ bool PC98AudioCoreInternal::init() {
 }
 
 void PC98AudioCoreInternal::writePort(uint16 port, uint8 value) {
+	Common::StackLock lock(_mutex);
 	if (port == _port1)
 		_address[0] = value;
 	else if (port == _port2 && _address[0] < 0xc0) {
@@ -153,6 +154,7 @@ void PC98AudioCoreInternal::writePort(uint16 port, uint8 value) {
 }
 
 uint8 PC98AudioCoreInternal::readPort(uint16 port) {
+	Common::StackLock lock(_mutex);
 	uint8 val = 0;
 	if (port == _port2 && _address[0] < 0xc0) {
 		val = readReg(0, _address[0]);
@@ -165,20 +167,24 @@ uint8 PC98AudioCoreInternal::readPort(uint16 port) {
 }
 
 void PC98AudioCoreInternal::setMusicVolume(int volume) {
+	Common::StackLock lock(_mutex);
 	_musicVolume = CLIP<uint16>(volume, 0, Audio::Mixer::kMaxMixerVolume);
 	setVolumeIntern(_musicVolume, _sfxVolume);
 }
 
 void PC98AudioCoreInternal::setSoundEffectVolume(int volume) {
+	Common::StackLock lock(_mutex);
 	_sfxVolume = CLIP<uint16>(volume, 0, Audio::Mixer::kMaxMixerVolume);
 	setVolumeIntern(_musicVolume, _sfxVolume);
 }
 
 void PC98AudioCoreInternal::setSoundEffectChanMask(int mask) {
+	Common::StackLock lock(_mutex);
 	setVolumeChannelMasks(~mask, mask);
 }
 
 void PC98AudioCoreInternal::ssgSetVolume(int volume) {
+	Common::StackLock lock(_mutex);
 	setLevelSSG(volume);
 }
 
@@ -187,6 +193,7 @@ Common::Mutex &PC98AudioCoreInternal::mutex() {
 }
 
 bool PC98AudioCoreInternal::assignPluginDriver(PC98AudioCore *owner, PC98AudioPluginDriver *driver, bool externalMutexHandling) {
+	Common::StackLock lock(_mutex);
 	if (_refCount <= 1)
 		return true;
 
@@ -194,28 +201,27 @@ bool PC98AudioCoreInternal::assignPluginDriver(PC98AudioCore *owner, PC98AudioPl
 		if (driver && driver != _drv)
 			return false;
 	} else {
-		Common::StackLock lock(_mutex);
 		_drv = driver;
 		_drvOwner = owner;
-		_externalMutex = externalMutexHandling;
 	}
 
 	return true;
 }
 
 void PC98AudioCoreInternal::removePluginDriver(PC98AudioCore *owner) {
-	if (_drvOwner == owner) {
-		Common::StackLock lock(_mutex);
+	Common::StackLock lock(_mutex);
+	if (_drvOwner == owner)
 		_drv = 0;
-	}
 }
 
 void PC98AudioCoreInternal::timerCallbackA() {
+	Common::StackLock lock(_mutex);
 	if (_drv && _ready)
 		_drv->timerCallbackA();
 }
 
 void PC98AudioCoreInternal::timerCallbackB() {
+	Common::StackLock lock(_mutex);
 	if (_drv && _ready)
 		_drv->timerCallbackB();
 }
@@ -224,8 +230,8 @@ PC98AudioCoreInternal *PC98AudioCoreInternal::_refInstance = 0;
 
 int PC98AudioCoreInternal::_refCount = 0;
 
-PC98AudioCore::PC98AudioCore(Audio::Mixer *mixer, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type, bool externalMutexHandling) {
-	_internal = PC98AudioCoreInternal::addNewRef(mixer, this, driver, type, externalMutexHandling);
+PC98AudioCore::PC98AudioCore(Audio::Mixer *mixer, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type) {
+	_internal = PC98AudioCoreInternal::addNewRef(mixer, this, driver, type);
 }
 
 PC98AudioCore::~PC98AudioCore() {
diff --git a/audio/softsynth/fmtowns_pc98/pc98_audio.h b/audio/softsynth/fmtowns_pc98/pc98_audio.h
index b6269f0..2bfae78 100644
--- a/audio/softsynth/fmtowns_pc98/pc98_audio.h
+++ b/audio/softsynth/fmtowns_pc98/pc98_audio.h
@@ -45,7 +45,7 @@ public:
 
 class PC98AudioCore {
 public:
-	PC98AudioCore(Audio::Mixer *mixer, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type, bool externalMutexHandling = false);
+	PC98AudioCore(Audio::Mixer *mixer, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type);
 	~PC98AudioCore();
 
 	bool init();
diff --git a/audio/softsynth/fmtowns_pc98/towns_audio.cpp b/audio/softsynth/fmtowns_pc98/towns_audio.cpp
index 1a12fa9..1464dd7 100644
--- a/audio/softsynth/fmtowns_pc98/towns_audio.cpp
+++ b/audio/softsynth/fmtowns_pc98/towns_audio.cpp
@@ -135,11 +135,11 @@ private:
 
 class TownsAudioInterfaceInternal : public TownsPC98_FmSynth {
 private:
-	TownsAudioInterfaceInternal(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver, bool externalMutexHandling = false);
+	TownsAudioInterfaceInternal(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver);
 public:
 	~TownsAudioInterfaceInternal();
 
-	static TownsAudioInterfaceInternal *addNewRef(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver, bool externalMutexHandling = false);
+	static TownsAudioInterfaceInternal *addNewRef(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver);
 	static void releaseRef(TownsAudioInterface *owner);
 
 	bool init();
@@ -154,7 +154,7 @@ public:
 	void setSoundEffectChanMask(int mask);
 
 private:
-	bool assignPluginDriver(TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver, bool externalMutexHandling = false);
+	bool assignPluginDriver(TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver);
 	void removePluginDriver(TownsAudioInterface *owner);
 
 	void nextTickEx(int32 *buffer, uint32 bufferSize);
@@ -275,8 +275,8 @@ private:
 	static const uint8 _fmDefaultInstrument[];
 };
 
-TownsAudioInterfaceInternal::TownsAudioInterfaceInternal(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver, bool externalMutexHandling) :
-	TownsPC98_FmSynth(mixer, kTypeTowns, externalMutexHandling),
+TownsAudioInterfaceInternal::TownsAudioInterfaceInternal(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver) :
+	TownsPC98_FmSynth(mixer, kTypeTowns),
 	_fmInstruments(0), _pcmInstruments(0), _pcmChan(0), _waveTables(0), _waveTablesTotalDataSize(0),
 	_baserate(55125.0f / (float)mixer->getOutputRate()), _tickLength(0), _timer(0), _drv(driver), _drvOwner(owner),
 	_pcmSfxChanMask(0),	_musicVolume(Audio::Mixer::kMaxMixerVolume), _sfxVolume(Audio::Mixer::kMaxMixerVolume),
@@ -404,11 +404,10 @@ TownsAudioInterfaceInternal::TownsAudioInterfaceInternal(Audio::Mixer *mixer, To
 }
 
 TownsAudioInterfaceInternal::~TownsAudioInterfaceInternal() {
+	Common::StackLock lock(_mutex);
 	_ready = false;
 	deinit();
 
-	Common::StackLock lock(_mutex);
-
 	delete[] _fmSaveReg[0];
 	delete[] _fmSaveReg[1];
 	delete[] _fmInstruments;
@@ -417,13 +416,13 @@ TownsAudioInterfaceInternal::~TownsAudioInterfaceInternal() {
 	delete[] _pcmChan;
 }
 
-TownsAudioInterfaceInternal *TownsAudioInterfaceInternal::addNewRef(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver, bool externalMutexHandling) {
+TownsAudioInterfaceInternal *TownsAudioInterfaceInternal::addNewRef(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver) {
 	_refCount++;
 	if (_refCount == 1 && _refInstance == 0)
-		_refInstance = new TownsAudioInterfaceInternal(mixer, owner, driver, externalMutexHandling);
+		_refInstance = new TownsAudioInterfaceInternal(mixer, owner, driver);
 	else if (_refCount < 2 || _refInstance == 0)
 		error("TownsAudioInterfaceInternal::addNewRef(): Internal reference management failure");
-	else if (!_refInstance->assignPluginDriver(owner, driver, externalMutexHandling))
+	else if (!_refInstance->assignPluginDriver(owner, driver))
 		error("TownsAudioInterfaceInternal::addNewRef(): Plugin driver conflict");
 
 	return _refInstance;
@@ -448,9 +447,6 @@ bool TownsAudioInterfaceInternal::init() {
 	if (_ready)
 		return true;
 
-	if (!TownsPC98_FmSynth::init())
-		return false;
-
 	_fmSaveReg[0] = new uint8[256];
 	_fmSaveReg[1] = new uint8[256];
 	_fmInstruments = new uint8[128 * 48];
@@ -460,6 +456,10 @@ bool TownsAudioInterfaceInternal::init() {
 
 	_timer = 0;
 
+	if (!TownsPC98_FmSynth::init())
+		return false;
+
+
 	setVolumeChannelMasks(-1, 0);
 
 	_ready = true;
@@ -469,6 +469,7 @@ bool TownsAudioInterfaceInternal::init() {
 }
 
 int TownsAudioInterfaceInternal::callback(int command, ...) {
+	Common::StackLock lock(_mutex);
 	if (!_ready)
 		return 1;
 
@@ -482,35 +483,39 @@ int TownsAudioInterfaceInternal::callback(int command, ...) {
 }
 
 int TownsAudioInterfaceInternal::processCommand(int command, va_list &args) {
+	Common::StackLock lock(_mutex);
 	if (!_ready)
 		return 1;
 
 	if (command < 0 || command > 81)
 		return 4;
-
-	Common::StackLock lock(_mutex);
+	
 	int res = (this->*_intfOpcodes[command])(args);
 
 	return res;
 }
 
 void TownsAudioInterfaceInternal::setMusicVolume(int volume) {
+	Common::StackLock lock(_mutex);
 	_musicVolume = CLIP<uint16>(volume, 0, Audio::Mixer::kMaxMixerVolume);
 	setVolumeIntern(_musicVolume, _sfxVolume);
 }
 
 void TownsAudioInterfaceInternal::setSoundEffectVolume(int volume) {
+	Common::StackLock lock(_mutex);
 	_sfxVolume = CLIP<uint16>(volume, 0, Audio::Mixer::kMaxMixerVolume);
 	setVolumeIntern(_musicVolume, _sfxVolume);
 }
 
 void TownsAudioInterfaceInternal::setSoundEffectChanMask(int mask) {
+	Common::StackLock lock(_mutex);
 	_pcmSfxChanMask = mask >> 6;
 	mask &= 0x3f;
 	setVolumeChannelMasks(~mask, mask);
 }
 
-bool TownsAudioInterfaceInternal::assignPluginDriver(TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver, bool externalMutexHandling) {
+bool TownsAudioInterfaceInternal::assignPluginDriver(TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver) {
+	Common::StackLock lock(_mutex);
 	if (_refCount <= 1)
 		return true;
 
@@ -518,23 +523,21 @@ bool TownsAudioInterfaceInternal::assignPluginDriver(TownsAudioInterface *owner,
 		if (driver && driver != _drv)
 			return false;
 	} else {
-		Common::StackLock lock(_mutex);
 		_drv = driver;
 		_drvOwner = owner;
-		_externalMutex = externalMutexHandling;
 	}
 
 	return true;
 }
 
 void TownsAudioInterfaceInternal::removePluginDriver(TownsAudioInterface *owner) {
-	if (_drvOwner == owner) {
-		Common::StackLock lock(_mutex);
+	Common::StackLock lock(_mutex);
+	if (_drvOwner == owner)
 		_drv = 0;
-	}
 }
 
 void TownsAudioInterfaceInternal::nextTickEx(int32 *buffer, uint32 bufferSize) {
+	Common::StackLock lock(_mutex);
 	if (!_ready)
 		return;
 
@@ -579,11 +582,13 @@ void TownsAudioInterfaceInternal::nextTickEx(int32 *buffer, uint32 bufferSize) {
 }
 
 void TownsAudioInterfaceInternal::timerCallbackA() {
+	Common::StackLock lock(_mutex);
 	if (_drv && _ready)
 		_drv->timerCallback(0);
 }
 
 void TownsAudioInterfaceInternal::timerCallbackB() {
+	Common::StackLock lock(_mutex);
 	if (_ready) {
 		if (_drv)
 			_drv->timerCallback(1);
@@ -1936,8 +1941,8 @@ void TownsAudio_WaveTable::clear() {
 	data = 0;
 }
 
-TownsAudioInterface::TownsAudioInterface(Audio::Mixer *mixer, TownsAudioInterfacePluginDriver *driver, bool externalMutexHandling) {
-	_intf = TownsAudioInterfaceInternal::addNewRef(mixer, this, driver, externalMutexHandling);
+TownsAudioInterface::TownsAudioInterface(Audio::Mixer *mixer, TownsAudioInterfacePluginDriver *driver) {
+	_intf = TownsAudioInterfaceInternal::addNewRef(mixer, this, driver);
 }
 
 TownsAudioInterface::~TownsAudioInterface() {
diff --git a/audio/softsynth/fmtowns_pc98/towns_audio.h b/audio/softsynth/fmtowns_pc98/towns_audio.h
index ba13bd1..815875c 100644
--- a/audio/softsynth/fmtowns_pc98/towns_audio.h
+++ b/audio/softsynth/fmtowns_pc98/towns_audio.h
@@ -37,7 +37,7 @@ public:
 
 class TownsAudioInterface {
 public:
-	TownsAudioInterface(Audio::Mixer *mixer, TownsAudioInterfacePluginDriver *driver, bool externalMutexHandling = false);
+	TownsAudioInterface(Audio::Mixer *mixer, TownsAudioInterfacePluginDriver *driver);
 	~TownsAudioInterface();
 
 	enum ErrorCode {
diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp
index 03b4502..178d33e 100644
--- a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp
+++ b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.cpp
@@ -27,9 +27,8 @@
 
 class TownsPC98_FmSynthOperator {
 public:
-	TownsPC98_FmSynthOperator(const uint32 timerbase, const uint32 rtt, const uint8 *rateTable,
-	                          const uint8 *shiftTable, const uint8 *attackDecayTable, const uint32 *frqTable,
-	                          const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable);
+	TownsPC98_FmSynthOperator(const uint32 timerbase, const uint32 rtt, const uint8 *rateTable,	const uint8 *shiftTable,
+		const uint8 *attackDecayTable, const uint32 *frqTable, const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable);
 	~TownsPC98_FmSynthOperator() {}
 
 	void keyOn();
@@ -101,17 +100,15 @@ protected:
 	} fs_a, fs_d, fs_s, fs_r;
 };
 
-TownsPC98_FmSynthOperator::TownsPC98_FmSynthOperator(const uint32 timerbase, const uint32 rtt,
-		const uint8 *rateTable, const uint8 *shiftTable, const uint8 *attackDecayTable,
-		const uint32 *frqTable, const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable) :
-	_rtt(rtt), _rateTbl(rateTable), _rshiftTbl(shiftTable), _adTbl(attackDecayTable), _fTbl(frqTable),
-	_sinTbl(sineTable), _tLvlTbl(tlevelOut), _detnTbl(detuneTable), _tickLength(timerbase * 2),
-	_specifiedAttackRate(0), _specifiedDecayRate(0), _specifiedReleaseRate(0), _envelopeShapeSpecs(0), _specifiedSustainRate(0),
-	_sustainLevel(0), _phase(0), _shapeState(0), _shapeScale(0), _state(kEnvReady), _holdKey(false), _timer(0), _keyScale1(0),
-	_keyScale2(0), _freqTemp(0), _currentLevel(1023), _ampMod(false), _tickCount(0), _phaseIncrement(0) {
+TownsPC98_FmSynthOperator::TownsPC98_FmSynthOperator(const uint32 timerbase, const uint32 rtt, const uint8 *rateTable, const uint8 *shiftTable,
+	const uint8 *attackDecayTable, const uint32 *frqTable, const uint32 *sineTable, const int32 *tlevelOut, const int32 *detuneTable) :
+	_rtt(rtt), _rateTbl(rateTable), _rshiftTbl(shiftTable), _adTbl(attackDecayTable), _fTbl(frqTable), _sinTbl(sineTable),
+	_tLvlTbl(tlevelOut), _detnTbl(detuneTable), _tickLength(timerbase * 2), _specifiedAttackRate(0), _specifiedDecayRate(0),
+	_specifiedReleaseRate(0), _envelopeShapeSpecs(0), _specifiedSustainRate(0), _sustainLevel(0), _phase(0), _shapeState(0),
+	_shapeScale(0), _state(kEnvReady), _holdKey(false), _timer(0), _keyScale1(0), _keyScale2(0), _freqTemp(0),
+	_currentLevel(1023), _ampMod(false), _tickCount(0), _phaseIncrement(0) {
 
 	fs_a.rate = fs_a.shift = fs_d.rate = fs_d.shift = fs_s.rate = fs_s.shift = fs_r.rate = fs_r.shift = 0;
-
 	reset();
 }
 
@@ -199,6 +196,7 @@ void TownsPC98_FmSynthOperator::generateOutput(int32 phasebuf, int32 *feed, int3
 			switch (_state) {
 			case kEnvReady:
 				return;
+
 			case kEnvAttacking:
 				targetLevel = 0;
 				nextState = _sustainLevel ? kEnvDecaying : kEnvSustaining;
@@ -211,18 +209,21 @@ void TownsPC98_FmSynthOperator::generateOutput(int32 phasebuf, int32 *feed, int3
 					continue;
 				}
 				break;
+
 			case kEnvDecaying:
 				targetTime = (1 << fs_d.shift) - 1;
 				nextState = kEnvSustaining;
 				targetLevel = _sustainLevel;
 				levelIncrement = _adTbl[fs_d.rate + ((_tickCount >> fs_d.shift) & 7)] << _shapeScale;
 				break;
+
 			case kEnvSustaining:
 				targetTime = (1 << fs_s.shift) - 1;
 				nextState = kEnvSustaining;
 				targetLevel = _shapeScale ? 832 : 1023;
 				levelIncrement = _adTbl[fs_s.rate + ((_tickCount >> fs_s.shift) & 7)] << _shapeScale;
 				break;
+
 			case kEnvReleasing:
 				targetTime = (1 << fs_r.shift) - 1;
 				nextState = kEnvReady;
@@ -238,8 +239,10 @@ void TownsPC98_FmSynthOperator::generateOutput(int32 phasebuf, int32 *feed, int3
 			if ((_state == kEnvAttacking && _currentLevel <= targetLevel) || (_state != kEnvAttacking && _currentLevel >= targetLevel)) {
 				if (_state != kEnvDecaying)
 					_currentLevel = targetLevel;
+
 				if (_state == kEnvSustaining && _shapeScale) {
 					_currentLevel += 191;
+
 					if (_shapeState & 1) {
 						if (!(_shapeState & 0x10))
 							_shapeState |= 0x40;
@@ -934,7 +937,7 @@ void TownsPC98_FmSynthPercussionSource::advanceInput(RhtChannel *ins) {
 }
 #endif // DISABLE_PC98_RHYTHM_CHANNEL
 
-TownsPC98_FmSynth::TownsPC98_FmSynth(Audio::Mixer *mixer, EmuType type, bool externalMutexHandling) :
+TownsPC98_FmSynth::TownsPC98_FmSynth(Audio::Mixer *mixer, EmuType type) :
 	_mixer(mixer),
 	_chanInternal(0), _ssg(0),
 #ifndef DISABLE_PC98_RHYTHM_CHANNEL
@@ -944,7 +947,7 @@ TownsPC98_FmSynth::TownsPC98_FmSynth(Audio::Mixer *mixer, EmuType type, bool ext
 	_hasPercussion(type == kType86 ? true : false),
 	_oprRates(0), _oprRateshift(0), _oprAttackDecay(0), _oprFrq(0), _oprSinTbl(0), _oprLevelOut(0), _oprDetune(0),
 	 _rtt(type == kTypeTowns ? 0x514767 : 0x5B8D80), _baserate(55125.0f / (float)mixer->getOutputRate()),
-	_volMaskA(0), _volMaskB(0), _volumeA(255), _volumeB(255), _externalMutex(externalMutexHandling), _ready(false) {
+	_volMaskA(0), _volMaskB(0), _volumeA(255), _volumeB(255), _ready(false) {
 
 	memset(&_timers[0], 0, sizeof(ChipTimer));
 	memset(&_timers[1], 0, sizeof(ChipTimer));
@@ -957,11 +960,10 @@ TownsPC98_FmSynth::TownsPC98_FmSynth(Audio::Mixer *mixer, EmuType type, bool ext
 }
 
 TownsPC98_FmSynth::~TownsPC98_FmSynth() {
+	Common::StackLock lock(_mutex);
 	if (_ready)
 		deinit();
 
-	Common::StackLock lock(_mutex);
-
 	delete _ssg;
 #ifndef DISABLE_PC98_RHYTHM_CHANNEL
 	delete _prc;
@@ -1006,8 +1008,7 @@ bool TownsPC98_FmSynth::init() {
 	_timers[0].cb = &TownsPC98_FmSynth::timerCallbackA;
 	_timers[1].cb = &TownsPC98_FmSynth::timerCallbackB;
 
-	_mixer->playStream(Audio::Mixer::kPlainSoundType,
-	                   &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 
 	_ready = true;
 
@@ -1015,10 +1016,10 @@ bool TownsPC98_FmSynth::init() {
 }
 
 void TownsPC98_FmSynth::reset() {
+	Common::StackLock lock(_mutex);
 	if (!_ready)
 		return;
 
-	Common::StackLock lock(_mutex);
 	for (int i = 0; i < _numChan; i++) {
 		for (int ii = 0; ii < 4; ii++)
 			_chanInternal[i].opr[ii]->reset();
@@ -1043,6 +1044,7 @@ void TownsPC98_FmSynth::reset() {
 }
 
 void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) {
+	Common::StackLock lock(_mutex);
 	if (!_ready)
 		return;
 
@@ -1051,8 +1053,6 @@ void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) {
 		return;
 	}
 
-	Common::StackLock lock(_mutex);
-
 	static const uint8 oprOrdr[] = { 0, 2, 1, 3 };
 
 	_registers[regAddress][part] = value;
@@ -1253,6 +1253,7 @@ void TownsPC98_FmSynth::writeReg(uint8 part, uint8 regAddress, uint8 value) {
 }
 
 uint8 TownsPC98_FmSynth::readReg(uint8 part, uint8 regAddress) {
+	Common::StackLock lock(_mutex);
 	if (!_ready || part > 1)
 		return 0;
 
@@ -1270,18 +1271,13 @@ uint8 TownsPC98_FmSynth::readReg(uint8 part, uint8 regAddress) {
 }
 
 int TownsPC98_FmSynth::readBuffer(int16 *buffer, const int numSamples) {
+	Common::StackLock lock(_mutex);
 	memset(buffer, 0, sizeof(int16) * numSamples);
 	int32 *tmp = new int32[numSamples];
 	int32 *tmpStart = tmp;
 	memset(tmp, 0, sizeof(int32) * numSamples);
 	int32 samplesLeft = numSamples >> 1;
 
-	bool locked = false;
-	if (_ready) {
-		_mutex.lock();
-		locked = true;
-	}
-
 	while (_ready && samplesLeft) {
 		int32 render = samplesLeft;
 
@@ -1289,18 +1285,8 @@ int TownsPC98_FmSynth::readBuffer(int16 *buffer, const int numSamples) {
 			if (_timers[i].enabled && _timers[i].cb) {
 				if (!_timers[i].smpTillCb) {
 
-					if (locked && _externalMutex) {
-						_mutex.unlock();
-						locked = false;
-					}
-
 					(this->*_timers[i].cb)();
 
-					if (!locked && _externalMutex) {
-						_mutex.lock();
-						locked = true;
-					}
-
 					_timers[i].smpTillCb = _timers[i].smpPerCb;
 
 					_timers[i].smpTillCbRem += _timers[i].smpPerCbRem;
@@ -1343,9 +1329,6 @@ int TownsPC98_FmSynth::readBuffer(int16 *buffer, const int numSamples) {
 		tmp += (render << 1);
 	}
 
-	if (locked)
-		_mutex.unlock();
-
 	delete[] tmpStart;
 
 	return numSamples;
@@ -1364,9 +1347,9 @@ int TownsPC98_FmSynth::getRate() const {
 }
 
 void TownsPC98_FmSynth::deinit() {
+	Common::StackLock lock(_mutex);
 	_ready = false;
 	_mixer->stopHandle(_soundHandle);
-	Common::StackLock lock(_mutex);
 	_timers[0].cb = _timers[1].cb = &TownsPC98_FmSynth::idleTimerCallback;
 }
 
@@ -1395,6 +1378,7 @@ void TownsPC98_FmSynth::setVolumeChannelMasks(int channelMaskA, int channelMaskB
 }
 
 void TownsPC98_FmSynth::setLevelSSG(int vol) {
+	Common::StackLock lock(_mutex);
 	if (_ssg)
 		_ssg->setOutputLevel(vol);
 }
diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h
index 0c4a778..730dbec 100644
--- a/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h
+++ b/audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h
@@ -59,7 +59,7 @@ public:
 		kType86
 	};
 
-	TownsPC98_FmSynth(Audio::Mixer *mixer, EmuType type, bool externalMutexHandling = false);
+	TownsPC98_FmSynth(Audio::Mixer *mixer, EmuType type);
 	virtual ~TownsPC98_FmSynth();
 
 	virtual bool init();
@@ -99,7 +99,6 @@ protected:
 	const bool _hasPercussion;
 
 	Common::Mutex _mutex;
-	bool _externalMutex;
 
 private:
 	void generateTables();
diff --git a/engines/kyra/sound/sound_towns_darkmoon.cpp b/engines/kyra/sound/sound_towns_darkmoon.cpp
index 55bd800..17ced42 100644
--- a/engines/kyra/sound/sound_towns_darkmoon.cpp
+++ b/engines/kyra/sound/sound_towns_darkmoon.cpp
@@ -35,7 +35,7 @@
 namespace Kyra {
 
 SoundTowns_Darkmoon::SoundTowns_Darkmoon(KyraEngine_v1 *vm, Audio::Mixer *mixer) : Sound(vm, mixer) {
-	_intf = new TownsAudioInterface(mixer, this, false);
+	_intf = new TownsAudioInterface(mixer, this);
 	_pcmData = 0;
 	_pcmVol = 0;
 	_timer = 0;
diff --git a/engines/sci/sound/drivers/fmtowns.cpp b/engines/sci/sound/drivers/fmtowns.cpp
index 4dd74bc..7475b01 100644
--- a/engines/sci/sound/drivers/fmtowns.cpp
+++ b/engines/sci/sound/drivers/fmtowns.cpp
@@ -405,7 +405,7 @@ int TownsMidiPart::allocateChannel() {
 }
 
 MidiDriver_FMTowns::MidiDriver_FMTowns(Audio::Mixer *mixer, SciVersion version) : _version(version), _timerProc(0), _timerProcPara(0), _baseTempo(10080), _ready(false), _isOpen(false), _masterVolume(0x0f), _soundOn(true) {
-	_intf = new TownsAudioInterface(mixer, this, true);
+	_intf = new TownsAudioInterface(mixer, this);
 	_out = new TownsChannel*[6];
 	for (int i = 0; i < 6; i++)
 		_out[i] = new TownsChannel(this, i);
diff --git a/engines/sci/sound/drivers/pc9801.cpp b/engines/sci/sound/drivers/pc9801.cpp
index fe7d5bf..a4b634f 100644
--- a/engines/sci/sound/drivers/pc9801.cpp
+++ b/engines/sci/sound/drivers/pc9801.cpp
@@ -1322,9 +1322,9 @@ MidiDriver_PC9801::MidiDriver_PC9801(Audio::Mixer *mixer, SciVersion version)
 	_polyphony(9), _channelMask1(0x10), _channelMask2(0x02) {
 	_pc98a =
 #ifdef SCI_PC98_AUDIO_EXTENDED
-		new PC98AudioCore(mixer, this, kType86, true);
+		new PC98AudioCore(mixer, this, kType86);
 #else
-		new PC98AudioCore(mixer, this, kType26, true);
+		new PC98AudioCore(mixer, this, kType26);
 #endif
 }
 
diff --git a/engines/scumm/imuse/drivers/fmtowns.cpp b/engines/scumm/imuse/drivers/fmtowns.cpp
index 37765e0..cf15fcd 100644
--- a/engines/scumm/imuse/drivers/fmtowns.cpp
+++ b/engines/scumm/imuse/drivers/fmtowns.cpp
@@ -827,8 +827,7 @@ const uint8 TownsMidiInputChannel::_programAdjustLevel[] = {
 
 MidiDriver_TOWNS::MidiDriver_TOWNS(Audio::Mixer *mixer) : _timerProc(0), _timerProcPara(0), _channels(0), _out(0),
 	_baseTempo(10080), _chanState(0), _operatorLevelTable(0), _tickCounter(0), _rand(1), _allocCurPos(0), _isOpen(false) {
-	// We set exteral mutex handling to true to avoid lockups in SCUMM which has its own mutex.
-	_intf = new TownsAudioInterface(mixer, this, true);
+	_intf = new TownsAudioInterface(mixer, this);
 
 	_channels = new TownsMidiInputChannel*[32];
 	for (int i = 0; i < 32; i++)


Commit: 47a2f62c151c164b3e53392335b00af14a69f7a9
    https://github.com/scummvm/scummvm/commit/47a2f62c151c164b3e53392335b00af14a69f7a9
Author: athrxx (athrxx at scummvm.org)
Date: 2019-08-07T16:43:06+02:00

Commit Message:
SCI: (FB01 sound driver) - several fixes

Fix SCI0 (LATE/EARLY) variants of the driver which were broken (didn't play at all). This might be of my own doing, since I introduced the initTrack() method into the driver class and the fb01 driver didn't have one so far.

SCI1 didn't seem to require much fixing. I modified some things according to my findings in the driver disasms. QFG2 and JONES seem to be fine. I am not too happy with KQ5. It has volume issues, but they might be present in the original, too.

I also added an isOpen() check and a mutex to avoid threading issues. When aborting SCI (either quitting ScummVM or returning ot the launcher) while using the fb01 driver I frequently (more often than not) got the assert from backends/midi/windows.cpp, line 95. This fixes that.

I've done plenty of checks and experiments with the sound bank initialization. But I found no bugs or possible improvements there. Hard to tell whether the sound is right. That device seems to have a mind of its own...

Changed paths:
    engines/sci/POTFILES
    engines/sci/sound/drivers/fb01.cpp


diff --git a/engines/sci/POTFILES b/engines/sci/POTFILES
index 015776c..24576e8 100644
--- a/engines/sci/POTFILES
+++ b/engines/sci/POTFILES
@@ -1,4 +1,5 @@
 engines/sci/detection.cpp
+engines/sci/sound/drivers/fb01.cpp
 engines/sci/engine/guest_additions.cpp
 engines/sci/engine/kfile.cpp
 engines/sci/engine/kgraphics.cpp
diff --git a/engines/sci/sound/drivers/fb01.cpp b/engines/sci/sound/drivers/fb01.cpp
index 3f3f581..6f104b2 100644
--- a/engines/sci/sound/drivers/fb01.cpp
+++ b/engines/sci/sound/drivers/fb01.cpp
@@ -29,6 +29,14 @@
 #include "common/file.h"
 #include "common/system.h"
 #include "common/textconsole.h"
+#include "common/mutex.h"
+
+// The original driver uses the master volume setting of the hardware device by sending sysex messages
+// (applies to SCI0 and SCI1). We have a software mastervolume implementation instead. I don't know the
+// reason for this. Nor do I know whether our software effective volume calculation matches the device's
+// internal volume calculation algorithm.
+// I add the original style master volume handling, but make it optional via a #define
+#define		HARDWARE_MASTERVOLUME
 
 namespace Sci {
 
@@ -55,15 +63,22 @@ public:
 
 	int open(ResourceManager *resMan);
 	void close();
+	void initTrack(SciSpan<const byte>& header);
 	void send(uint32 b);
 	void sysEx(const byte *msg, uint16 length);
 	bool hasRhythmChannel() const { return false; }
 	byte getPlayId() const;
-	int getPolyphony() const { return kVoices; } // 9 in SCI1?
+	int getPolyphony() const { return _version <= SCI_VERSION_0_LATE ? 8 : 9; }
 	void setVolume(byte volume);
 	int getVolume();
 	void playSwitch(bool play);
 
+	bool isOpen() const { return _isOpen; }
+	void lockMutex() { _mutex.lock(); }
+	void unlockMutex() { _mutex.unlock(); }
+
+	const char *reportMissingFiles() { return _missingFiles; }
+
 private:
 	void noteOn(int channel, int note, int velocity);
 	void noteOff(int channel, int note);
@@ -102,18 +117,23 @@ private:
 
 	struct Voice {
 		int8 channel;			// MIDI channel that this voice is assigned to or -1
+		uint8 poly;				// Number of hardware voices (SCI0); for SCI1 we just set this to 1
 		int8 note;				// Currently playing MIDI note or -1
 		int bank;				// Current bank setting or -1
 		int patch;				// Currently playing patch or -1
-		uint8 velocity;			// Note velocity
-		bool isSustained;		// Flag indicating a note that is being sustained by the hold pedal
+		//uint8 velocity;		// Note velocity
+		//bool isSustained;		// Flag indicating a note that is being sustained by the hold pedal
 		uint16 age;				// Age of the current note
 
-		Voice() : channel(-1), note(-1), bank(-1), patch(-1), velocity(0), isSustained(false), age(0) { }
+		Voice() : channel(-1), note(-1), bank(-1), patch(-1), /*velocity(0), isSustained(false),*/ age(0), poly(1) { }
 	};
 
 	bool _playSwitch;
 	int _masterVolume;
+	int _numParts;
+
+	bool _isOpen;
+	Common::Mutex _mutex;
 
 	Channel _channels[16];
 	Voice _voices[kVoices];
@@ -123,10 +143,14 @@ private:
 	static void midiTimerCallback(void *p);
 	void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc);
 
+	const char *_missingFiles;
+	static const char _requiredFiles[2][12];
+
 	byte _sysExBuf[kMaxSysExSize];
 };
 
-MidiPlayer_Fb01::MidiPlayer_Fb01(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _timerParam(NULL), _timerProc(NULL) {
+MidiPlayer_Fb01::MidiPlayer_Fb01(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _timerParam(NULL), _timerProc(NULL),
+	_numParts(version > SCI_VERSION_0_LATE ? kVoices : 0), _isOpen(false), _missingFiles(0) {
 	MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI);
 	_driver = MidiDriver::createMidi(dev);
 
@@ -135,10 +159,23 @@ MidiPlayer_Fb01::MidiPlayer_Fb01(SciVersion version) : MidiPlayer(version), _pla
 }
 
 MidiPlayer_Fb01::~MidiPlayer_Fb01() {
+	Common::StackLock lock(_mutex);
+	close();
 	delete _driver;
 }
 
 void MidiPlayer_Fb01::voiceMapping(int channel, int voices) {
+	if (_version <= SCI_VERSION_0_LATE) {
+		// The original SCI0 drivers don't do any software voice mapping. Only inside the device...
+		for (int i = 0; i < _numParts; ++i) {
+			if (_voices[i].channel == channel && _voices[i].poly != voices) {
+				_voices[i].poly = voices;
+				setVoiceParam(i, 0, voices);
+			}
+		}
+		return;
+	}
+
 	int curVoices = 0;
 
 	for (int i = 0; i < kVoices; i++)
@@ -163,6 +200,8 @@ void MidiPlayer_Fb01::assignVoices(int channel, int voices) {
 	for (int i = 0; i < kVoices; i++) {
 		if (_voices[i].channel == -1) {
 			_voices[i].channel = channel;
+			if (_voices[i].note != -1)
+				voiceOff(i);
 			if (--voices == 0)
 				break;
 		}
@@ -264,16 +303,23 @@ int MidiPlayer_Fb01::findVoice(int channel) {
 }
 
 void MidiPlayer_Fb01::sendToChannel(byte channel, byte command, byte op1, byte op2) {
-	for (int i = 0; i < kVoices; i++) {
+	for (int i = 0; i < _numParts; i++) {
 		// Send command to all voices assigned to this channel
+		// I case of SCI0 the voice mapping is done inside the device.
 		if (_voices[i].channel == channel)
-			_driver->send(command | i, op1, op2);
+			_driver->send(command | (_version <= SCI_VERSION_0_LATE ? channel : i), op1, op2);
 	}
 }
 
 void MidiPlayer_Fb01::setPatch(int channel, int patch) {
 	int bank = 0;
 
+	if (_version <= SCI_VERSION_0_LATE && channel == 15) {
+		// The original driver has some parsing related handling for program 127.
+		// We can't handle that here.
+		return;
+	}
+
 	_channels[channel].patch = patch;
 
 	if (patch >= 48) {
@@ -281,13 +327,13 @@ void MidiPlayer_Fb01::setPatch(int channel, int patch) {
 		bank = 1;
 	}
 
-	for (int voice = 0; voice < kVoices; voice++) {
+	for (int voice = 0; voice < _numParts; voice++) {
 		if (_voices[voice].channel == channel) {
 			if (_voices[voice].bank != bank) {
 				_voices[voice].bank = bank;
 				setVoiceParam(voice, 4, bank);
 			}
-			_driver->send(0xc0 | voice, patch, 0);
+			_driver->send(0xc0 | (_version <= SCI_VERSION_0_LATE ? channel : voice), patch, 0);
 		}
 	}
 }
@@ -320,12 +366,20 @@ void MidiPlayer_Fb01::noteOn(int channel, int note, int velocity) {
 		return noteOff(channel, note);
 
 	if (_version > SCI_VERSION_0_LATE)
-		velocity = volumeTable[velocity >> 1] << 1;
+		velocity >>= 1;
 
 	int voice;
 	for (voice = 0; voice < kVoices; voice++) {
 		if ((_voices[voice].channel == channel) && (_voices[voice].note == note)) {
 			voiceOff(voice);
+
+			// Original bug #1:	The table is only applied here, but not to the voiceOn() call below.
+			// Original bug #2:	The velocity value also gets another right shift although the table
+			//					size of the volume table is 64 and not 32.
+			// --> disable this ? It certainly can't do any good.
+			if (_version > SCI_VERSION_0_LATE)
+				velocity = volumeTable[velocity >> 1] << 1;
+
 			voiceOn(voice, note, velocity);
 			return;
 		}
@@ -342,6 +396,12 @@ void MidiPlayer_Fb01::noteOn(int channel, int note, int velocity) {
 }
 
 void MidiPlayer_Fb01::controlChange(int channel, int control, int value) {
+	// Events for the control channel shouldn't arrive here.
+	// The original driver handles some specific parsing related midi events
+	// sent on channel 15, but we (hopefully) do that in the SCI music engine.
+	if (_version <= SCI_VERSION_0_LATE && channel == 15)
+		return;
+
 	switch (control) {
 	case 0x07: {
 		_channels[channel].volume = value;
@@ -349,12 +409,16 @@ void MidiPlayer_Fb01::controlChange(int channel, int control, int value) {
 		if (_version > SCI_VERSION_0_LATE)
 			value = volumeTable[value >> 1] << 1;
 
+#ifdef HARDWARE_MASTERVOLUME
+		sendToChannel(channel, 0xb0, control, value);
+#else
 		byte vol = _masterVolume;
 
 		if (vol > 0)
 			vol = CLIP<byte>(vol + 3, 0, 15);
 
 		sendToChannel(channel, 0xb0, control, (value * vol / 15) & 0x7f);
+#endif
 		break;
 	}
 	case 0x0a:
@@ -366,16 +430,19 @@ void MidiPlayer_Fb01::controlChange(int channel, int control, int value) {
 		sendToChannel(channel, 0xb0, control, value);
 		break;
 	case 0x4b:
-		// In early SCI0, voice count 15 signifies that the channel should be ignored
-		// for this song. Assuming that there are no embedded voice count commands in
-		// the MIDI stream, we should be able to get away with simply setting the voice
-		// count for this channel to 0.
-		voiceMapping(channel, (value != 15 ? value : 0));
+		voiceMapping(channel, value);
 		break;
 	case 0x7b:
-		for (int i = 0; i < kVoices; i++)
-			if ((_voices[i].channel == channel) && (_voices[i].note != -1))
-				voiceOff(i);
+		for (int i = 0; i < _numParts; i++) {
+			if ((_voices[i].channel == channel) && (_voices[i].note != -1)) {
+				_voices[i].note = -1;
+				sendToChannel(channel, 0xb0, control, value);
+			}
+		}
+		break;
+	default:
+		sendToChannel(channel, 0xb0, control, value);
+		break;
 	}
 }
 
@@ -385,6 +452,16 @@ void MidiPlayer_Fb01::send(uint32 b) {
 	byte op1 = (b >> 8) & 0x7f;
 	byte op2 = (b >> 16) & 0x7f;
 
+	if (_version <= SCI_VERSION_0_LATE && command != 0xB0 && command != 0xC0) {
+		// Since the voice mapping takes place inside the hardware, most messages
+		// are simply passed through. Channel 15 is never assigned to a part but is
+		// used for certain parsing related events which we cannot handle here.
+		// Just making sure that no nonsense is sent to the device...
+		if (channel != 15)
+			sendToChannel(channel, command, op1, op2);
+		return;
+	}
+
 	switch (command) {
 	case 0x80:
 		noteOff(channel, op1);
@@ -409,9 +486,12 @@ void MidiPlayer_Fb01::send(uint32 b) {
 
 void MidiPlayer_Fb01::setVolume(byte volume) {
 	_masterVolume = volume;
-
+#ifdef HARDWARE_MASTERVOLUME
+	setSystemParam(0, 0x24, (CLIP<byte>(_masterVolume + 3, 0, 15) << 3) + 7);
+#else
 	for (uint i = 0; i < MIDI_CHANNELS; i++)
 		controlChange(i, 0x07, _channels[i].volume & 0x7f);
+#endif
 }
 
 int MidiPlayer_Fb01::getVolume() {
@@ -419,11 +499,20 @@ int MidiPlayer_Fb01::getVolume() {
 }
 
 void MidiPlayer_Fb01::playSwitch(bool play) {
+	_playSwitch = play;
 }
 
 void MidiPlayer_Fb01::midiTimerCallback(void *p) {
+	if (!p)
+		return;
+
 	MidiPlayer_Fb01 *m = (MidiPlayer_Fb01 *)p;
 
+	m->lockMutex();
+
+	if (!m->isOpen())
+		return;
+
 	// Increase the age of the notes
 	for (int i = 0; i < kVoices; i++) {
 		if (m->_voices[i].note != -1)
@@ -432,6 +521,8 @@ void MidiPlayer_Fb01::midiTimerCallback(void *p) {
 
 	if (m->_timerProc)
 		m->_timerProc(m->_timerParam);
+
+	m->unlockMutex();
 }
 
 void MidiPlayer_Fb01::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
@@ -456,11 +547,15 @@ void MidiPlayer_Fb01::sendBanks(const SciSpan<const byte> &data) {
 	}
 
 	// Send second bank if available
-	if (data.size() >= 6146 && data.getUint16BEAt(3072) == 0xabcd) {
-		for (int i = 0; i < 48; i++) {
-			sendVoiceData(0, data.subspan(3074 + i * 64));
-			storeVoiceData(0, 1, i);
-		}
+	if (data.size() < 6146)
+		return;
+
+	if (data.getUint16BEAt(3072) != 0xabcd)
+		return;
+
+	for (int i = 0; i < 48; i++) {
+		sendVoiceData(0, data.subspan(3074 + i * 64));
+		storeVoiceData(0, 1, i);
 	}
 }
 
@@ -473,9 +568,8 @@ int MidiPlayer_Fb01::open(ResourceManager *resMan) {
 		return retval;
 	}
 
-	// Set system channel to 0. We send this command over all 16 system channels
-	for (int i = 0; i < 16; i++)
-		setSystemParam(i, 0x20, 0);
+	// Set system channel to 0.
+	setSystemParam(0, 0x20, 0);
 
 	// Turn off memory protection
 	setSystemParam(0, 0x21, 0);
@@ -511,9 +605,19 @@ int MidiPlayer_Fb01::open(ResourceManager *resMan) {
 			if (offset >= buf->size())
 				error("Failed to locate start of FB-01 sound bank");
 
+			// The newer IMF.DRV versions without the sound bank will still have the 32 byte
+			// "SIERRA 1" header, but after that they want to send the patch.002 resource.
+			// These driver version are easy to identify by their small size.
+			if (buf->subspan(offset).size() < 3072) {
+				_missingFiles = _requiredFiles[1];
+				return MidiDriver::MERR_DEVICE_NOT_AVAILABLE;
+			}
+
 			sendBanks(buf->subspan(offset));
-		} else
-			error("Failed to open IMF.DRV");
+		} else {
+			_missingFiles = _version == SCI_VERSION_0_EARLY ? _requiredFiles[0] : _requiredFiles[1];
+			return MidiDriver::MERR_DEVICE_NOT_AVAILABLE;
+		}
 	}
 
 	// Set up voices to use MIDI channels 0 - 7
@@ -525,13 +629,65 @@ int MidiPlayer_Fb01::open(ResourceManager *resMan) {
 	// Set master volume
 	setSystemParam(0, 0x24, 0x7f);
 
+	_isOpen = true;
+
 	return 0;
 }
 
 void MidiPlayer_Fb01::close() {
+	Common::StackLock lock(_mutex);
+	_isOpen = false;
 	_driver->close();
 }
 
+void MidiPlayer_Fb01::initTrack(SciSpan<const byte>& header) {
+	if (!_isOpen || _version > SCI_VERSION_0_LATE)
+		return;
+
+	uint8 readPos = 0;
+	uint8 caps = header.getInt8At(readPos++);
+	if (caps != 0 && (_version == SCI_VERSION_0_EARLY || caps != 2))
+		return;
+
+	for (int i = 0; i < 8; ++i)
+		_voices[i] = Voice();
+
+	_numParts = 0;
+	for (int i = 0; i < 16; ++i) {
+		if (_version == SCI_VERSION_0_LATE) {
+			uint8 num = header.getInt8At(readPos++) & 0x7F;
+			uint8 flags = header.getInt8At(readPos++);
+
+			if (flags & 0x02) {
+				_voices[_numParts].channel = i;
+				_voices[_numParts].poly = num;
+				_numParts++;
+			}
+		} else {
+			uint8 val = header.getInt8At(readPos++);
+			if (val & 0x01) {
+				if (val & 0x08) {
+					if (val >> 4)
+						debugC(9, kDebugLevelSound, "MidiPlayer_Fb01::initTrack(): Unused rhythm channel found: 0x%.02x", i);
+				} else if ((val >> 4) != 0x0F) {
+					_voices[_numParts].channel = i;
+					_voices[_numParts].poly = val >> 4;
+					_numParts++;
+				}
+			} else if (val & 0x08) {
+				debugC(9, kDebugLevelSound, "MidiPlayer_Fb01::initTrack(): Control channel found: 0x%.02x", i);
+			}
+		}
+	}
+
+	for (int i = 0; i < _numParts; ++i)
+		setVoiceParam(i, 1, _voices[i].channel);
+
+	initVoices();
+	
+	setVolume(_masterVolume);
+}
+
 void MidiPlayer_Fb01::setVoiceParam(byte voice, byte param, byte value) {
 	_sysExBuf[2] = 0x00;
 	_sysExBuf[3] = 0x18 | voice;
@@ -592,28 +748,29 @@ void MidiPlayer_Fb01::initVoices() {
 		_sysExBuf[i++] = 0x00;
 	}
 
-	// Set up the 8 MIDI channels we will be using
-	for (int j = 0; j < 8; j++) {
+	// Set up the MIDI channels we will be using
+	for (int j = 0; j < _numParts; j++) {
+		int8 chan = _version > SCI_VERSION_0_LATE ? j : _voices[j].channel;
 		// One voice
-		_sysExBuf[i++] = 0x70 | j;
+		_sysExBuf[i++] = 0x70 | chan;
 		_sysExBuf[i++] = 0x00;
-		_sysExBuf[i++] = 0x01;
+		_sysExBuf[i++] = _voices[j].poly;
 
 		// Full range of keys
-		_sysExBuf[i++] = 0x70 | j;
+		_sysExBuf[i++] = 0x70 | chan;
 		_sysExBuf[i++] = 0x02;
 		_sysExBuf[i++] = 0x7f;
-		_sysExBuf[i++] = 0x70 | j;
+		_sysExBuf[i++] = 0x70 | chan;
 		_sysExBuf[i++] = 0x03;
 		_sysExBuf[i++] = 0x00;
 
 		// Voice bank 0
-		_sysExBuf[i++] = 0x70 | j;
+		_sysExBuf[i++] = 0x70 | chan;
 		_sysExBuf[i++] = 0x04;
 		_sysExBuf[i++] = 0x00;
 
 		// Voice 10
-		_sysExBuf[i++] = 0x70 | j;
+		_sysExBuf[i++] = 0x70 | chan;
 		_sysExBuf[i++] = 0x05;
 		_sysExBuf[i++] = 0x0a;
 	}
@@ -636,7 +793,7 @@ void MidiPlayer_Fb01::sysEx(const byte *msg, uint16 length) {
 byte MidiPlayer_Fb01::getPlayId() const {
 	switch (_version) {
 	case SCI_VERSION_0_EARLY:
-		return 0x01;
+		return 0x09;
 	case SCI_VERSION_0_LATE:
 		return 0x02;
 	default:
@@ -644,8 +801,15 @@ byte MidiPlayer_Fb01::getPlayId() const {
 	}
 }
 
+const char MidiPlayer_Fb01::_requiredFiles[2][12] = {
+	"'IMF.DRV'",
+	"'PATCH.002'"
+};
+
 MidiPlayer *MidiPlayer_Fb01_create(SciVersion version) {
 	return new MidiPlayer_Fb01(version);
 }
 
 } // End of namespace Sci
+
+#undef HARDWARE_MASTERVOLUME
\ No newline at end of file


Commit: 9ea6c43c975661273950efa60a7e75a6b45cf8af
    https://github.com/scummvm/scummvm/commit/9ea6c43c975661273950efa60a7e75a6b45cf8af
Author: athrxx (athrxx at scummvm.org)
Date: 2019-08-07T16:43:07+02:00

Commit Message:
SCI: error dialog for missing sound patch/driver files

After implementing such a dialog into the fb01 driver it did make sense to me to also have this as a feature for all other aftermarket drivers/patches.

So now the sound drivers can report missing files after the failed open() call which will then be displayed in a dialog. Which will at least be more helpful than our usual error messages...

Changed paths:
    engines/sci/POTFILES
    engines/sci/sound/drivers/cms.cpp
    engines/sci/sound/drivers/mididriver.h
    engines/sci/sound/music.cpp


diff --git a/engines/sci/POTFILES b/engines/sci/POTFILES
index 24576e8..4b8e6e6 100644
--- a/engines/sci/POTFILES
+++ b/engines/sci/POTFILES
@@ -1,5 +1,4 @@
 engines/sci/detection.cpp
-engines/sci/sound/drivers/fb01.cpp
 engines/sci/engine/guest_additions.cpp
 engines/sci/engine/kfile.cpp
 engines/sci/engine/kgraphics.cpp
@@ -8,3 +7,4 @@ engines/sci/engine/savegame.cpp
 engines/sci/graphics/controls32.cpp
 engines/sci/resource.cpp
 engines/sci/sci.cpp
+engines/sci/sound/music.cpp
diff --git a/engines/sci/sound/drivers/cms.cpp b/engines/sci/sound/drivers/cms.cpp
index 0f79251..091aab2 100644
--- a/engines/sci/sound/drivers/cms.cpp
+++ b/engines/sci/sound/drivers/cms.cpp
@@ -740,7 +740,7 @@ int MidiDriver_CMS::open() {
 	Resource *res = _resMan->findResource(ResourceId(kResourceTypePatch, 101), false);
 	if (!res)
 		return -1;
-	
+
 	_patchData->allocateFromSpan(_version < SCI_VERSION_1_EARLY ? res->subspan(30) : *res);
 
 	_rate = _mixer->getOutputRate();
@@ -1294,7 +1294,7 @@ void MidiDriver_CMS::generateSamples(int16 *buffer, int len) {
 
 class MidiPlayer_CMS : public MidiPlayer {
 public:
-	MidiPlayer_CMS(SciVersion version) : MidiPlayer(version) {}
+	MidiPlayer_CMS(SciVersion version) : MidiPlayer(version), _filesMissing(false) {}
 
 	int open(ResourceManager *resMan);
 	void close();
@@ -1306,6 +1306,12 @@ public:
 	int getPolyphony() const { return 12; }
 
 	void playSwitch(bool play) { _driver->property(MidiDriver_CMS::MIDI_PROP_PLAYSWITCH, play ? 1 : 0); }
+
+	const char *reportMissingFiles() { return _filesMissing ? _requiredFiles : 0; }
+
+private:
+	bool _filesMissing;
+	static const char _requiredFiles[];
 };
 
 int MidiPlayer_CMS::open(ResourceManager *resMan) {
@@ -1314,10 +1320,11 @@ int MidiPlayer_CMS::open(ResourceManager *resMan) {
 
 	_driver = new MidiDriver_CMS(g_system->getMixer(), resMan, _version);
 	int driverRetVal = _driver->open();
-	if (driverRetVal != 0)
-		return driverRetVal;
 
-	return 0;
+	if (driverRetVal == -1)
+		_filesMissing = true;
+
+	return driverRetVal;
 }
 
 void MidiPlayer_CMS::close() {
@@ -1332,6 +1339,8 @@ void MidiPlayer_CMS::initTrack(SciSpan<const byte>& header) {
 		static_cast<MidiDriver_CMS*>(_driver)->initTrack(header);
 }
 
+const char MidiPlayer_CMS::_requiredFiles[] = "'PATCH.101'";
+
 MidiPlayer *MidiPlayer_CMS_create(SciVersion version) {
 	return new MidiPlayer_CMS(version);
 }
diff --git a/engines/sci/sound/drivers/mididriver.h b/engines/sci/sound/drivers/mididriver.h
index 73c2fd5..de87d9d 100644
--- a/engines/sci/sound/drivers/mididriver.h
+++ b/engines/sci/sound/drivers/mididriver.h
@@ -126,6 +126,14 @@ public:
 	// Some drivers also do other things in here.
 	virtual void initTrack(SciSpan<const byte> &) {}
 
+	// There are several sound drivers which weren' part of the
+	// original game setup and came in the form of aftermarket patches.
+	// This method allows each driver to report missing patch or other
+	// required files which will then be displayed in an error dialog box.
+	// The method returns only a single string (instead of a string list),
+	// because no more than two files will be required.
+	virtual const char *reportMissingFiles() { return 0; }
+
 protected:
 	SciVersion _version;
 };
diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp
index dc62acb..581f1a8 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -23,6 +23,8 @@
 #include "audio/audiostream.h"
 #include "audio/decoders/raw.h"
 #include "common/config-manager.h"
+#include "common/translation.h"
+#include "gui/error.h"
 
 #include "sci/sci.h"
 #include "sci/console.h"
@@ -153,6 +155,23 @@ void SciMusic::init() {
 			// of the Adlib driver (adl.drv) that it includes is unsupported. That demo
 			// doesn't have any sound anyway, so this shouldn't be fatal.
 		} else {
+			const char *missingFiles = _pMidiDrv->reportMissingFiles();
+			if (missingFiles) {
+				Common::String message = _(
+					"The selected audio driver requires the following file(s):\n\n"
+				);
+				message += missingFiles;
+				message += _("\n\n"
+					"Some audio drivers (at least for some games) were made\n"
+					"available by Sierra as aftermarket patches and thus might not\n"
+					"have been installed as part of the original game setup.\n\n"
+					"Please copy these file(s) into your game data directory.\n\n"
+					"However, please note that the file(s) might not be available\n"
+					"separately but only as content of (patched) resource bundles.\n"
+					"In that case you may need to apply the original Sierra patch.\n\n"
+				);
+				::GUI::displayErrorDialog(message.c_str());
+			}
 			error("Failed to initialize sound driver");
 		}
 	}


Commit: f8c98d76e61ffd43d663baebbe009e5f182a1e7d
    https://github.com/scummvm/scummvm/commit/f8c98d76e61ffd43d663baebbe009e5f182a1e7d
Author: athrxx (athrxx at scummvm.org)
Date: 2019-08-07T16:43:07+02:00

Commit Message:
SCI: (ADL driver) - remove unused declaration

Changed paths:
    engines/sci/sound/drivers/adlib.cpp


diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index b8e9080..472f3df 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -183,7 +183,6 @@ public:
 	bool hasRhythmChannel() const { return false; }
 	void setVolume(byte volume) { static_cast<MidiDriver_AdLib *>(_driver)->setVolume(volume); }
 	void playSwitch(bool play) { static_cast<MidiDriver_AdLib *>(_driver)->playSwitch(play); }
-	void loadInstrument(int idx, byte *data);
 
 	int getLastChannel() const { return (static_cast<const MidiDriver_AdLib *>(_driver)->useRhythmChannel() ? 8 : 15); }
 };


Commit: 09f007fa5ed7c85eb69f40f768b06962e4e03ac8
    https://github.com/scummvm/scummvm/commit/09f007fa5ed7c85eb69f40f768b06962e4e03ac8
Author: athrxx (athrxx at scummvm.org)
Date: 2019-08-07T16:43:08+02:00

Commit Message:
SCI: (ADL driver) - implement/fix voice mapping

- Backport some code of waltervn's fork at his recommendation (taken from 97604200 and 4c3bfee5). That code has caught some bitrot and I were too lazy to manually resolve the merge conflicts. All changes have been re-checked with disasm.
- Add initTrack() implementation for SCI0 EARLY/LATE
- Minor cleanup
- Please note that this commit does not fix any shortcomings of the SCI sound engine

Changed paths:
    engines/sci/sound/drivers/adlib.cpp


diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index 472f3df..265ad71 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -41,9 +41,6 @@ namespace Sci {
 #define STEREO true
 #endif
 
-// 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 {
 public:
 	enum {
@@ -51,14 +48,17 @@ public:
 		kRhythmKeys = 62
 	};
 
-	MidiDriver_AdLib(Audio::Mixer *mixer) :_playSwitch(true), _masterVolume(15), _rhythmKeyMap(), _opl(0), _isOpen(false) { }
+	MidiDriver_AdLib(SciVersion version) : _version(version), _isSCI0(version < SCI_VERSION_1_EARLY), _playSwitch(true), _masterVolume(15),
+		_numVoiceMax(version == SCI_VERSION_0_EARLY ? 8 : kVoices), _rhythmKeyMap(), _opl(0), _adlibTimerParam(0), _adlibTimerProc(0), _stereo(false), _isOpen(false) { }
 	virtual ~MidiDriver_AdLib() { }
 
 	// MidiDriver
 	int open() { return -1; } // Dummy implementation (use openAdLib)
-	int openAdLib(bool isSCI0);
+	int openAdLib();
 	void close();
 	void send(uint32 b);
+	void initTrack(SciSpan<const byte> &header);
+
 	MidiChannel *allocateChannel() { return NULL; }
 	MidiChannel *getPercussionChannel() { return NULL; }
 	bool isOpen() const { return _isOpen; }
@@ -116,32 +116,39 @@ private:
 		uint16 pitchWheel;		// Pitch wheel setting (0-16383, 8192 is center)
 		uint8 lastVoice;		// Last voice used for this MIDI channel
 		bool enableVelocity;	// Enable velocity control (SCI0)
+		uint8 voices;			// Number of voices currently used by this channel
+		uint8 mappedVoices;		// Number of voices currently mapped to this channel
 
 		Channel() : patch(0), volume(63), pan(64), holdPedal(0), extraVoices(0),
-					pitchWheel(8192), lastVoice(0), enableVelocity(false) { }
+					pitchWheel(8192), lastVoice(0), enableVelocity(false), voices(0),
+					mappedVoices(0) { }
 	};
 
 	struct AdLibVoice {
-		int8 channel;			// MIDI channel that this voice is assigned to or -1
+		int8 channel;			// MIDI channel that is currently using this voice, or -1
+		int8 mappedChannel;		// MIDI channel that this voice is mapped to, or -1
 		int8 note;				// Currently playing MIDI note or -1
 		int patch;				// Currently playing patch or -1
 		uint8 velocity;			// Note velocity
 		bool isSustained;		// Flag indicating a note that is being sustained by the hold pedal
 		uint16 age;				// Age of the current note
 
-		AdLibVoice() : channel(-1), note(-1), patch(-1), velocity(0), isSustained(false), age(0) { }
+		AdLibVoice() : channel(-1), mappedChannel(-1), note(-1), patch(-1), velocity(0), isSustained(false), age(0) { }
 	};
 
 	bool _stereo;
 	bool _isSCI0;
+	SciVersion _version;
 	OPL::OPL *_opl;
 	bool _isOpen;
 	bool _playSwitch;
 	int _masterVolume;
+	const uint8 _numVoiceMax;
 	Channel _channels[MIDI_CHANNELS];
 	AdLibVoice _voices[kVoices];
 	Common::SpanOwner<SciSpan<const byte> > _rhythmKeyMap;
 	Common::Array<AdLibPatch> _patches;
+	Common::List<int> _voiceQueue;
 
 	Common::TimerManager::TimerProc _adlibTimerProc;
 	void *_adlibTimerParam;
@@ -158,18 +165,19 @@ private:
 	void noteOn(int channel, int note, int velocity);
 	void noteOff(int channel, int note);
 	int findVoice(int channel);
+	int findVoiceLateSci11(int channel);
 	void voiceMapping(int channel, int voices);
 	void assignVoices(int channel, int voices);
 	void releaseVoices(int channel, int voices);
 	void donateVoices();
-	int findVoiceBasic(int channel);
+	void queueMoveToBack(int voice);
 	void setVelocityReg(int regOffset, int velocity, int kbScaleLevel, int pan);
 	int calcVelocity(int voice, int op);
 };
 
 class MidiPlayer_AdLib : public MidiPlayer {
 public:
-	MidiPlayer_AdLib(SciVersion soundVersion) : MidiPlayer(soundVersion) { _driver = new MidiDriver_AdLib(g_system->getMixer()); }
+	MidiPlayer_AdLib(SciVersion soundVersion) : MidiPlayer(soundVersion) { _driver = new MidiDriver_AdLib(soundVersion); }
 	~MidiPlayer_AdLib() {
 		delete _driver;
 		_driver = 0;
@@ -183,7 +191,7 @@ public:
 	bool hasRhythmChannel() const { return false; }
 	void setVolume(byte volume) { static_cast<MidiDriver_AdLib *>(_driver)->setVolume(volume); }
 	void playSwitch(bool play) { static_cast<MidiDriver_AdLib *>(_driver)->playSwitch(play); }
-
+	void initTrack(SciSpan<const byte> &header) { static_cast<MidiDriver_AdLib *>(_driver)->initTrack(header); }
 	int getLastChannel() const { return (static_cast<const MidiDriver_AdLib *>(_driver)->useRhythmChannel() ? 8 : 15); }
 };
 
@@ -219,15 +227,18 @@ static const int ym3812_note[13] = {
 	0x2ae
 };
 
-int MidiDriver_AdLib::openAdLib(bool isSCI0) {
+int MidiDriver_AdLib::openAdLib() {
 	_stereo = STEREO;
 
-	debug(3, "ADLIB: Starting driver in %s mode", (isSCI0 ? "SCI0" : "SCI1"));
-	_isSCI0 = isSCI0;
+	debug(3, "ADLIB: Starting driver in %s mode", (_isSCI0 ? "SCI0" : "SCI1"));
+
+	// Fill in the voice queue
+	for (int i = 0; i < kVoices; ++i)
+		_voiceQueue.push_back(i);
 
 	_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.
+	// Try falling back to mono, thus plain OPL2 emulator, when no Dual OPL2 is available.
 	if (!_opl && _stereo) {
 		_stereo = false;
 		_opl = OPL::Config::create(OPL::Config::kOpl2);
@@ -330,6 +341,53 @@ void MidiDriver_AdLib::send(uint32 b) {
 	}
 }
 
+void MidiDriver_AdLib::initTrack(SciSpan<const byte> &header) {
+	if (!_isOpen || !_isSCI0)
+		return;
+
+	uint8 readPos = 0;
+	uint8 caps = header.getInt8At(readPos++);
+	if (caps != 0 && (_version == SCI_VERSION_0_EARLY || caps != 2))
+		return;
+
+	for (int i = 0; i < kVoices; ++i) {
+		_voices[i].channel = _voices[i].mappedChannel = _voices[i].note = -1;
+		_voices[i].isSustained = false;
+		_voices[i].patch = 13;
+		_voices[i].velocity = 0;
+		_voices[i].age = 0;
+	}
+
+	int numVoices = 0;
+	for (int i = 0; i < 16; ++i) {
+		_channels[i].patch = 13;
+		_channels[i].extraVoices = 0;
+		_channels[i].mappedVoices = 0;
+
+		if (_version == SCI_VERSION_0_LATE) {
+			uint8 num = header.getInt8At(readPos++) & 0x7F;
+			uint8 flags = header.getInt8At(readPos++);
+			if ((flags & 0x04) && num)
+				assignVoices(i, num);
+		} else {
+			uint8 val = header.getInt8At(readPos++);
+			if (val & 0x01) {
+				uint8 num = val >> 4;
+				if (!(val & 0x08) && num && num != 0x0F) {
+					while (num--) {
+						if (numVoices >= _numVoiceMax)
+							continue;
+						_voices[numVoices++].mappedChannel = i;
+						_channels[i].mappedVoices++;
+					}
+				}
+			} else if (val & 0x08) {
+				debugC(9, kDebugLevelSound, "MidiDriver_AdLib::initTrack(): Control channel found: 0x%.02x", i);
+			}
+		}
+	}
+}
+
 void MidiDriver_AdLib::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
 	_adlibTimerProc = timerProc;
 	_adlibTimerParam = timerParam;
@@ -377,8 +435,8 @@ void MidiDriver_AdLib::loadInstrument(const SciSpan<const byte> &ins) {
 void MidiDriver_AdLib::voiceMapping(int channel, int voices) {
 	int curVoices = 0;
 
-	for (int i = 0; i < kVoices; i++)
-		if (_voices[i].channel == channel)
+	for (int i = 0; i < _numVoiceMax; i++)
+		if (_voices[i].mappedChannel == channel)
 			curVoices++;
 
 	curVoices += _channels[channel].extraVoices;
@@ -396,14 +454,19 @@ void MidiDriver_AdLib::voiceMapping(int channel, int voices) {
 void MidiDriver_AdLib::assignVoices(int channel, int voices) {
 	assert(voices > 0);
 
-	for (int i = 0; i < kVoices; i++)
-		if (_voices[i].channel == -1) {
-			_voices[i].channel = channel;
+	for (int i = 0; i < _numVoiceMax; i++)
+		if (_voices[i].mappedChannel == -1) {
+			if (_voices[i].note != -1) // Late SCI1.1, stop note on unmapped channel
+				voiceOff(i);
+			_voices[i].mappedChannel = channel;
+			++_channels[channel].mappedVoices;
 			if (--voices == 0)
 				return;
 		}
 
-	_channels[channel].extraVoices += voices;
+	// This is already too advanced for SCI0...
+	if (!_isSCI0)
+		_channels[channel].extraVoices += voices;
 }
 
 void MidiDriver_AdLib::releaseVoices(int channel, int voices) {
@@ -415,18 +478,20 @@ void MidiDriver_AdLib::releaseVoices(int channel, int voices) {
 	voices -= _channels[channel].extraVoices;
 	_channels[channel].extraVoices = 0;
 
-	for (int i = 0; i < kVoices; i++) {
-		if ((_voices[i].channel == channel) && (_voices[i].note == -1)) {
-			_voices[i].channel = -1;
+	for (int i = 0; i < _numVoiceMax; i++) {
+		if ((_voices[i].mappedChannel == channel) && (_voices[i].note == -1)) {
+			_voices[i].mappedChannel = -1;
+			--_channels[channel].mappedVoices;
 			if (--voices == 0)
 				return;
 		}
 	}
 
-	for (int i = 0; i < kVoices; i++) {
-		if (_voices[i].channel == channel) {
+	for (int i = 0; i < _numVoiceMax; i++) {
+		if (_voices[i].mappedChannel == channel) {
 			voiceOff(i);
-			_voices[i].channel = -1;
+			_voices[i].mappedChannel = -1;
+			--_channels[channel].mappedVoices;
 			if (--voices == 0)
 				return;
 		}
@@ -434,10 +499,13 @@ void MidiDriver_AdLib::releaseVoices(int channel, int voices) {
 }
 
 void MidiDriver_AdLib::donateVoices() {
+	if (_isSCI0)
+		return;
+
 	int freeVoices = 0;
 
 	for (int i = 0; i < kVoices; i++)
-		if (_voices[i].channel == -1)
+		if (_voices[i].mappedChannel == -1)
 			freeVoices++;
 
 	if (freeVoices == 0)
@@ -484,11 +552,7 @@ void MidiDriver_AdLib::noteOn(int channel, int note, int velocity) {
 		}
 	}
 
-#ifdef ADLIB_DISABLE_VOICE_MAPPING
-	int voice = findVoiceBasic(channel);
-#else
-	int voice = findVoice(channel);
-#endif
+	int voice = _rhythmKeyMap ? findVoiceLateSci11(channel) : findVoice(channel);
 
 	if (voice == -1) {
 		debug(3, "ADLIB: failed to find free voice assigned to channel %i", channel);
@@ -498,77 +562,97 @@ void MidiDriver_AdLib::noteOn(int channel, int note, int velocity) {
 	voiceOn(voice, note, velocity);
 }
 
-// FIXME: Temporary, see comment at top of file regarding ADLIB_DISABLE_VOICE_MAPPING
-int MidiDriver_AdLib::findVoiceBasic(int channel) {
+int MidiDriver_AdLib::findVoice(int channel) {
 	int voice = -1;
 	int oldestVoice = -1;
-	int oldestAge = -1;
+	uint32 oldestAge = 0;
 
 	// Try to find a voice assigned to this channel that is free (round-robin)
 	for (int i = 0; i < kVoices; i++) {
 		int v = (_channels[channel].lastVoice + i + 1) % kVoices;
 
-		if (_voices[v].note == -1) {
-			voice = v;
-			break;
-		}
+		if (_voices[v].mappedChannel == channel) {
+			if (_voices[v].note == -1) {
+				voice = v;
+				_voices[voice].channel = channel;
+				break;
+			}
 
-		// We also keep track of the oldest note in case the search fails
-		if (_voices[v].age > oldestAge) {
-			oldestAge = _voices[v].age;
-			oldestVoice = v;
+			// We also keep track of the oldest note in case the search fails
+			// Notes started in the current time slice will not be selected
+			if (_voices[v].age >= oldestAge) {
+				oldestAge = _voices[v].age;
+				oldestVoice = v;
+			}
 		}
 	}
 
 	if (voice == -1) {
-		if (oldestVoice >= 0) {
-			voiceOff(oldestVoice);
-			voice = oldestVoice;
-		} else {
+		if (!oldestAge)
 			return -1;
-		}
+		voiceOff(oldestVoice);
+		voice = oldestVoice;
+		_voices[voice].channel = channel;
 	}
 
-	_voices[voice].channel = channel;
 	_channels[channel].lastVoice = voice;
+
 	return voice;
 }
 
-int MidiDriver_AdLib::findVoice(int channel) {
-	int voice = -1;
-	int oldestVoice = -1;
-	uint32 oldestAge = 0;
+int MidiDriver_AdLib::findVoiceLateSci11(int channel) {
+	Common::List<int>::const_iterator it;
 
-	// Try to find a voice assigned to this channel that is free (round-robin)
-	for (int i = 0; i < kVoices; i++) {
-		int v = (_channels[channel].lastVoice + i + 1) % kVoices;
+	// Search for unused voice
+	for (it = _voiceQueue.begin(); it != _voiceQueue.end(); ++it) {
+		int voice = *it;
+		if (_voices[voice].note == -1 && _voices[voice].patch == _channels[channel].patch) {
+			_voices[voice].channel = channel;
+			return voice;
+		}
+	}
 
-		if (_voices[v].channel == channel) {
-			if (_voices[v].note == -1) {
-				voice = v;
-				break;
-			}
+	// Same as before, minus the program check
+	for (it = _voiceQueue.begin(); it != _voiceQueue.end(); ++it) {
+		int voice = *it;
+		if (_voices[voice].note == -1) {
+			_voices[voice].channel = channel;
+			return voice;
+		}
+	}
 
-			// We also keep track of the oldest note in case the search fails
-			// Notes started in the current time slice will not be selected
-			if (_voices[v].age > oldestAge) {
-				oldestAge = _voices[v].age;
-				oldestVoice = v;
+	// Search for channel with highest excess of voices
+	int maxExceed = 0;
+	int maxExceedChan = 0;
+	for (uint i = 0; i < MIDI_CHANNELS; ++i) {
+		if (_channels[i].voices > _channels[i].mappedVoices) {
+			int exceed = _channels[i].voices - _channels[i].mappedVoices;
+			if (exceed > maxExceed) {
+				maxExceed = exceed;
+				maxExceedChan = i;
 			}
 		}
 	}
 
-	if (voice == -1) {
-		if (oldestVoice >= 0) {
-			voiceOff(oldestVoice);
-			voice = oldestVoice;
-		} else {
-			return -1;
+	// Stop voice on channel with highest excess if possible, otherwise stop
+	// note on this channel.
+	int stopChan = (maxExceed > 0) ? maxExceedChan : channel;
+
+	for (it = _voiceQueue.begin(); it != _voiceQueue.end(); ++it) {
+		int voice = *it;
+		if (_voices[voice].channel == stopChan) {
+			voiceOff(voice);
+			_voices[voice].channel = channel;
+			return voice;
 		}
 	}
 
-	_channels[channel].lastVoice = voice;
-	return voice;
+	return -1;
+}
+
+void MidiDriver_AdLib::queueMoveToBack(int voice) {
+	_voiceQueue.remove(voice);
+	_voiceQueue.push_back(voice);
 }
 
 void MidiDriver_AdLib::noteOff(int channel, int note) {
@@ -585,18 +669,18 @@ void MidiDriver_AdLib::noteOff(int channel, int note) {
 
 void MidiDriver_AdLib::voiceOn(int voice, int note, int velocity) {
 	int channel = _voices[voice].channel;
-	int patch;
+	int patch = _channels[channel].patch;
 
 	_voices[voice].age = 0;
+	++_channels[channel].voices;
+	queueMoveToBack(voice);
 
-	if (channel == 9 && _rhythmKeyMap) {
+	if ((channel == 9) && _rhythmKeyMap) {
 		patch = CLIP(note, 27, 88) + 101;
-	} else {
-		patch = _channels[channel].patch;
 	}
 
 	// Set patch if different from current patch
-	if (patch != _voices[voice].patch)
+	if (patch != _voices[voice].patch && _playSwitch)
 		setPatch(voice, patch);
 
 	_voices[voice].velocity = velocity;
@@ -604,10 +688,14 @@ void MidiDriver_AdLib::voiceOn(int voice, int note, int velocity) {
 }
 
 void MidiDriver_AdLib::voiceOff(int voice) {
+	int channel = _voices[voice].channel;
+
 	_voices[voice].isSustained = false;
 	setNote(voice, _voices[voice].note, 0);
 	_voices[voice].note = -1;
 	_voices[voice].age = 0;
+	queueMoveToBack(voice);
+	--_channels[channel].voices;
 }
 
 void MidiDriver_AdLib::setNote(int voice, int note, bool key) {
@@ -616,9 +704,8 @@ void MidiDriver_AdLib::setNote(int voice, int note, bool key) {
 	float delta;
 	int bend = _channels[channel].pitchWheel;
 
-	if (channel == 9 && _rhythmKeyMap) {
+	if ((channel == 9) && _rhythmKeyMap)
 		note = _rhythmKeyMap[CLIP(note, 27, 88) - 27];
-	}
 
 	_voices[voice].note = note;
 
@@ -837,7 +924,7 @@ int MidiPlayer_AdLib::open(ResourceManager *resMan) {
 		return -1;
 	}
 
-	return static_cast<MidiDriver_AdLib *>(_driver)->openAdLib(_version <= SCI_VERSION_0_LATE);
+	return static_cast<MidiDriver_AdLib *>(_driver)->openAdLib();
 }
 
 void MidiPlayer_AdLib::close() {
@@ -849,7 +936,7 @@ void MidiPlayer_AdLib::close() {
 byte MidiPlayer_AdLib::getPlayId() const {
 	switch (_version) {
 	case SCI_VERSION_0_EARLY:
-		return 0x01;
+		return 0x09;
 	case SCI_VERSION_0_LATE:
 		return 0x04;
 	default:


Commit: 522d077ad89ef55848002eb4c0fac62ca4701563
    https://github.com/scummvm/scummvm/commit/522d077ad89ef55848002eb4c0fac62ca4701563
Author: Walter van Niftrik (walter at vanniftrik-it.nl)
Date: 2019-08-07T16:43:08+02:00

Commit Message:
SCI: Fix pitch wheel bug in adlib driver

Changed paths:
    engines/sci/sound/drivers/adlib.cpp


diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index 265ad71..64727ee 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -221,10 +221,14 @@ static const byte velocityMap2[64] = {
 	0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f
 };
 
-static const int ym3812_note[13] = {
-	0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca,
-	0x1e5, 0x202, 0x220, 0x241, 0x263, 0x287,
-	0x2ae
+// One octave with three pitch wheel positions after each note
+static const int adlibFreq[48] = {
+	0x157, 0x15c, 0x161, 0x166, 0x16b, 0x171, 0x176, 0x17b,
+	0x181, 0x186, 0x18c, 0x192, 0x198, 0x19e, 0x1a4, 0x1aa,
+	0x1b0, 0x1b6, 0x1bd, 0x1c3, 0x1ca, 0x1d0, 0x1d7, 0x1de,
+	0x1e5, 0x1ec, 0x1f3, 0x1fa, 0x202, 0x209, 0x211, 0x218,
+	0x220, 0x228, 0x230, 0x238, 0x241, 0x249, 0x252, 0x25a,
+	0x263, 0x26c, 0x275, 0x27e, 0x287, 0x290, 0x29a, 0x2a4
 };
 
 int MidiDriver_AdLib::openAdLib() {
@@ -700,37 +704,52 @@ void MidiDriver_AdLib::voiceOff(int voice) {
 
 void MidiDriver_AdLib::setNote(int voice, int note, bool key) {
 	int channel = _voices[voice].channel;
-	int n, fre, oct;
-	float delta;
-	int bend = _channels[channel].pitchWheel;
 
 	if ((channel == 9) && _rhythmKeyMap)
 		note = _rhythmKeyMap[CLIP(note, 27, 88) - 27];
 
 	_voices[voice].note = note;
 
-	n = note % 12;
+	int index = note << 2;
+	uint16 pitchWheel = _channels[channel].pitchWheel;
+	int sign;
 
-	if (bend < 8192)
-		bend = 8192 - bend;
-	delta = (float)pow(2.0, (bend % 8192) / 8192.0);
+	if (pitchWheel == 0x2000) {
+		pitchWheel = 0;
+		sign = 0;
+	} else if (pitchWheel > 0x2000) {
+		pitchWheel -= 0x2000;
+		sign = 1;
+	} else {
+		pitchWheel = 0x2000 - pitchWheel;
+		sign = -1;
+	}
 
-	if (bend > 8192)
-		fre = (int)(ym3812_note[n] * delta);
+	pitchWheel /= 171;
+
+	if (sign == 1)
+		index += pitchWheel;
 	else
-		fre = (int)(ym3812_note[n] / delta);
+		index -= pitchWheel;
 
-	oct = note / 12 - 1;
+	if (index > 0x1fc) // Limit to max MIDI note (<< 2)
+		index = 0x1fc;
 
-	if (oct < 0)
-		oct = 0;
+	if (index < 0) // Not in SSCI
+		index = 0;
 
-	if (oct > 7)
-		oct = 7;
+	int freq = adlibFreq[index % 48];
 
-	setRegister(0xA0 + voice, fre & 0xff);
-	setRegister(0xB0 + voice, (key << 5) | (oct << 2) | (fre >> 8));
+	setRegister(0xA0 + voice, freq & 0xff);
+
+	int oct = index / 48;
+	if (oct > 0)
+		--oct;
+
+	if (oct > 7) // Not in SSCI
+		oct = 7;
 
+	setRegister(0xB0 + voice, (key << 5) | (oct << 2) | (freq >> 8));
 	setVelocity(voice);
 }
 





More information about the Scummvm-git-logs mailing list