/* 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. * */ // Only compiled if SCUMM is built-in or we're building for dynamic modules #if !defined(AUDIO_MODS_TFMX_H) && (defined(ENABLE_SCUMM) || defined(DYNAMIC_MODULES)) #define AUDIO_MODS_TFMX_H #include "audio/mods/paula.h" namespace Audio { class Tfmx : public Paula { public: Tfmx(int rate, bool stereo); virtual ~Tfmx(); /** * Stops a playing Song (but leaves macros running) and optionally also stops the player * * @param stopAudio stops player and audio output * @param dataSize number of bytes to be written * @return the number of bytes which were actually written. */ void stopSong(bool stopAudio = true) { Common::StackLock lock(_mutex); stopSongImpl(stopAudio); } /** * Stops currently playing Song (if any) and cues up a new one. * if stopAudio is specified, the player gets reset before starting the new song * * @param songPos index of Song to play * @param stopAudio stops player and audio output * @param dataSize number of bytes to be written * @return the number of bytes which were actually written. */ void doSong(int songPos, bool stopAudio = false); /** * plays an effect from the sfx-table, does not start audio-playback. * * @param sfxIndex index of effect to play * @param unlockChannel overwrite higher priority effects * @return index of the channel which now queued up the effect. * -1 in case the effect couldnt be queued up */ int doSfx(uint16 sfxIndex, bool unlockChannel = false); /** * stop a running macro channel * * @param channel index of effect to stop */ void stopMacroEffect(int channel); void doMacro(int note, int macro, int relVol = 0, int finetune = 0, int channelNo = 0); int getTicks() const { return _playerCtx.tickCount; } int getSongIndex() const { return _playerCtx.song; } void setSignalPtr(uint16 *ptr, uint16 numSignals) { _playerCtx.signal = ptr; _playerCtx.numSignals = numSignals; } void freeResources() { _deleteResource = true; freeResourceDataImpl(); } bool load(Common::SeekableReadStream &musicData, Common::SeekableReadStream &sampleData, bool autoDelete = true); void setModuleData(Tfmx &otherPlayer); protected: void interrupt(); private: enum { kPalDefaultCiaVal = 11822, kNtscDefaultCiaVal = 14320, kCiaBaseInterval = 0x1B51F8 }; enum { kNumVoices = 4, kNumChannels = 8, kNumSubsongs = 32, kMaxPatternOffsets = 128, kMaxMacroOffsets = 128 }; struct MdatResource { const byte *mdatAlloc; ///< allocated Block of Memory const byte *mdatData; ///< Start of mdat-File, might point before mdatAlloc to correct Offset uint32 mdatLen; uint16 headerFlags; // uint32 headerUnknown; // char textField[6 * 40]; struct Subsong { uint16 songstart; ///< Index in Trackstep-Table uint16 songend; ///< Last index in Trackstep-Table uint16 tempo; } subsong[kNumSubsongs]; uint32 trackstepOffset; ///< Offset in mdat uint32 sfxTableOffset; uint32 patternOffset[kMaxPatternOffsets]; ///< Offset in mdat uint32 macroOffset[kMaxMacroOffsets]; ///< Offset in mdat void boundaryCheck(const void *address, size_t accessLen = 1) const { assert(mdatAlloc <= address && (const byte *)address + accessLen <= (const byte *)mdatData + mdatLen); } } const *_resource; struct SampleResource { const int8 *sampleData; ///< The whole sample-File uint32 sampleLen; void boundaryCheck(const void *address, size_t accessLen = 2) const { assert(sampleData <= address && (const byte *)address + accessLen <= (const byte *)sampleData + sampleLen); } } _resourceSample; bool _deleteResource; bool hasResources() { return _resource && _resource->mdatLen && _resourceSample.sampleLen; } struct ChannelContext { byte paulaChannel; // byte macroIndex; uint16 macroWait; uint32 macroOffset; uint32 macroReturnOffset; uint16 macroStep; uint16 macroReturnStep; uint8 macroLoopCount; bool macroRun; int8 macroSfxRun; ///< values are the folowing: -1 macro disabled, 0 macro init, 1 macro running uint32 customMacro; uint8 customMacroIndex; uint8 customMacroPrio; bool sfxLocked; int16 sfxLockTime; bool keyUp; bool deferWait; uint16 dmaIntCount; uint32 sampleStart; uint16 sampleLen; uint16 refPeriod; uint16 period; int8 volume; uint8 relVol; uint8 note; uint8 prevNote; int16 fineTune; // always a signextended byte uint8 portaSkip; uint8 portaCount; uint16 portaDelta; uint16 portaValue; uint8 envSkip; uint8 envCount; uint8 envDelta; int8 envEndVolume; uint8 vibLength; uint8 vibCount; int16 vibValue; int8 vibDelta; uint8 addBeginLength; uint8 addBeginCount; int32 addBeginDelta; } _channelCtx[kNumVoices]; struct PatternContext { uint32 offset; // patternStart, Offset from mdat uint32 savedOffset; // for subroutine calls uint16 step; // distance from patternStart uint16 savedStep; uint8 command; int8 expose; uint8 loopCount; uint8 wait; ///< how many ticks to wait before next Command } _patternCtx[kNumChannels]; struct TrackStepContext { uint16 startInd; uint16 stopInd; uint16 posInd; int16 loopCount; } _trackCtx; struct PlayerContext { int8 song; ///< >= 0 if Song is running (means process Patterns) uint16 patternCount; uint16 patternSkip; ///< skip that amount of CIA-Interrupts int8 volume; ///< Master Volume uint8 fadeSkip; uint8 fadeCount; int8 fadeEndVolume; int8 fadeDelta; int tickCount; uint16 *signal; uint16 numSignals; bool stopWithLastPattern; ///< hack to automatically stop the whole player if no Pattern is running } _playerCtx; const byte *getSfxPtr(uint16 index = 0) const { const byte *sfxPtr = (const byte *)(_resource->mdatData + _resource->sfxTableOffset + index * 8); _resource->boundaryCheck(sfxPtr, 8); return sfxPtr; } const uint16 *getTrackPtr(uint16 trackstep = 0) const { const uint16 *trackData = (const uint16 *)(_resource->mdatData + _resource->trackstepOffset + 16 * trackstep); _resource->boundaryCheck(trackData, 16); return trackData; } const uint32 *getPatternPtr(uint32 offset) const { const uint32 *pattData = (const uint32 *)(_resource->mdatData + offset); _resource->boundaryCheck(pattData, 4); return pattData; } const uint32 *getMacroPtr(uint32 offset) const { const uint32 *macroData = (const uint32 *)(_resource->mdatData + offset); _resource->boundaryCheck(macroData, 4); return macroData; } const int8 *getSamplePtr(const uint32 offset) const { const int8 *sample = _resourceSample.sampleData + offset; _resourceSample.boundaryCheck(sample, 2); return sample; } static inline void initMacroProgramm(ChannelContext &channel); static inline void clearEffects(ChannelContext &channel); static inline void haltMacroProgramm(ChannelContext &channel); static inline void unlockMacroChannel(ChannelContext &channel); static inline void initPattern(PatternContext &pattern, uint8 cmd, int8 expose, uint32 offset); void stopSongImpl(bool stopAudio = true); static inline void setNoteMacro(ChannelContext &channel, uint note, int fineTune); void initFadeCommand(const uint8 fadeTempo, const int8 endVol); void setModuleData(const MdatResource *resource, const int8 *sampleData, uint32 sampleLen, bool autoDelete = true); static const MdatResource *loadMdatFile(Common::SeekableReadStream &musicData); static const int8 *loadSampleFile(uint32 &sampleLen, Common::SeekableReadStream &sampleStream); void freeResourceDataImpl(); void effects(ChannelContext &channel); void macroRun(ChannelContext &channel); void advancePatterns(); bool patternRun(PatternContext &pattern); bool trackRun(bool incStep = false); void noteCommand(uint8 note, uint8 param1, uint8 param2, uint8 param3); }; } // End of namespace Audio #endif // !defined(AUDIO_MODS_TFMX_H)