scummvm/sound/mods/tfmx.h

293 lines
7.6 KiB
C
Raw Normal View History

/* 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 SOUND_MODS_TFMX_H
#define SOUND_MODS_TFMX_H
#include "sound/mods/paula.h"
namespace {
#ifndef NDEBUG
inline void boundaryCheck(const void *bufStart, size_t bufLen, const void *address, size_t accessLen = 1) {
assert(bufStart <= address && (const byte *)address + accessLen <= (const byte *)bufStart + bufLen);
}
#else
inline void boundaryCheck(const void *, size_t, void *, size_t = 1) {}
#endif
}
namespace Audio {
class Tfmx : public Paula {
public:
Tfmx(int rate, bool stereo);
virtual ~Tfmx();
void interrupt();
void stopSong(bool stopAudio = true);
void doSong(int songPos, bool stopAudio = false);
int doSfx(uint16 sfxIndex, bool unlockChannel = false);
void doMacro(int note, int macro, int relVol = 0, int finetune = 0, int channelNo = 0);
bool load(Common::SeekableReadStream &musicData, Common::SeekableReadStream &sampleData);
int getTicks() const {return _playerCtx.tickCount;}
int getSongIndex() const {return _playerCtx.song;}
void setSignalPtr(uint16 *ptr) {_playerCtx.signal = ptr;}
void stopMacroEffect(int channel) {
assert(0 <= channel && channel < kNumVoices);
Common::StackLock lock(_mutex);
unlockMacroChannel(_channelCtx[channel]);
clearMacroProgramm(_channelCtx[channel]);
Paula::disableChannel(_channelCtx[channel].paulaChannel);
}
// Note: everythings public so the debug-Routines work.
// private:
enum {kPalDefaultCiaVal = 11822, kNtscDefaultCiaVal = 14320, kCiaBaseInterval = 0x1B51F8};
enum {kNumVoices = 4, kNumChannels = 8, kNumSubsongs = 32, kMaxPatternOffsets = 128, kMaxMacroOffsets = 128};
static const uint16 noteIntervalls[64];
struct Resource {
uint32 _trackstepOffset; //!< Offset in mdat
uint32 _sfxTableOffset;
byte *_mdatData; //!< Currently the whole mdat-File
byte *_sampleData; //!< Currently the whole sample-File
uint32 _mdatLen;
uint32 _sampleLen;
byte header[10];
uint16 headerFlags;
uint32 headerUnknown;
char textField[6 * 40];
const byte *getSfxPtr(uint16 index = 0) {
byte *sfxPtr = (byte *)(_mdatData + _sfxTableOffset + index * 8);
boundaryCheck(_mdatData, _mdatLen, sfxPtr, 8);
return sfxPtr;
}
const uint16 *getTrackPtr(uint16 trackstep = 0) {
uint16 *trackData = (uint16 *)(_mdatData + _trackstepOffset + 16 * trackstep);
boundaryCheck(_mdatData, _mdatLen, trackData, 16);
return trackData;
}
const uint32 *getPatternPtr(uint32 offset) {
uint32 *pattData = (uint32 *)(_mdatData + offset);
boundaryCheck(_mdatData, _mdatLen, pattData, 4);
return pattData;
}
const uint32 *getMacroPtr(uint32 offset) {
uint32 *macroData = (uint32 *)(_mdatData + offset);
boundaryCheck(_mdatData, _mdatLen, macroData, 4);
return macroData;
}
const int8 *getSamplePtr(const uint32 offset) {
int8 *sampleData = (int8 *)(_sampleData + offset);
boundaryCheck(_sampleData, _sampleLen, sampleData, 2);
return sampleData;
}
Resource() : _mdatData(), _mdatLen(), _sampleData(), _sampleLen() {}
~Resource() {
delete[] _mdatData;
delete[] _sampleData;
}
} _resource;
uint32 _patternOffset[kMaxPatternOffsets]; //!< Offset in mdat
uint32 _macroOffset[kMaxMacroOffsets]; //!< Offset in mdat
struct Subsong {
uint16 songstart; //!< Index in Trackstep-Table
uint16 songend; //!< Last index in Trackstep-Table
uint16 tempo;
} _subsong[kNumSubsongs];
struct ChannelContext {
byte paulaChannel;
byte macroIndex;
uint16 macroWait;
uint32 macroOffset;
uint32 macroReturnOffset;
uint16 macroStep;
uint16 macroReturnStep;
uint8 macroLoopCount;
bool macroRun;
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 addBeginTime;
uint8 addBeginReset;
int16 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;
bool stopWithLastPattern; //!< hack to automatically stop the whole player if no Pattern is running
} _playerCtx;
private:
static void initMacroProgramm(ChannelContext &channel) {
channel.macroStep = 0;
channel.macroWait = 0;
channel.macroRun = true;
channel.macroLoopCount = 0xFF;
channel.dmaIntCount = 0;
}
static void clearEffects(ChannelContext &channel) {
channel.envSkip = 0;
channel.vibLength = 0;
channel.portaDelta = 0;
}
static void clearMacroProgramm(ChannelContext &channel) {
channel.macroRun = false;
channel.dmaIntCount = 0;
}
static void unlockMacroChannel(ChannelContext &channel) {
channel.customMacro = 0;
channel.customMacroPrio = false;
channel.sfxLocked = false;
channel.sfxLockTime = -1;
}
void stopPatternChannels() {
for (int i = 0; i < kNumChannels; ++i) {
_patternCtx[i].command = 0xFF;
_patternCtx[i].expose = 0;
}
}
void stopMacroChannels() {
for (int i = 0; i < kNumVoices; ++i) {
clearEffects(_channelCtx[i]);
unlockMacroChannel(_channelCtx[i]);
clearMacroProgramm(_channelCtx[i]);
_channelCtx[i].note = 0;
_channelCtx[i].volume = 0;
}
}
static void setNoteMacro(ChannelContext &channel, uint note, int fineTune) {
const uint16 noteInt = noteIntervalls[note & 0x3F];
const uint16 finetune = (uint16)(fineTune + channel.fineTune + (1 << 8));
channel.refPeriod = ((uint32)noteInt * finetune >> 8);
if (!channel.portaDelta)
channel.period = channel.refPeriod;
}
void effects(ChannelContext &channel);
inline bool macroStep(ChannelContext &channel);
void advancePatterns();
inline bool patternStep(PatternContext &pattern, bool &pendingTrackstep);
bool trackStep();
void noteCommand(uint8 note, uint8 param1, uint8 param2, uint8 param3);
};
}
#endif