aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/sound/iterator/core.cpp
diff options
context:
space:
mode:
authorFilippos Karapetis2010-01-05 01:22:16 +0000
committerFilippos Karapetis2010-01-05 01:22:16 +0000
commit84cd8d2dc7673bf883945cfdf600d98769817bc6 (patch)
tree9a57872c63fbf0a144b5fdd463d6bda43aa714df /engines/sci/sound/iterator/core.cpp
parentd8c59f5baa386a6baaf62685b794c2531b9cdd64 (diff)
downloadscummvm-rg350-84cd8d2dc7673bf883945cfdf600d98769817bc6.tar.gz
scummvm-rg350-84cd8d2dc7673bf883945cfdf600d98769817bc6.tar.bz2
scummvm-rg350-84cd8d2dc7673bf883945cfdf600d98769817bc6.zip
Renamed /gui to /graphics and /sfx to /sound, to better illustrate their purpose
svn-id: r47007
Diffstat (limited to 'engines/sci/sound/iterator/core.cpp')
-rw-r--r--engines/sci/sound/iterator/core.cpp1015
1 files changed, 1015 insertions, 0 deletions
diff --git a/engines/sci/sound/iterator/core.cpp b/engines/sci/sound/iterator/core.cpp
new file mode 100644
index 0000000000..89e4c7b14f
--- /dev/null
+++ b/engines/sci/sound/iterator/core.cpp
@@ -0,0 +1,1015 @@
+/* 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/sound/iterator/core.h"
+#include "sci/sound/iterator/iterator.h"
+#include "sci/sound/softseq/mididriver.h"
+
+#include "sci/sound/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:
+ SciVersion _soundVersion;
+ 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(SciVersion soundVersion);
+ ~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(SciVersion soundVersion)
+ : _soundVersion(soundVersion), _wakeupTime(0, SFX_TICKS_PER_SEC), _currentTime(0, 1) {
+ _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(_soundVersion)));
+ 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, SciVersion soundVersion) {
+ _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(soundVersion);
+
+ 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