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

thebluegr at users.sourceforge.net thebluegr at users.sourceforge.net
Sun Dec 20 14:38:13 CET 2009


Revision: 46430
          http://scummvm.svn.sourceforge.net/scummvm/?rev=46430&view=rev
Author:   thebluegr
Date:     2009-12-20 13:38:13 +0000 (Sun, 20 Dec 2009)

Log Message:
-----------
Moved all of the sound iterator code in its own directory, and added a slight hack to the SoundCommandParser constructor

Modified Paths:
--------------
    scummvm/trunk/engines/sci/console.cpp
    scummvm/trunk/engines/sci/engine/savegame.cpp
    scummvm/trunk/engines/sci/engine/state.h
    scummvm/trunk/engines/sci/module.mk
    scummvm/trunk/engines/sci/sci.cpp
    scummvm/trunk/engines/sci/sfx/soundcmd.cpp
    scummvm/trunk/engines/sci/sfx/soundcmd.h

Added Paths:
-----------
    scummvm/trunk/engines/sci/sfx/iterator/
    scummvm/trunk/engines/sci/sfx/iterator/core.cpp
    scummvm/trunk/engines/sci/sfx/iterator/core.h
    scummvm/trunk/engines/sci/sfx/iterator/iterator.cpp
    scummvm/trunk/engines/sci/sfx/iterator/iterator.h
    scummvm/trunk/engines/sci/sfx/iterator/iterator_internal.h
    scummvm/trunk/engines/sci/sfx/iterator/songlib.cpp
    scummvm/trunk/engines/sci/sfx/iterator/songlib.h
    scummvm/trunk/engines/sci/sfx/iterator/test-iterator.cpp

Removed Paths:
-------------
    scummvm/trunk/engines/sci/sfx/core.cpp
    scummvm/trunk/engines/sci/sfx/core.h
    scummvm/trunk/engines/sci/sfx/iterator.cpp
    scummvm/trunk/engines/sci/sfx/iterator.h
    scummvm/trunk/engines/sci/sfx/iterator_internal.h
    scummvm/trunk/engines/sci/sfx/songlib.cpp
    scummvm/trunk/engines/sci/sfx/songlib.h
    scummvm/trunk/engines/sci/sfx/test-iterator.cpp

Modified: scummvm/trunk/engines/sci/console.cpp
===================================================================
--- scummvm/trunk/engines/sci/console.cpp	2009-12-20 13:28:23 UTC (rev 46429)
+++ scummvm/trunk/engines/sci/console.cpp	2009-12-20 13:38:13 UTC (rev 46430)
@@ -41,8 +41,8 @@
 #include "sci/gfx/gfx_widgets.h"	// for getPort
 #endif
 #ifdef USE_OLD_MUSIC_FUNCTIONS
-#include "sci/sfx/songlib.h"	// for SongLibrary
-#include "sci/sfx/iterator.h"	// for SCI_SONG_ITERATOR_TYPE_SCI0
+#include "sci/sfx/iterator/songlib.h"	// for SongLibrary
+#include "sci/sfx/iterator/iterator.h"	// for SCI_SONG_ITERATOR_TYPE_SCI0
 #endif
 #include "sci/sfx/softseq/mididriver.h"
 #include "sci/vocabulary.h"

Modified: scummvm/trunk/engines/sci/engine/savegame.cpp
===================================================================
--- scummvm/trunk/engines/sci/engine/savegame.cpp	2009-12-20 13:28:23 UTC (rev 46429)
+++ scummvm/trunk/engines/sci/engine/savegame.cpp	2009-12-20 13:38:13 UTC (rev 46430)
@@ -43,8 +43,8 @@
 #include "sci/gui/gui.h"
 #include "sci/sfx/audio.h"
 #ifdef USE_OLD_MUSIC_FUNCTIONS
-#include "sci/sfx/core.h"
-#include "sci/sfx/iterator.h"
+#include "sci/sfx/iterator/core.h"
+#include "sci/sfx/iterator/iterator.h"
 #endif
 
 namespace Sci {

Modified: scummvm/trunk/engines/sci/engine/state.h
===================================================================
--- scummvm/trunk/engines/sci/engine/state.h	2009-12-20 13:28:23 UTC (rev 46429)
+++ scummvm/trunk/engines/sci/engine/state.h	2009-12-20 13:38:13 UTC (rev 46430)
@@ -44,7 +44,7 @@
 #include "sci/gfx/gfx_system.h"
 #include "sci/sfx/audio.h"
 #ifdef USE_OLD_MUSIC_FUNCTIONS
-#include "sci/sfx/core.h"
+#include "sci/sfx/iterator/core.h"
 #endif
 #include "sci/sfx/soundcmd.h"
 

Modified: scummvm/trunk/engines/sci/module.mk
===================================================================
--- scummvm/trunk/engines/sci/module.mk	2009-12-20 13:28:23 UTC (rev 46429)
+++ scummvm/trunk/engines/sci/module.mk	2009-12-20 13:38:13 UTC (rev 46430)
@@ -69,11 +69,11 @@
 	gui32/res_pic.o \
 	gui32/res_view.o \
 	sfx/audio.o \
-	sfx/core.o \
-	sfx/iterator.o \
 	sfx/music.o \
-	sfx/songlib.o \
 	sfx/soundcmd.o \
+	sfx/iterator/core.o \
+	sfx/iterator/iterator.o \
+	sfx/iterator/songlib.o \
 	sfx/seq/gm.o \
 	sfx/seq/instrument-map.o \
 	sfx/seq/map-mt32-to-gm.o \

Modified: scummvm/trunk/engines/sci/sci.cpp
===================================================================
--- scummvm/trunk/engines/sci/sci.cpp	2009-12-20 13:28:23 UTC (rev 46429)
+++ scummvm/trunk/engines/sci/sci.cpp	2009-12-20 13:38:13 UTC (rev 46430)
@@ -166,7 +166,7 @@
 	// since we cannot let the game control where saves are stored)
 	strcpy(_gamestate->sys_strings->_strings[SYS_STRING_SAVEDIR]._value, "/");
 
-	_gamestate->_soundCmd = new SoundCommandParser(_resMan, segMan, &_gamestate->_sound, _audio, _gamestate->detectDoSoundType());
+	_gamestate->_soundCmd = new SoundCommandParser(_resMan, segMan, _audio, _gamestate->detectDoSoundType());
 
 	GfxState gfx_state;
 	_gamestate->gfx_state = &gfx_state;
@@ -269,7 +269,9 @@
 }
 
 void SciEngine::pauseEngineIntern(bool pause) {
+#ifdef USE_OLD_MUSIC_FUNCTIONS
 	_gamestate->_sound.sfx_suspend(pause);
+#endif
 	_mixer->pauseAll(pause);
 }
 

Deleted: scummvm/trunk/engines/sci/sfx/core.cpp
===================================================================
--- scummvm/trunk/engines/sci/sfx/core.cpp	2009-12-20 13:28:23 UTC (rev 46429)
+++ scummvm/trunk/engines/sci/sfx/core.cpp	2009-12-20 13:38:13 UTC (rev 46430)
@@ -1,1009 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-/* Sound subsystem core: Event handler, sound player dispatching */
-
-#include "sci/sci.h"
-#include "sci/sfx/core.h"
-#include "sci/sfx/iterator.h"
-#include "sci/sfx/softseq/mididriver.h"
-
-#include "sci/sfx/softseq/pcjr.h"
-
-#include "common/system.h"
-#include "common/timer.h"
-
-#include "sound/mixer.h"
-
-namespace Sci {
-
-/* Plays a song iterator that found a PCM through a PCM device, if possible
-** Parameters: (SongIterator *) it: The iterator to play
-**             (SongHandle) handle: Debug handle
-** Returns   : (int) 0 if the effect will not be played, nonzero if it will
-** This assumes that the last call to 'it->next()' returned SI_PCM.
-*/
-static int sfx_play_iterator_pcm(SongIterator *it, SongHandle handle);
-
-
-#pragma mark -
-
-
-class SfxPlayer {
-public:
-	/** Number of voices that can play simultaneously */
-	int _polyphony;
-
-protected:
-	MidiPlayer *_mididrv;
-
-	SongIterator *_iterator;
-	Audio::Timestamp _wakeupTime;
-	Audio::Timestamp _currentTime;
-	uint32 _pauseTimeDiff;
-
-	bool _paused;
-	bool _iteratorIsDone;
-	uint32 _tempo;
-
-	Common::Mutex _mutex;
-	int _volume;
-
-	void play_song(SongIterator *it);
-	static void player_timer_callback(void *refCon);
-
-public:
-	SfxPlayer();
-	~SfxPlayer();
-
-	/**
-	 * Initializes the player.
-	 * @param resMan	a resource manager for driver initialization
-	 * @param expected_latency	expected delay in between calls to 'maintenance' (in microseconds)
-	 * @return	Common::kNoError on success, Common::kUnknownError on failure
-	 */
-	Common::Error init(ResourceManager *resMan, int expected_latency);
-
-	/**
-	 * Adds an iterator to the song player
-	 * @param it		The iterator to play
-	 * @param start_time	The time to assume as the time the first MIDI command executes at
-	 * @return	Common::kNoError on success, Common::kUnknownError on failure
-	 *
-	 * The iterator should not be cloned (to avoid memory leaks) and
-	 * may be modified according to the needs of the player.
-	 * Implementors may use the 'sfx_iterator_combine()' function
-	 * to add iterators onto their already existing iterators.
-	 */
-	Common::Error add_iterator(SongIterator *it, uint32 start_time);
-
-	/**
-	 * Stops the currently playing song and deletes the associated iterator.
-	 * @return	Common::kNoError on success, Common::kUnknownError on failure
-	 */
-	Common::Error stop();
-
-	/**
-	 * Transmits a song iterator message to the active song.
-	 * @param msg	the message to transmit
-	 * @return	Common::kNoError on success, Common::kUnknownError on failure
-	 */
-	Common::Error iterator_message(const SongIterator::Message &msg);
-
-	/**
-	 * Pauses song playing.
-	 * @return	Common::kNoError on success, Common::kUnknownError on failure
-	 */
-	Common::Error pause();
-
-	/**
-	 * Resumes song playing after a pause.
-	 * @return	Common::kNoError on success, Common::kUnknownError on failure
-	 */
-	Common::Error resume();
-
-	/**
-	 * Pass a raw MIDI event to the synth.
-	 * @param argc	length of buffer holding the midi event
-	 * @param argv	the buffer itself
-	 */
-	void tell_synth(int buf_nr, byte *buf);
-
-	void setVolume(int vol);
-
-	int getVolume();
-};
-
-SfxPlayer::SfxPlayer() {
-	_polyphony = 0;
-
-	_mididrv = 0;
-
-	_iterator = NULL;
-	_pauseTimeDiff = 0;
-
-	_paused = false;
-	_iteratorIsDone = false;
-	_tempo = 0;
-
-	_volume = 15;
-}
-
-SfxPlayer::~SfxPlayer() {
-	if (_mididrv) {
-		_mididrv->close();
-		delete _mididrv;
-	}
-	delete _iterator;
-	_iterator = NULL;
-}
-
-void SfxPlayer::play_song(SongIterator *it) {
-	while (_iterator && _wakeupTime.msecsDiff(_currentTime) <= 0) {
-		int delay;
-		byte buf[8];
-		int result;
-
-		switch ((delay = songit_next(&(_iterator),
-		                             buf, &result,
-		                             IT_READER_MASK_ALL
-		                             | IT_READER_MAY_FREE
-		                             | IT_READER_MAY_CLEAN))) {
-
-		case SI_FINISHED:
-			delete _iterator;
-			_iterator = NULL;
-			_iteratorIsDone = true;
-			return;
-
-		case SI_IGNORE:
-		case SI_LOOP:
-		case SI_RELATIVE_CUE:
-		case SI_ABSOLUTE_CUE:
-			break;
-
-		case SI_PCM:
-			sfx_play_iterator_pcm(_iterator, 0);
-			break;
-
-		case 0:
-			static_cast<MidiDriver *>(_mididrv)->send(buf[0], buf[1], buf[2]);
-
-			break;
-
-		default:
-			_wakeupTime = _wakeupTime.addFrames(delay);
-		}
-	}
-}
-
-void SfxPlayer::tell_synth(int buf_nr, byte *buf) {
-	byte op1 = (buf_nr < 2 ? 0 : buf[1]);
-	byte op2 = (buf_nr < 3 ? 0 : buf[2]);
-
-	static_cast<MidiDriver *>(_mididrv)->send(buf[0], op1, op2);
-}
-
-void SfxPlayer::player_timer_callback(void *refCon) {
-	SfxPlayer *thePlayer = (SfxPlayer *)refCon;
-	assert(refCon);
-	Common::StackLock lock(thePlayer->_mutex);
-
-	if (thePlayer->_iterator && !thePlayer->_iteratorIsDone && !thePlayer->_paused) {
-		thePlayer->play_song(thePlayer->_iterator);
-	}
-
-	thePlayer->_currentTime = thePlayer->_currentTime.addFrames(1);
-}
-
-/* API implementation */
-
-Common::Error SfxPlayer::init(ResourceManager *resMan, int expected_latency) {
-	MidiDriverType musicDriver = MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_ADLIB);
-
-	switch (musicDriver) {
-	case MD_ADLIB:
-		// FIXME: There's no Amiga sound option, so we hook it up to Adlib
-		if (((SciEngine *)g_engine)->getPlatform() == Common::kPlatformAmiga)
-			_mididrv = MidiPlayer_Amiga_create();
-		else
-			_mididrv = MidiPlayer_Adlib_create();
-		break;
-	case MD_PCJR:
-		_mididrv = new MidiPlayer_PCJr();
-		break;
-	case MD_PCSPK:
-		_mididrv = new MidiPlayer_PCSpeaker();
-		break;
-	default:
-		break;
-	}
-
-	assert(_mididrv);
-
-	_polyphony = _mididrv->getPolyphony();
-
-	_tempo = _mididrv->getBaseTempo();
-    uint32 time = g_system->getMillis();
-	_currentTime = Audio::Timestamp(time, 1000000 / _tempo);
-	_wakeupTime = Audio::Timestamp(time, SFX_TICKS_PER_SEC);
-
-	_mididrv->setTimerCallback(this, player_timer_callback);
-	_mididrv->open(resMan);
-	_mididrv->setVolume(_volume);
-
-	return Common::kNoError;
-}
-
-Common::Error SfxPlayer::add_iterator(SongIterator *it, uint32 start_time) {
-	Common::StackLock lock(_mutex);
-	SIMSG_SEND(it, SIMSG_SET_PLAYMASK(_mididrv->getPlayMask()));
-	SIMSG_SEND(it, SIMSG_SET_RHYTHM(_mididrv->hasRhythmChannel()));
-
-	if (_iterator == NULL) {
-		// Resync with clock
-		_currentTime = Audio::Timestamp(g_system->getMillis(), 1000000 / _tempo);
-		_wakeupTime = Audio::Timestamp(start_time, SFX_TICKS_PER_SEC);
-	}
-
-	_iterator = sfx_iterator_combine(_iterator, it);
-	_iteratorIsDone = false;
-
-	return Common::kNoError;
-}
-
-Common::Error SfxPlayer::stop() {
-	debug(3, "Player: Stopping song iterator %p", (void *)_iterator);
-	Common::StackLock lock(_mutex);
-	delete _iterator;
-	_iterator = NULL;
-	for (int i = 0; i < MIDI_CHANNELS; i++)
-		static_cast<MidiDriver *>(_mididrv)->send(0xb0 + i, SCI_MIDI_CHANNEL_NOTES_OFF, 0);
-
-	return Common::kNoError;
-}
-
-Common::Error SfxPlayer::iterator_message(const SongIterator::Message &msg) {
-	Common::StackLock lock(_mutex);
-	if (!_iterator) {
-		return Common::kUnknownError;
-	}
-
-	songit_handle_message(&_iterator, msg);
-
-	return Common::kNoError;
-}
-
-Common::Error SfxPlayer::pause() {
-	Common::StackLock lock(_mutex);
-
-	_paused = true;
-	_pauseTimeDiff = _wakeupTime.msecsDiff(_currentTime);
-
-	_mididrv->playSwitch(false);
-
-	return Common::kNoError;
-}
-
-Common::Error SfxPlayer::resume() {
-	Common::StackLock lock(_mutex);
-
-	_wakeupTime = Audio::Timestamp(_currentTime.msecs() + _pauseTimeDiff, SFX_TICKS_PER_SEC);
-	_mididrv->playSwitch(true);
-	_paused = false;
-
-	return Common::kNoError;
-}
-
-void SfxPlayer::setVolume(int vol) {
-	_mididrv->setVolume(vol);
-}
-
-int SfxPlayer::getVolume() {
-	return _mididrv->getVolume();
-}
-
-#pragma mark -
-
-void SfxState::sfx_reset_player() {
-	if (_player)
-		_player->stop();
-}
-
-void SfxState::sfx_player_tell_synth(int buf_nr, byte *buf) {
-	if (_player)
-		_player->tell_synth(buf_nr, buf);
-}
-
-int SfxState::sfx_get_player_polyphony() {
-	if (_player)
-		return _player->_polyphony;
-	else
-		return 0;
-}
-
-SfxState::SfxState() {
-	_player = NULL;
-	_it = NULL;
-	_flags = 0;
-	_song = NULL;
-	_suspended = 0;
-}
-
-SfxState::~SfxState() {
-}
-
-
-void SfxState::freezeTime() {
-	/* Freezes the top song delay time */
-	const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC);
-	Song *song = _song;
-
-	while (song) {
-		song->_delay = song->_wakeupTime.frameDiff(ctime);
-		if (song->_delay < 0)
-			song->_delay = 0;
-
-		song = song->_nextPlaying;
-	}
-}
-
-void SfxState::thawTime() {
-	/* inverse of freezeTime() */
-	const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC);
-	Song *song = _song;
-
-	while (song) {
-		song->_wakeupTime = ctime.addFrames(song->_delay);
-
-		song = song->_nextPlaying;
-	}
-}
-
-#if 0
-// Unreferenced - removed
-static void _dump_playing_list(SfxState *self, char *msg) {
-	Song *song = self->_song;
-
-	fprintf(stderr, "[] Song list : [ ");
-	song = *(self->_songlib.lib);
-	while (song) {
-		fprintf(stderr, "%08lx:%d ", song->handle, song->_status);
-		song = song->_nextPlaying;
-	}
-	fprintf(stderr, "]\n");
-
-	fprintf(stderr, "[] Play list (%s) : [ " , msg);
-
-	while (song) {
-		fprintf(stderr, "%08lx ", song->handle);
-		song = song->_nextPlaying;
-	}
-
-	fprintf(stderr, "]\n");
-}
-#endif
-
-#if 0
-static void _dump_songs(SfxState *self) {
-	Song *song = self->_song;
-
-	fprintf(stderr, "Cue iterators:\n");
-	song = *(self->_songlib.lib);
-	while (song) {
-		fprintf(stderr, "  **\tHandle %08x (p%d): status %d\n",
-		        song->handle, song->_priority, song->_status);
-		SIMSG_SEND(song->_it, SIMSG_PRINT(1));
-		song = song->_next;
-	}
-
-	if (self->_player) {
-		fprintf(stderr, "Audio iterator:\n");
-		self->_player->iterator_message(SongIterator::Message(0, SIMSG_PRINT(1)));
-	}
-}
-#endif
-
-bool SfxState::isPlaying(Song *song) {
-	Song *playing_song = _song;
-
-	/*	_dump_playing_list(this, "is-playing");*/
-
-	while (playing_song) {
-		if (playing_song == song)
-			return true;
-		playing_song = playing_song->_nextPlaying;
-	}
-	return false;
-}
-
-void SfxState::setSongStatus(Song *song, int status) {
-	const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC);
-
-	switch (status) {
-
-	case SOUND_STATUS_STOPPED:
-		// Reset
-		song->_it->init();
-		break;
-
-	case SOUND_STATUS_SUSPENDED:
-	case SOUND_STATUS_WAITING:
-		if (song->_status == SOUND_STATUS_PLAYING) {
-			// Update delay, set wakeup_time
-			song->_delay += song->_wakeupTime.frameDiff(ctime);
-			song->_wakeupTime = ctime;
-		}
-		if (status == SOUND_STATUS_SUSPENDED)
-			break;
-
-		/* otherwise... */
-
-	case SOUND_STATUS_PLAYING:
-		if (song->_status == SOUND_STATUS_STOPPED) {
-			// Starting anew
-			song->_wakeupTime = ctime;
-		}
-
-		if (isPlaying(song))
-			status = SOUND_STATUS_PLAYING;
-		else
-			status = SOUND_STATUS_WAITING;
-		break;
-
-	default:
-		fprintf(stderr, "%s L%d: Attempt to set invalid song"
-		        " state %d!\n", __FILE__, __LINE__, status);
-		return;
-
-	}
-	song->_status = status;
-}
-
-/* Update internal state iff only one song may be played */
-void SfxState::updateSingleSong() {
-	Song *newsong = _songlib.findFirstActive();
-
-	if (newsong != _song) {
-		freezeTime(); /* Store song delay time */
-
-		if (_player)
-			_player->stop();
-
-		if (newsong) {
-			if (!newsong->_it)
-				return; /* Restore in progress and not ready for this yet */
-
-			/* Change song */
-			if (newsong->_status == SOUND_STATUS_WAITING)
-				setSongStatus(newsong, SOUND_STATUS_PLAYING);
-
-			/* Change instrument mappings */
-		} else {
-			/* Turn off sound */
-		}
-		if (_song) {
-			if (_song->_status == SOUND_STATUS_PLAYING)
-				setSongStatus(newsong, SOUND_STATUS_WAITING);
-		}
-
-		Common::String debugMessage = "[SFX] Changing active song:";
-		if (!_song) {
-			debugMessage += " New song:";
-		} else {
-			char tmp[50];
-			sprintf(tmp, " pausing %08lx, now playing ", _song->_handle);
-			debugMessage += tmp;
-		}
-
-		if (newsong) {
-			char tmp[20];
-			sprintf(tmp, "%08lx\n", newsong->_handle);
-			debugMessage += tmp;
-		} else {
-			debugMessage += " none\n";
-		}
-
-		debugC(2, kDebugLevelSound, "%s", debugMessage.c_str());
-				
-		_song = newsong;
-		thawTime(); /* Recover song delay time */
-
-		if (newsong && _player) {
-			SongIterator *clonesong = newsong->_it->clone(newsong->_delay);
-
-			_player->add_iterator(clonesong, newsong->_wakeupTime.msecs());
-		}
-	}
-}
-
-
-void SfxState::updateMultiSong() {
-	Song *oldfirst = _song;
-	Song *oldseeker;
-	Song *newsong = _songlib.findFirstActive();
-	Song *newseeker;
-	Song not_playing_anymore; /* Dummy object, referenced by
-				    ** songs which are no longer
-				    ** active.  */
-
-	/*	_dump_playing_list(this, "before");*/
-	freezeTime(); /* Store song delay time */
-
-	// WORKAROUND: sometimes, newsong can be NULL (e.g. in SQ4).
-	// Handle this here, so that we avoid a crash
-	if (!newsong) {	
-		// Iterators should get freed when there's only one song left playing
-		if(oldfirst && oldfirst->_status == SOUND_STATUS_STOPPED) {
-			debugC(2, kDebugLevelSound, "[SFX] Stopping song %lx\n", oldfirst->_handle);
-			if (_player && oldfirst->_it)
-				_player->iterator_message(SongIterator::Message(oldfirst->_it->ID, SIMSG_STOP));
-		}
-		return;
-	}
-
-	for (newseeker = newsong; newseeker;
-	        newseeker = newseeker->_nextPlaying) {
-		if (!newseeker || !newseeker->_it)
-			return; /* Restore in progress and not ready for this yet */
-	}
-
-	/* First, put all old songs into the 'stopping' list and
-	** mark their 'next-playing' as not_playing_anymore.  */
-	for (oldseeker = oldfirst; oldseeker;
-	        oldseeker = oldseeker->_nextStopping) {
-		oldseeker->_nextStopping = oldseeker->_nextPlaying;
-		oldseeker->_nextPlaying = &not_playing_anymore;
-
-		if (oldseeker == oldseeker->_nextPlaying) { 
-			error("updateMultiSong() failed. Breakpoint in %s, line %d", __FILE__, __LINE__);
-		}
-	}
-
-	/* Second, re-generate the new song queue. */
-	for (newseeker = newsong; newseeker; newseeker = newseeker->_nextPlaying) {
-		newseeker->_nextPlaying = _songlib.findNextActive(newseeker);
-
-		if (newseeker == newseeker->_nextPlaying) { 
-			error("updateMultiSong() failed. Breakpoint in %s, line %d", __FILE__, __LINE__);
-		}
-	}
-	/* We now need to update the currently playing song list, because we're
-	** going to use some functions that require this list to be in a sane
-	** state (particularly isPlaying(), indirectly */
-	_song = newsong;
-
-	/* Third, stop all old songs */
-	for (oldseeker = oldfirst; oldseeker;
-	        oldseeker = oldseeker->_nextStopping)
-		if (oldseeker->_nextPlaying == &not_playing_anymore) {
-			setSongStatus(oldseeker, SOUND_STATUS_SUSPENDED);
-			debugC(2, kDebugLevelSound, "[SFX] Stopping song %lx\n", oldseeker->_handle);
-
-			if (_player && oldseeker->_it)
-				_player->iterator_message(SongIterator::Message(oldseeker->_it->ID, SIMSG_STOP));
-			oldseeker->_nextPlaying = NULL; /* Clear this pointer; we don't need the tag anymore */
-		}
-
-	for (newseeker = newsong; newseeker; newseeker = newseeker->_nextPlaying) {
-		if (newseeker->_status != SOUND_STATUS_PLAYING && _player) {
-			debugC(2, kDebugLevelSound, "[SFX] Adding song %lx\n", newseeker->_it->ID);
-
-			SongIterator *clonesong = newseeker->_it->clone(newseeker->_delay);
-			_player->add_iterator(clonesong, g_system->getMillis());
-		}
-		setSongStatus(newseeker, SOUND_STATUS_PLAYING);
-	}
-
-	_song = newsong;
-	thawTime();
-	/*	_dump_playing_list(this, "after");*/
-}
-
-/* Update internal state */
-void SfxState::update() {
-	if (_flags & SFX_STATE_FLAG_MULTIPLAY)
-		updateMultiSong();
-	else
-		updateSingleSong();
-}
-
-static int sfx_play_iterator_pcm(SongIterator *it, SongHandle handle) {
-#ifdef DEBUG_SONG_API
-	fprintf(stderr, "[sfx-core] Playing PCM: %08lx\n", handle);
-#endif
-	if (g_system->getMixer()->isReady()) {
-		Audio::AudioStream *newfeed = it->getAudioStream();
-		if (newfeed) {
-			g_system->getMixer()->playInputStream(Audio::Mixer::kSFXSoundType, 0, newfeed);
-			return 1;
-		}
-	}
-	return 0;
-}
-
-#define DELAY (1000000 / SFX_TICKS_PER_SEC)
-
-void SfxState::sfx_init(ResourceManager *resMan, int flags) {
-	_songlib._lib = 0;
-	_song = NULL;
-	_flags = flags;
-
-	_player = NULL;
-
-	if (flags & SFX_STATE_FLAG_NOSOUND) {
-		warning("[SFX] Sound disabled");
-		return;
-	}
-
-#ifdef DEBUG_SONG_API
-	fprintf(stderr, "[sfx-core] Initialising: flags=%x\n", flags);
-#endif
-
-	/*-------------------*/
-	/* Initialise player */
-	/*-------------------*/
-
-	if (!resMan) {
-		warning("[SFX] Warning: No resource manager present, cannot initialise player");
-		return;
-	}
-
-	_player = new SfxPlayer();
-
-	if (!_player) {
-		warning("[SFX] No song player found");
-		return;
-	}
-
-	if (_player->init(resMan, DELAY / 1000)) {
-		warning("[SFX] Song player reported error, disabled");
-		delete _player;
-		_player = NULL;
-	}
-
-	_resMan = resMan;
-}
-
-void SfxState::sfx_exit() {
-#ifdef DEBUG_SONG_API
-	fprintf(stderr, "[sfx-core] Uninitialising\n");
-#endif
-
-	delete _player;
-	_player = 0;
-
-	g_system->getMixer()->stopAll();
-
-	_songlib.freeSounds();
-}
-
-void SfxState::sfx_suspend(bool suspend) {
-#ifdef DEBUG_SONG_API
-	fprintf(stderr, "[sfx-core] Suspending? = %d\n", suspend);
-#endif
-	if (suspend && (!_suspended)) {
-		/* suspend */
-
-		freezeTime();
-		if (_player)
-			_player->pause();
-		/* Suspend song player */
-
-	} else if (!suspend && (_suspended)) {
-		/* unsuspend */
-
-		thawTime();
-		if (_player)
-			_player->resume();
-
-		/* Unsuspend song player */
-	}
-
-	_suspended = suspend;
-}
-
-int SfxState::sfx_poll(SongHandle *handle, int *cue) {
-	if (!_song)
-		return 0; /* No milk today */
-
-	*handle = _song->_handle;
-
-#ifdef DEBUG_SONG_API
-	fprintf(stderr, "[sfx-core] Polling any (%08lx)\n", *handle);
-#endif
-	return sfx_poll_specific(*handle, cue);
-}
-
-int SfxState::sfx_poll_specific(SongHandle handle, int *cue) {
-	const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC);
-	Song *song = _song;
-
-	while (song && song->_handle != handle)
-		song = song->_nextPlaying;
-
-	if (!song)
-		return 0; /* Song not playing */
-
-	debugC(2, kDebugLevelSound, "[SFX:CUE] Polled song %08lx ", handle);
-
-	while (1) {
-		if (song->_wakeupTime.frameDiff(ctime) > 0)
-			return 0; /* Patience, young hacker! */
-
-		byte buf[8];
-		int result = songit_next(&(song->_it), buf, cue, IT_READER_MASK_ALL);
-
-		switch (result) {
-
-		case SI_FINISHED:
-			setSongStatus(song, SOUND_STATUS_STOPPED);
-			update();
-			/* ...fall through... */
-		case SI_LOOP:
-		case SI_RELATIVE_CUE:
-		case SI_ABSOLUTE_CUE:
-			if (result == SI_FINISHED)
-				debugC(2, kDebugLevelSound, " => finished");
-			else {
-				if (result == SI_LOOP)
-					debugC(2, kDebugLevelSound, " => Loop: %d (0x%x)", *cue, *cue);
-				else
-					debugC(2, kDebugLevelSound, " => Cue: %d (0x%x)", *cue, *cue);
-
-			}
-			return result;
-
-		default:
-			if (result > 0)
-				song->_wakeupTime = song->_wakeupTime.addFrames(result);
-
-			/* Delay */
-			break;
-		}
-	}
-
-}
-
-
-/*****************/
-/*  Song basics  */
-/*****************/
-
-void SfxState::sfx_add_song(SongIterator *it, int priority, SongHandle handle, int number) {
-	Song *song = _songlib.findSong(handle);
-
-#ifdef DEBUG_SONG_API
-	fprintf(stderr, "[sfx-core] Adding song: %08lx at %d, it=%p\n", handle, priority, it);
-#endif
-	if (!it) {
-		error("[SFX] Attempt to add empty song with handle %08lx", handle);
-		return;
-	}
-
-	it->init();
-
-	/* If we're already playing this, stop it */
-	/* Tell player to shut up */
-//	_dump_songs(this);
-
-	if (_player)
-		_player->iterator_message(SongIterator::Message(handle, SIMSG_STOP));
-
-	if (song) {
-		setSongStatus( song, SOUND_STATUS_STOPPED);
-
-		fprintf(stderr, "Overwriting old song (%08lx) ...\n", handle);
-		if (song->_status == SOUND_STATUS_PLAYING || song->_status == SOUND_STATUS_SUSPENDED) {
-			delete it;
-			error("Unexpected (error): Song %ld still playing/suspended (%d)",
-			        handle, song->_status);
-			return;
-		} else {
-			_songlib.removeSong(handle); /* No duplicates */
-		}
-
-	}
-
-	song = new Song(handle, it, priority);
-	song->_resourceNum = number;
-	song->_hold = 0;
-	song->_loops = 0;
-	song->_wakeupTime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC);
-	_songlib.addSong(song);
-	_song = NULL; /* As above */
-	update();
-
-	return;
-}
-
-void SfxState::sfx_remove_song(SongHandle handle) {
-#ifdef DEBUG_SONG_API
-	fprintf(stderr, "[sfx-core] Removing song: %08lx\n", handle);
-#endif
-	if (_song && _song->_handle == handle)
-		_song = NULL;
-
-	_songlib.removeSong(handle);
-	update();
-}
-
-
-
-/**********************/
-/* Song modifications */
-/**********************/
-
-#define ASSERT_SONG(s) if (!(s)) { warning("Looking up song handle %08lx failed in %s, L%d", handle, __FILE__, __LINE__); return; }
-
-void SfxState::sfx_song_set_status(SongHandle handle, int status) {
-	Song *song = _songlib.findSong(handle);
-	ASSERT_SONG(song);
-#ifdef DEBUG_SONG_API
-	fprintf(stderr, "[sfx-core] Setting song status to %d"
-	        " (0:stop, 1:play, 2:susp, 3:wait): %08lx\n", status, handle);
-#endif
-
-	setSongStatus(song, status);
-
-	update();
-}
-
-void SfxState::sfx_song_set_fade(SongHandle handle, fade_params_t *params) {
-#ifdef DEBUG_SONG_API
-	static const char *stopmsg[] = {"??? Should not happen", "Do not stop afterwards", "Stop afterwards"};
-#endif
-	Song *song = _songlib.findSong(handle);
-
-	ASSERT_SONG(song);
-
-#ifdef DEBUG_SONG_API
-	fprintf(stderr, "[sfx-core] Setting fade params of %08lx to "
-	        "final volume %d in steps of %d per %d ticks. %s.",
-	        handle, fade->final_volume, fade->step_size, fade->ticks_per_step,
-	        stopmsg[fade->action]);
-#endif
-
-	SIMSG_SEND_FADE(song->_it, params);
-
-	update();
-}
-
-void SfxState::sfx_song_renice(SongHandle handle, int priority) {
-	Song *song = _songlib.findSong(handle);
-	ASSERT_SONG(song);
-#ifdef DEBUG_SONG_API
-	fprintf(stderr, "[sfx-core] Renicing song %08lx to %d\n",
-	        handle, priority);
-#endif
-
-	song->_priority = priority;
-
-	update();
-}
-
-void SfxState::sfx_song_set_loops(SongHandle handle, int loops) {
-	Song *song = _songlib.findSong(handle);
-	SongIterator::Message msg = SongIterator::Message(handle, SIMSG_SET_LOOPS(loops));
-	ASSERT_SONG(song);
-
-	song->_loops = loops;
-#ifdef DEBUG_SONG_API
-	fprintf(stderr, "[sfx-core] Setting loops on %08lx to %d\n",
-	        handle, loops);
-#endif
-	songit_handle_message(&(song->_it), msg);
-
-	if (_player/* && _player->send_iterator_message*/)
-		/* FIXME: The above should be optional! */
-		_player->iterator_message(msg);
-}
-
-void SfxState::sfx_song_set_hold(SongHandle handle, int hold) {
-	Song *song = _songlib.findSong(handle);
-	SongIterator::Message msg = SongIterator::Message(handle, SIMSG_SET_HOLD(hold));
-	ASSERT_SONG(song);
-
-	song->_hold = hold;
-#ifdef DEBUG_SONG_API
-	fprintf(stderr, "[sfx-core] Setting hold on %08lx to %d\n",
-	        handle, hold);
-#endif
-	songit_handle_message(&(song->_it), msg);
-
-	if (_player/* && _player->send_iterator_message*/)
-		/* FIXME: The above should be optional! */
-		_player->iterator_message(msg);
-}
-
-/* Different from the one in iterator.c */
-static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0,
-                                    3, 3, 0, 3, 2, 0, 3, 0
-                                   };
-
-static const SongHandle midi_send_base = 0xffff0000;
-
-Common::Error SfxState::sfx_send_midi(SongHandle handle, int channel,
-	int command, int arg1, int arg2) {
-	byte buffer[5];
-
-	/* Yes, in that order. SCI channel mutes are actually done via
-	   a counting semaphore. 0 means to decrement the counter, 1
-	   to increment it. */
-	static const char *channel_state[] = {"ON", "OFF"};
-
-	if (command == 0xb0 &&
-	        arg1 == SCI_MIDI_CHANNEL_MUTE) {
-		warning("TODO: channel mute (channel %d %s)", channel, channel_state[arg2]);
-		/* We need to have a GET_PLAYMASK interface to use
-		   here. SET_PLAYMASK we've got.
-		*/
-		return Common::kNoError;
-	}
-
-	buffer[0] = channel | command; /* No channel remapping yet */
-
-	switch (command) {
-	case 0x80 :
-	case 0x90 :
-	case 0xb0 :
-		buffer[1] = arg1 & 0xff;
-		buffer[2] = arg2 & 0xff;
-		break;
-	case 0xc0 :
-		buffer[1] = arg1 & 0xff;
-		break;
-	case 0xe0 :
-		buffer[1] = (arg1 & 0x7f) | 0x80;
-		buffer[2] = (arg1 & 0xff00) >> 7;
-		break;
-	default:
-		warning("Unexpected explicit MIDI command %02x", command);
-		return Common::kUnknownError;
-	}
-
-	if (_player)
-		_player->tell_synth(MIDI_cmdlen[command >> 4], buffer);
-	return Common::kNoError;
-}
-
-int SfxState::sfx_getVolume() {
-	return _player->getVolume();
-}
-
-void SfxState::sfx_setVolume(int volume) {
-	_player->setVolume(volume);
-}
-
-void SfxState::sfx_all_stop() {
-#ifdef DEBUG_SONG_API
-	fprintf(stderr, "[sfx-core] All stop\n");
-#endif
-
-	_songlib.freeSounds();
-	update();
-}
-
-} // End of namespace Sci

Deleted: scummvm/trunk/engines/sci/sfx/core.h
===================================================================
--- scummvm/trunk/engines/sci/sfx/core.h	2009-12-20 13:28:23 UTC (rev 46429)
+++ scummvm/trunk/engines/sci/sfx/core.h	2009-12-20 13:38:13 UTC (rev 46430)
@@ -1,203 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-/* Sound engine */
-#ifndef SCI_SFX_CORE_H
-#define SCI_SFX_CORE_H
-
-#include "common/error.h"
-#include "sci/sfx/songlib.h"
-#include "sci/resource.h"
-
-namespace Sci {
-
-class SfxPlayer;
-class SongIterator;
-struct fade_params_t;
-
-#define SFX_TICKS_PER_SEC 60 /* MIDI ticks per second */
-
-
-#define SFX_STATE_FLAG_MULTIPLAY (1 << 0) /* More than one song playable
-** simultaneously ? */
-#define SFX_STATE_FLAG_NOSOUND	 (1 << 1) /* Completely disable sound playing */
-
-class SfxState {
-private:
-	SfxPlayer *_player;
-
-public:	// FIXME, make private
-	SongIterator *_it; /**< The song iterator at the heart of things */
-	uint _flags; /**< SFX_STATE_FLAG_* */
-	SongLibrary _songlib; /**< Song library */
-	Song *_song; /**< Active song, or start of active song chain */
-	bool _suspended; /**< Whether we are suspended */
-	ResourceManager *_resMan;
-
-public:
-	SfxState();
-	~SfxState();
-
-	/***********/
-	/* General */
-	/***********/
-
-	/* Initializes the sound engine
-	** Parameters: (ResourceManager *) resMan: Resource manager for initialization
-	**             (int) flags: SFX_STATE_FLAG_*
-	*/
-	void sfx_init(ResourceManager *resMan, int flags);
-
-	/** Deinitializes the sound subsystem. */
-	void sfx_exit();
-
-	/* Suspends/unsuspends the sound sybsystem
-	** Parameters: (int) suspend: Whether to suspend (non-null) or to unsuspend
-	*/
-	void sfx_suspend(bool suspend);
-
-	/* Polls the sound server for cues etc.
-	** Returns   : (int) 0 if the cue queue is empty, SI_LOOP, SI_CUE, or SI_FINISHED otherwise
-	**             (SongHandle) *handle: The affected handle
-	**             (int) *cue: The sound cue number (if SI_CUE), or the loop number (if SI_LOOP)
-	*/
-	int sfx_poll(SongHandle *handle, int *cue);
-
-	/* Polls the sound server for cues etc.
-	** Parameters: (SongHandle) handle: The handle to poll
-	** Returns   : (int) 0 if the cue queue is empty, SI_LOOP, SI_CUE, or SI_FINISHED otherwise
-	**             (int) *cue: The sound cue number (if SI_CUE), or the loop number (if SI_LOOP)
-	*/
-	int sfx_poll_specific(SongHandle handle, int *cue);
-
-	/* Determines the current global volume settings
-	** Returns   : (int) The global volume, between 0 (silent) and 127 (max. volume)
-	*/
-	int sfx_getVolume();
-
-	/* Determines the current global volume settings
-	** Parameters: (int) volume: The new global volume, between 0 and 127 (see above)
-	*/
-	void sfx_setVolume(int volume);
-
-	/* Stops all songs currently playing, purges song library
-	*/
-	void sfx_all_stop();
-
-
-	/*****************/
-	/*  Song basics  */
-	/*****************/
-
-	/* Adds a song to the internal sound library
-	** Parameters: (SongIterator *) it: The iterator describing the song
-	**             (int) priority: Initial song priority (higher <-> more important)
-	**             (SongHandle) handle: The handle to associate with the song
-	*/
-	void sfx_add_song(SongIterator *it, int priority, SongHandle handle, int resnum);
-
-
-	/* Deletes a song and its associated song iterator from the song queue
-	** Parameters: (SongHandle) handle: The song to remove
-	*/
-	void sfx_remove_song(SongHandle handle);
-
-
-	/**********************/
-	/* Song modifications */
-	/**********************/
-
-
-	/* Sets the song status, i.e. whether it is playing, suspended, or stopped.
-	** Parameters: (SongHandle) handle: Handle of the song to modify
-	**             (int) status: The song status the song should assume
-	** WAITING and PLAYING are set implicitly and essentially describe the same state
-	** as far as this function is concerned.
-	*/
-	void sfx_song_set_status(SongHandle handle, int status);
-
-	/* Sets the new song priority
-	** Parameters: (SongHandle) handle: The handle to modify
-	**             (int) priority: The priority to set
-	*/
-	void sfx_song_renice(SongHandle handle, int priority);
-
-	/* Sets the number of loops for the specified song
-	** Parameters: (SongHandle) handle: The song handle to reference
-	**             (int) loops: Number of loops to set
-	*/
-	void sfx_song_set_loops(SongHandle handle, int loops);
-
-	/* Sets the number of loops for the specified song
-	** Parameters: (SongHandle) handle: The song handle to reference
-	**             (int) hold: Number of loops to setn
-	*/
-	void sfx_song_set_hold(SongHandle handle, int hold);
-
-	/* Instructs a song to be faded out
-	** Parameters: (SongHandle) handle: The song handle to reference
-	**             (fade_params_t *) fade_setup: The precise fade-out configuration to use
-	*/
-	void sfx_song_set_fade(SongHandle handle, fade_params_t *fade_setup);
-
-
-	// Previously undocumented:
-	Common::Error sfx_send_midi(SongHandle handle, int channel,
-		int command, int arg1, int arg2);
-
-	// misc
-
-	/**
-	 * Determines the polyphony of the player in use.
-	 * @return Number of voices the active player can emit
-	 */
-	int sfx_get_player_polyphony();
-
-	/**
-	 * Tells the player to stop its internal iterator.
-	 */
-	void sfx_reset_player();
-
-	/**
-	 * Pass a raw MIDI event to the synth of the player.
-	 * @param	argc	Length of buffer holding the midi event
-	 * @param	argv	The buffer itself
-	 */
-	void sfx_player_tell_synth(int buf_nr, byte *buf);
-
-protected:
-	void freezeTime();
-	void thawTime();
-
-	bool isPlaying(Song *song);
-	void setSongStatus(Song *song, int status);
-	void updateSingleSong();
-	void updateMultiSong();
-	void update();
-};
-
-} // End of namespace Sci
-
-#endif // SCI_SFX_CORE_H


Property changes on: scummvm/trunk/engines/sci/sfx/iterator
___________________________________________________________________
Added: svn:ignore
   + .deps
*.o
lib*.a


Copied: scummvm/trunk/engines/sci/sfx/iterator/core.cpp (from rev 46418, scummvm/trunk/engines/sci/sfx/core.cpp)
===================================================================
--- scummvm/trunk/engines/sci/sfx/iterator/core.cpp	                        (rev 0)
+++ scummvm/trunk/engines/sci/sfx/iterator/core.cpp	2009-12-20 13:38:13 UTC (rev 46430)
@@ -0,0 +1,1013 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/* Sound subsystem core: Event handler, sound player dispatching */
+
+#include "sci/sci.h"
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+
+#include "sci/sfx/iterator/core.h"
+#include "sci/sfx/iterator/iterator.h"
+#include "sci/sfx/softseq/mididriver.h"
+
+#include "sci/sfx/softseq/pcjr.h"
+
+#include "common/system.h"
+#include "common/timer.h"
+
+#include "sound/mixer.h"
+
+namespace Sci {
+
+/* Plays a song iterator that found a PCM through a PCM device, if possible
+** Parameters: (SongIterator *) it: The iterator to play
+**             (SongHandle) handle: Debug handle
+** Returns   : (int) 0 if the effect will not be played, nonzero if it will
+** This assumes that the last call to 'it->next()' returned SI_PCM.
+*/
+static int sfx_play_iterator_pcm(SongIterator *it, SongHandle handle);
+
+
+#pragma mark -
+
+
+class SfxPlayer {
+public:
+	/** Number of voices that can play simultaneously */
+	int _polyphony;
+
+protected:
+	MidiPlayer *_mididrv;
+
+	SongIterator *_iterator;
+	Audio::Timestamp _wakeupTime;
+	Audio::Timestamp _currentTime;
+	uint32 _pauseTimeDiff;
+
+	bool _paused;
+	bool _iteratorIsDone;
+	uint32 _tempo;
+
+	Common::Mutex _mutex;
+	int _volume;
+
+	void play_song(SongIterator *it);
+	static void player_timer_callback(void *refCon);
+
+public:
+	SfxPlayer();
+	~SfxPlayer();
+
+	/**
+	 * Initializes the player.
+	 * @param resMan	a resource manager for driver initialization
+	 * @param expected_latency	expected delay in between calls to 'maintenance' (in microseconds)
+	 * @return	Common::kNoError on success, Common::kUnknownError on failure
+	 */
+	Common::Error init(ResourceManager *resMan, int expected_latency);
+
+	/**
+	 * Adds an iterator to the song player
+	 * @param it		The iterator to play
+	 * @param start_time	The time to assume as the time the first MIDI command executes at
+	 * @return	Common::kNoError on success, Common::kUnknownError on failure
+	 *
+	 * The iterator should not be cloned (to avoid memory leaks) and
+	 * may be modified according to the needs of the player.
+	 * Implementors may use the 'sfx_iterator_combine()' function
+	 * to add iterators onto their already existing iterators.
+	 */
+	Common::Error add_iterator(SongIterator *it, uint32 start_time);
+
+	/**
+	 * Stops the currently playing song and deletes the associated iterator.
+	 * @return	Common::kNoError on success, Common::kUnknownError on failure
+	 */
+	Common::Error stop();
+
+	/**
+	 * Transmits a song iterator message to the active song.
+	 * @param msg	the message to transmit
+	 * @return	Common::kNoError on success, Common::kUnknownError on failure
+	 */
+	Common::Error iterator_message(const SongIterator::Message &msg);
+
+	/**
+	 * Pauses song playing.
+	 * @return	Common::kNoError on success, Common::kUnknownError on failure
+	 */
+	Common::Error pause();
+
+	/**
+	 * Resumes song playing after a pause.
+	 * @return	Common::kNoError on success, Common::kUnknownError on failure
+	 */
+	Common::Error resume();
+
+	/**
+	 * Pass a raw MIDI event to the synth.
+	 * @param argc	length of buffer holding the midi event
+	 * @param argv	the buffer itself
+	 */
+	void tell_synth(int buf_nr, byte *buf);
+
+	void setVolume(int vol);
+
+	int getVolume();
+};
+
+SfxPlayer::SfxPlayer() {
+	_polyphony = 0;
+
+	_mididrv = 0;
+
+	_iterator = NULL;
+	_pauseTimeDiff = 0;
+
+	_paused = false;
+	_iteratorIsDone = false;
+	_tempo = 0;
+
+	_volume = 15;
+}
+
+SfxPlayer::~SfxPlayer() {
+	if (_mididrv) {
+		_mididrv->close();
+		delete _mididrv;
+	}
+	delete _iterator;
+	_iterator = NULL;
+}
+
+void SfxPlayer::play_song(SongIterator *it) {
+	while (_iterator && _wakeupTime.msecsDiff(_currentTime) <= 0) {
+		int delay;
+		byte buf[8];
+		int result;
+
+		switch ((delay = songit_next(&(_iterator),
+		                             buf, &result,
+		                             IT_READER_MASK_ALL
+		                             | IT_READER_MAY_FREE
+		                             | IT_READER_MAY_CLEAN))) {
+
+		case SI_FINISHED:
+			delete _iterator;
+			_iterator = NULL;
+			_iteratorIsDone = true;
+			return;
+
+		case SI_IGNORE:
+		case SI_LOOP:
+		case SI_RELATIVE_CUE:
+		case SI_ABSOLUTE_CUE:
+			break;
+
+		case SI_PCM:
+			sfx_play_iterator_pcm(_iterator, 0);
+			break;
+
+		case 0:
+			static_cast<MidiDriver *>(_mididrv)->send(buf[0], buf[1], buf[2]);
+
+			break;
+
+		default:
+			_wakeupTime = _wakeupTime.addFrames(delay);
+		}
+	}
+}
+
+void SfxPlayer::tell_synth(int buf_nr, byte *buf) {
+	byte op1 = (buf_nr < 2 ? 0 : buf[1]);
+	byte op2 = (buf_nr < 3 ? 0 : buf[2]);
+
+	static_cast<MidiDriver *>(_mididrv)->send(buf[0], op1, op2);
+}
+
+void SfxPlayer::player_timer_callback(void *refCon) {
+	SfxPlayer *thePlayer = (SfxPlayer *)refCon;
+	assert(refCon);
+	Common::StackLock lock(thePlayer->_mutex);
+
+	if (thePlayer->_iterator && !thePlayer->_iteratorIsDone && !thePlayer->_paused) {
+		thePlayer->play_song(thePlayer->_iterator);
+	}
+
+	thePlayer->_currentTime = thePlayer->_currentTime.addFrames(1);
+}
+
+/* API implementation */
+
+Common::Error SfxPlayer::init(ResourceManager *resMan, int expected_latency) {
+	MidiDriverType musicDriver = MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_ADLIB);
+
+	switch (musicDriver) {
+	case MD_ADLIB:
+		// FIXME: There's no Amiga sound option, so we hook it up to Adlib
+		if (((SciEngine *)g_engine)->getPlatform() == Common::kPlatformAmiga)
+			_mididrv = MidiPlayer_Amiga_create();
+		else
+			_mididrv = MidiPlayer_Adlib_create();
+		break;
+	case MD_PCJR:
+		_mididrv = new MidiPlayer_PCJr();
+		break;
+	case MD_PCSPK:
+		_mididrv = new MidiPlayer_PCSpeaker();
+		break;
+	default:
+		break;
+	}
+
+	assert(_mididrv);
+
+	_polyphony = _mididrv->getPolyphony();
+
+	_tempo = _mididrv->getBaseTempo();
+    uint32 time = g_system->getMillis();
+	_currentTime = Audio::Timestamp(time, 1000000 / _tempo);
+	_wakeupTime = Audio::Timestamp(time, SFX_TICKS_PER_SEC);
+
+	_mididrv->setTimerCallback(this, player_timer_callback);
+	_mididrv->open(resMan);
+	_mididrv->setVolume(_volume);
+
+	return Common::kNoError;
+}
+
+Common::Error SfxPlayer::add_iterator(SongIterator *it, uint32 start_time) {
+	Common::StackLock lock(_mutex);
+	SIMSG_SEND(it, SIMSG_SET_PLAYMASK(_mididrv->getPlayMask()));
+	SIMSG_SEND(it, SIMSG_SET_RHYTHM(_mididrv->hasRhythmChannel()));
+
+	if (_iterator == NULL) {
+		// Resync with clock
+		_currentTime = Audio::Timestamp(g_system->getMillis(), 1000000 / _tempo);
+		_wakeupTime = Audio::Timestamp(start_time, SFX_TICKS_PER_SEC);
+	}
+
+	_iterator = sfx_iterator_combine(_iterator, it);
+	_iteratorIsDone = false;
+
+	return Common::kNoError;
+}
+
+Common::Error SfxPlayer::stop() {
+	debug(3, "Player: Stopping song iterator %p", (void *)_iterator);
+	Common::StackLock lock(_mutex);
+	delete _iterator;
+	_iterator = NULL;
+	for (int i = 0; i < MIDI_CHANNELS; i++)
+		static_cast<MidiDriver *>(_mididrv)->send(0xb0 + i, SCI_MIDI_CHANNEL_NOTES_OFF, 0);
+
+	return Common::kNoError;
+}
+
+Common::Error SfxPlayer::iterator_message(const SongIterator::Message &msg) {
+	Common::StackLock lock(_mutex);
+	if (!_iterator) {
+		return Common::kUnknownError;
+	}
+
+	songit_handle_message(&_iterator, msg);
+
+	return Common::kNoError;
+}
+
+Common::Error SfxPlayer::pause() {
+	Common::StackLock lock(_mutex);
+
+	_paused = true;
+	_pauseTimeDiff = _wakeupTime.msecsDiff(_currentTime);
+
+	_mididrv->playSwitch(false);
+
+	return Common::kNoError;
+}
+
+Common::Error SfxPlayer::resume() {
+	Common::StackLock lock(_mutex);
+
+	_wakeupTime = Audio::Timestamp(_currentTime.msecs() + _pauseTimeDiff, SFX_TICKS_PER_SEC);
+	_mididrv->playSwitch(true);
+	_paused = false;
+
+	return Common::kNoError;
+}
+
+void SfxPlayer::setVolume(int vol) {
+	_mididrv->setVolume(vol);
+}
+
+int SfxPlayer::getVolume() {
+	return _mididrv->getVolume();
+}
+
+#pragma mark -
+
+void SfxState::sfx_reset_player() {
+	if (_player)
+		_player->stop();
+}
+
+void SfxState::sfx_player_tell_synth(int buf_nr, byte *buf) {
+	if (_player)
+		_player->tell_synth(buf_nr, buf);
+}
+
+int SfxState::sfx_get_player_polyphony() {
+	if (_player)
+		return _player->_polyphony;
+	else
+		return 0;
+}
+
+SfxState::SfxState() {
+	_player = NULL;
+	_it = NULL;
+	_flags = 0;
+	_song = NULL;
+	_suspended = 0;
+}
+
+SfxState::~SfxState() {
+}
+
+
+void SfxState::freezeTime() {
+	/* Freezes the top song delay time */
+	const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC);
+	Song *song = _song;
+
+	while (song) {
+		song->_delay = song->_wakeupTime.frameDiff(ctime);
+		if (song->_delay < 0)
+			song->_delay = 0;
+
+		song = song->_nextPlaying;
+	}
+}
+
+void SfxState::thawTime() {
+	/* inverse of freezeTime() */
+	const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC);
+	Song *song = _song;
+
+	while (song) {
+		song->_wakeupTime = ctime.addFrames(song->_delay);
+
+		song = song->_nextPlaying;
+	}
+}
+
+#if 0
+// Unreferenced - removed
+static void _dump_playing_list(SfxState *self, char *msg) {
+	Song *song = self->_song;
+
+	fprintf(stderr, "[] Song list : [ ");
+	song = *(self->_songlib.lib);
+	while (song) {
+		fprintf(stderr, "%08lx:%d ", song->handle, song->_status);
+		song = song->_nextPlaying;
+	}
+	fprintf(stderr, "]\n");
+
+	fprintf(stderr, "[] Play list (%s) : [ " , msg);
+
+	while (song) {
+		fprintf(stderr, "%08lx ", song->handle);
+		song = song->_nextPlaying;
+	}
+
+	fprintf(stderr, "]\n");
+}
+#endif
+
+#if 0
+static void _dump_songs(SfxState *self) {
+	Song *song = self->_song;
+
+	fprintf(stderr, "Cue iterators:\n");
+	song = *(self->_songlib.lib);
+	while (song) {
+		fprintf(stderr, "  **\tHandle %08x (p%d): status %d\n",
+		        song->handle, song->_priority, song->_status);
+		SIMSG_SEND(song->_it, SIMSG_PRINT(1));
+		song = song->_next;
+	}
+
+	if (self->_player) {
+		fprintf(stderr, "Audio iterator:\n");
+		self->_player->iterator_message(SongIterator::Message(0, SIMSG_PRINT(1)));
+	}
+}
+#endif
+
+bool SfxState::isPlaying(Song *song) {
+	Song *playing_song = _song;
+
+	/*	_dump_playing_list(this, "is-playing");*/
+
+	while (playing_song) {
+		if (playing_song == song)
+			return true;
+		playing_song = playing_song->_nextPlaying;
+	}
+	return false;
+}
+
+void SfxState::setSongStatus(Song *song, int status) {
+	const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC);
+
+	switch (status) {
+
+	case SOUND_STATUS_STOPPED:
+		// Reset
+		song->_it->init();
+		break;
+
+	case SOUND_STATUS_SUSPENDED:
+	case SOUND_STATUS_WAITING:
+		if (song->_status == SOUND_STATUS_PLAYING) {
+			// Update delay, set wakeup_time
+			song->_delay += song->_wakeupTime.frameDiff(ctime);
+			song->_wakeupTime = ctime;
+		}
+		if (status == SOUND_STATUS_SUSPENDED)
+			break;
+
+		/* otherwise... */
+
+	case SOUND_STATUS_PLAYING:
+		if (song->_status == SOUND_STATUS_STOPPED) {
+			// Starting anew
+			song->_wakeupTime = ctime;
+		}
+
+		if (isPlaying(song))
+			status = SOUND_STATUS_PLAYING;
+		else
+			status = SOUND_STATUS_WAITING;
+		break;
+
+	default:
+		fprintf(stderr, "%s L%d: Attempt to set invalid song"
+		        " state %d!\n", __FILE__, __LINE__, status);
+		return;
+
+	}
+	song->_status = status;
+}
+
+/* Update internal state iff only one song may be played */
+void SfxState::updateSingleSong() {
+	Song *newsong = _songlib.findFirstActive();
+
+	if (newsong != _song) {
+		freezeTime(); /* Store song delay time */
+
+		if (_player)
+			_player->stop();
+
+		if (newsong) {
+			if (!newsong->_it)
+				return; /* Restore in progress and not ready for this yet */
+
+			/* Change song */
+			if (newsong->_status == SOUND_STATUS_WAITING)
+				setSongStatus(newsong, SOUND_STATUS_PLAYING);
+
+			/* Change instrument mappings */
+		} else {
+			/* Turn off sound */
+		}
+		if (_song) {
+			if (_song->_status == SOUND_STATUS_PLAYING)
+				setSongStatus(newsong, SOUND_STATUS_WAITING);
+		}
+
+		Common::String debugMessage = "[SFX] Changing active song:";
+		if (!_song) {
+			debugMessage += " New song:";
+		} else {
+			char tmp[50];
+			sprintf(tmp, " pausing %08lx, now playing ", _song->_handle);
+			debugMessage += tmp;
+		}
+
+		if (newsong) {
+			char tmp[20];
+			sprintf(tmp, "%08lx\n", newsong->_handle);
+			debugMessage += tmp;
+		} else {
+			debugMessage += " none\n";
+		}
+
+		debugC(2, kDebugLevelSound, "%s", debugMessage.c_str());
+				
+		_song = newsong;
+		thawTime(); /* Recover song delay time */
+
+		if (newsong && _player) {
+			SongIterator *clonesong = newsong->_it->clone(newsong->_delay);
+
+			_player->add_iterator(clonesong, newsong->_wakeupTime.msecs());
+		}
+	}
+}
+
+
+void SfxState::updateMultiSong() {
+	Song *oldfirst = _song;
+	Song *oldseeker;
+	Song *newsong = _songlib.findFirstActive();
+	Song *newseeker;
+	Song not_playing_anymore; /* Dummy object, referenced by
+				    ** songs which are no longer
+				    ** active.  */
+
+	/*	_dump_playing_list(this, "before");*/
+	freezeTime(); /* Store song delay time */
+
+	// WORKAROUND: sometimes, newsong can be NULL (e.g. in SQ4).
+	// Handle this here, so that we avoid a crash
+	if (!newsong) {	
+		// Iterators should get freed when there's only one song left playing
+		if(oldfirst && oldfirst->_status == SOUND_STATUS_STOPPED) {
+			debugC(2, kDebugLevelSound, "[SFX] Stopping song %lx\n", oldfirst->_handle);
+			if (_player && oldfirst->_it)
+				_player->iterator_message(SongIterator::Message(oldfirst->_it->ID, SIMSG_STOP));
+		}
+		return;
+	}
+
+	for (newseeker = newsong; newseeker;
+	        newseeker = newseeker->_nextPlaying) {
+		if (!newseeker || !newseeker->_it)
+			return; /* Restore in progress and not ready for this yet */
+	}
+
+	/* First, put all old songs into the 'stopping' list and
+	** mark their 'next-playing' as not_playing_anymore.  */
+	for (oldseeker = oldfirst; oldseeker;
+	        oldseeker = oldseeker->_nextStopping) {
+		oldseeker->_nextStopping = oldseeker->_nextPlaying;
+		oldseeker->_nextPlaying = &not_playing_anymore;
+
+		if (oldseeker == oldseeker->_nextPlaying) { 
+			error("updateMultiSong() failed. Breakpoint in %s, line %d", __FILE__, __LINE__);
+		}
+	}
+
+	/* Second, re-generate the new song queue. */
+	for (newseeker = newsong; newseeker; newseeker = newseeker->_nextPlaying) {
+		newseeker->_nextPlaying = _songlib.findNextActive(newseeker);
+
+		if (newseeker == newseeker->_nextPlaying) { 
+			error("updateMultiSong() failed. Breakpoint in %s, line %d", __FILE__, __LINE__);
+		}
+	}
+	/* We now need to update the currently playing song list, because we're
+	** going to use some functions that require this list to be in a sane
+	** state (particularly isPlaying(), indirectly */
+	_song = newsong;
+
+	/* Third, stop all old songs */
+	for (oldseeker = oldfirst; oldseeker;
+	        oldseeker = oldseeker->_nextStopping)
+		if (oldseeker->_nextPlaying == &not_playing_anymore) {
+			setSongStatus(oldseeker, SOUND_STATUS_SUSPENDED);
+			debugC(2, kDebugLevelSound, "[SFX] Stopping song %lx\n", oldseeker->_handle);
+
+			if (_player && oldseeker->_it)
+				_player->iterator_message(SongIterator::Message(oldseeker->_it->ID, SIMSG_STOP));
+			oldseeker->_nextPlaying = NULL; /* Clear this pointer; we don't need the tag anymore */
+		}
+
+	for (newseeker = newsong; newseeker; newseeker = newseeker->_nextPlaying) {
+		if (newseeker->_status != SOUND_STATUS_PLAYING && _player) {
+			debugC(2, kDebugLevelSound, "[SFX] Adding song %lx\n", newseeker->_it->ID);
+
+			SongIterator *clonesong = newseeker->_it->clone(newseeker->_delay);
+			_player->add_iterator(clonesong, g_system->getMillis());
+		}
+		setSongStatus(newseeker, SOUND_STATUS_PLAYING);
+	}
+
+	_song = newsong;
+	thawTime();
+	/*	_dump_playing_list(this, "after");*/
+}
+
+/* Update internal state */
+void SfxState::update() {
+	if (_flags & SFX_STATE_FLAG_MULTIPLAY)
+		updateMultiSong();
+	else
+		updateSingleSong();
+}
+
+static int sfx_play_iterator_pcm(SongIterator *it, SongHandle handle) {
+#ifdef DEBUG_SONG_API
+	fprintf(stderr, "[sfx-core] Playing PCM: %08lx\n", handle);
+#endif
+	if (g_system->getMixer()->isReady()) {
+		Audio::AudioStream *newfeed = it->getAudioStream();
+		if (newfeed) {
+			g_system->getMixer()->playInputStream(Audio::Mixer::kSFXSoundType, 0, newfeed);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+#define DELAY (1000000 / SFX_TICKS_PER_SEC)
+
+void SfxState::sfx_init(ResourceManager *resMan, int flags) {
+	_songlib._lib = 0;
+	_song = NULL;
+	_flags = flags;
+
+	_player = NULL;
+
+	if (flags & SFX_STATE_FLAG_NOSOUND) {
+		warning("[SFX] Sound disabled");
+		return;
+	}
+
+#ifdef DEBUG_SONG_API
+	fprintf(stderr, "[sfx-core] Initialising: flags=%x\n", flags);
+#endif
+
+	/*-------------------*/
+	/* Initialise player */
+	/*-------------------*/
+
+	if (!resMan) {
+		warning("[SFX] Warning: No resource manager present, cannot initialise player");
+		return;
+	}
+
+	_player = new SfxPlayer();
+
+	if (!_player) {
+		warning("[SFX] No song player found");
+		return;
+	}
+
+	if (_player->init(resMan, DELAY / 1000)) {
+		warning("[SFX] Song player reported error, disabled");
+		delete _player;
+		_player = NULL;
+	}
+
+	_resMan = resMan;
+}
+
+void SfxState::sfx_exit() {
+#ifdef DEBUG_SONG_API
+	fprintf(stderr, "[sfx-core] Uninitialising\n");
+#endif
+
+	delete _player;
+	_player = 0;
+
+	g_system->getMixer()->stopAll();
+
+	_songlib.freeSounds();
+}
+
+void SfxState::sfx_suspend(bool suspend) {
+#ifdef DEBUG_SONG_API
+	fprintf(stderr, "[sfx-core] Suspending? = %d\n", suspend);
+#endif
+	if (suspend && (!_suspended)) {
+		/* suspend */
+
+		freezeTime();
+		if (_player)
+			_player->pause();
+		/* Suspend song player */
+
+	} else if (!suspend && (_suspended)) {
+		/* unsuspend */
+
+		thawTime();
+		if (_player)
+			_player->resume();
+
+		/* Unsuspend song player */
+	}
+
+	_suspended = suspend;
+}
+
+int SfxState::sfx_poll(SongHandle *handle, int *cue) {
+	if (!_song)
+		return 0; /* No milk today */
+
+	*handle = _song->_handle;
+
+#ifdef DEBUG_SONG_API
+	fprintf(stderr, "[sfx-core] Polling any (%08lx)\n", *handle);
+#endif
+	return sfx_poll_specific(*handle, cue);
+}
+
+int SfxState::sfx_poll_specific(SongHandle handle, int *cue) {
+	const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC);
+	Song *song = _song;
+
+	while (song && song->_handle != handle)
+		song = song->_nextPlaying;
+
+	if (!song)
+		return 0; /* Song not playing */
+
+	debugC(2, kDebugLevelSound, "[SFX:CUE] Polled song %08lx ", handle);
+
+	while (1) {
+		if (song->_wakeupTime.frameDiff(ctime) > 0)
+			return 0; /* Patience, young hacker! */
+
+		byte buf[8];
+		int result = songit_next(&(song->_it), buf, cue, IT_READER_MASK_ALL);
+
+		switch (result) {
+
+		case SI_FINISHED:
+			setSongStatus(song, SOUND_STATUS_STOPPED);
+			update();
+			/* ...fall through... */
+		case SI_LOOP:
+		case SI_RELATIVE_CUE:
+		case SI_ABSOLUTE_CUE:
+			if (result == SI_FINISHED)
+				debugC(2, kDebugLevelSound, " => finished");
+			else {
+				if (result == SI_LOOP)
+					debugC(2, kDebugLevelSound, " => Loop: %d (0x%x)", *cue, *cue);
+				else
+					debugC(2, kDebugLevelSound, " => Cue: %d (0x%x)", *cue, *cue);
+
+			}
+			return result;
+
+		default:
+			if (result > 0)
+				song->_wakeupTime = song->_wakeupTime.addFrames(result);
+
+			/* Delay */
+			break;
+		}
+	}
+
+}
+
+
+/*****************/
+/*  Song basics  */
+/*****************/
+
+void SfxState::sfx_add_song(SongIterator *it, int priority, SongHandle handle, int number) {
+	Song *song = _songlib.findSong(handle);
+
+#ifdef DEBUG_SONG_API
+	fprintf(stderr, "[sfx-core] Adding song: %08lx at %d, it=%p\n", handle, priority, it);
+#endif
+	if (!it) {
+		error("[SFX] Attempt to add empty song with handle %08lx", handle);
+		return;
+	}
+
+	it->init();
+
+	/* If we're already playing this, stop it */
+	/* Tell player to shut up */
+//	_dump_songs(this);
+
+	if (_player)
+		_player->iterator_message(SongIterator::Message(handle, SIMSG_STOP));
+
+	if (song) {
+		setSongStatus( song, SOUND_STATUS_STOPPED);
+
+		fprintf(stderr, "Overwriting old song (%08lx) ...\n", handle);
+		if (song->_status == SOUND_STATUS_PLAYING || song->_status == SOUND_STATUS_SUSPENDED) {
+			delete it;
+			error("Unexpected (error): Song %ld still playing/suspended (%d)",
+			        handle, song->_status);
+			return;
+		} else {
+			_songlib.removeSong(handle); /* No duplicates */
+		}
+
+	}
+
+	song = new Song(handle, it, priority);
+	song->_resourceNum = number;
+	song->_hold = 0;
+	song->_loops = 0;
+	song->_wakeupTime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC);
+	_songlib.addSong(song);
+	_song = NULL; /* As above */
+	update();
+
+	return;
+}
+
+void SfxState::sfx_remove_song(SongHandle handle) {
+#ifdef DEBUG_SONG_API
+	fprintf(stderr, "[sfx-core] Removing song: %08lx\n", handle);
+#endif
+	if (_song && _song->_handle == handle)
+		_song = NULL;
+
+	_songlib.removeSong(handle);
+	update();
+}
+
+
+
+/**********************/
+/* Song modifications */
+/**********************/
+
+#define ASSERT_SONG(s) if (!(s)) { warning("Looking up song handle %08lx failed in %s, L%d", handle, __FILE__, __LINE__); return; }
+
+void SfxState::sfx_song_set_status(SongHandle handle, int status) {
+	Song *song = _songlib.findSong(handle);
+	ASSERT_SONG(song);
+#ifdef DEBUG_SONG_API
+	fprintf(stderr, "[sfx-core] Setting song status to %d"
+	        " (0:stop, 1:play, 2:susp, 3:wait): %08lx\n", status, handle);
+#endif
+
+	setSongStatus(song, status);
+
+	update();
+}
+
+void SfxState::sfx_song_set_fade(SongHandle handle, fade_params_t *params) {
+#ifdef DEBUG_SONG_API
+	static const char *stopmsg[] = {"??? Should not happen", "Do not stop afterwards", "Stop afterwards"};
+#endif
+	Song *song = _songlib.findSong(handle);
+
+	ASSERT_SONG(song);
+
+#ifdef DEBUG_SONG_API
+	fprintf(stderr, "[sfx-core] Setting fade params of %08lx to "
+	        "final volume %d in steps of %d per %d ticks. %s.",
+	        handle, fade->final_volume, fade->step_size, fade->ticks_per_step,
+	        stopmsg[fade->action]);
+#endif
+
+	SIMSG_SEND_FADE(song->_it, params);
+
+	update();
+}
+
+void SfxState::sfx_song_renice(SongHandle handle, int priority) {
+	Song *song = _songlib.findSong(handle);
+	ASSERT_SONG(song);
+#ifdef DEBUG_SONG_API
+	fprintf(stderr, "[sfx-core] Renicing song %08lx to %d\n",
+	        handle, priority);
+#endif
+
+	song->_priority = priority;
+
+	update();
+}
+
+void SfxState::sfx_song_set_loops(SongHandle handle, int loops) {
+	Song *song = _songlib.findSong(handle);
+	SongIterator::Message msg = SongIterator::Message(handle, SIMSG_SET_LOOPS(loops));
+	ASSERT_SONG(song);
+
+	song->_loops = loops;
+#ifdef DEBUG_SONG_API
+	fprintf(stderr, "[sfx-core] Setting loops on %08lx to %d\n",
+	        handle, loops);
+#endif
+	songit_handle_message(&(song->_it), msg);
+
+	if (_player/* && _player->send_iterator_message*/)
+		/* FIXME: The above should be optional! */
+		_player->iterator_message(msg);
+}
+
+void SfxState::sfx_song_set_hold(SongHandle handle, int hold) {
+	Song *song = _songlib.findSong(handle);
+	SongIterator::Message msg = SongIterator::Message(handle, SIMSG_SET_HOLD(hold));
+	ASSERT_SONG(song);
+
+	song->_hold = hold;
+#ifdef DEBUG_SONG_API
+	fprintf(stderr, "[sfx-core] Setting hold on %08lx to %d\n",
+	        handle, hold);
+#endif
+	songit_handle_message(&(song->_it), msg);
+
+	if (_player/* && _player->send_iterator_message*/)
+		/* FIXME: The above should be optional! */
+		_player->iterator_message(msg);
+}
+
+/* Different from the one in iterator.c */
+static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0,
+                                    3, 3, 0, 3, 2, 0, 3, 0
+                                   };
+
+static const SongHandle midi_send_base = 0xffff0000;
+
+Common::Error SfxState::sfx_send_midi(SongHandle handle, int channel,
+	int command, int arg1, int arg2) {
+	byte buffer[5];
+
+	/* Yes, in that order. SCI channel mutes are actually done via
+	   a counting semaphore. 0 means to decrement the counter, 1
+	   to increment it. */
+	static const char *channel_state[] = {"ON", "OFF"};
+
+	if (command == 0xb0 &&
+	        arg1 == SCI_MIDI_CHANNEL_MUTE) {
+		warning("TODO: channel mute (channel %d %s)", channel, channel_state[arg2]);
+		/* We need to have a GET_PLAYMASK interface to use
+		   here. SET_PLAYMASK we've got.
+		*/
+		return Common::kNoError;
+	}
+
+	buffer[0] = channel | command; /* No channel remapping yet */
+
+	switch (command) {
+	case 0x80 :
+	case 0x90 :
+	case 0xb0 :
+		buffer[1] = arg1 & 0xff;
+		buffer[2] = arg2 & 0xff;
+		break;
+	case 0xc0 :
+		buffer[1] = arg1 & 0xff;
+		break;
+	case 0xe0 :
+		buffer[1] = (arg1 & 0x7f) | 0x80;
+		buffer[2] = (arg1 & 0xff00) >> 7;
+		break;
+	default:
+		warning("Unexpected explicit MIDI command %02x", command);
+		return Common::kUnknownError;
+	}
+
+	if (_player)
+		_player->tell_synth(MIDI_cmdlen[command >> 4], buffer);
+	return Common::kNoError;
+}
+
+int SfxState::sfx_getVolume() {
+	return _player->getVolume();
+}
+
+void SfxState::sfx_setVolume(int volume) {
+	_player->setVolume(volume);
+}
+
+void SfxState::sfx_all_stop() {
+#ifdef DEBUG_SONG_API
+	fprintf(stderr, "[sfx-core] All stop\n");
+#endif
+
+	_songlib.freeSounds();
+	update();
+}
+
+} // End of namespace Sci
+
+#endif	// USE_OLD_MUSIC_FUNCTIONS

Copied: scummvm/trunk/engines/sci/sfx/iterator/core.h (from rev 46418, scummvm/trunk/engines/sci/sfx/core.h)
===================================================================
--- scummvm/trunk/engines/sci/sfx/iterator/core.h	                        (rev 0)
+++ scummvm/trunk/engines/sci/sfx/iterator/core.h	2009-12-20 13:38:13 UTC (rev 46430)
@@ -0,0 +1,209 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/* Sound engine */
+#ifndef SCI_SFX_CORE_H
+#define SCI_SFX_CORE_H
+
+#include "common/error.h"
+
+#include "sci/sci.h"	// for USE_OLD_MUSIC_FUNCTIONS
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+#include "sci/sfx/iterator/songlib.h"
+#include "sci/resource.h"
+
+namespace Sci {
+
+class SfxPlayer;
+class SongIterator;
+struct fade_params_t;
+
+#define SFX_TICKS_PER_SEC 60 /* MIDI ticks per second */
+
+
+#define SFX_STATE_FLAG_MULTIPLAY (1 << 0) /* More than one song playable
+** simultaneously ? */
+#define SFX_STATE_FLAG_NOSOUND	 (1 << 1) /* Completely disable sound playing */
+
+class SfxState {
+private:
+	SfxPlayer *_player;
+
+public:	// FIXME, make private
+	SongIterator *_it; /**< The song iterator at the heart of things */
+	uint _flags; /**< SFX_STATE_FLAG_* */
+	SongLibrary _songlib; /**< Song library */
+	Song *_song; /**< Active song, or start of active song chain */
+	bool _suspended; /**< Whether we are suspended */
+	ResourceManager *_resMan;
+
+public:
+	SfxState();
+	~SfxState();
+
+	/***********/
+	/* General */
+	/***********/
+
+	/* Initializes the sound engine
+	** Parameters: (ResourceManager *) resMan: Resource manager for initialization
+	**             (int) flags: SFX_STATE_FLAG_*
+	*/
+	void sfx_init(ResourceManager *resMan, int flags);
+
+	/** Deinitializes the sound subsystem. */
+	void sfx_exit();
+
+	/* Suspends/unsuspends the sound sybsystem
+	** Parameters: (int) suspend: Whether to suspend (non-null) or to unsuspend
+	*/
+	void sfx_suspend(bool suspend);
+
+	/* Polls the sound server for cues etc.
+	** Returns   : (int) 0 if the cue queue is empty, SI_LOOP, SI_CUE, or SI_FINISHED otherwise
+	**             (SongHandle) *handle: The affected handle
+	**             (int) *cue: The sound cue number (if SI_CUE), or the loop number (if SI_LOOP)
+	*/
+	int sfx_poll(SongHandle *handle, int *cue);
+
+	/* Polls the sound server for cues etc.
+	** Parameters: (SongHandle) handle: The handle to poll
+	** Returns   : (int) 0 if the cue queue is empty, SI_LOOP, SI_CUE, or SI_FINISHED otherwise
+	**             (int) *cue: The sound cue number (if SI_CUE), or the loop number (if SI_LOOP)
+	*/
+	int sfx_poll_specific(SongHandle handle, int *cue);
+
+	/* Determines the current global volume settings
+	** Returns   : (int) The global volume, between 0 (silent) and 127 (max. volume)
+	*/
+	int sfx_getVolume();
+
+	/* Determines the current global volume settings
+	** Parameters: (int) volume: The new global volume, between 0 and 127 (see above)
+	*/
+	void sfx_setVolume(int volume);
+
+	/* Stops all songs currently playing, purges song library
+	*/
+	void sfx_all_stop();
+
+
+	/*****************/
+	/*  Song basics  */
+	/*****************/
+
+	/* Adds a song to the internal sound library
+	** Parameters: (SongIterator *) it: The iterator describing the song
+	**             (int) priority: Initial song priority (higher <-> more important)
+	**             (SongHandle) handle: The handle to associate with the song
+	*/
+	void sfx_add_song(SongIterator *it, int priority, SongHandle handle, int resnum);
+
+
+	/* Deletes a song and its associated song iterator from the song queue
+	** Parameters: (SongHandle) handle: The song to remove
+	*/
+	void sfx_remove_song(SongHandle handle);
+
+
+	/**********************/
+	/* Song modifications */
+	/**********************/
+
+
+	/* Sets the song status, i.e. whether it is playing, suspended, or stopped.
+	** Parameters: (SongHandle) handle: Handle of the song to modify
+	**             (int) status: The song status the song should assume
+	** WAITING and PLAYING are set implicitly and essentially describe the same state
+	** as far as this function is concerned.
+	*/
+	void sfx_song_set_status(SongHandle handle, int status);
+
+	/* Sets the new song priority
+	** Parameters: (SongHandle) handle: The handle to modify
+	**             (int) priority: The priority to set
+	*/
+	void sfx_song_renice(SongHandle handle, int priority);
+
+	/* Sets the number of loops for the specified song
+	** Parameters: (SongHandle) handle: The song handle to reference
+	**             (int) loops: Number of loops to set
+	*/
+	void sfx_song_set_loops(SongHandle handle, int loops);
+
+	/* Sets the number of loops for the specified song
+	** Parameters: (SongHandle) handle: The song handle to reference
+	**             (int) hold: Number of loops to setn
+	*/
+	void sfx_song_set_hold(SongHandle handle, int hold);
+
+	/* Instructs a song to be faded out
+	** Parameters: (SongHandle) handle: The song handle to reference
+	**             (fade_params_t *) fade_setup: The precise fade-out configuration to use
+	*/
+	void sfx_song_set_fade(SongHandle handle, fade_params_t *fade_setup);
+
+
+	// Previously undocumented:
+	Common::Error sfx_send_midi(SongHandle handle, int channel,
+		int command, int arg1, int arg2);
+
+	// misc
+
+	/**
+	 * Determines the polyphony of the player in use.
+	 * @return Number of voices the active player can emit
+	 */
+	int sfx_get_player_polyphony();
+
+	/**
+	 * Tells the player to stop its internal iterator.
+	 */
+	void sfx_reset_player();
+
+	/**
+	 * Pass a raw MIDI event to the synth of the player.
+	 * @param	argc	Length of buffer holding the midi event
+	 * @param	argv	The buffer itself
+	 */
+	void sfx_player_tell_synth(int buf_nr, byte *buf);
+
+protected:
+	void freezeTime();
+	void thawTime();
+
+	bool isPlaying(Song *song);
+	void setSongStatus(Song *song, int status);
+	void updateSingleSong();
+	void updateMultiSong();
+	void update();
+};
+
+} // End of namespace Sci
+
+#endif	// USE_OLD_MUSIC_FUNCTIONS
+
+#endif // SCI_SFX_CORE_H

Copied: scummvm/trunk/engines/sci/sfx/iterator/iterator.cpp (from rev 46418, scummvm/trunk/engines/sci/sfx/iterator.cpp)
===================================================================
--- scummvm/trunk/engines/sci/sfx/iterator/iterator.cpp	                        (rev 0)
+++ scummvm/trunk/engines/sci/sfx/iterator/iterator.cpp	2009-12-20 13:38:13 UTC (rev 46430)
@@ -0,0 +1,1707 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/* Song iterators */
+
+#include "common/util.h"
+
+#include "sci/sci.h"
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+
+#include "sci/sfx/iterator/iterator_internal.h"
+#include "sci/engine/state.h"	// for sfx_player_tell_synth :/
+#include "sci/sfx/iterator/core.h"	// for sfx_player_tell_synth
+
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+namespace Sci {
+
+
+static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0,
+                                    2, 2, 2, 2, 1, 1, 2, 0
+                                   };
+
+/*#define DEBUG_DECODING*/
+/*#define DEBUG_VERBOSE*/
+
+/** Find first set bit in bits and return its index. Returns 0 if bits is 0. */
+static int sci_ffs(int bits) {
+	if (!bits)
+		return 0;
+
+	int retval = 1;
+
+	while (!(bits & 1)) {
+		retval++;
+		bits >>= 1;
+	}
+
+	return retval;
+}
+
+static void print_tabs_id(int nr, songit_id_t id) {
+	while (nr-- > 0)
+		fprintf(stderr, "\t");
+
+	fprintf(stderr, "[%08lx] ", id);
+}
+
+BaseSongIterator::BaseSongIterator(byte *data, uint size, songit_id_t id)
+	: _data(data, size) {
+	ID = id;
+}
+
+/************************************/
+/*-- SCI0 iterator implementation --*/
+/************************************/
+
+#define SCI0_MIDI_OFFSET 33
+#define SCI0_END_OF_SONG 0xfc /* proprietary MIDI command */
+
+#define SCI0_PCM_SAMPLE_RATE_OFFSET 0x0e
+#define SCI0_PCM_SIZE_OFFSET 0x20
+#define SCI0_PCM_DATA_OFFSET 0x2c
+
+#define CHECK_FOR_END_ABSOLUTE(offset) \
+	if (offset > _data.size()) { \
+		warning("Reached end of song without terminator (%x/%x) at %d", offset, _data.size(), __LINE__); \
+		return SI_FINISHED; \
+	}
+
+#define CHECK_FOR_END(offset_augment) \
+	if ((channel->offset + (offset_augment)) > channel->end) { \
+		channel->state = SI_STATE_FINISHED; \
+		warning("Reached end of track %d without terminator (%x+%x/%x) at %d", channel->id, channel->offset, offset_augment, channel->end, __LINE__); \
+		return SI_FINISHED; \
+	}
+
+
+static int _parse_ticks(byte *data, int *offset_p, int size) {
+	int ticks = 0;
+	int tempticks;
+	int offset = 0;
+
+	do {
+		tempticks = data[offset++];
+		ticks += (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX) ?
+		         SCI_MIDI_TIME_EXPANSION_LENGTH : tempticks;
+	} while (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX
+	         && offset < size);
+
+	if (offset_p)
+		*offset_p = offset;
+
+	return ticks;
+}
+
+
+static int _sci0_get_pcm_data(Sci0SongIterator *self, int *rate, int *xoffset, uint *xsize);
+
+
+#define PARSE_FLAG_LOOPS_UNLIMITED (1 << 0) /* Unlimited # of loops? */
+#define PARSE_FLAG_PARAMETRIC_CUE (1 << 1) /* Assume that cues take an additional "cue value" argument */
+/* This implements a difference between SCI0 and SCI1 cues. */
+
+void SongIteratorChannel::init(int id_, int offset_, int end_) {
+	playmask = PLAYMASK_NONE; /* Disable all channels */
+	id = id_;
+	state = SI_STATE_DELTA_TIME;
+	loop_timepos = 0;
+	total_timepos = 0;
+	timepos_increment = 0;
+	delay = 0; /* Only used for more than one channel */
+	last_cmd = 0xfe;
+
+	offset = loop_offset = initial_offset = offset_;
+	end = end_;
+}
+
+void SongIteratorChannel::resetSynthChannels() {
+	byte buf[5];
+
+	// FIXME: Evil hack
+	SfxState &sound = ((SciEngine*)g_engine)->getEngineState()->_sound;
+
+	for (int i = 0; i < MIDI_CHANNELS; i++) {
+		if (playmask & (1 << i)) {
+			buf[0] = 0xe0 | i; /* Pitch bend */
+			buf[1] = 0x80; /* Wheel center */
+			buf[2] = 0x40;
+			sound.sfx_player_tell_synth(3, buf);
+
+			buf[0] = 0xb0 | i; // Set control
+			buf[1] = 0x40; // Hold pedal
+			buf[2] = 0x00; // Off
+			sound.sfx_player_tell_synth(3, buf);
+			/* TODO: Reset other controls? */
+		}
+	}
+}
+
+int BaseSongIterator::parseMidiCommand(byte *buf, int *result, SongIteratorChannel *channel, int flags) {
+	byte cmd;
+	int paramsleft;
+	int midi_op;
+	int midi_channel;
+
+	channel->state = SI_STATE_DELTA_TIME;
+
+	cmd = _data[channel->offset++];
+
+	if (!(cmd & 0x80)) {
+		/* 'Running status' mode */
+		channel->offset--;
+		cmd = channel->last_cmd;
+	}
+
+	if (cmd == 0xfe) {
+		warning("song iterator subsystem: Corrupted sound resource detected.");
+		return SI_FINISHED;
+	}
+
+	midi_op = cmd >> 4;
+	midi_channel = cmd & 0xf;
+	paramsleft = MIDI_cmdlen[midi_op];
+
+#if 0
+	if (1) {
+		fprintf(stderr, "[IT]: off=%x, cmd=%02x, takes %d args ",
+		        channel->offset - 1, cmd, paramsleft);
+		fprintf(stderr, "[%02x %02x <%02x> %02x %02x %02x]\n",
+		        _data[channel->offset-3],
+		        _data[channel->offset-2],
+		        _data[channel->offset-1],
+		        _data[channel->offset],
+		        _data[channel->offset+1],
+		        _data[channel->offset+2]);
+	}
+#endif
+
+	buf[0] = cmd;
+
+
+	CHECK_FOR_END(paramsleft);
+	memcpy(buf + 1, _data.begin() + channel->offset, paramsleft);
+	*result = 1 + paramsleft;
+
+	channel->offset += paramsleft;
+
+	channel->last_cmd = cmd;
+
+	/* Are we supposed to play this channel? */
+	if (
+	    /* First, exclude "global" properties-- such as cues-- from consideration */
+	    (midi_op < 0xf
+	     && !(cmd == SCI_MIDI_SET_SIGNAL)
+	     && !(SCI_MIDI_CONTROLLER(cmd)
+	          && buf[1] == SCI_MIDI_CUMULATIVE_CUE))
+
+	    /* Next, check if the channel is allowed */
+	    && (!((1 << midi_channel) & channel->playmask)))
+		return  /* Execute next command */
+		    nextCommand(buf, result);
+
+
+	if (cmd == SCI_MIDI_EOT) {
+		/* End of track? */
+		channel->resetSynthChannels();
+		if (_loops > 1) {
+			/* If allowed, decrement the number of loops */
+			if (!(flags & PARSE_FLAG_LOOPS_UNLIMITED))
+				*result = --_loops;
+
+#ifdef DEBUG_DECODING
+			fprintf(stderr, "%s L%d: (%p):%d Looping ", __FILE__, __LINE__, this, channel->id);
+			if (flags & PARSE_FLAG_LOOPS_UNLIMITED)
+				fprintf(stderr, "(indef.)");
+			else
+				fprintf(stderr, "(%d)", _loops);
+			fprintf(stderr, " %x -> %x\n",
+			        channel->offset, channel->loop_offset);
+#endif
+			channel->offset = channel->loop_offset;
+			channel->state = SI_STATE_DELTA_TIME;
+			channel->total_timepos = channel->loop_timepos;
+			channel->last_cmd = 0xfe;
+			debugC(2, kDebugLevelSound, "Looping song iterator %08lx.\n", ID);
+			return SI_LOOP;
+		} else {
+			channel->state = SI_STATE_FINISHED;
+			return SI_FINISHED;
+		}
+
+	} else if (cmd == SCI_MIDI_SET_SIGNAL) {
+		if (buf[1] == SCI_MIDI_SET_SIGNAL_LOOP) {
+			channel->loop_offset = channel->offset;
+			channel->loop_timepos = channel->total_timepos;
+
+			return /* Execute next command */
+			    nextCommand(buf, result);
+		} else {
+			/* Used to be conditional <= 127 */
+			*result = buf[1]; /* Absolute cue */
+			return SI_ABSOLUTE_CUE;
+		}
+	} else if (SCI_MIDI_CONTROLLER(cmd)) {
+		switch (buf[1]) {
+
+		case SCI_MIDI_CUMULATIVE_CUE:
+			if (flags & PARSE_FLAG_PARAMETRIC_CUE)
+				_ccc += buf[2];
+			else { /* No parameter to CC */
+				_ccc++;
+				/*				channel->offset--; */
+			}
+			*result = _ccc;
+			return SI_RELATIVE_CUE;
+
+		case SCI_MIDI_RESET_ON_SUSPEND:
+			_resetflag = buf[2];
+			break;
+
+		case SCI_MIDI_SET_POLYPHONY:
+			_polyphony[midi_channel] = buf[2];
+
+#if 0
+			{
+				Sci1SongIterator *self1 = (Sci1SongIterator *)this;
+				int i;
+				int voices = 0;
+				for (i = 0; i < self1->_numChannels; i++) {
+					voices += _polyphony[i];
+				}
+
+				printf("SET_POLYPHONY(%d, %d) for a total of %d voices\n", midi_channel, buf[2], voices);
+				printf("[iterator] DEBUG: Polyphony = [ ");
+				for (i = 0; i < self1->_numChannels; i++)
+					printf("%d ", _polyphony[i]);
+				printf("]\n");
+				printf("[iterator] DEBUG: Importance = [ ");
+				printf("]\n");
+			}
+#endif
+			break;
+
+		case SCI_MIDI_SET_REVERB:
+			break;
+
+		case SCI_MIDI_CHANNEL_MUTE:
+			warning("CHANNEL_MUTE(%d, %d)", midi_channel, buf[2]);
+			break;
+
+		case SCI_MIDI_HOLD: {
+			// Safe cast: This controller is only used in SCI1
+			Sci1SongIterator *self1 = (Sci1SongIterator *)this;
+
+			if (buf[2] == self1->_hold) {
+				channel->offset = channel->initial_offset;
+				channel->state = SI_STATE_COMMAND;
+				channel->total_timepos = 0;
+
+				self1->_numLoopedChannels = self1->_numActiveChannels - 1;
+
+				// FIXME:
+				// This implementation of hold breaks getting out of the
+				// limo when visiting the airport near the start of LSL5.
+				// It seems like all channels should be reset here somehow,
+				// but not sure how.
+				// Forcing all channel offsets to 0 seems to fix the hang,
+				// but somehow slows the exit sequence down to take 20 seconds
+				// instead of about 3.
+
+				return SI_LOOP;
+			}
+
+			break;
+		}
+		case 0x04: /* UNKNOWN NYI (happens in LSL2 gameshow) */
+		case 0x46: /* UNKNOWN NYI (happens in LSL3 binoculars) */
+		case 0x61: /* UNKNOWN NYI (special for adlib? Iceman) */
+		case 0x73: /* UNKNOWN NYI (happens in Hoyle) */
+		case 0xd1: /* UNKNOWN NYI (happens in KQ4 when riding the unicorn) */
+			return /* Execute next command */
+			    nextCommand(buf, result);
+
+		case 0x01: /* modulation */
+		case 0x07: /* volume */
+		case 0x0a: /* panpot */
+		case 0x0b: /* expression */
+		case 0x40: /* hold */
+		case 0x79: /* reset all */
+			/* No special treatment neccessary */
+			break;
+
+		}
+		return 0;
+
+	} else {
+#if 0
+		/* Perform remapping, if neccessary */
+		if (cmd != SCI_MIDI_SET_SIGNAL
+				&& cmd < 0xf0) { /* Not a generic command */
+			int chan = cmd & 0xf;
+			int op = cmd & 0xf0;
+
+			chan = channel_remap[chan];
+			buf[0] = chan | op;
+		}
+#endif
+
+		/* Process as normal MIDI operation */
+		return 0;
+	}
+}
+
+int BaseSongIterator::processMidi(byte *buf, int *result,
+	SongIteratorChannel *channel, int flags) {
+	CHECK_FOR_END(0);
+
+	switch (channel->state) {
+
+	case SI_STATE_PCM: {
+		if (_data[channel->offset] == 0
+		        && _data[channel->offset + 1] == SCI_MIDI_EOT)
+			/* Fake one extra tick to trick the interpreter into not killing the song iterator right away */
+			channel->state = SI_STATE_PCM_MAGIC_DELTA;
+		else
+			channel->state = SI_STATE_DELTA_TIME;
+		return SI_PCM;
+	}
+
+	case SI_STATE_PCM_MAGIC_DELTA: {
+		int rate;
+		int offset;
+		uint size;
+		int delay;
+		if (_sci0_get_pcm_data((Sci0SongIterator *)this, &rate, &offset, &size))
+			return SI_FINISHED; /* 'tis broken */
+		channel->state = SI_STATE_FINISHED;
+		delay = (size * 50 + rate - 1) / rate; /* number of ticks to completion*/
+
+		debugC(2, kDebugLevelSound, "delaying %d ticks\n", delay);
+		return delay;
+	}
+
+	case SI_STATE_UNINITIALISED:
+		warning("Attempt to read command from uninitialized iterator");
+		init();
+		return nextCommand(buf, result);
+
+	case SI_STATE_FINISHED:
+		return SI_FINISHED;
+
+	case SI_STATE_DELTA_TIME: {
+		int offset;
+		int ticks = _parse_ticks(_data.begin() + channel->offset,
+		                         &offset,
+		                         _data.size() - channel->offset);
+
+		channel->offset += offset;
+		channel->delay += ticks;
+		channel->timepos_increment = ticks;
+
+		CHECK_FOR_END(0);
+
+		channel->state = SI_STATE_COMMAND;
+
+		if (ticks)
+			return ticks;
+	}
+
+	/* continute otherwise... */
+
+	case SI_STATE_COMMAND: {
+		int retval;
+		channel->total_timepos += channel->timepos_increment;
+		channel->timepos_increment = 0;
+
+		retval = parseMidiCommand(buf, result, channel, flags);
+
+		if (retval == SI_FINISHED) {
+			if (_numActiveChannels)
+				--(_numActiveChannels);
+#ifdef DEBUG_DECODING
+			fprintf(stderr, "%s L%d: (%p):%d Finished channel, %d channels left\n",
+			        __FILE__, __LINE__, this, channel->id,
+			        _numActiveChannels);
+#endif
+			/* If we still have channels left... */
+			if (_numActiveChannels) {
+				return nextCommand(buf, result);
+			}
+
+			/* Otherwise, we have reached the end */
+			_loops = 0;
+		}
+
+		return retval;
+	}
+
+	default:
+		error("Invalid iterator state %d", channel->state);
+		return SI_FINISHED;
+	}
+}
+
+int Sci0SongIterator::nextCommand(byte *buf, int *result) {
+	return processMidi(buf, result, &_channel, PARSE_FLAG_PARAMETRIC_CUE);
+}
+
+static int _sci0_header_magic_p(byte *data, int offset, int size) {
+	if (offset + 0x10 > size)
+		return 0;
+	return (data[offset] == 0x1a)
+	    && (data[offset + 1] == 0x00)
+	    && (data[offset + 2] == 0x01)
+	    && (data[offset + 3] == 0x00);
+}
+
+
+static int _sci0_get_pcm_data(Sci0SongIterator *self,
+	int *rate, int *xoffset, uint *xsize) {
+	int tries = 2;
+	bool found_it = false;
+	byte *pcm_data;
+	int size;
+	uint offset = SCI0_MIDI_OFFSET;
+
+	if (self->_data[0] != 2)
+		return 1;
+	/* No such luck */
+
+	while ((tries--) && (offset < self->_data.size()) && (!found_it)) {
+		// Search through the garbage manually
+		// FIXME: Replace offset by an iterator
+		Common::Array<byte>::iterator iter = Common::find(self->_data.begin() + offset, self->_data.end(), SCI0_END_OF_SONG);
+
+		if (iter == self->_data.end()) {
+			warning("Playing unterminated song");
+			return 1;
+		}
+
+		// add one to move it past the END_OF_SONG marker
+		iter++;
+		offset = iter - self->_data.begin();	// FIXME
+
+
+		if (_sci0_header_magic_p(self->_data.begin(), offset, self->_data.size()))
+			found_it = true;
+	}
+
+	if (!found_it) {
+		warning("Song indicates presence of PCM, but"
+		        " none found (finally at offset %04x)", offset);
+
+		return 1;
+	}
+
+	pcm_data = self->_data.begin() + offset;
+
+	size = READ_LE_UINT16(pcm_data + SCI0_PCM_SIZE_OFFSET);
+
+	/* Two of the format parameters are fixed by design: */
+	*rate = READ_LE_UINT16(pcm_data + SCI0_PCM_SAMPLE_RATE_OFFSET);
+
+	if (offset + SCI0_PCM_DATA_OFFSET + size != self->_data.size()) {
+		int d = offset + SCI0_PCM_DATA_OFFSET + size - self->_data.size();
+
+		warning("PCM advertizes %d bytes of data, but %d"
+		        " bytes are trailing in the resource",
+		        size, self->_data.size() - (offset + SCI0_PCM_DATA_OFFSET));
+
+		if (d > 0)
+			size -= d; /* Fix this */
+	}
+
+	*xoffset = offset;
+	*xsize = size;
+
+	return 0;
+}
+
+static Audio::AudioStream *makeStream(byte *data, int size, int rate) {
+	debugC(2, kDebugLevelSound, "Playing PCM data of size %d, rate %d\n", size, rate);
+
+	// Duplicate the data
+	byte *sound = (byte *)malloc(size);
+	memcpy(sound, data, size);
+
+	// Convert stream format flags
+	int flags = Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_UNSIGNED;
+	return Audio::makeLinearInputStream(sound, size, rate, flags, 0, 0);
+}
+
+Audio::AudioStream *Sci0SongIterator::getAudioStream() {
+	int rate;
+	int offset;
+	uint size;
+	if (_sci0_get_pcm_data(this, &rate, &offset, &size))
+		return NULL;
+
+	_channel.state = SI_STATE_FINISHED; /* Don't play both PCM and music */
+
+	return makeStream(_data.begin() + offset + SCI0_PCM_DATA_OFFSET, size, rate);
+}
+
+SongIterator *Sci0SongIterator::handleMessage(Message msg) {
+	if (msg._class == _SIMSG_BASE) {
+		switch (msg._type) {
+
+		case _SIMSG_BASEMSG_PRINT:
+			print_tabs_id(msg._arg.i, ID);
+			debugC(2, kDebugLevelSound, "SCI0: dev=%d, active-chan=%d, size=%d, loops=%d\n",
+			        _deviceId, _numActiveChannels, _data.size(), _loops);
+			break;
+
+		case _SIMSG_BASEMSG_SET_LOOPS:
+			_loops = msg._arg.i;
+			break;
+
+		case _SIMSG_BASEMSG_STOP: {
+			songit_id_t sought_id = msg.ID;
+
+			if (sought_id == ID)
+				_channel.state = SI_STATE_FINISHED;
+			break;
+		}
+
+		case _SIMSG_BASEMSG_SET_PLAYMASK: {
+			int i;
+			_deviceId = msg._arg.i;
+
+			/* Set all but the rhytm channel mask bits */
+			_channel.playmask &= ~(1 << MIDI_RHYTHM_CHANNEL);
+
+			for (i = 0; i < MIDI_CHANNELS; i++)
+				if (_data[2 + (i << 1)] & _deviceId
+				        && i != MIDI_RHYTHM_CHANNEL)
+					_channel.playmask |= (1 << i);
+		}
+		break;
+
+		case _SIMSG_BASEMSG_SET_RHYTHM:
+			_channel.playmask &= ~(1 << MIDI_RHYTHM_CHANNEL);
+			if (msg._arg.i)
+				_channel.playmask |= (1 << MIDI_RHYTHM_CHANNEL);
+			break;
+
+		case _SIMSG_BASEMSG_SET_FADE: {
+			fade_params_t *fp = (fade_params_t *) msg._arg.p;
+			fade.action = fp->action;
+			fade.final_volume = fp->final_volume;
+			fade.ticks_per_step = fp->ticks_per_step;
+			fade.step_size = fp->step_size;
+			break;
+		}
+
+		default:
+			return NULL;
+		}
+
+		return this;
+	}
+	return NULL;
+}
+
+int Sci0SongIterator::getTimepos() {
+	return _channel.total_timepos;
+}
+
+Sci0SongIterator::Sci0SongIterator(byte *data, uint size, songit_id_t id)
+ : BaseSongIterator(data, size, id) {
+	channel_mask = 0xffff;	// Allocate all channels by default
+	_channel.state = SI_STATE_UNINITIALISED;
+
+	for (int i = 0; i < MIDI_CHANNELS; i++)
+		_polyphony[i] = data[1 + (i << 1)];
+
+	init();
+}
+
+void Sci0SongIterator::init() {
+	fade.action = FADE_ACTION_NONE;
+	_resetflag = 0;
+	_loops = 0;
+	priority = 0;
+
+	_ccc = 0; /* Reset cumulative cue counter */
+	_numActiveChannels = 1;
+	_channel.init(0, SCI0_MIDI_OFFSET, _data.size());
+	_channel.resetSynthChannels();
+
+	if (_data[0] == 2) /* Do we have an embedded PCM? */
+		_channel.state = SI_STATE_PCM;
+}
+
+SongIterator *Sci0SongIterator::clone(int delta) {
+	Sci0SongIterator *newit = new Sci0SongIterator(*this);
+	return newit;
+}
+
+
+/***************************/
+/*-- SCI1 song iterators --*/
+/***************************/
+
+#define SCI01_INVALID_DEVICE 0xff
+
+/* Second index determines whether PCM output is supported */
+static const int sci0_to_sci1_device_map[][2] = {
+	{0x06, 0x0c}, /* MT-32 */
+	{0xff, 0xff}, /* YM FB-01 */
+	{0x00, 0x00}, /* CMS/Game Blaster-- we assume OPL/2 here... */
+	{0xff, 0xff}, /* Casio MT540/CT460 */
+	{0x13, 0x13}, /* Tandy 3-voice */
+	{0x12, 0x12}, /* PC speaker */
+	{0xff, 0xff},
+	{0xff, 0xff},
+}; /* Maps bit number to device ID */
+
+int Sci1SongIterator::initSample(const int offset) {
+	Sci1Sample sample;
+	int rate;
+	int length;
+	int begin;
+	int end;
+
+	CHECK_FOR_END_ABSOLUTE((uint)offset + 10);
+	if (_data[offset + 1] != 0)
+		warning("[iterator-1] In sample at offset 0x04x: Byte #1 is %02x instead of zero",
+		          _data[offset + 1]);
+
+	rate = (int16)READ_LE_UINT16(_data.begin() + offset + 2);
+	length = READ_LE_UINT16(_data.begin() + offset + 4);
+	begin = (int16)READ_LE_UINT16(_data.begin() + offset + 6);
+	end = (int16)READ_LE_UINT16(_data.begin() + offset + 8);
+
+	CHECK_FOR_END_ABSOLUTE((uint)(offset + 10 + length));
+
+	sample.delta = begin;
+	sample.size = length;
+	sample._data = _data.begin() + offset + 10;
+
+#ifdef DEBUG_VERBOSE
+	fprintf(stderr, "[SAMPLE] %x/%x/%x/%x l=%x\n",
+	        offset + 10, begin, end, _data.size(), length);
+#endif
+
+	sample.rate = rate;
+
+	sample.announced = false;
+
+	/* Insert into the sample list at the right spot, keeping it sorted by delta */
+	Common::List<Sci1Sample>::iterator seeker = _samples.begin();
+	while (seeker != _samples.end() && seeker->delta < begin)
+		++seeker;
+	_samples.insert(seeker, sample);
+
+	return 0; /* Everything's fine */
+}
+
+int Sci1SongIterator::initSong() {
+	int last_time;
+	uint offset = 0;
+	_numChannels = 0;
+	_samples.clear();
+//	_deviceId = 0x0c;
+
+	if (_data[offset] == 0xf0) {
+		priority = _data[offset + 1];
+
+		offset += 8;
+	}
+
+	while (_data[offset] != 0xff
+	        && _data[offset] != _deviceId) {
+		offset++;
+		CHECK_FOR_END_ABSOLUTE(offset + 1);
+		while (_data[offset] != 0xff) {
+			CHECK_FOR_END_ABSOLUTE(offset + 7);
+			offset += 6;
+		}
+		offset++;
+	}
+
+	if (_data[offset] == 0xff) {
+		warning("[iterator] Song does not support hardware 0x%02x", _deviceId);
+		return 1;
+	}
+
+	offset++;
+
+	while (_data[offset] != 0xff) { /* End of list? */
+		uint track_offset;
+		int end;
+		offset += 2;
+
+		CHECK_FOR_END_ABSOLUTE(offset + 4);
+
+		track_offset = READ_LE_UINT16(_data.begin() + offset);
+		end = READ_LE_UINT16(_data.begin() + offset + 2);
+
+		CHECK_FOR_END_ABSOLUTE(track_offset - 1);
+
+		if (_data[track_offset] == 0xfe) {
+			if (initSample(track_offset))
+				return 1; /* Error */
+		} else {
+			/* Regular MIDI channel */
+			if (_numChannels >= MIDI_CHANNELS) {
+				warning("[iterator] Song has more than %d channels, cutting them off",
+				          MIDI_CHANNELS);
+				break; /* Scan for remaining samples */
+			} else {
+				int channel_nr = _data[track_offset] & 0xf;
+				SongIteratorChannel &channel = _channels[_numChannels++];
+
+				/*
+				if (_data[track_offset] & 0xf0)
+					printf("Channel %d has mapping bits %02x\n",
+					       channel_nr, _data[track_offset] & 0xf0);
+				*/
+
+				// Add 2 to skip over header bytes */
+				channel.init(channel_nr, track_offset + 2, track_offset + end);
+				channel.resetSynthChannels();
+
+				_polyphony[_numChannels - 1] = _data[channel.offset - 1] & 15;
+
+				channel.playmask = ~0; /* Enable all */
+				channel_mask |= (1 << channel_nr);
+
+				CHECK_FOR_END_ABSOLUTE(offset + end);
+			}
+		}
+		offset += 4;
+		CHECK_FOR_END_ABSOLUTE(offset);
+	}
+
+	/* Now ensure that sample deltas are relative to the previous sample */
+	last_time = 0;
+	_numActiveChannels = _numChannels;
+	_numLoopedChannels = 0;
+
+	for (Common::List<Sci1Sample>::iterator seeker = _samples.begin();
+			seeker != _samples.end(); ++seeker) {
+		int prev_last_time = last_time;
+		//printf("[iterator] Detected sample: %d Hz, %d bytes at time %d\n",
+		//          seeker->format.rate, seeker->size, seeker->delta);
+		last_time = seeker->delta;
+		seeker->delta -= prev_last_time;
+	}
+
+	return 0; /* Success */
+}
+
+int Sci1SongIterator::getSmallestDelta() const {
+	int d = -1;
+	for (int i = 0; i < _numChannels; i++)
+		if (_channels[i].state == SI_STATE_COMMAND
+		        && (d == -1 || _channels[i].delay < d))
+			d = _channels[i].delay;
+
+	if (!_samples.empty() && _samples.begin()->delta < d)
+		return _samples.begin()->delta;
+	else
+		return d;
+}
+
+void Sci1SongIterator::updateDelta(int delta) {
+	if (!_samples.empty())
+		_samples.begin()->delta -= delta;
+
+	for (int i = 0; i < _numChannels; i++)
+		if (_channels[i].state == SI_STATE_COMMAND)
+			_channels[i].delay -= delta;
+}
+
+bool Sci1SongIterator::noDeltaTime() const {
+	for (int i = 0; i < _numChannels; i++)
+		if (_channels[i].state == SI_STATE_DELTA_TIME)
+			return false;
+	return true;
+}
+
+#define COMMAND_INDEX_NONE -1
+#define COMMAND_INDEX_PCM -2
+
+int Sci1SongIterator::getCommandIndex() const {
+	/* Determine the channel # of the next active event, or -1 */
+	int i;
+	int base_delay = 0x7ffffff;
+	int best_chan = COMMAND_INDEX_NONE;
+
+	for (i = 0; i < _numChannels; i++)
+		if ((_channels[i].state != SI_STATE_PENDING)
+		        && (_channels[i].state != SI_STATE_FINISHED))  {
+
+			if ((_channels[i].state == SI_STATE_DELTA_TIME)
+			        && (_channels[i].delay == 0))
+				return i;
+			/* First, read all unknown delta times */
+
+			if (_channels[i].delay < base_delay) {
+				best_chan = i;
+				base_delay = _channels[i].delay;
+			}
+		}
+
+	if (!_samples.empty() && base_delay >= _samples.begin()->delta)
+		return COMMAND_INDEX_PCM;
+
+	return best_chan;
+}
+
+
+Audio::AudioStream *Sci1SongIterator::getAudioStream() {
+	Common::List<Sci1Sample>::iterator sample = _samples.begin();
+	if (sample != _samples.end() && sample->delta <= 0) {
+		Audio::AudioStream *feed = makeStream(sample->_data, sample->size, sample->rate);
+		_samples.erase(sample);
+
+		return feed;
+	} else
+		return NULL;
+}
+
+int Sci1SongIterator::nextCommand(byte *buf, int *result) {
+
+	if (!_initialised) {
+		//printf("[iterator] DEBUG: Initialising for %d\n", _deviceId);
+		_initialised = true;
+		if (initSong())
+			return SI_FINISHED;
+	}
+
+
+	if (_delayRemaining) {
+		int delay = _delayRemaining;
+		_delayRemaining = 0;
+		return delay;
+	}
+
+	int retval = 0;
+	do {	 /* All delays must be processed separately */
+		int chan = getCommandIndex();
+
+		if (chan == COMMAND_INDEX_NONE) {
+			return SI_FINISHED;
+		}
+
+		if (chan == COMMAND_INDEX_PCM) {
+
+			if (_samples.begin()->announced) {
+				/* Already announced; let's discard it */
+				Audio::AudioStream *feed = getAudioStream();
+				delete feed;
+			} else {
+				int delay = _samples.begin()->delta;
+
+				if (delay) {
+					updateDelta(delay);
+					return delay;
+				}
+				/* otherwise we're touching a PCM */
+				_samples.begin()->announced = true;
+				return SI_PCM;
+			}
+		} else { /* Not a PCM */
+
+			retval = processMidi(buf, result,
+			                     &(_channels[chan]),
+			                     PARSE_FLAG_LOOPS_UNLIMITED);
+
+			if (retval == SI_LOOP) {
+				_numLoopedChannels++;
+				_channels[chan].state = SI_STATE_PENDING;
+				_channels[chan].delay = 0;
+
+				if (_numLoopedChannels == _numActiveChannels) {
+					int i;
+
+					/* Everyone's ready: Let's loop */
+					for (i = 0; i < _numChannels; i++)
+						if (_channels[i].state == SI_STATE_PENDING)
+							_channels[i].state = SI_STATE_DELTA_TIME;
+
+					_numLoopedChannels = 0;
+					return SI_LOOP;
+				}
+			} else if (retval == SI_FINISHED) {
+#ifdef DEBUG
+				fprintf(stderr, "FINISHED some channel\n");
+#endif
+			} else if (retval > 0) {
+				int sd ;
+				sd = getSmallestDelta();
+
+				if (noDeltaTime() && sd) {
+					/* No other channel is ready */
+					updateDelta(sd);
+
+					/* Only from here do we return delta times */
+					return sd;
+				}
+			}
+
+		} /* Not a PCM */
+
+	} while (retval > 0);
+
+	return retval;
+}
+
+SongIterator *Sci1SongIterator::handleMessage(Message msg) {
+	if (msg._class == _SIMSG_BASE) { /* May extend this in the future */
+		switch (msg._type) {
+
+		case _SIMSG_BASEMSG_PRINT: {
+			int playmask = 0;
+			int i;
+
+			for (i = 0; i < _numChannels; i++)
+				playmask |= _channels[i].playmask;
+
+			print_tabs_id(msg._arg.i, ID);
+			debugC(2, kDebugLevelSound, "SCI1: chan-nr=%d, playmask=%04x\n",
+			        _numChannels, playmask);
+		}
+		break;
+
+		case _SIMSG_BASEMSG_STOP: {
+			songit_id_t sought_id = msg.ID;
+			int i;
+
+			if (sought_id == ID) {
+				ID = 0;
+
+				for (i = 0; i < _numChannels; i++)
+					_channels[i].state = SI_STATE_FINISHED;
+			}
+			break;
+		}
+
+		case _SIMSG_BASEMSG_SET_PLAYMASK:
+			if (msg.ID == ID) {
+				channel_mask = 0;
+
+				_deviceId
+				= sci0_to_sci1_device_map
+				  [sci_ffs(msg._arg.i & 0xff) - 1]
+				  [g_system->getMixer()->isReady()]
+				  ;
+
+				if (_deviceId == 0xff) {
+					warning("[iterator] Device %d(%d) not supported",
+					          msg._arg.i & 0xff, g_system->getMixer()->isReady());
+				}
+				if (_initialised) {
+					int i;
+					int toffset = -1;
+
+					for (i = 0; i < _numChannels; i++)
+						if (_channels[i].state != SI_STATE_FINISHED
+						        && _channels[i].total_timepos > toffset) {
+							toffset = _channels[i].total_timepos
+							          + _channels[i].timepos_increment
+							          - _channels[i].delay;
+						}
+
+					/* Find an active channel so that we can
+					** get the correct time offset  */
+
+					initSong();
+
+					toffset -= _delayRemaining;
+					_delayRemaining = 0;
+
+					if (toffset > 0)
+						return new_fast_forward_iterator(this, toffset);
+				} else {
+					initSong();
+					_initialised = true;
+				}
+
+				break;
+
+			}
+
+		case _SIMSG_BASEMSG_SET_LOOPS:
+			if (msg.ID == ID)
+				_loops = (msg._arg.i > 32767) ? 99 : 0;
+			/* 99 is arbitrary, but we can't use '1' because of
+			** the way we're testing in the decoding section.  */
+			break;
+
+		case _SIMSG_BASEMSG_SET_HOLD:
+			_hold = msg._arg.i;
+			break;
+		case _SIMSG_BASEMSG_SET_RHYTHM:
+			/* Ignore */
+			break;
+
+		case _SIMSG_BASEMSG_SET_FADE: {
+			fade_params_t *fp = (fade_params_t *) msg._arg.p;
+			fade.action = fp->action;
+			fade.final_volume = fp->final_volume;
+			fade.ticks_per_step = fp->ticks_per_step;
+			fade.step_size = fp->step_size;
+			break;
+		}
+
+		default:
+			warning("Unsupported command %d to SCI1 iterator", msg._type);
+		}
+		return this;
+	}
+	return NULL;
+}
+
+Sci1SongIterator::Sci1SongIterator(byte *data, uint size, songit_id_t id)
+ : BaseSongIterator(data, size, id) {
+	channel_mask = 0; // Defer channel allocation
+
+	for (int i = 0; i < MIDI_CHANNELS; i++)
+		_polyphony[i] = 0; // Unknown
+
+	init();
+}
+
+void Sci1SongIterator::init() {
+	fade.action = FADE_ACTION_NONE;
+	_resetflag = 0;
+	_loops = 0;
+	priority = 0;
+
+	_ccc = 0;
+	_deviceId = 0x00; // Default to Sound Blaster/Adlib for purposes of cue computation
+	_numChannels = 0;
+	_initialised = false;
+	_delayRemaining = 0;
+	_loops = 0;
+	_hold = 0;
+	memset(_polyphony, 0, sizeof(_polyphony));
+}
+
+Sci1SongIterator::~Sci1SongIterator() {
+}
+
+
+SongIterator *Sci1SongIterator::clone(int delta) {
+	Sci1SongIterator *newit = new Sci1SongIterator(*this);
+	newit->_delayRemaining = delta;
+	return newit;
+}
+
+int Sci1SongIterator::getTimepos() {
+	int max = 0;
+	int i;
+
+	for (i = 0; i < _numChannels; i++)
+		if (_channels[i].total_timepos > max)
+			max = _channels[i].total_timepos;
+
+	return max;
+}
+
+/**
+ * A song iterator with the purpose of sending notes-off channel commands.
+ */
+class CleanupSongIterator : public SongIterator {
+public:
+	CleanupSongIterator(uint channels) {
+		channel_mask = channels;
+		ID = 17;
+	}
+
+	int nextCommand(byte *buf, int *result);
+	Audio::AudioStream *getAudioStream() { return NULL; }
+	SongIterator *handleMessage(Message msg);
+	int getTimepos() { return 0; }
+	SongIterator *clone(int delta) { return new CleanupSongIterator(*this); }
+};
+
+SongIterator *CleanupSongIterator::handleMessage(Message msg) {
+	if (msg._class == _SIMSG_BASEMSG_PRINT && msg._type == _SIMSG_BASEMSG_PRINT) {
+		print_tabs_id(msg._arg.i, ID);
+		debugC(2, kDebugLevelSound, "CLEANUP\n");
+	}
+
+	return NULL;
+}
+
+int CleanupSongIterator::nextCommand(byte *buf, int *result) {
+	/* Task: Return channel-notes-off for each channel */
+	if (channel_mask) {
+		int bs = sci_ffs(channel_mask) - 1;
+
+		channel_mask &= ~(1 << bs);
+		buf[0] = 0xb0 | bs; /* Controller */
+		buf[1] = SCI_MIDI_CHANNEL_NOTES_OFF;
+		buf[2] = 0; /* Hmm... */
+		*result = 3;
+		return 0;
+	} else
+		return SI_FINISHED;
+}
+
+/**********************/
+/*-- Timer iterator --*/
+/**********************/
+int TimerSongIterator::nextCommand(byte *buf, int *result) {
+	if (_delta) {
+		int d = _delta;
+		_delta = 0;
+		return d;
+	}
+	return SI_FINISHED;
+}
+
+SongIterator *new_timer_iterator(int delta) {
+	return new TimerSongIterator(delta);
+}
+
+/**********************************/
+/*-- Fast-forward song iterator --*/
+/**********************************/
+
+int FastForwardSongIterator::nextCommand(byte *buf, int *result) {
+	if (_delta <= 0)
+		return SI_MORPH; /* Did our duty */
+
+	while (1) {
+		int rv = _delegate->nextCommand(buf, result);
+
+		if (rv > 0) {
+			/* Subtract from the delta we want to wait */
+			_delta -= rv;
+
+			/* Done */
+			if (_delta < 0)
+				return -_delta;
+		}
+
+		if (rv <= 0)
+			return rv;
+	}
+}
+
+Audio::AudioStream *FastForwardSongIterator::getAudioStream() {
+	return _delegate->getAudioStream();
+}
+
+SongIterator *FastForwardSongIterator::handleMessage(Message msg) {
+	if (msg._class == _SIMSG_PLASTICWRAP) {
+		assert(msg._type == _SIMSG_PLASTICWRAP_ACK_MORPH);
+
+		if (_delta <= 0) {
+			SongIterator *it = _delegate;
+			delete this;
+			return it;
+		}
+
+		warning("[ff-iterator] Morphing without need");
+		return this;
+	}
+
+	if (msg._class == _SIMSG_BASE && msg._type == _SIMSG_BASEMSG_PRINT) {
+		print_tabs_id(msg._arg.i, ID);
+		debugC(2, kDebugLevelSound, "FASTFORWARD:\n");
+		msg._arg.i++;
+	}
+
+	// And continue with the delegate
+	songit_handle_message(&_delegate, msg);
+
+	return NULL;
+}
+
+
+int FastForwardSongIterator::getTimepos() {
+	return _delegate->getTimepos();
+}
+
+FastForwardSongIterator::FastForwardSongIterator(SongIterator *capsit, int delta)
+	: _delegate(capsit), _delta(delta) {
+
+	channel_mask = capsit->channel_mask;
+}
+
+SongIterator *FastForwardSongIterator::clone(int delta) {
+	FastForwardSongIterator *newit = new FastForwardSongIterator(*this);
+	newit->_delegate = _delegate->clone(delta);
+	return newit;
+}
+
+SongIterator *new_fast_forward_iterator(SongIterator *capsit, int delta) {
+	if (capsit == NULL)
+		return NULL;
+
+	FastForwardSongIterator *it = new FastForwardSongIterator(capsit, delta);
+	return it;
+}
+
+
+/********************/
+/*-- Tee iterator --*/
+/********************/
+
+
+static void song_iterator_add_death_listener(SongIterator *it, TeeSongIterator *client) {
+	for (int i = 0; i < SONGIT_MAX_LISTENERS; ++i) {

@@ Diff output truncated at 100000 characters. @@

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