/* 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$ * */ #ifndef SCI_SFX_SFX_ITERATOR_INTERNAL #define SCI_SFX_SFX_ITERATOR_INTERNAL #include "sci/sfx/iterator.h" #include "sci/sfx/sci_midi.h" #include "common/array.h" #include "common/list.h" namespace Sci { /* Iterator types */ enum { SI_STATE_UNINITIALISED = -1, SI_STATE_DELTA_TIME = 0, ///< Now at a delta time SI_STATE_COMMAND = 1, ///< Now at a MIDI operation SI_STATE_PENDING = 2, ///< Pending for loop SI_STATE_FINISHED = 3, ///< End of song SI_STATE_PCM = 4, ///< Should report a PCM next (-> DELTA_TIME) SI_STATE_PCM_MAGIC_DELTA = 5 ///< Should report a ``magic'' one tick delta time next (goes on to FINISHED) }; struct SongIteratorChannel { int state; ///< State of this song iterator channel int offset; ///< Offset into the data chunk */ int end; ///< Last allowed byte in track */ int id; ///< Some channel ID */ /** * Number of ticks before the specified channel is next used, or * CHANNEL_DELAY_MISSING to indicate that the delay has not yet * been read. */ int delay; /* Two additional offsets for recovering: */ int loop_offset; int initial_offset; int playmask; ///< Active playmask (MIDI channels to play in here) */ int notes_played; ///< #of notes played since the last loop start */ int loop_timepos; ///< Total delay for this channel's loop marker */ int total_timepos; ///< Number of ticks since the beginning, ignoring loops */ int timepos_increment; ///< Number of ticks until the next command (to add) */ int saw_notes; ///< Bitmask of channels we have currently played notes on */ byte last_cmd; ///< Last operation executed, for running status */ public: void init(int id, int offset, int end); void resetSynthChannels(); }; class BaseSongIterator : public SongIterator { public: int _polyphony[MIDI_CHANNELS]; ///< # of simultaneous notes on each int _importance[MIDI_CHANNELS]; ///< priority rating for each channel, 0 means unrated. int _ccc; ///< Cumulative cue counter, for those who need it byte _resetflag; ///< for 0x4C -- on DoSound StopSound, do we return to start? int _deviceId; ///< ID of the device we generating events for int _numActiveChannels; ///< Number of active channels Common::Array _data; ///< Song data int _loops; ///< Number of loops remaining public: BaseSongIterator(byte *data, uint size, songit_id_t id); protected: int parseMidiCommand(byte *buf, int *result, SongIteratorChannel *channel, int flags); int processMidi(byte *buf, int *result, SongIteratorChannel *channel, int flags); }; /********************************/ /*--------- SCI 0 --------------*/ /********************************/ class Sci0SongIterator : public BaseSongIterator { public: SongIteratorChannel _channel; public: Sci0SongIterator(byte *data, uint size, songit_id_t id); int nextCommand(byte *buf, int *result); Audio::AudioStream *getAudioStream(); SongIterator *handleMessage(Message msg); void init(); int getTimepos(); SongIterator *clone(int delta); }; /********************************/ /*--------- SCI 1 --------------*/ /********************************/ struct Sci1Sample { /** * Time left-- initially, this is 'Sample point 1'. * After initialisation, it is 'sample point 1 minus the sample * point of the previous sample' */ int delta; int size; bool announced; /* Announced for download (SI_PCM) */ sfx_pcm_config_t format; byte *_data; }; class Sci1SongIterator : public BaseSongIterator { public: SongIteratorChannel _channels[MIDI_CHANNELS]; /* Invariant: Whenever channels[i].delay == CHANNEL_DELAY_MISSING, ** channel_offset[i] points to a delta time object. */ bool _initialised; /**!< Whether the MIDI channel setup has been initialised */ int _numChannels; /**!< Number of channels actually used */ Common::List _samples; int _numLoopedChannels; /**!< Number of channels that are ready to loop */ int _delayRemaining; /**!< Number of ticks that haven't been polled yet */ int _hold; public: Sci1SongIterator(byte *data, uint size, songit_id_t id); ~Sci1SongIterator(); int nextCommand(byte *buf, int *result); Audio::AudioStream *getAudioStream(); SongIterator *handleMessage(Message msg); void init(); int getTimepos(); SongIterator *clone(int delta); private: int initSample(const int offset); int initSong(); int getSmallestDelta() const; void updateDelta(int delta); /** Checks that none of the channels is waiting for its delta to be read */ bool noDeltaTime() const; /** Determine the channel # of the next active event, or -1 */ int getCommandIndex() const; }; #define PLAYMASK_NONE 0x0 /***************************/ /*--------- Timer ---------*/ /***************************/ /** * A song iterator which waits a specified time and then fires * SI_FINISHED. Used by DoSound, where audio resources are played (SCI1) */ class TimerSongIterator : public SongIterator { protected: int _delta; /**!< Remaining time */ public: TimerSongIterator(int delta); int nextCommand(byte *buf, int *result); Audio::AudioStream *getAudioStream(); SongIterator *handleMessage(Message msg); int getTimepos(); SongIterator *clone(int delta); }; /**********************************/ /*--------- Fast Forward ---------*/ /**********************************/ /** * A song iterator which fast-forwards another iterator. * Skips all delta times until a specified 'delta' has been used up. */ class FastForwardSongIterator : public SongIterator { protected: SongIterator *_delegate; int _delta; /**!< Remaining time */ public: FastForwardSongIterator(SongIterator *capsit, int delta); int nextCommand(byte *buf, int *result); Audio::AudioStream *getAudioStream(); SongIterator *handleMessage(Message msg); int getTimepos(); SongIterator *clone(int delta); }; /**********************************/ /*--------- Tee iterator ---------*/ /**********************************/ enum { TEE_LEFT = 0, TEE_RIGHT = 1, TEE_LEFT_ACTIVE = (1<<0), TEE_RIGHT_ACTIVE = (1<<1), TEE_LEFT_READY = (1<<2), /**!< left result is ready */ TEE_RIGHT_READY = (1<<3), /**!< right result is ready */ TEE_LEFT_PCM = (1<<4), TEE_RIGHT_PCM = (1<<5) }; /** * This iterator combines two iterators, returns the next event available from either. */ class TeeSongIterator : public SongIterator { public: int _status; bool _readyToMorph; /**!< One of TEE_MORPH_* above */ struct { SongIterator *it; byte buf[4]; int result; int retval; } _children[2]; public: TeeSongIterator(SongIterator *left, SongIterator *right); ~TeeSongIterator(); int nextCommand(byte *buf, int *result); Audio::AudioStream *getAudioStream(); SongIterator *handleMessage(Message msg); void init(); int getTimepos() { return 0; } SongIterator *clone(int delta); }; } // End of namespace Sci #endif // SCI_SFX_SFX_ITERATOR_INTERNAL