[Scummvm-cvs-logs] SF.net SVN: scummvm:[46812] scummvm/trunk/engines/sci

thebluegr at users.sourceforge.net thebluegr at users.sourceforge.net
Fri Jan 1 07:41:52 CET 2010


Revision: 46812
          http://scummvm.svn.sourceforge.net/scummvm/?rev=46812&view=rev
Author:   thebluegr
Date:     2010-01-01 06:41:52 +0000 (Fri, 01 Jan 2010)

Log Message:
-----------
SCI/new music code:
- Resolved a deadlock with the mixer, and added appropriate mutexes (a result of the fact that SCI mixes MIDI and digital audio in the same list)
- Fixed sound playing when loading games, by properly resetting the MIDI driver
- Reverted savegame version to 14 - the changes in versions 15 and 16 don't have any effect on the currently enabled old music code, and the new music code is disabled by default, and is still prone to changes
- Now saving/loading signal, loop and hold for each sound, as well as reverb
- Added stub code for setting reverb and channel hold
- The signal, loop and hold values of each song are cached, like in SSCI and like what happens in Greg's SCI implementation. This allows a clear separation of the engine code from the rest of the engine. Reverted commits 46792 and 46797
- Removed duplicate song list accessing code
- Song cues are now updated in kAnimate for SCI0, like the old music code does, to compensate for the fact that SCI0 didn't poll for music changes via cmdUpdateCues, like what SCI01 and newer do
- Cleanup

Modified Paths:
--------------
    scummvm/trunk/engines/sci/engine/kgraphics.cpp
    scummvm/trunk/engines/sci/engine/savegame.cpp
    scummvm/trunk/engines/sci/engine/savegame.h
    scummvm/trunk/engines/sci/sfx/midiparser.cpp
    scummvm/trunk/engines/sci/sfx/music.cpp
    scummvm/trunk/engines/sci/sfx/music.h
    scummvm/trunk/engines/sci/sfx/soundcmd.cpp
    scummvm/trunk/engines/sci/sfx/soundcmd.h

Modified: scummvm/trunk/engines/sci/engine/kgraphics.cpp
===================================================================
--- scummvm/trunk/engines/sci/engine/kgraphics.cpp	2009-12-31 23:59:28 UTC (rev 46811)
+++ scummvm/trunk/engines/sci/engine/kgraphics.cpp	2010-01-01 06:41:52 UTC (rev 46812)
@@ -962,6 +962,9 @@
 	// Take care of incoming events (kAnimate is called semi-regularly)
 #ifdef USE_OLD_MUSIC_FUNCTIONS
 	process_sound_events(s);
+#else
+	if (s->detectDoSoundType() <= SCI_VERSION_0_LATE)
+		s->_soundCmd->updateSci0Cues();
 #endif
 
 	s->_gui->animate(castListReference, cycle, argc, argv);

Modified: scummvm/trunk/engines/sci/engine/savegame.cpp
===================================================================
--- scummvm/trunk/engines/sci/engine/savegame.cpp	2009-12-31 23:59:28 UTC (rev 46811)
+++ scummvm/trunk/engines/sci/engine/savegame.cpp	2010-01-01 06:41:52 UTC (rev 46812)
@@ -116,8 +116,8 @@
 		uint32 restoreTime = 0;
 		s.syncAsSint32LE(restoreTime);
 		ticker = restoreTime * 60 / 1000;
-		s.skip(4);	// loop
-		s.skip(4);	// hold
+		s.syncAsSint32LE(loop);
+		s.syncAsSint32LE(hold);
 		// volume and dataInc will be synced from the sound objects
 		// when the sound list is reconstructed in gamestate_restore()
 		volume = 100;
@@ -133,9 +133,11 @@
 		s.syncAsSint16LE(resnum);
 		s.syncAsSint16LE(dataInc);
 		s.syncAsSint16LE(ticker);
+		s.syncAsSint16LE(signal);
 		s.syncAsByte(prio);
-		s.skip(1, VER(15), VER(15));
+		s.syncAsSint16LE(loop);
 		s.syncAsByte(volume);
+		s.syncAsByte(hold);
 		s.syncAsByte(fadeTo);
 		s.syncAsSint16LE(fadeStep);
 		s.syncAsSint32LE(fadeTicker);
@@ -628,16 +630,19 @@
 		s.syncAsByte(_soundOn);
 		s.syncAsByte(masterVolume);
 	} else if (s.isLoading()) {
-		if (s.getVersion() >= 15) {
+		if (s.getVersion() >= 14) {
 			s.syncAsByte(_soundOn);
 			s.syncAsByte(masterVolume);
+			s.syncAsByte(_reverb);
 		} else {
 			_soundOn = true;
 			masterVolume = 15;
+			_reverb = 0;
 		}
 
 		soundSetSoundOn(_soundOn);
 		soundSetMasterVolume(masterVolume);
+		setReverb(_reverb);
 	}
 
 	if (s.isSaving())

Modified: scummvm/trunk/engines/sci/engine/savegame.h
===================================================================
--- scummvm/trunk/engines/sci/engine/savegame.h	2009-12-31 23:59:28 UTC (rev 46811)
+++ scummvm/trunk/engines/sci/engine/savegame.h	2010-01-01 06:41:52 UTC (rev 46812)
@@ -36,7 +36,7 @@
 struct EngineState;
 
 enum {
-	CURRENT_SAVEGAME_VERSION = 16,
+	CURRENT_SAVEGAME_VERSION = 14,
 	MINIMUM_SAVEGAME_VERSION = 9
 };
 

Modified: scummvm/trunk/engines/sci/sfx/midiparser.cpp
===================================================================
--- scummvm/trunk/engines/sci/sfx/midiparser.cpp	2009-12-31 23:59:28 UTC (rev 46811)
+++ scummvm/trunk/engines/sci/sfx/midiparser.cpp	2010-01-01 06:41:52 UTC (rev 46812)
@@ -83,12 +83,10 @@
 }
 
 void MidiParser_SCI::parseNextEvent(EventInfo &info) {
-	SegManager *segMan = ((SciEngine *)g_engine)->getEngineState()->_segMan;	// HACK
-
 	// Set signal AFTER waiting for delta, otherwise we would set signal too soon resulting in all sorts of bugs
 	if (_signalSet) {
 		_signalSet = false;
-		PUT_SEL32V(segMan, _pSnd->soundObj, signal, _signalToSet);
+		_pSnd->signal = _signalToSet;
 		debugC(2, kDebugLevelSound, "signal %04x", _signalToSet);
 	}
 
@@ -131,7 +129,19 @@
 		info.basic.param1 = *(_position._play_pos++);
 		info.basic.param2 = *(_position._play_pos++);
 		if (info.channel() == 0xF) {// SCI special
-			if (info.basic.param1 == 0x60) {
+			// Reference for some events:
+			// http://wiki.scummvm.org/index.php/SCI/Specifications/Sound/SCI0_Resource_Format#Status_Reference
+			// Also, sci/sfx/iterator/iterator.cpp, function BaseSongIterator::parseMidiCommand()
+			switch (info.basic.param1) {
+			case 0x50:	// set volume
+				// This is documented to be "reverb", but it looks like channel
+				// volume, at least in SCI11, so treat it as such
+				_pSnd->volume = info.basic.param2;
+				break;
+			case 0x52:	// set hold
+				_pSnd->hold = info.basic.param2;
+				break;
+			case 0x60:	// update dataInc
 				switch (_soundVersion) {
 				case SCI_VERSION_0_EARLY:
 				case SCI_VERSION_0_LATE:
@@ -146,10 +156,13 @@
 				default:
 					break;
 				}
+				break;
+			case 0x1:	// unknown (example case: LB2CD)
+			case 0xA:	// unknown (example case: LB2CD)
+			default:
+				warning("Unhandled SCI SysEx 0x%x (parameter %d)", info.basic.param1, info.basic.param2);
+				break;
 			}
-			// BF 50 x - set reverb to x
-			// BF 60 x - dataInc++
-			// BF 52 x - bHold=x
 		}
 		if (info.basic.param1 == 7) // channel volume change -scale it
 			info.basic.param2 = info.basic.param2 * _volume / 0x7F;
@@ -200,25 +213,15 @@
 			info.ext.data = _position._play_pos;
 			_position._play_pos += info.length;
 			if (info.ext.type == 0x2F) {// end of track reached
-				// We deviate from SSCI here: in SSCI, the loop is cached, whereas it's
-				// read directly from the sound code here (changed in rev. r46792).
-				// Theoretically, this shouldn't matter at all, as the loop should always
-				// be in sync and should never be changed. In SCI01 and later, the loop is
-				// set from the scripts via cmdSetHandleLoop, so again if there is any
-				// problem with this approach, it'll likely be apparent in SCI0 games
-				// (but no problems have been encountered because of this so far)
-				int16 loop = GET_SEL32V(segMan, _pSnd->soundObj, loop);
-				if (loop)
-					loop--;
-				PUT_SEL32V(segMan, _pSnd->soundObj, loop, loop);
-				if (loop) {
+				if (_pSnd->loop)
+					_pSnd->loop--;
+				if (_pSnd->loop) {
 					// We need to play it again...
 					jumpToTick(_loopTick);
 				} else {
 					_pSnd->status = kSoundStopped;
-					PUT_SEL32V(segMan, _pSnd->soundObj, signal, 0xFFFF);
-					if (_soundVersion <= SCI_VERSION_0_LATE)
-						PUT_SEL32V(segMan, _pSnd->soundObj, state, kSoundStopped);
+					_pSnd->signal = SIGNAL_OFFSET;
+
 					debugC(2, kDebugLevelSound, "signal EOT");
 				}
 			}

Modified: scummvm/trunk/engines/sci/sfx/music.cpp
===================================================================
--- scummvm/trunk/engines/sci/sfx/music.cpp	2009-12-31 23:59:28 UTC (rev 46811)
+++ scummvm/trunk/engines/sci/sfx/music.cpp	2010-01-01 06:41:52 UTC (rev 46812)
@@ -38,8 +38,7 @@
 namespace Sci {
 
 SciMusic::SciMusic(ResourceManager *resMan, SegManager *segMan, SciVersion soundVersion)
-	: _resMan(resMan), _segMan(segMan), _soundVersion(soundVersion),
-	  _soundOn(true), _inCriticalSection(false) {
+	: _soundVersion(soundVersion), _soundOn(true) {
 
 	// Reserve some space in the playlist, to avoid expensive insertion
 	// operations
@@ -103,30 +102,26 @@
 void SciMusic::clearPlayList() {
 	_pMixer->stopAll();
 
+	_mutex.lock();
 	while (!_playList.empty()) {
 		soundStop(_playList[0]);
 		soundKill(_playList[0]);
 	}
+	_mutex.unlock();
 }
 
-void SciMusic::stopAll() {
-	const MusicList::iterator end = _playList.end();
-	for (MusicList::iterator i = _playList.begin(); i != end; ++i) {
-		if (_soundVersion <= SCI_VERSION_0_LATE)
-			PUT_SEL32V(_segMan, (*i)->soundObj, state, kSoundStopped);
-		else
-			PUT_SEL32V(_segMan, (*i)->soundObj, signal, SIGNAL_OFFSET);
-
-		(*i)->dataInc = 0;
-		soundStop(*i);
-	}
-}
-
 void SciMusic::miditimerCallback(void *p) {
 	SciMusic *aud = (SciMusic *)p;
+
+	Common::StackLock lock(aud->_mutex);
 	aud->onTimer();
 }
 
+void SciMusic::soundSetSoundOn(bool soundOnFlag) {
+	_soundOn = soundOnFlag;
+	_pMidiDrv->playSwitch(soundOnFlag);
+}
+
 uint16 SciMusic::soundGetVoices() {
 	switch (_midiType) {
 	case MD_PCSPK:
@@ -142,6 +137,35 @@
 	}
 }
 
+MusicEntry *SciMusic::getSlot(reg_t obj) {
+	Common::StackLock lock(_mutex);
+
+	const MusicList::iterator end = _playList.end();
+	for (MusicList::iterator i = _playList.begin(); i != end; ++i) {
+		if ((*i)->soundObj == obj)
+			return *i;
+	}
+
+	return NULL;
+}
+
+void SciMusic::setReverb(byte reverb) {
+	_reverb = reverb;
+
+	// TODO: actually set reverb for MT-32
+
+	// A good test case for this are the first two rooms in Longbow:
+	// reverb is set for the first room (the cave) and is subsequently
+	// cleared when Robin exits the cave
+}
+
+void SciMusic::resetDriver() {
+	_pMidiDrv->close();
+	_pMidiDrv->open();
+	_pMidiDrv->setTimerCallback(this, &miditimerCallback);
+	_dwTempo = _pMidiDrv->getBaseTempo();
+}
+
 static int f_compare(const void *arg1, const void *arg2) {
 	return ((const MusicEntry *)arg2)->prio - ((const MusicEntry *)arg1)->prio;
 }
@@ -312,6 +336,7 @@
 			pSnd->hCurrentAud = Audio::SoundHandle();
 		} else {
 			// play MIDI track
+			_mutex.lock();
 			if (pSnd->pMidiParser == NULL) {
 				pSnd->pMidiParser = new MidiParser_SCI();
 				pSnd->pMidiParser->setMidiDriver(_pMidiDrv);
@@ -323,69 +348,23 @@
 			// Find out what channels to filter for SCI0
 			channelFilterMask = pSnd->soundRes->getChannelFilterMask(_pMidiDrv->getPlayMask(_soundVersion));
 			pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion);
+
+			pSnd->pMidiParser->jumpToTick(pSnd->ticker);	// for resuming when loading
+			_mutex.unlock();
 		}
 	}
 }
 
 void SciMusic::onTimer() {
-	Common::StackLock lock(_mutex);
-
-	if (_inCriticalSection)
-		return;
-
 	const MusicList::iterator end = _playList.end();
 	for (MusicList::iterator i = _playList.begin(); i != end; ++i) {
 		(*i)->onTimer(_soundVersion, _pMixer);
 	}
 }
 
-void MusicEntry::onTimer(SciVersion soundVersion, Audio::Mixer *mixer) {
-	if (status != kSoundPlaying)
-		return;
-	if (pMidiParser) {
-		if (fadeStep)
-			doFade();
-		pMidiParser->onTimer();
-		ticker = (uint16)pMidiParser->getTick();
-	} else if (pStreamAud) {
-		if (!mixer->isSoundHandleActive(hCurrentAud)) {
-			ticker = 0xFFFF;
-			status = kSoundStopped;
-
-			// Signal the engine scripts that the sound is done playing
-			// FIXME: is there any other place this can be triggered properly?
-			SegManager *segMan = ((SciEngine *)g_engine)->getEngineState()->_segMan;	// HACK
-			PUT_SEL32V(segMan, soundObj, signal, SIGNAL_OFFSET);
-			if (soundVersion <= SCI_VERSION_0_LATE)
-				PUT_SEL32V(segMan, soundObj, state, kSoundStopped);
-		} else {
-			ticker = (uint16)(mixer->getSoundElapsedTime(hCurrentAud) * 0.06);
-		}
-	}
-}
-
-void MusicEntry::doFade() {
-	// This is called from inside onTimer, where the mutex is already locked
-
-	if (fadeTicker)
-		fadeTicker--;
-	else {
-		fadeTicker = fadeTickerStep;
-		volume += fadeStep;
-		SegManager *segMan = ((SciEngine *)g_engine)->getEngineState()->_segMan;	// HACK
-		if (((fadeStep > 0) && (volume >= fadeTo)) || ((fadeStep < 0) && (volume <= fadeTo))) {
-			volume = fadeTo;
-			fadeStep = 0;
-			PUT_SEL32V(segMan, soundObj, signal, 0xFFFF);
-		}
-		PUT_SEL32V(segMan, soundObj, vol, volume);
-
-		pMidiParser->setVolume(volume);
-	}
-}
-
-
 void SciMusic::soundPlay(MusicEntry *pSnd) {
+	_mutex.lock();
+
 	uint sz = _playList.size(), i;
 	// searching if sound is already in _playList
 	for (i = 0; i < sz && _playList[i] != pSnd; i++)
@@ -394,14 +373,18 @@
 		_playList.push_back(pSnd);
 		sortPlayList();
 	}
+	
+	_mutex.unlock();
 
 	if (pSnd->pStreamAud && !_pMixer->isSoundHandleActive(pSnd->hCurrentAud)) {
 		_pMixer->playInputStream(Audio::Mixer::kSFXSoundType, &pSnd->hCurrentAud,
 				pSnd->pStreamAud, -1, pSnd->volume, 0, false);
 	} else if (pSnd->pMidiParser) {
+		_mutex.lock();
 		pSnd->pMidiParser->setVolume(pSnd->volume);
 		if (pSnd->status == kSoundStopped)
 			pSnd->pMidiParser->jumpToTick(0);
+		_mutex.unlock();
 	}
 
 	pSnd->status = kSoundPlaying;
@@ -411,18 +394,26 @@
 	pSnd->status = kSoundStopped;
 	if (pSnd->pStreamAud)
 		_pMixer->stopHandle(pSnd->hCurrentAud);
+
+	_mutex.lock();
 	if (pSnd->pMidiParser)
 		pSnd->pMidiParser->stop();
+	_mutex.unlock();
 }
 
 void SciMusic::soundSetVolume(MusicEntry *pSnd, byte volume) {
-	if (pSnd->pStreamAud)
+	if (pSnd->pStreamAud) {
 		_pMixer->setChannelVolume(pSnd->hCurrentAud, volume);
-	else if (pSnd->pMidiParser)
+	} else if (pSnd->pMidiParser) {
+		_mutex.lock();
 		pSnd->pMidiParser->setVolume(volume);
+		_mutex.unlock();
+	}
 }
 
 void SciMusic::soundSetPriority(MusicEntry *pSnd, byte prio) {
+	Common::StackLock lock(_mutex);
+
 	pSnd->prio = prio;
 	sortPlayList();
 }
@@ -431,15 +422,18 @@
 	pSnd->status = kSoundStopped;
 
 	if (pSnd->pMidiParser) {
+		_mutex.lock();
 		pSnd->pMidiParser->unloadMusic();
 		delete pSnd->pMidiParser;
 		pSnd->pMidiParser = NULL;
+		_mutex.unlock();
 	}
 	if (pSnd->pStreamAud) {
 		_pMixer->stopHandle(pSnd->hCurrentAud);
 		pSnd->pStreamAud = NULL;
 	}
 
+	_mutex.lock();
 	uint sz = _playList.size(), i;
 	// Remove sound from playlist
 	for (i = 0; i < sz; i++) {
@@ -450,6 +444,7 @@
 			break;
 		}
 	}
+	_mutex.unlock();
 }
 
 void SciMusic::soundPause(MusicEntry *pSnd) {
@@ -457,10 +452,13 @@
 	if (pSnd->status != kSoundPlaying)
 		return;
 	pSnd->status = kSoundPaused;
-	if (pSnd->pStreamAud)
+	if (pSnd->pStreamAud) {
 		_pMixer->pauseHandle(pSnd->hCurrentAud, true);
-	else if (pSnd->pMidiParser)
+	} else if (pSnd->pMidiParser) {
+		_mutex.lock();
 		pSnd->pMidiParser->pause();
+		_mutex.unlock();
+	}
 }
 
 void SciMusic::soundResume(MusicEntry *pSnd) {
@@ -498,55 +496,6 @@
 	}
 }
 
-void SciMusic::reconstructPlayList(int savegame_version) {
-	// Stop the music driver
-	if (_pMidiDrv) {
-		_pMidiDrv->close();
-		delete _pMidiDrv;
-	}
-
-	Common::StackLock lock(_mutex);
-
-	// Reinit the music driver
-	init();
-
-	const MusicList::iterator end = _playList.end();
-	for (MusicList::iterator i = _playList.begin(); i != end; ++i) {
-		if (savegame_version < 14) {
-			if (_soundVersion >= SCI_VERSION_1_EARLY) {
-				(*i)->dataInc = GET_SEL32V(_segMan, (*i)->soundObj, dataInc);
-				(*i)->volume = GET_SEL32V(_segMan, (*i)->soundObj, vol);
-			} else {
-				(*i)->volume = 100;
-			}
-		}
-
-		(*i)->soundRes = new SoundResource((*i)->resnum, _resMan, _soundVersion);
-		soundInitSnd(*i);
-		if ((*i)->pMidiParser)
-			(*i)->pMidiParser->jumpToTick((*i)->ticker);
-		// Replay all paused sounds - a lot of games pause sounds while saving, so
-		// we need to restore the correct sound state
-		if ((*i)->status == kSoundPaused) {
-			soundPlay(*i);
-		}
-	}
-}
-
-// There is some weird code going on in sierra sci. it checks the first 2 playlist entries if they are 0
-//  we should never need to care about this, anyway this is just meant as note
-MusicList::iterator SciMusic::enumPlayList(MusicList::iterator slotLoop) {
-	if (!slotLoop) {
-		if (_playList.begin() == _playList.end())
-			return NULL;
-		return _playList.begin();
-	}
-	slotLoop++;
-	if (slotLoop == _playList.end())
-		return NULL;
-	return slotLoop;
-}
-
 MusicEntry::MusicEntry() {
 	soundObj = NULL_REG;
 
@@ -555,8 +504,11 @@
 
 	dataInc = 0;
 	ticker = 0;
+	signal = 0;
 	prio = 0;
+	loop = 0;
 	volume = 0;
+	hold = 0;
 
 	pauseCounter = 0;
 
@@ -574,4 +526,39 @@
 MusicEntry::~MusicEntry() {
 }
 
+void MusicEntry::onTimer(SciVersion soundVersion, Audio::Mixer *mixer) {
+	if (status != kSoundPlaying)
+		return;
+	if (pMidiParser) {
+		if (fadeStep)
+			doFade();
+		pMidiParser->onTimer();
+		ticker = (uint16)pMidiParser->getTick();
+	} else if (pStreamAud) {
+		if (!mixer->isSoundHandleActive(hCurrentAud)) {
+			ticker = SIGNAL_OFFSET;
+			signal = SIGNAL_OFFSET;
+			status = kSoundStopped;
+		} else {
+			ticker = (uint16)(mixer->getSoundElapsedTime(hCurrentAud) * 0.06);
+		}
+	}
+}
+
+void MusicEntry::doFade() {
+	if (fadeTicker)
+		fadeTicker--;
+	else {
+		fadeTicker = fadeTickerStep;
+		volume += fadeStep;
+		if (((fadeStep > 0) && (volume >= fadeTo)) || ((fadeStep < 0) && (volume <= fadeTo))) {
+			volume = fadeTo;
+			fadeStep = 0;
+			signal = SIGNAL_OFFSET;
+		}
+
+		pMidiParser->setVolume(volume);
+	}
+}
+
 } // End of namespace Sci

Modified: scummvm/trunk/engines/sci/sfx/music.h
===================================================================
--- scummvm/trunk/engines/sci/sfx/music.h	2009-12-31 23:59:28 UTC (rev 46811)
+++ scummvm/trunk/engines/sci/sfx/music.h	2010-01-01 06:41:52 UTC (rev 46812)
@@ -65,6 +65,11 @@
 #endif
 {
 public:
+	// Do not get these directly for the sound objects!
+	// It's a bad idea, as the sound code (i.e. the SciMusic
+	// class) should be as separate as possible from the rest
+	// of the engine
+
 	reg_t soundObj;
 
 	SoundResource *soundRes;
@@ -72,8 +77,11 @@
 
 	uint16 dataInc;
 	uint16 ticker;
+	uint16 signal;
 	byte prio;
-	int16 volume;
+	uint16 loop;
+	byte volume;
+	byte hold;
 
 	int16 pauseCounter;
 
@@ -121,7 +129,6 @@
 #endif
 	void onTimer();
 	void clearPlayList();
-	void stopAll();
 
 	// sound and midi functions
 	void soundInitSnd(MusicEntry *pSnd);
@@ -135,42 +142,43 @@
 	uint16 soundGetMasterVolume();
 	void soundSetMasterVolume(uint16 vol);
 	uint16 soundGetSoundOn() const { return _soundOn; }
-	void soundSetSoundOn(bool soundOnFlag) {
-		_soundOn = soundOnFlag;
-		_pMidiDrv->playSwitch(soundOnFlag);
-	}
+	void soundSetSoundOn(bool soundOnFlag);
 	uint16 soundGetVoices();
 	uint32 soundGetTempo() const { return _dwTempo; }
 
-	MusicEntry *getSlot(reg_t obj) { 
-		const MusicList::iterator end = _playList.end();
-		for (MusicList::iterator i = _playList.begin(); i != end; ++i) {
-			if ((*i)->soundObj == obj) {
-				return *i;
-			}
-		}
+	MusicEntry *getSlot(reg_t obj);
 
-		return NULL;
-	}
-
 	void pushBackSlot(MusicEntry *slotEntry) {
+		Common::StackLock lock(_mutex);
 		_playList.push_back(slotEntry);
 	}
 
 	void printPlayList(Console *con);
 
-	void reconstructPlayList(int savegame_version);
-	MusicList::iterator enumPlayList(MusicList::iterator slotLoop);
+	// The following two methods are NOT thread safe - make sure that
+	// the mutex is always locked before calling them
+	MusicList::iterator getPlayListStart() { return _playList.begin(); }
+	MusicList::iterator getPlayListEnd() { return _playList.end(); }
 
-	void enterCriticalSection() { _inCriticalSection = true; }
-	void leaveCriticalSection() { _inCriticalSection = false; }
+	void sendMidiCommand (uint32 cmd) {
+		Common::StackLock lock(_mutex);
+		_pMidiDrv->send(cmd);
+	}
 
-	void sendMidiCommand (uint32 cmd) { _pMidiDrv->send(cmd); }
+	void setReverb(byte reverb);
 
+	void resetDriver();
+
 #ifndef USE_OLD_MUSIC_FUNCTIONS
 	virtual void saveLoadWithSerializer(Common::Serializer &ser);
 #endif
 
+	// Mutex for music code. Used to guard access to the song playlist, to the
+	// MIDI parser and to the MIDI driver/player. Note that guarded code must NOT
+	// include references to the mixer, otherwise there will probably be situations
+	// where a deadlock can occur
+	Common::Mutex _mutex;
+
 protected:
 	byte findAudEntry(uint16 nAud, byte&oVolume, uint32& oOffset, uint32&oSize);
 	void sortPlayList();
@@ -185,7 +193,6 @@
 	Audio::Mixer *_pMixer;
 	MidiPlayer *_pMidiDrv;
 	MidiDriverType _midiType;
-	Common::Mutex _mutex;
 
 	uint32 _dwTempo;
 	bool _bMultiMidi; // use adlib's digital track if midi track don't have one
@@ -194,10 +201,7 @@
 
 	MusicList _playList;
 	bool _soundOn;
-	bool _inCriticalSection;
-
-	SegManager *_segMan;
-	ResourceManager *_resMan;
+	byte _reverb;
 };
 
 } // End of namespace Sci

Modified: scummvm/trunk/engines/sci/sfx/soundcmd.cpp
===================================================================
--- scummvm/trunk/engines/sci/sfx/soundcmd.cpp	2009-12-31 23:59:28 UTC (rev 46811)
+++ scummvm/trunk/engines/sci/sfx/soundcmd.cpp	2010-01-01 06:41:52 UTC (rev 46812)
@@ -170,7 +170,7 @@
 		SOUNDCOMMAND(cmdUpdateCues);
 		SOUNDCOMMAND(cmdSendMidi);
 		SOUNDCOMMAND(cmdReverb);
-		SOUNDCOMMAND(cmdHoldHandle);
+		SOUNDCOMMAND(cmdSetHandleHold);
 		break;
 	case SCI_VERSION_1_LATE:
 		SOUNDCOMMAND(cmdVolume);
@@ -185,7 +185,7 @@
 		SOUNDCOMMAND(cmdStopHandle);
 		SOUNDCOMMAND(cmdPauseHandle);
 		SOUNDCOMMAND(cmdFadeHandle);
-		SOUNDCOMMAND(cmdHoldHandle);
+		SOUNDCOMMAND(cmdSetHandleHold);
 		SOUNDCOMMAND(cmdDummy);
 		SOUNDCOMMAND(cmdSetHandleVolume);
 		SOUNDCOMMAND(cmdSetHandlePriority);
@@ -234,17 +234,7 @@
 			debugC(2, kDebugLevelSound, "%s, object %04x:%04x", _soundCommands[command]->desc, PRINT_REG(obj));
 		}
 
-		// If the command is operating on an object of the sound list, don't allow onTimer to kick in till the
-		// command is done
-#ifndef USE_OLD_MUSIC_FUNCTIONS
-		if (obj != NULL_REG)
-			_music->enterCriticalSection();
-#endif
 		(this->*(_soundCommands[command]->sndCmd))(obj, value);
-#ifndef USE_OLD_MUSIC_FUNCTIONS
-		if (obj != NULL_REG)
-			_music->leaveCriticalSection();
-#endif
 	} else {
 		warning("Invalid sound command requested (%d), valid range is 0-%d", command, _soundCommands.size() - 1);
 	}
@@ -288,6 +278,7 @@
 	if (number && _resMan->testResource(ResourceId(kResourceTypeSound, number)))
 		newSound->soundRes = new SoundResource(number, _resMan, _soundVersion);
 	newSound->soundObj = obj;
+	newSound->loop = GET_SEL32V(_segMan, obj, loop);
 	newSound->prio = GET_SEL32V(_segMan, obj, pri) & 0xFF;
 	newSound->volume = CLIP<int>(GET_SEL32V(_segMan, obj, vol), 0, Audio::Mixer::kMaxChannelVolume);
 
@@ -296,8 +287,6 @@
 	if (oldSound)
 		cmdDisposeHandle(obj, value);
 
-	_music->pushBackSlot(newSound);
-
 	// In SCI1.1 games, sound effects are started from here. If we can find
 	// a relevant audio resource, play it, otherwise switch to synthesized
 	// effects. If the resource exists, play it using map 65535 (sound
@@ -311,8 +300,12 @@
 		if (newSound->soundRes)
 			_music->soundInitSnd(newSound);
 	}
+
+	_music->pushBackSlot(newSound);
+
 #endif
 
+	// Notify the engine
 	if (_soundVersion <= SCI_VERSION_0_LATE)
 		PUT_SEL32V(_segMan, obj, state, kSoundInitialized);
 	else
@@ -420,6 +413,7 @@
 		PUT_SEL32V(_segMan, obj, state, kSoundPlaying);
 	}
 
+	musicSlot->loop = GET_SEL32V(_segMan, obj, loop);
 	musicSlot->prio = GET_SEL32V(_segMan, obj, priority);
 	// vol selector doesnt get used before sci1late
 	if (_soundVersion < SCI_VERSION_1_LATE)
@@ -503,6 +497,7 @@
 		PUT_SEL32V(_segMan, obj, signal, SIGNAL_OFFSET);
 
 	musicSlot->dataInc = 0;
+	musicSlot->signal = 0;
 	_music->soundStop(musicSlot);
 #endif
 }
@@ -518,14 +513,16 @@
 		changeHandleStatus(obj, value ? SOUND_STATUS_SUSPENDED : SOUND_STATUS_PLAYING);
 #else
 
+	Common::StackLock lock(_music->_mutex);
 	MusicEntry *musicSlot = NULL;
 	MusicList::iterator slotLoop = NULL;
 
 	if (!obj.segment) {
-		// Pausing/Resuming whole playlist was introduced sci1late (soundversion-wise)
+		// Pausing/Resuming the whole playlist was introduced 
+		// in the SCI1 late sound scheme
 		if (_soundVersion <= SCI_VERSION_1_EARLY)
 			return;
-		slotLoop = _music->enumPlayList(NULL);
+		slotLoop = _music->getPlayListStart();
 		musicSlot = *slotLoop;
 	} else {
 		musicSlot = _music->getSlot(obj);
@@ -545,10 +542,12 @@
 			else
 				_music->soundResume(musicSlot);
 		}
+
 		if (slotLoop) {
-			slotLoop = _music->enumPlayList(slotLoop);
-			if (slotLoop)
-				musicSlot = *slotLoop;
+			if (slotLoop == _music->getPlayListEnd())
+				break;
+			else
+				musicSlot = *(slotLoop++);
 		}
 	} while (slotLoop);
 #endif
@@ -563,9 +562,7 @@
 #ifdef USE_OLD_MUSIC_FUNCTIONS
 	changeHandleStatus(obj, SOUND_STATUS_PLAYING);
 #else
-	MusicEntry *musicSlot = NULL;
-
-	musicSlot = _music->getSlot(obj);
+	MusicEntry *musicSlot = _music->getSlot(obj);
 	if (!musicSlot) {
 		warning("cmdResumeHandle: Slot not found");
 		return;
@@ -675,6 +672,7 @@
 		return;
 	}
 
+	musicSlot->loop = GET_SEL32V(_segMan, obj, loop);
 	int16 objVol = CLIP<int>(GET_SEL32V(_segMan, obj, vol), 0, 255);
 	if (objVol != musicSlot->volume)
 		_music->soundSetVolume(musicSlot, objVol);
@@ -760,36 +758,59 @@
 		PUT_SEL32V(_segMan, obj, frame, frame);
 	}
 #else
+	updateCues(obj);
+#endif
+}
 
+#ifndef USE_OLD_MUSIC_FUNCTIONS
+
+void SoundCommandParser::updateSci0Cues() {
+	Common::StackLock(_music->_mutex);
+
+	const MusicList::iterator end = _music->getPlayListEnd();
+	for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
+		updateCues((*i)->soundObj);
+	}
+}
+
+void SoundCommandParser::updateCues(reg_t obj) {
 	MusicEntry *musicSlot = _music->getSlot(obj);
 	if (!musicSlot) {
 		warning("cmdUpdateCues: Slot not found");
 		return;
 	}
 
-	uint16 signal = GET_SEL32V(_segMan, obj, signal);
-	uint16 dataInc = musicSlot->dataInc;
-
-	switch (signal) {
+	switch (musicSlot->signal) {
 		case 0:
-			if (dataInc != GET_SEL32V(_segMan, obj, dataInc)) {
-				PUT_SEL32V(_segMan, obj, dataInc, dataInc);
-				PUT_SEL32V(_segMan, obj, signal, dataInc + 127);
+			if (musicSlot->dataInc != GET_SEL32V(_segMan, obj, dataInc)) {
+				PUT_SEL32V(_segMan, obj, dataInc, musicSlot->dataInc);
+				PUT_SEL32V(_segMan, obj, signal, musicSlot->dataInc + 127);
 			}
 			break;
-		case 0xFFFF:
-			cmdStopHandle(obj, value);
+		case SIGNAL_OFFSET:
+			cmdStopHandle(obj, 0);
 			break;
 		default:
+			// Sync the signal of the sound object
+			PUT_SEL32V(_segMan, obj, signal, musicSlot->signal);
 			break;
 	}
 
-	uint16 ticker = musicSlot->ticker;
-	PUT_SEL32V(_segMan, obj, min, ticker / 3600);
-	PUT_SEL32V(_segMan, obj, sec, ticker % 3600 / 60);
-	PUT_SEL32V(_segMan, obj, frame, ticker);
+	// Signal the game when a digital sound effect is done playing
+	if (musicSlot->pStreamAud && musicSlot->status == kSoundStopped && 
+		musicSlot->signal == SIGNAL_OFFSET) {
+		cmdStopHandle(obj, 0);
+	}
+
+	musicSlot->signal = 0;
+
+	if (_soundVersion >= SCI_VERSION_1_EARLY) {
+		PUT_SEL32V(_segMan, obj, min, musicSlot->ticker / 3600);
+		PUT_SEL32V(_segMan, obj, sec, musicSlot->ticker % 3600 / 60);
+		PUT_SEL32V(_segMan, obj, frame, musicSlot->ticker);
+	}
+}
 #endif
-}
 
 void SoundCommandParser::cmdSendMidi(reg_t obj, int16 value) {
 #ifdef USE_OLD_MUSIC_FUNCTIONS
@@ -801,28 +822,25 @@
 }
 
 void SoundCommandParser::cmdReverb(reg_t obj, int16 value) {
-	// TODO
-	// This function has one parameter, enabling the reverb effect
-	// in MT-32 if the parameter is non-zero. This is either the
-	// reverb level, or delay time. The reverb type is probably fixed
-	// to 1 ("room"). I'm not quite sure how and if this works for
-	// Adlib.
-	// Refer to http://www.midi.org/techspecs/midimessages.php
-	// and http://www.youngmonkey.ca/nose/audio_tech/synth/Roland-MT32.html
-	// Also, /sound/softsynth/mt32/synth.h is a good reference
-	// A good test case for this are the first two rooms in Longbow:
-	// reverb is set for the first room (the cave) and is subsequently
-	// cleared when Robin exits the cave
-	warning("STUB: cmdReverb (%d)", obj.toUint16());
+#ifndef USE_OLD_MUSIC_FUNCTIONS
+	_music->setReverb(obj.toUint16() & 0xF);
+#endif
 }
 
-void SoundCommandParser::cmdHoldHandle(reg_t obj, int16 value) {
+void SoundCommandParser::cmdSetHandleHold(reg_t obj, int16 value) {
 #ifdef USE_OLD_MUSIC_FUNCTIONS
 	SongHandle handle = FROBNICATE_HANDLE(obj);
 	_state->sfx_song_set_hold(handle, value);
 #else
-	// TODO: implement this...
-	warning("STUB: cmdHoldHandle");
+	MusicEntry *musicSlot = _music->getSlot(obj);
+	if (!musicSlot) {
+		warning("cmdSetHandleHold: Slot not found");
+		return;
+	}
+
+	musicSlot->hold = value;
+
+	// TODO: actually handle channel hold!
 #endif
 }
 
@@ -833,7 +851,18 @@
 
 void SoundCommandParser::cmdStopAllSounds(reg_t obj, int16 value) {
 #ifndef USE_OLD_MUSIC_FUNCTIONS
-	_music->stopAll();
+	Common::StackLock(_music->_mutex);
+
+	const MusicList::iterator end = _music->getPlayListEnd();
+	for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
+		if (_soundVersion <= SCI_VERSION_0_LATE)
+			PUT_SEL32V(_segMan, (*i)->soundObj, state, kSoundStopped);
+		else
+			PUT_SEL32V(_segMan, (*i)->soundObj, signal, SIGNAL_OFFSET);
+
+		(*i)->dataInc = 0;
+		_music->soundStop(*i);
+	}
 #endif
 }
 
@@ -892,10 +921,12 @@
 		return;
 	}
 	if (value == -1) {
-		PUT_SEL32V(_segMan, obj, loop, 0xFFFF);
+		musicSlot->loop = 0xFFFF;
 	} else {
-		PUT_SEL32V(_segMan, obj, loop, 1); // actually plays the music once
+		musicSlot->loop = 1; // actually plays the music once
 	}
+
+	PUT_SEL32V(_segMan, obj, loop, musicSlot->loop);
 #endif
 }
 
@@ -906,19 +937,42 @@
 
 void SoundCommandParser::clearPlayList() {
 #ifndef USE_OLD_MUSIC_FUNCTIONS
+	Common::StackLock lock(_music->_mutex);
 	_music->clearPlayList();
 #endif
 }
 
 void SoundCommandParser::syncPlayList(Common::Serializer &s) {
 #ifndef USE_OLD_MUSIC_FUNCTIONS
+	Common::StackLock lock(_music->_mutex);
 	_music->saveLoadWithSerializer(s);
 #endif
 }
 
 void SoundCommandParser::reconstructPlayList(int savegame_version) {
 #ifndef USE_OLD_MUSIC_FUNCTIONS
-	_music->reconstructPlayList(savegame_version);
+
+	Common::StackLock lock(_music->_mutex);
+
+	const MusicList::iterator end = _music->getPlayListEnd();
+	for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
+		if (savegame_version < 14) {
+			(*i)->dataInc = GET_SEL32V(_segMan, (*i)->soundObj, dataInc);
+			(*i)->signal = GET_SEL32V(_segMan, (*i)->soundObj, signal);
+
+			if (_soundVersion >= SCI_VERSION_1_EARLY)
+				(*i)->volume = GET_SEL32V(_segMan, (*i)->soundObj, vol);
+			else
+				(*i)->volume = 100;
+		}
+
+		(*i)->soundRes = new SoundResource((*i)->resnum, _resMan, _soundVersion);
+		_music->soundInitSnd(*i);
+		if ((*i)->status == kSoundPlaying)
+			cmdPlayHandle((*i)->soundObj, 0);
+	}
+
+	_music->resetDriver();
 #endif
 }
 

Modified: scummvm/trunk/engines/sci/sfx/soundcmd.h
===================================================================
--- scummvm/trunk/engines/sci/sfx/soundcmd.h	2009-12-31 23:59:28 UTC (rev 46811)
+++ scummvm/trunk/engines/sci/sfx/soundcmd.h	2010-01-01 06:41:52 UTC (rev 46812)
@@ -59,6 +59,10 @@
 	void reconstructPlayList(int savegame_version);
 	void printPlayList(Console *con);
 
+#ifndef USE_OLD_MUSIC_FUNCTIONS
+	void updateSci0Cues();
+#endif
+
 private:
 	Common::Array<MusicEntryCommand*> _soundCommands;
 	ResourceManager *_resMan;
@@ -75,6 +79,17 @@
 	reg_t _acc;
 	int _midiCmd, _controller, _param;
 
+#ifndef USE_OLD_MUSIC_FUNCTIONS
+	/**
+	 * Synchronizes the current state of the music list to the rest of the engine, so that
+	 * the changes that the sound thread makes to the music are registered with the engine
+	 * scripts. In SCI0, we invoke this from kAnimate (which is called very often). SCI01
+	 * and later have a specific callback function, cmdUpdateCues, which is called regularly
+	 * by the engine scripts themselves, so the engine itself polls for changes to the music
+	 */
+	void updateCues(reg_t obj);
+#endif
+
 	void cmdInitHandle(reg_t obj, int16 value);
 	void cmdPlayHandle(reg_t obj, int16 value);
 	void cmdDummy(reg_t obj, int16 value);
@@ -94,7 +109,7 @@
 	void cmdUpdateCues(reg_t obj, int16 value);
 	void cmdSendMidi(reg_t obj, int16 value);
 	void cmdReverb(reg_t obj, int16 value);
-	void cmdHoldHandle(reg_t obj, int16 value);
+	void cmdSetHandleHold(reg_t obj, int16 value);
 	void cmdGetAudioCapability(reg_t obj, int16 value);
 	void cmdSetHandleVolume(reg_t obj, int16 value);
 	void cmdSetHandlePriority(reg_t obj, int16 value);


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list