[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 = ¬_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 == ¬_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 = ¬_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 == ¬_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