2009-06-08 18:33:20 +00:00
|
|
|
/* 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 Audio {
|
|
|
|
|
|
|
|
class Tfmx : public Paula {
|
2009-08-02 00:03:42 +00:00
|
|
|
private:
|
|
|
|
struct MdatResource;
|
2009-06-08 18:33:20 +00:00
|
|
|
public:
|
|
|
|
Tfmx(int rate, bool stereo);
|
|
|
|
virtual ~Tfmx();
|
|
|
|
|
2009-06-19 22:13:43 +00:00
|
|
|
void stopSong(bool stopAudio = true);
|
|
|
|
void doSong(int songPos, bool stopAudio = false);
|
2009-06-25 08:57:15 +00:00
|
|
|
int doSfx(uint16 sfxIndex, bool unlockChannel = false);
|
2009-06-19 21:42:14 +00:00
|
|
|
void doMacro(int note, int macro, int relVol = 0, int finetune = 0, int channelNo = 0);
|
2009-07-01 15:15:58 +00:00
|
|
|
int getTicks() const { return _playerCtx.tickCount; }
|
|
|
|
int getSongIndex() const { return _playerCtx.song; }
|
2009-07-31 14:29:04 +00:00
|
|
|
void setSignalPtr(uint16 *ptr, uint16 numSignals) { _playerCtx.signal = ptr; _playerCtx.numSignals = numSignals; }
|
|
|
|
void stopMacroEffect(int channel);
|
|
|
|
|
2009-08-02 00:03:42 +00:00
|
|
|
void freeResources() { _deleteResource = true; freeResourceDataImpl(); }
|
2009-08-01 23:02:45 +00:00
|
|
|
bool load(Common::SeekableReadStream &musicData, Common::SeekableReadStream &sampleData, bool autoDelete = true);
|
|
|
|
void setModuleData(Tfmx &otherPlayer);
|
2009-08-02 00:03:42 +00:00
|
|
|
/* must be called with resources loaded by loadMdatFile */
|
|
|
|
void setModuleDataVoid(const void *resource, const int8 *sampleData, uint32 sampleLen, bool autoDelete = true) {
|
|
|
|
setModuleData((const MdatResource *)resource, sampleData, sampleLen, autoDelete);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const MdatResource *loadMdatFile(Common::SeekableReadStream &musicData);
|
|
|
|
static const int8 *loadSampleFile(uint32 &sampleLen, Common::SeekableReadStream &sampleStream);
|
2009-08-01 23:02:45 +00:00
|
|
|
|
2009-07-31 14:29:04 +00:00
|
|
|
protected:
|
|
|
|
void interrupt();
|
2009-06-08 18:33:20 +00:00
|
|
|
|
2009-07-31 14:29:04 +00:00
|
|
|
private:
|
2009-08-02 00:03:42 +00:00
|
|
|
void setModuleData(const MdatResource *resource, const int8 *sampleData, uint32 sampleLen, bool autoDelete = true);
|
|
|
|
|
2009-07-01 15:15:58 +00:00
|
|
|
enum { kPalDefaultCiaVal = 11822, kNtscDefaultCiaVal = 14320, kCiaBaseInterval = 0x1B51F8 };
|
|
|
|
enum { kNumVoices = 4, kNumChannels = 8, kNumSubsongs = 32, kMaxPatternOffsets = 128, kMaxMacroOffsets = 128 };
|
2009-06-08 18:33:20 +00:00
|
|
|
static const uint16 noteIntervalls[64];
|
|
|
|
|
2009-08-01 23:02:45 +00:00
|
|
|
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;
|
2009-06-08 18:33:20 +00:00
|
|
|
|
|
|
|
uint16 headerFlags;
|
2009-08-01 23:02:45 +00:00
|
|
|
// uint32 headerUnknown;
|
|
|
|
// char textField[6 * 40];
|
2009-06-08 18:33:20 +00:00
|
|
|
|
2009-08-01 23:02:45 +00:00
|
|
|
struct Subsong {
|
|
|
|
uint16 songstart; //!< Index in Trackstep-Table
|
|
|
|
uint16 songend; //!< Last index in Trackstep-Table
|
|
|
|
uint16 tempo;
|
|
|
|
} subsong[kNumSubsongs];
|
2009-06-08 18:33:20 +00:00
|
|
|
|
2009-08-01 23:02:45 +00:00
|
|
|
uint32 trackstepOffset; //!< Offset in mdat
|
|
|
|
uint32 sfxTableOffset;
|
2009-06-08 18:33:20 +00:00
|
|
|
|
2009-08-01 23:02:45 +00:00
|
|
|
uint32 patternOffset[kMaxPatternOffsets]; //!< Offset in mdat
|
|
|
|
uint32 macroOffset[kMaxMacroOffsets]; //!< Offset in mdat
|
2009-06-08 18:33:20 +00:00
|
|
|
|
2009-08-01 23:02:45 +00:00
|
|
|
void boundaryCheck(const void *address, size_t accessLen = 1) const {
|
|
|
|
assert(mdatAlloc <= address && (const byte *)address + accessLen <= (const byte *)mdatData + mdatLen);
|
2009-06-08 18:33:20 +00:00
|
|
|
}
|
2009-08-02 00:03:42 +00:00
|
|
|
} const *_resource;
|
2009-06-08 18:33:20 +00:00
|
|
|
|
2009-08-01 23:02:45 +00:00
|
|
|
struct SampleResource {
|
|
|
|
const int8 *sampleData; //!< The whole sample-File
|
|
|
|
uint32 sampleLen;
|
2009-06-08 18:33:20 +00:00
|
|
|
|
2009-08-01 23:02:45 +00:00
|
|
|
void boundaryCheck(const void *address, size_t accessLen = 2) const {
|
|
|
|
assert(sampleData <= address && (const byte *)address + accessLen <= (const byte *)sampleData + sampleLen);
|
2009-06-08 18:33:20 +00:00
|
|
|
}
|
2009-08-01 23:02:45 +00:00
|
|
|
} _resourceSample;
|
2009-06-08 18:33:20 +00:00
|
|
|
|
2009-08-01 23:02:45 +00:00
|
|
|
bool _deleteResource;
|
2009-06-08 18:33:20 +00:00
|
|
|
|
|
|
|
struct ChannelContext {
|
|
|
|
byte paulaChannel;
|
|
|
|
|
2009-06-09 20:54:55 +00:00
|
|
|
byte macroIndex;
|
2009-06-08 18:33:20 +00:00
|
|
|
uint16 macroWait;
|
|
|
|
uint32 macroOffset;
|
|
|
|
uint32 macroReturnOffset;
|
|
|
|
uint16 macroStep;
|
2009-06-08 23:12:35 +00:00
|
|
|
uint16 macroReturnStep;
|
2009-06-08 18:33:20 +00:00
|
|
|
uint8 macroLoopCount;
|
|
|
|
bool macroRun;
|
2009-07-31 14:29:04 +00:00
|
|
|
int8 macroSfxRun; //!< values are the folowing: -1 macro disabled, 0 macro init, 1 macro running
|
2009-06-08 18:33:20 +00:00
|
|
|
|
2009-06-19 14:30:09 +00:00
|
|
|
uint32 customMacro;
|
|
|
|
uint8 customMacroIndex;
|
|
|
|
uint8 customMacroPrio;
|
|
|
|
|
2009-06-08 18:33:20 +00:00
|
|
|
bool sfxLocked;
|
|
|
|
int16 sfxLockTime;
|
|
|
|
bool keyUp;
|
|
|
|
|
|
|
|
bool deferWait;
|
2009-06-10 00:49:26 +00:00
|
|
|
uint16 dmaIntCount;
|
2009-06-08 18:33:20 +00:00
|
|
|
|
|
|
|
uint32 sampleStart;
|
|
|
|
uint16 sampleLen;
|
2009-06-09 20:54:55 +00:00
|
|
|
uint16 refPeriod;
|
2009-06-08 18:33:20 +00:00
|
|
|
uint16 period;
|
|
|
|
|
2009-06-10 00:49:26 +00:00
|
|
|
int8 volume;
|
2009-06-08 18:33:20 +00:00
|
|
|
uint8 relVol;
|
|
|
|
uint8 note;
|
|
|
|
uint8 prevNote;
|
2009-06-19 11:24:06 +00:00
|
|
|
int16 fineTune; // always a signextended byte
|
2009-06-08 18:33:20 +00:00
|
|
|
|
2009-06-09 20:54:55 +00:00
|
|
|
uint8 portaSkip;
|
|
|
|
uint8 portaCount;
|
|
|
|
uint16 portaDelta;
|
|
|
|
uint16 portaValue;
|
2009-06-08 18:33:20 +00:00
|
|
|
|
2009-06-09 20:54:55 +00:00
|
|
|
uint8 envSkip;
|
|
|
|
uint8 envCount;
|
|
|
|
uint8 envDelta;
|
2009-06-10 00:49:26 +00:00
|
|
|
int8 envEndVolume;
|
2009-06-08 18:33:20 +00:00
|
|
|
|
2009-06-08 23:12:35 +00:00
|
|
|
uint8 vibLength;
|
|
|
|
uint8 vibCount;
|
|
|
|
int16 vibValue;
|
|
|
|
int8 vibDelta;
|
2009-06-08 18:33:20 +00:00
|
|
|
|
2009-06-27 07:07:20 +00:00
|
|
|
uint8 addBeginLength;
|
|
|
|
uint8 addBeginCount;
|
|
|
|
int32 addBeginDelta;
|
2009-06-08 18:33:20 +00:00
|
|
|
} _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
|
|
|
|
|
2009-06-19 23:27:10 +00:00
|
|
|
uint8 fadeSkip;
|
|
|
|
uint8 fadeCount;
|
|
|
|
int8 fadeEndVolume;
|
|
|
|
int8 fadeDelta;
|
2009-06-14 19:40:24 +00:00
|
|
|
|
|
|
|
int tickCount;
|
2009-06-15 17:03:49 +00:00
|
|
|
|
2009-06-25 08:57:15 +00:00
|
|
|
uint16 *signal;
|
2009-07-31 14:29:04 +00:00
|
|
|
uint16 numSignals;
|
2009-06-19 20:17:53 +00:00
|
|
|
|
2009-06-15 17:03:49 +00:00
|
|
|
bool stopWithLastPattern; //!< hack to automatically stop the whole player if no Pattern is running
|
2009-06-08 18:33:20 +00:00
|
|
|
} _playerCtx;
|
2009-07-31 14:29:04 +00:00
|
|
|
|
2009-08-01 23:02:45 +00:00
|
|
|
const byte *getSfxPtr(uint16 index = 0) const {
|
|
|
|
const byte *sfxPtr = (byte *)(_resource->mdatData + _resource->sfxTableOffset + index * 8);
|
|
|
|
|
|
|
|
_resource->boundaryCheck(sfxPtr, 8);
|
|
|
|
return sfxPtr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint16 *getTrackPtr(uint16 trackstep = 0) const {
|
|
|
|
const uint16 *trackData = (uint16 *)(_resource->mdatData + _resource->trackstepOffset + 16 * trackstep);
|
|
|
|
|
|
|
|
_resource->boundaryCheck(trackData, 16);
|
|
|
|
return trackData;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint32 *getPatternPtr(uint32 offset) const {
|
|
|
|
const uint32 *pattData = (uint32 *)(_resource->mdatData + offset);
|
|
|
|
|
|
|
|
_resource->boundaryCheck(pattData, 4);
|
|
|
|
return pattData;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint32 *getMacroPtr(uint32 offset) const {
|
|
|
|
const uint32 *macroData = (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;
|
|
|
|
}
|
|
|
|
|
2009-06-20 13:49:02 +00:00
|
|
|
static void initMacroProgramm(ChannelContext &channel) {
|
2009-06-08 18:33:20 +00:00
|
|
|
channel.macroStep = 0;
|
|
|
|
channel.macroWait = 0;
|
|
|
|
channel.macroRun = true;
|
2009-06-30 17:41:24 +00:00
|
|
|
channel.macroSfxRun = 0;
|
2009-06-08 18:33:20 +00:00
|
|
|
channel.macroLoopCount = 0xFF;
|
2009-06-10 00:49:26 +00:00
|
|
|
channel.dmaIntCount = 0;
|
2009-08-01 16:10:00 +00:00
|
|
|
channel.deferWait = false;
|
2009-06-08 18:33:20 +00:00
|
|
|
}
|
|
|
|
|
2009-06-20 13:49:02 +00:00
|
|
|
static void clearEffects(ChannelContext &channel) {
|
2009-06-27 07:07:20 +00:00
|
|
|
channel.addBeginLength = 0;
|
2009-06-09 20:54:55 +00:00
|
|
|
channel.envSkip = 0;
|
2009-06-08 23:12:35 +00:00
|
|
|
channel.vibLength = 0;
|
2009-06-09 20:54:55 +00:00
|
|
|
channel.portaDelta = 0;
|
2009-06-08 23:12:35 +00:00
|
|
|
}
|
|
|
|
|
2009-06-20 13:49:02 +00:00
|
|
|
static void clearMacroProgramm(ChannelContext &channel) {
|
2009-06-19 21:42:14 +00:00
|
|
|
channel.macroRun = false;
|
2009-06-30 17:41:24 +00:00
|
|
|
channel.macroSfxRun = 0;
|
2009-06-19 21:42:14 +00:00
|
|
|
channel.dmaIntCount = 0;
|
|
|
|
}
|
|
|
|
|
2009-06-20 13:49:02 +00:00
|
|
|
static void unlockMacroChannel(ChannelContext &channel) {
|
2009-06-19 21:42:14 +00:00
|
|
|
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;
|
2009-06-19 22:13:43 +00:00
|
|
|
_channelCtx[i].volume = 0;
|
2009-06-19 21:42:14 +00:00
|
|
|
}
|
2009-06-08 18:33:20 +00:00
|
|
|
}
|
|
|
|
|
2009-06-25 08:57:15 +00:00
|
|
|
static void setNoteMacro(ChannelContext &channel, uint note, int fineTune) {
|
2009-06-10 00:49:26 +00:00
|
|
|
const uint16 noteInt = noteIntervalls[note & 0x3F];
|
|
|
|
const uint16 finetune = (uint16)(fineTune + channel.fineTune + (1 << 8));
|
|
|
|
channel.refPeriod = ((uint32)noteInt * finetune >> 8);
|
2009-06-25 08:57:15 +00:00
|
|
|
if (!channel.portaDelta)
|
2009-06-10 00:49:26 +00:00
|
|
|
channel.period = channel.refPeriod;
|
|
|
|
}
|
|
|
|
|
2009-07-31 14:29:04 +00:00
|
|
|
void initFadeCommand(const uint8 fadeTempo, const int8 endVol) {
|
|
|
|
_playerCtx.fadeCount = _playerCtx.fadeSkip = fadeTempo;
|
|
|
|
_playerCtx.fadeEndVolume = endVol;
|
|
|
|
|
|
|
|
if (fadeTempo) {
|
|
|
|
const int diff = _playerCtx.fadeEndVolume - _playerCtx.volume;
|
|
|
|
_playerCtx.fadeDelta = (diff != 0) ? ((diff > 0) ? 1 : -1) : 0;
|
|
|
|
} else {
|
|
|
|
_playerCtx.volume = endVol;
|
|
|
|
_playerCtx.fadeDelta = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-01 23:02:45 +00:00
|
|
|
|
2009-08-02 00:03:42 +00:00
|
|
|
void freeResourceDataImpl();
|
2009-06-08 18:33:20 +00:00
|
|
|
void effects(ChannelContext &channel);
|
2009-06-27 07:07:20 +00:00
|
|
|
void macroRun(ChannelContext &channel);
|
2009-06-08 18:33:20 +00:00
|
|
|
void advancePatterns();
|
2009-06-27 07:07:20 +00:00
|
|
|
bool patternRun(PatternContext &pattern);
|
|
|
|
bool trackRun(bool incStep = false);
|
2009-06-08 18:33:20 +00:00
|
|
|
void noteCommand(uint8 note, uint8 param1, uint8 param2, uint8 param3);
|
|
|
|
};
|
|
|
|
|
2009-07-01 23:11:56 +00:00
|
|
|
} // End of namespace Audio
|
2009-06-08 18:33:20 +00:00
|
|
|
|
|
|
|
#endif
|