1908 lines
45 KiB
C++
1908 lines
45 KiB
C++
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "audio/softsynth/fmtowns_pc98/towns_audio.h"
|
|
#include "audio/softsynth/fmtowns_pc98/pcm_common.h"
|
|
#include "audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h"
|
|
|
|
#include "common/debug.h"
|
|
#include "common/endian.h"
|
|
#include "common/util.h"
|
|
#include "common/textconsole.h"
|
|
#include "backends/audiocd/audiocd.h"
|
|
|
|
class TownsAudio_WaveTable {
|
|
friend class TownsAudioInterfaceInternal;
|
|
friend class TownsAudio_PCMChannel;
|
|
public:
|
|
TownsAudio_WaveTable();
|
|
~TownsAudio_WaveTable();
|
|
|
|
private:
|
|
void readHeader(const uint8 *buffer);
|
|
void readData(const uint8 *buffer);
|
|
void clear();
|
|
|
|
char name[9];
|
|
int32 id;
|
|
uint32 size;
|
|
uint32 loopStart;
|
|
uint32 loopLen;
|
|
uint16 rate;
|
|
uint16 rateOffs;
|
|
uint16 baseNote;
|
|
int8 *data;
|
|
};
|
|
|
|
class TownsAudio_PCMChannel final : public PCMChannel_Base {
|
|
public:
|
|
TownsAudio_PCMChannel();
|
|
~TownsAudio_PCMChannel();
|
|
|
|
void clear() override;
|
|
void loadDataFromWaveTable(TownsAudio_WaveTable *w);
|
|
void loadData(uint8 *buffer, uint32 size);
|
|
|
|
int initInstrument(uint8 ¬e, TownsAudio_WaveTable *&tables, int numTables);
|
|
void keyOn(uint8 note, uint8 velo, TownsAudio_WaveTable *w);
|
|
void keyOff();
|
|
|
|
void updateEnvelopeGenerator();
|
|
|
|
void setInstrument(uint8 *instr);
|
|
void setLevel(uint8 lvl);
|
|
void setPitch(uint32 pt);
|
|
void setBalance(uint8 blc);
|
|
|
|
bool isPlaying() const override;
|
|
|
|
bool _keyPressed;
|
|
bool _reserved;
|
|
bool _activeKey;
|
|
bool _activeEffect;
|
|
|
|
private:
|
|
void stopInternal() override;
|
|
|
|
void setNote(uint8 note, TownsAudio_WaveTable *w, bool stepLimit = false);
|
|
void setVelo(uint8 velo);
|
|
|
|
void envAttack();
|
|
void envDecay();
|
|
void envSustain();
|
|
void envRelease();
|
|
|
|
uint8 *_curInstrument;
|
|
int8 *_extData;
|
|
|
|
uint8 _note;
|
|
uint8 _velo;
|
|
uint8 _level;
|
|
|
|
uint16 _stepNote;
|
|
uint16 _stepPitch;
|
|
|
|
uint8 _envTotalLevel;
|
|
uint8 _envAttackRate;
|
|
uint8 _envDecayRate;
|
|
uint8 _envSustainLevel;
|
|
uint8 _envSustainRate;
|
|
uint8 _envReleaseRate;
|
|
int16 _envStep;
|
|
int16 _envCurrentLevel;
|
|
|
|
EnvelopeState _envState;
|
|
|
|
static const uint16 _pcmPhase1[];
|
|
static const uint16 _pcmPhase2[];
|
|
};
|
|
|
|
class TownsAudioInterfaceInternal final : public TownsPC98_FmSynth {
|
|
private:
|
|
TownsAudioInterfaceInternal(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver, bool externalMutex);
|
|
public:
|
|
~TownsAudioInterfaceInternal();
|
|
|
|
static TownsAudioInterfaceInternal *addNewRef(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver, bool externalMutex);
|
|
static void releaseRef(TownsAudioInterface *owner);
|
|
|
|
bool init() override;
|
|
|
|
int callback(int command, ...);
|
|
int processCommand(int command, va_list &args);
|
|
|
|
void setMusicVolume(int volume);
|
|
void setSoundEffectVolume(int volume);
|
|
// Defines the channels used as sound effect channels for the purpose of ScummVM GUI volume control.
|
|
// The first 6 bits are the 6 fm channels. The next 8 bits are pcm channels.
|
|
void setSoundEffectChanMask(int mask);
|
|
|
|
private:
|
|
bool assignPluginDriver(TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver);
|
|
void removePluginDriver(TownsAudioInterface *owner);
|
|
|
|
void nextTickEx(int32 *buffer, uint32 bufferSize) override;
|
|
|
|
void timerCallbackA() override;
|
|
void timerCallbackB() override;
|
|
|
|
typedef int (TownsAudioInterfaceInternal::*TownsAudioIntfCallback)(va_list &);
|
|
const TownsAudioIntfCallback *_intfOpcodes;
|
|
|
|
int intf_reset(va_list &args);
|
|
int intf_keyOn(va_list &args);
|
|
int intf_keyOff(va_list &args);
|
|
int intf_setPanPos(va_list &args);
|
|
int intf_setInstrument(va_list &args);
|
|
int intf_loadInstrument(va_list &args);
|
|
int intf_setPitch(va_list &args);
|
|
int intf_setLevel(va_list &args);
|
|
int intf_chanOff(va_list &args);
|
|
int intf_writeReg(va_list &args);
|
|
int intf_writeRegBuffer(va_list &args);
|
|
int intf_readRegBuffer(va_list &args);
|
|
int intf_setTimerA(va_list &args);
|
|
int intf_setTimerB(va_list &args);
|
|
int intf_enableTimerA(va_list &args);
|
|
int intf_enableTimerB(va_list &args);
|
|
int intf_loadSamples(va_list &args);
|
|
int intf_reserveEffectChannels(va_list &args);
|
|
int intf_loadWaveTable(va_list &args);
|
|
int intf_unloadWaveTable(va_list &args);
|
|
int intf_pcmPlayEffect(va_list &args);
|
|
int intf_pcmChanOff(va_list &args);
|
|
int intf_pcmEffectPlaying(va_list &args);
|
|
int intf_pcmDisableAllChannels(va_list &args);
|
|
int intf_fmKeyOn(va_list &args);
|
|
int intf_fmKeyOff(va_list &args);
|
|
int intf_fmSetPanPos(va_list &args);
|
|
int intf_fmSetInstrument(va_list &args);
|
|
int intf_fmLoadInstrument(va_list &args);
|
|
int intf_fmSetPitch(va_list &args);
|
|
int intf_fmSetLevel(va_list &args);
|
|
int intf_fmReset(va_list &args);
|
|
int intf_setOutputVolume(va_list &args);
|
|
int intf_resetOutputVolume(va_list &args);
|
|
int intf_getOutputVolume(va_list &args);
|
|
int intf_setOutputMute(va_list &args);
|
|
int intf_cdaToggle(va_list &args);
|
|
int intf_getOutputVolume2(va_list &args);
|
|
int intf_getOutputMute(va_list &args);
|
|
int intf_pcmUpdateEnvelopeGenerator(va_list &args);
|
|
|
|
int intf_notImpl(va_list &args);
|
|
|
|
void fmReset();
|
|
int fmKeyOn(int chan, int note, int velo);
|
|
int fmKeyOff(int chan);
|
|
int fmChanOff(int chan);
|
|
int fmSetPanPos(int chan, int mode);
|
|
int fmSetInstrument(int chan, int instrId);
|
|
int fmLoadInstrument(int instrId, const uint8 *data);
|
|
int fmSetPitch(int chan, int pitch);
|
|
int fmSetLevel(int chan, int lvl);
|
|
|
|
void bufferedWriteReg(uint8 part, uint8 regAddress, uint8 value);
|
|
|
|
uint8 _fmChanPlaying;
|
|
uint8 _fmChanNote[6];
|
|
int16 _fmChanPitch[6];
|
|
|
|
uint8 *_fmSaveReg[2];
|
|
uint8 *_fmInstruments;
|
|
|
|
void pcmReset();
|
|
int pcmKeyOn(int chan, int note, int velo);
|
|
int pcmKeyOff(int chan);
|
|
int pcmChanOff(int chan);
|
|
int pcmSetPanPos(int chan, int mode);
|
|
int pcmSetInstrument(int chan, int instrId);
|
|
int pcmLoadInstrument(int instrId, const uint8 *data);
|
|
int pcmSetPitch(int chan, int pitch);
|
|
int pcmSetLevel(int chan, int lvl);
|
|
|
|
TownsAudio_PCMChannel **_pcmChan;
|
|
PCMDevice_Base *_pcmDev;
|
|
|
|
uint8 _numReservedChannels;
|
|
uint8 *_pcmInstruments;
|
|
|
|
TownsAudio_WaveTable *_waveTables;
|
|
uint8 _numWaveTables;
|
|
uint32 _waveTablesTotalDataSize;
|
|
|
|
void updateOutputVolume();
|
|
void updateOutputVolumeInternal();
|
|
uint8 _outputVolumeFlags;
|
|
uint8 _outputLevel[16];
|
|
uint8 _outputMute[16];
|
|
bool _updateOutputVol;
|
|
|
|
uint16 _musicVolume;
|
|
uint16 _sfxVolume;
|
|
|
|
TownsAudioInterfacePluginDriver *_drv;
|
|
void *_drvOwner;
|
|
bool _externalMutex;
|
|
bool _ready;
|
|
|
|
static TownsAudioInterfaceInternal *_refInstance;
|
|
static int _refCount;
|
|
|
|
static const uint8 _chanFlags[];
|
|
static const uint16 _frequency[];
|
|
static const uint8 _carrier[];
|
|
static const uint8 _fmDefaultInstrument[];
|
|
};
|
|
|
|
TownsAudioInterfaceInternal::TownsAudioInterfaceInternal(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver, bool externalMutex) :
|
|
TownsPC98_FmSynth(mixer, kTypeTowns), _fmInstruments(nullptr), _pcmInstruments(nullptr), _pcmChan(nullptr), _waveTables(nullptr), _waveTablesTotalDataSize(0),
|
|
_drv(driver), _drvOwner(owner), _externalMutex(externalMutex), _outputVolumeFlags(0), _pcmDev(nullptr),
|
|
_fmChanPlaying(0), _musicVolume(Audio::Mixer::kMaxMixerVolume), _sfxVolume(Audio::Mixer::kMaxMixerVolume),
|
|
_numReservedChannels(0), _numWaveTables(0), _updateOutputVol(false), _ready(false) {
|
|
#define INTCB(x) &TownsAudioInterfaceInternal::intf_##x
|
|
static const TownsAudioIntfCallback intfCb[] = {
|
|
// 0
|
|
INTCB(reset),
|
|
INTCB(keyOn),
|
|
INTCB(keyOff),
|
|
INTCB(setPanPos),
|
|
// 4
|
|
INTCB(setInstrument),
|
|
INTCB(loadInstrument),
|
|
INTCB(notImpl),
|
|
INTCB(setPitch),
|
|
// 8
|
|
INTCB(setLevel),
|
|
INTCB(chanOff),
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
// 12
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
// 16
|
|
INTCB(notImpl),
|
|
INTCB(writeReg),
|
|
INTCB(notImpl),
|
|
INTCB(writeRegBuffer),
|
|
// 20
|
|
INTCB(readRegBuffer),
|
|
INTCB(setTimerA),
|
|
INTCB(setTimerB),
|
|
INTCB(enableTimerA),
|
|
// 24
|
|
INTCB(enableTimerB),
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
// 28
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
// 32
|
|
INTCB(loadSamples),
|
|
INTCB(reserveEffectChannels),
|
|
INTCB(loadWaveTable),
|
|
INTCB(unloadWaveTable),
|
|
// 36
|
|
INTCB(notImpl),
|
|
INTCB(pcmPlayEffect),
|
|
INTCB(notImpl),
|
|
INTCB(pcmChanOff),
|
|
// 40
|
|
INTCB(pcmEffectPlaying),
|
|
INTCB(pcmDisableAllChannels),
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
// 44
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
// 48
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
INTCB(fmKeyOn),
|
|
INTCB(fmKeyOff),
|
|
// 52
|
|
INTCB(fmSetPanPos),
|
|
INTCB(fmSetInstrument),
|
|
INTCB(fmLoadInstrument),
|
|
INTCB(notImpl),
|
|
// 56
|
|
INTCB(fmSetPitch),
|
|
INTCB(fmSetLevel),
|
|
INTCB(fmReset),
|
|
INTCB(notImpl),
|
|
// 60
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
// 64
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
INTCB(setOutputVolume),
|
|
// 68
|
|
INTCB(resetOutputVolume),
|
|
INTCB(getOutputVolume),
|
|
INTCB(setOutputMute),
|
|
INTCB(notImpl),
|
|
// 72
|
|
INTCB(notImpl),
|
|
INTCB(cdaToggle),
|
|
INTCB(getOutputVolume2),
|
|
INTCB(getOutputMute),
|
|
// 76
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
INTCB(notImpl),
|
|
// 80
|
|
INTCB(pcmUpdateEnvelopeGenerator),
|
|
INTCB(notImpl)
|
|
};
|
|
#undef INTCB
|
|
|
|
_intfOpcodes = intfCb;
|
|
|
|
memset(_fmSaveReg, 0, sizeof(_fmSaveReg));
|
|
memset(_fmChanNote, 0, sizeof(_fmChanNote));
|
|
memset(_fmChanPitch, 0, sizeof(_fmChanPitch));
|
|
memset(_outputLevel, 0, sizeof(_outputLevel));
|
|
memset(_outputMute, 0, sizeof(_outputMute));
|
|
}
|
|
|
|
TownsAudioInterfaceInternal::~TownsAudioInterfaceInternal() {
|
|
deinit();
|
|
Common::StackLock lock(_mutex);
|
|
_ready = false;
|
|
|
|
delete[] _fmSaveReg[0];
|
|
delete[] _fmSaveReg[1];
|
|
delete[] _fmInstruments;
|
|
delete[] _pcmInstruments;
|
|
delete[] _waveTables;
|
|
|
|
if (_pcmChan) {
|
|
for (int i = 0; i < 8; ++i)
|
|
delete _pcmChan[i];
|
|
delete[] _pcmChan;
|
|
}
|
|
delete _pcmDev;
|
|
|
|
// Restore cd audio settings to default (the settings will be kept when returning
|
|
// to the launcher, since the manager is a global object).
|
|
g_system->getAudioCDManager()->setVolume(Audio::Mixer::kMaxChannelVolume);
|
|
g_system->getAudioCDManager()->setBalance(0);
|
|
}
|
|
|
|
TownsAudioInterfaceInternal *TownsAudioInterfaceInternal::addNewRef(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver, bool externalMutex) {
|
|
_refCount++;
|
|
if (_refCount == 1 && _refInstance == nullptr)
|
|
_refInstance = new TownsAudioInterfaceInternal(mixer, owner, driver, externalMutex);
|
|
else if (_refCount < 2 || _refInstance == nullptr)
|
|
error("TownsAudioInterfaceInternal::addNewRef(): Internal reference management failure");
|
|
else if (!_refInstance->assignPluginDriver(owner, driver))
|
|
error("TownsAudioInterfaceInternal::addNewRef(): Plugin driver conflict");
|
|
|
|
return _refInstance;
|
|
}
|
|
|
|
void TownsAudioInterfaceInternal::releaseRef(TownsAudioInterface *owner) {
|
|
if (!_refCount)
|
|
return;
|
|
|
|
_refCount--;
|
|
|
|
if (_refCount) {
|
|
if (_refInstance)
|
|
_refInstance->removePluginDriver(owner);
|
|
} else {
|
|
delete _refInstance;
|
|
_refInstance = nullptr;
|
|
}
|
|
}
|
|
|
|
bool TownsAudioInterfaceInternal::init() {
|
|
if (_ready)
|
|
return true;
|
|
|
|
_fmSaveReg[0] = new uint8[256];
|
|
_fmSaveReg[1] = new uint8[256];
|
|
_fmInstruments = new uint8[128 * 48];
|
|
_pcmInstruments = new uint8[32 * 128];
|
|
_waveTables = new TownsAudio_WaveTable[128];
|
|
_pcmChan = new TownsAudio_PCMChannel*[8];
|
|
_pcmDev = new PCMDevice_Base(19300, 128, 8);
|
|
for (int i = 0; i < 8; ++i) {
|
|
_pcmChan[i] = new TownsAudio_PCMChannel();
|
|
_pcmDev->assignChannel(i, _pcmChan[i]);
|
|
}
|
|
|
|
if (!TownsPC98_FmSynth::init())
|
|
return false;
|
|
|
|
setVolumeChannelMasks(-1, 0);
|
|
|
|
_ready = true;
|
|
callback(0);
|
|
|
|
return true;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::callback(int command, ...) {
|
|
Common::StackLock lock(_mutex);
|
|
if (!_ready)
|
|
return 1;
|
|
|
|
va_list args;
|
|
va_start(args, command);
|
|
|
|
int res = processCommand(command, args);
|
|
|
|
va_end(args);
|
|
return res;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::processCommand(int command, va_list &args) {
|
|
Common::StackLock lock(_mutex);
|
|
if (!_ready)
|
|
return 1;
|
|
|
|
if (command < 0 || command > 81)
|
|
return 4;
|
|
|
|
int res = (this->*_intfOpcodes[command])(args);
|
|
|
|
return res;
|
|
}
|
|
|
|
void TownsAudioInterfaceInternal::setMusicVolume(int volume) {
|
|
Common::StackLock lock(_mutex);
|
|
_musicVolume = CLIP<uint16>(volume, 0, Audio::Mixer::kMaxMixerVolume);
|
|
if (!_ready)
|
|
return;
|
|
_pcmDev->setMusicVolume(_musicVolume);
|
|
setVolumeIntern(_musicVolume, _sfxVolume);
|
|
}
|
|
|
|
void TownsAudioInterfaceInternal::setSoundEffectVolume(int volume) {
|
|
Common::StackLock lock(_mutex);
|
|
_sfxVolume = CLIP<uint16>(volume, 0, Audio::Mixer::kMaxMixerVolume);
|
|
if (!_ready)
|
|
return;
|
|
_pcmDev->setSfxVolume(_sfxVolume);
|
|
setVolumeIntern(_musicVolume, _sfxVolume);
|
|
}
|
|
|
|
void TownsAudioInterfaceInternal::setSoundEffectChanMask(int mask) {
|
|
Common::StackLock lock(_mutex);
|
|
if (!_ready)
|
|
return;
|
|
_pcmDev->setSfxChanMask(mask >> 6);
|
|
mask &= 0x3f;
|
|
setVolumeChannelMasks(~mask, mask);
|
|
}
|
|
|
|
bool TownsAudioInterfaceInternal::assignPluginDriver(TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver) {
|
|
Common::StackLock lock(_mutex);
|
|
if (_refCount <= 1)
|
|
return true;
|
|
|
|
if (_drv) {
|
|
if (driver && driver != _drv)
|
|
return false;
|
|
} else {
|
|
_drv = driver;
|
|
_drvOwner = owner;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void TownsAudioInterfaceInternal::removePluginDriver(TownsAudioInterface *owner) {
|
|
Common::StackLock lock(_mutex);
|
|
if (_drvOwner == owner)
|
|
_drv = nullptr;
|
|
}
|
|
|
|
void TownsAudioInterfaceInternal::nextTickEx(int32 *buffer, uint32 bufferSize) {
|
|
Common::StackLock lock(_mutex);
|
|
if (!_ready)
|
|
return;
|
|
|
|
if (_updateOutputVol)
|
|
updateOutputVolumeInternal();
|
|
|
|
_pcmDev->readBuffer(buffer, bufferSize);
|
|
}
|
|
|
|
void TownsAudioInterfaceInternal::timerCallbackA() {
|
|
if (_drv && _ready) {
|
|
int restore = 0;
|
|
if (_externalMutex) {
|
|
for (; restore < _mixerThreadLockCounter; ++restore)
|
|
_mutex.unlock();
|
|
}
|
|
_drv->timerCallback(0);
|
|
while (restore--)
|
|
_mutex.lock();
|
|
}
|
|
}
|
|
|
|
void TownsAudioInterfaceInternal::timerCallbackB() {
|
|
if (_ready) {
|
|
if (_drv) {
|
|
int restore = 0;
|
|
if (_externalMutex) {
|
|
for (; restore < _mixerThreadLockCounter; ++restore)
|
|
_mutex.unlock();
|
|
}
|
|
_drv->timerCallback(1);
|
|
while (restore--)
|
|
_mutex.lock();
|
|
}
|
|
callback(80);
|
|
}
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_reset(va_list &args) {
|
|
fmReset();
|
|
pcmReset();
|
|
callback(68);
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_keyOn(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
int note = va_arg(args, int);
|
|
int velo = va_arg(args, int);
|
|
return (chan & 0x40) ? pcmKeyOn(chan, note, velo) : fmKeyOn(chan, note, velo);
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_keyOff(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
return (chan & 0x40) ? pcmKeyOff(chan) : fmKeyOff(chan);
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_setPanPos(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
int mode = va_arg(args, int);
|
|
return (chan & 0x40) ? pcmSetPanPos(chan, mode) : fmSetPanPos(chan, mode);
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_setInstrument(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
int instrId = va_arg(args, int);
|
|
return (chan & 0x40) ? pcmSetInstrument(chan, instrId) : fmSetInstrument(chan, instrId);
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_loadInstrument(va_list &args) {
|
|
int chanType = va_arg(args, int);
|
|
int instrId = va_arg(args, int);
|
|
uint8 *instrData = va_arg(args, uint8 *);
|
|
return (chanType & 0x40) ? pcmLoadInstrument(instrId, instrData) : fmLoadInstrument(instrId, instrData);
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_setPitch(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
int16 pitch = (int16)(va_arg(args, int) & 0xffff);
|
|
return (chan & 0x40) ? pcmSetPitch(chan, pitch) : fmSetPitch(chan, pitch);
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_setLevel(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
int lvl = va_arg(args, int);
|
|
return (chan & 0x40) ? pcmSetLevel(chan, lvl) : fmSetLevel(chan, lvl);
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_chanOff(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
return (chan & 0x40) ? pcmChanOff(chan) : fmChanOff(chan);
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_writeReg(va_list &args) {
|
|
int part = va_arg(args, int) ? 1 : 0;
|
|
int reg = va_arg(args, int);
|
|
int val = va_arg(args, int);
|
|
if ((!part && reg < 0x20) || (part && reg < 0x30) || (reg > 0xb6))
|
|
return 3;
|
|
|
|
bufferedWriteReg(part, reg, val);
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_writeRegBuffer(va_list &args) {
|
|
int part = va_arg(args, int) ? 1 : 0;
|
|
int reg = va_arg(args, int);
|
|
int val = va_arg(args, int);
|
|
|
|
if ((!part && reg < 0x20) || (part && reg < 0x30) || (reg > 0xef))
|
|
return 3;
|
|
|
|
_fmSaveReg[part][reg] = val;
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_readRegBuffer(va_list &args) {
|
|
int part = va_arg(args, int) ? 1 : 0;
|
|
int reg = va_arg(args, int);
|
|
uint8 *dst = va_arg(args, uint8 *);
|
|
*dst = 0;
|
|
|
|
if ((!part && reg < 0x20) || (part && reg < 0x30) || (reg > 0xef))
|
|
return 3;
|
|
|
|
*dst = _fmSaveReg[part][reg];
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_setTimerA(va_list &args) {
|
|
int enable = va_arg(args, int);
|
|
int tempo = va_arg(args, int);
|
|
|
|
if (enable) {
|
|
bufferedWriteReg(0, 0x25, tempo & 3);
|
|
bufferedWriteReg(0, 0x24, (tempo >> 2) & 0xff);
|
|
bufferedWriteReg(0, 0x27, _fmSaveReg[0][0x27] | 0x05);
|
|
} else {
|
|
bufferedWriteReg(0, 0x27, (_fmSaveReg[0][0x27] & 0xfa) | 0x10);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_setTimerB(va_list &args) {
|
|
int enable = va_arg(args, int);
|
|
int tempo = va_arg(args, int);
|
|
|
|
if (enable) {
|
|
bufferedWriteReg(0, 0x26, tempo & 0xff);
|
|
bufferedWriteReg(0, 0x27, _fmSaveReg[0][0x27] | 0x0A);
|
|
} else {
|
|
bufferedWriteReg(0, 0x27, (_fmSaveReg[0][0x27] & 0xf5) | 0x20);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_enableTimerA(va_list &args) {
|
|
bufferedWriteReg(0, 0x27, _fmSaveReg[0][0x27] | 0x15);
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_enableTimerB(va_list &args) {
|
|
bufferedWriteReg(0, 0x27, _fmSaveReg[0][0x27] | 0x2a);
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_loadSamples(va_list &args) {
|
|
uint32 dest = va_arg(args, uint32);
|
|
int size = va_arg(args, int);
|
|
uint8 *src = va_arg(args, uint8*);
|
|
|
|
if (dest >= 65536 || size == 0 || size > 65536)
|
|
return 3;
|
|
if (size + dest > 65536)
|
|
// EOB II FM-TOWNS tries to load more than 65536 bytes of wave sounds for the outro sequence.
|
|
// This means that some sfx would not play. Since we don't really need the memory limit,
|
|
// I have commented out the error return and added a debug message instead.
|
|
debugN(9, "FM-TOWNS AUDIO: exceeding wave memory size by %d bytes", size + dest - 65536);
|
|
// return 5;
|
|
|
|
int dwIndex = _numWaveTables - 1;
|
|
for (uint32 t = _waveTablesTotalDataSize; dwIndex && (dest < t); dwIndex--)
|
|
t -= _waveTables[dwIndex].size;
|
|
|
|
TownsAudio_WaveTable *s = &_waveTables[dwIndex];
|
|
_waveTablesTotalDataSize -= s->size;
|
|
s->size = size;
|
|
s->readData(src);
|
|
_waveTablesTotalDataSize += s->size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_reserveEffectChannels(va_list &args) {
|
|
int numChan = va_arg(args, int);
|
|
if (numChan > 8)
|
|
return 3;
|
|
if ((numChan << 13) + _waveTablesTotalDataSize > 65536)
|
|
return 5;
|
|
|
|
if (numChan == _numReservedChannels)
|
|
return 0;
|
|
|
|
if (numChan < _numReservedChannels) {
|
|
int c = 8 - _numReservedChannels;
|
|
for (int i = numChan; i; i--)
|
|
_pcmChan[c--]->_activeEffect = false;
|
|
} else {
|
|
int c = 7 - _numReservedChannels;
|
|
for (int i = numChan - _numReservedChannels; i; i--) {
|
|
_pcmChan[c]->_keyPressed = false;
|
|
_pcmChan[c--]->_activeKey = false;
|
|
}
|
|
}
|
|
|
|
_numReservedChannels = numChan;
|
|
for (int i = 0; i < 8; i++)
|
|
_pcmChan[i]->_reserved = i >= (8 - _numReservedChannels) ? true : false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_loadWaveTable(va_list &args) {
|
|
uint8 *data = va_arg(args, uint8 *);
|
|
if (_numWaveTables > 127)
|
|
return 3;
|
|
|
|
TownsAudio_WaveTable w;
|
|
w.readHeader(data);
|
|
if (!w.size)
|
|
return 6;
|
|
|
|
if (_waveTablesTotalDataSize + w.size > 65504)
|
|
return 5;
|
|
|
|
callback(41);
|
|
|
|
for (int i = 0; i < _numWaveTables; i++) {
|
|
if (_waveTables[i].id == w.id)
|
|
return 10;
|
|
}
|
|
|
|
TownsAudio_WaveTable *s = &_waveTables[_numWaveTables++];
|
|
s->readHeader(data);
|
|
|
|
_waveTablesTotalDataSize += s->size;
|
|
int res = callback(32, _waveTablesTotalDataSize, s->size, data + 32);
|
|
if (res) {
|
|
_waveTablesTotalDataSize -= s->size;
|
|
_numWaveTables--;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_unloadWaveTable(va_list &args) {
|
|
int id = va_arg(args, int);
|
|
callback(41);
|
|
|
|
if (id == -1) {
|
|
for (int i = 0; i < 128; i++)
|
|
_waveTables[i].clear();
|
|
_numWaveTables = 0;
|
|
_waveTablesTotalDataSize = 0;
|
|
} else {
|
|
if (_waveTables) {
|
|
for (int i = 0; i < _numWaveTables; i++) {
|
|
if (_waveTables[i].id == id) {
|
|
_numWaveTables--;
|
|
_waveTablesTotalDataSize -= _waveTables[i].size;
|
|
_waveTables[i].clear();
|
|
for (; i < _numWaveTables; i++)
|
|
_waveTables[i] = _waveTables[i + 1];
|
|
return 0;
|
|
}
|
|
}
|
|
return 9;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_pcmPlayEffect(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
int note = va_arg(args, int);
|
|
int velo = va_arg(args, int);
|
|
uint8 *data = va_arg(args, uint8 *);
|
|
|
|
if (chan < 0x40 || chan > 0x47)
|
|
return 1;
|
|
|
|
if (note & 0x80 || velo & 0x80)
|
|
return 3;
|
|
|
|
chan -= 0x40;
|
|
|
|
if (!_pcmChan[chan]->_reserved)
|
|
return 7;
|
|
|
|
if (_pcmChan[chan]->_activeEffect)
|
|
return 2;
|
|
|
|
TownsAudio_WaveTable w;
|
|
w.readHeader(data);
|
|
|
|
if (w.size < (w.loopStart + w.loopLen))
|
|
return 13;
|
|
|
|
if (!w.size)
|
|
return 6;
|
|
|
|
_pcmChan[chan]->loadData(data + 32, w.size);
|
|
_pcmChan[chan]->keyOn(note, velo, &w);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_pcmChanOff(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
pcmChanOff(chan);
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_pcmEffectPlaying(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
if (chan < 0x40 || chan > 0x47)
|
|
return 1;
|
|
chan -= 0x40;
|
|
return _pcmChan[chan]->_activeEffect ? 1 : 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_pcmDisableAllChannels(va_list &args) {
|
|
for (int i = 0; i < 8; ++i)
|
|
_pcmChan[i]->deactivate();
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_fmKeyOn(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
int note = va_arg(args, int);
|
|
int velo = va_arg(args, int);
|
|
return fmKeyOn(chan, note, velo);
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_fmKeyOff(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
return fmKeyOff(chan);
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_fmSetPanPos(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
int mode = va_arg(args, int);
|
|
return fmSetPanPos(chan, mode);
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_fmSetInstrument(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
int instrId = va_arg(args, int);
|
|
return fmSetInstrument(chan, instrId);
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_fmLoadInstrument(va_list &args) {
|
|
int instrId = va_arg(args, int);
|
|
uint8 *instrData = va_arg(args, uint8 *);
|
|
return fmLoadInstrument(instrId, instrData);
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_fmSetPitch(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
uint16 freq = va_arg(args, int) & 0xffff;
|
|
return fmSetPitch(chan, freq);
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_fmSetLevel(va_list &args) {
|
|
int chan = va_arg(args, int);
|
|
int lvl = va_arg(args, int);
|
|
return fmSetLevel(chan, lvl);
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_fmReset(va_list &args) {
|
|
fmReset();
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_setOutputVolume(va_list &args) {
|
|
int chanType = va_arg(args, int);
|
|
int left = va_arg(args, int);
|
|
int right = va_arg(args, int);
|
|
|
|
if (left & 0xff80 || right & 0xff80)
|
|
return 3;
|
|
|
|
static const uint8 flags[] = { 0x0C, 0x30, 0x40, 0x80 };
|
|
|
|
uint8 chan = (chanType & 0x40) ? 8 : 12;
|
|
|
|
chanType &= 3;
|
|
left = (left & 0x7e) >> 1;
|
|
right = (right & 0x7e) >> 1;
|
|
|
|
if (chan == 12)
|
|
_outputVolumeFlags |= flags[chanType];
|
|
else
|
|
_outputVolumeFlags &= ~flags[chanType];
|
|
|
|
if (chanType > 1) {
|
|
_outputLevel[chan + chanType] = left;
|
|
_outputMute[chan + chanType] = 0;
|
|
} else {
|
|
if (chanType == 0)
|
|
chan -= 8;
|
|
_outputLevel[chan] = left;
|
|
_outputLevel[chan + 1] = right;
|
|
_outputMute[chan] = _outputMute[chan + 1] = 0;
|
|
}
|
|
|
|
updateOutputVolume();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_resetOutputVolume(va_list &args) {
|
|
memset(_outputLevel, 0, sizeof(_outputLevel));
|
|
_outputVolumeFlags = 0;
|
|
updateOutputVolume();
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_getOutputVolume(va_list &args) {
|
|
int chanType = va_arg(args, int);
|
|
int *left = va_arg(args, int*);
|
|
int *right = va_arg(args, int*);
|
|
|
|
uint8 chan = (chanType & 0x40) ? 8 : 12;
|
|
chanType &= 3;
|
|
|
|
if (chanType > 1) {
|
|
*left = _outputLevel[chan + chanType] & 0x3f;
|
|
} else {
|
|
if (chanType == 0)
|
|
chan -= 8;
|
|
*left = _outputLevel[chan] & 0x3f;
|
|
*right = _outputLevel[chan + 1] & 0x3f;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_setOutputMute(va_list &args) {
|
|
int flags = va_arg(args, int);
|
|
_outputVolumeFlags = flags;
|
|
uint8 mute = flags & 3;
|
|
uint8 f = flags & 0xff;
|
|
|
|
memset(_outputMute, 1, 8);
|
|
if (mute & 2)
|
|
memset(&_outputMute[12], 1, 4);
|
|
if (mute & 1)
|
|
memset(&_outputMute[8], 1, 4);
|
|
|
|
_outputMute[(f < 0x80) ? 11 : 15] = 0;
|
|
f += f;
|
|
_outputMute[(f < 0x80) ? 10 : 14] = 0;
|
|
f += f;
|
|
_outputMute[(f < 0x80) ? 8 : 12] = 0;
|
|
f += f;
|
|
_outputMute[(f < 0x80) ? 9 : 13] = 0;
|
|
f += f;
|
|
_outputMute[(f < 0x80) ? 0 : 4] = 0;
|
|
f += f;
|
|
_outputMute[(f < 0x80) ? 1 : 5] = 0;
|
|
f += f;
|
|
|
|
updateOutputVolume();
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_cdaToggle(va_list &args) {
|
|
//int mode = va_arg(args, int);
|
|
//_unkMask = mode ? 0x7f : 0x3f;
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_getOutputVolume2(va_list &args) {
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_getOutputMute (va_list &args) {
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_pcmUpdateEnvelopeGenerator(va_list &args) {
|
|
for (int i = 0; i < 8; i++)
|
|
_pcmChan[i]->updateEnvelopeGenerator();
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::intf_notImpl(va_list &args) {
|
|
return 4;
|
|
}
|
|
|
|
void TownsAudioInterfaceInternal::fmReset() {
|
|
TownsPC98_FmSynth::reset();
|
|
|
|
_fmChanPlaying = 0;
|
|
memset(_fmChanNote, 0, sizeof(_fmChanNote));
|
|
memset(_fmChanPitch, 0, sizeof(_fmChanPitch));
|
|
|
|
memset(_fmSaveReg[0], 0, 240);
|
|
memset(&_fmSaveReg[0][240], 0x7f, 16);
|
|
memset(_fmSaveReg[1], 0, 256);
|
|
memset(&_fmSaveReg[1][240], 0x7f, 16);
|
|
_fmSaveReg[0][243] = _fmSaveReg[0][247] = _fmSaveReg[0][251] = _fmSaveReg[0][255] = _fmSaveReg[1][243] = _fmSaveReg[1][247] = _fmSaveReg[1][251] = _fmSaveReg[1][255] = 0xff;
|
|
|
|
for (int i = 0; i < 128; i++)
|
|
fmLoadInstrument(i, _fmDefaultInstrument);
|
|
|
|
bufferedWriteReg(0, 0x21, 0);
|
|
bufferedWriteReg(0, 0x2C, 0x80);
|
|
bufferedWriteReg(0, 0x2B, 0);
|
|
bufferedWriteReg(0, 0x27, 0x30);
|
|
|
|
for (int i = 0; i < 6; i++) {
|
|
fmKeyOff(i);
|
|
fmSetInstrument(i, 0);
|
|
fmSetLevel(i, 127);
|
|
}
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::fmKeyOn(int chan, int note, int velo) {
|
|
if (chan > 5)
|
|
return 1;
|
|
if (note < 12 || note > 107 || (velo & 0x80))
|
|
return 3;
|
|
if (_fmChanPlaying & _chanFlags[chan])
|
|
return 2;
|
|
|
|
_fmChanPlaying |= _chanFlags[chan];
|
|
note -= 12;
|
|
|
|
_fmChanNote[chan] = note;
|
|
int16 pitch = _fmChanPitch[chan];
|
|
|
|
uint8 part = chan > 2 ? 1 : 0;
|
|
if (chan > 2)
|
|
chan -= 3;
|
|
|
|
int frq = 0;
|
|
uint8 bl = 0;
|
|
|
|
if (note) {
|
|
frq = _frequency[(note - 1) % 12];
|
|
bl = (note - 1) / 12;
|
|
} else {
|
|
frq = 616;
|
|
}
|
|
|
|
frq += pitch;
|
|
|
|
if (frq < 616) {
|
|
if (!bl) {
|
|
frq = 616;
|
|
} else {
|
|
frq += 616;
|
|
--bl;
|
|
}
|
|
} else if (frq > 1232) {
|
|
if (bl == 7) {
|
|
frq = 15500;
|
|
} else {
|
|
frq -= 616;
|
|
++bl;
|
|
}
|
|
}
|
|
|
|
frq |= (bl << 11);
|
|
|
|
bufferedWriteReg(part, chan + 0xa4, (frq >> 8) & 0xff);
|
|
bufferedWriteReg(part, chan + 0xa0, frq & 0xff);
|
|
|
|
velo = (velo >> 2) + 96;
|
|
uint16 c = _carrier[_fmSaveReg[part][0xb0 + chan] & 7];
|
|
_fmSaveReg[part][0xe0 + chan] = velo;
|
|
|
|
for (uint8 reg = 0x40 + chan; reg < 0x50; reg += 4) {
|
|
c += c;
|
|
if (c & 0x100) {
|
|
c &= 0xff;
|
|
bufferedWriteReg(part, reg, (((((((_fmSaveReg[part][0x80 + reg] ^ 0x7f) * velo) >> 7) + 1) * _fmSaveReg[part][0xd0 + chan]) >> 7) + 1) ^ 0x7f);
|
|
}
|
|
}
|
|
|
|
uint8 v = chan;
|
|
if (part)
|
|
v |= 4;
|
|
|
|
for (uint8 reg = 0x80 + chan; reg < 0x90; reg += 4)
|
|
writeReg(part, reg, _fmSaveReg[part][reg] | 0x0f);
|
|
|
|
writeReg(0, 0x28, v);
|
|
|
|
for (uint8 reg = 0x80 + chan; reg < 0x90; reg += 4)
|
|
writeReg(part, reg, _fmSaveReg[part][reg]);
|
|
|
|
bufferedWriteReg(0, 0x28, v | 0xf0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::fmKeyOff(int chan) {
|
|
if (chan > 5)
|
|
return 1;
|
|
_fmChanPlaying &= ~_chanFlags[chan];
|
|
if (chan > 2)
|
|
chan++;
|
|
bufferedWriteReg(0, 0x28, chan);
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::fmChanOff(int chan) {
|
|
if (chan > 5)
|
|
return 1;
|
|
_fmChanPlaying &= ~_chanFlags[chan];
|
|
|
|
uint8 part = chan > 2 ? 1 : 0;
|
|
if (chan > 2)
|
|
chan -= 3;
|
|
|
|
for (uint8 reg = 0x80 + chan; reg < 0x90; reg += 4)
|
|
writeReg(part, reg, _fmSaveReg[part][reg] | 0x0f);
|
|
|
|
if (part)
|
|
chan += 4;
|
|
writeReg(0, 0x28, chan);
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::fmSetPanPos(int chan, int value) {
|
|
if (chan > 5)
|
|
return 1;
|
|
|
|
uint8 part = chan > 2 ? 1 : 0;
|
|
if (chan > 2)
|
|
chan -= 3;
|
|
|
|
if (value > 0x40)
|
|
value = 0x40;
|
|
else if (value < 0x40)
|
|
value = 0x80;
|
|
else
|
|
value = 0xC0;
|
|
|
|
bufferedWriteReg(part, 0xb4 + chan, (_fmSaveReg[part][0xb4 + chan] & 0x3f) | value);
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::fmSetInstrument(int chan, int instrId) {
|
|
if (chan > 5)
|
|
return 1;
|
|
if (instrId > 127)
|
|
return 3;
|
|
|
|
uint8 part = chan > 2 ? 1 : 0;
|
|
if (chan > 2)
|
|
chan -= 3;
|
|
|
|
uint8 *src = &_fmInstruments[instrId * 48 + 8];
|
|
|
|
uint16 c = _carrier[src[24] & 7];
|
|
uint8 reg = 0x30 + chan;
|
|
|
|
for (; reg < 0x40; reg += 4)
|
|
bufferedWriteReg(part, reg, *src++);
|
|
|
|
for (; reg < 0x50; reg += 4) {
|
|
uint8 v = *src++;
|
|
_fmSaveReg[part][0x80 + reg] = _fmSaveReg[part][reg] = v;
|
|
c += c;
|
|
if (c & 0x100) {
|
|
c &= 0xff;
|
|
v = 127;
|
|
}
|
|
writeReg(part, reg, v);
|
|
}
|
|
|
|
for (; reg < 0x90; reg += 4)
|
|
bufferedWriteReg(part, reg, *src++);
|
|
|
|
reg += 0x20;
|
|
bufferedWriteReg(part, reg, *src++);
|
|
|
|
uint8 v = *src++;
|
|
reg += 4;
|
|
if (v < 64)
|
|
v |= (_fmSaveReg[part][reg] & 0xc0);
|
|
bufferedWriteReg(part, reg, v);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::fmLoadInstrument(int instrId, const uint8 *data) {
|
|
if (instrId > 127)
|
|
return 3;
|
|
assert(data);
|
|
memcpy(&_fmInstruments[instrId * 48], data, 48);
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::fmSetPitch(int chan, int pitch) {
|
|
if (chan > 5)
|
|
return 1;
|
|
|
|
uint8 bl = _fmChanNote[chan];
|
|
int frq = 0;
|
|
|
|
if (pitch < 0) {
|
|
if (bl) {
|
|
if (pitch < -8008)
|
|
pitch = -8008;
|
|
pitch *= -1;
|
|
pitch /= 13;
|
|
frq = _frequency[(bl - 1) % 12] - pitch;
|
|
bl = (bl - 1) / 12;
|
|
_fmChanPitch[chan] = -pitch;
|
|
|
|
if (frq < 616) {
|
|
if (bl) {
|
|
frq += 616;
|
|
bl--;
|
|
} else {
|
|
frq = 616;
|
|
bl = 0;
|
|
}
|
|
}
|
|
} else {
|
|
frq = 616;
|
|
bl = 0;
|
|
}
|
|
|
|
} else if (pitch > 0) {
|
|
if (bl < 96) {
|
|
if (pitch > 8008)
|
|
pitch = 8008;
|
|
pitch /= 13;
|
|
|
|
if (bl) {
|
|
frq = _frequency[(bl - 1) % 12] + pitch;
|
|
bl = (bl - 1) / 12;
|
|
} else {
|
|
frq = 616;
|
|
bl = 0;
|
|
}
|
|
|
|
_fmChanPitch[chan] = pitch;
|
|
|
|
if (frq > 1232) {
|
|
if (bl < 7) {
|
|
frq -= 616;
|
|
bl++;
|
|
} else {
|
|
frq = 1164;
|
|
bl = 7;
|
|
}
|
|
} else {
|
|
if (bl >= 7 && frq > 1164)
|
|
frq = 1164;
|
|
}
|
|
|
|
} else {
|
|
frq = 1164;
|
|
bl = 7;
|
|
}
|
|
} else {
|
|
_fmChanPitch[chan] = 0;
|
|
if (bl) {
|
|
frq = _frequency[(bl - 1) % 12];
|
|
bl = (bl - 1) / 12;
|
|
} else {
|
|
frq = 616;
|
|
bl = 0;
|
|
}
|
|
}
|
|
|
|
uint8 part = chan > 2 ? 1 : 0;
|
|
if (chan > 2)
|
|
chan -= 3;
|
|
|
|
frq |= (bl << 11);
|
|
|
|
bufferedWriteReg(part, chan + 0xa4, (frq >> 8));
|
|
bufferedWriteReg(part, chan + 0xa0, (frq & 0xff));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::fmSetLevel(int chan, int lvl) {
|
|
if (chan > 5)
|
|
return 1;
|
|
if (lvl > 127)
|
|
return 3;
|
|
|
|
uint8 part = chan > 2 ? 1 : 0;
|
|
if (chan > 2)
|
|
chan -= 3;
|
|
|
|
uint16 c = _carrier[_fmSaveReg[part][0xb0 + chan] & 7];
|
|
_fmSaveReg[part][0xd0 + chan] = lvl;
|
|
|
|
for (uint8 reg = 0x40 + chan; reg < 0x50; reg += 4) {
|
|
c += c;
|
|
if (c & 0x100) {
|
|
c &= 0xff;
|
|
bufferedWriteReg(part, reg, (((((((_fmSaveReg[part][0x80 + reg] ^ 0x7f) * lvl) >> 7) + 1) * _fmSaveReg[part][0xe0 + chan]) >> 7) + 1) ^ 0x7f);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void TownsAudioInterfaceInternal::bufferedWriteReg(uint8 part, uint8 regAddress, uint8 value) {
|
|
_fmSaveReg[part][regAddress] = value;
|
|
writeReg(part, regAddress, value);
|
|
}
|
|
|
|
void TownsAudioInterfaceInternal::pcmReset() {
|
|
_numReservedChannels = 0;
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
_pcmChan[i]->clear();
|
|
|
|
memset(_pcmInstruments, 0, 128 * 32);
|
|
static const uint8 name[] = { 0x4E, 0x6F, 0x20, 0x44, 0x61, 0x74, 0x61, 0x21 };
|
|
for (int i = 0; i < 32; i++)
|
|
memcpy(_pcmInstruments + i * 128, name, 8);
|
|
|
|
for (int i = 0; i < 128; i++)
|
|
_waveTables[i].clear();
|
|
_numWaveTables = 0;
|
|
_waveTablesTotalDataSize = 0;
|
|
|
|
for (int i = 0x40; i < 0x48; i++) {
|
|
pcmSetInstrument(i, 0);
|
|
pcmSetLevel(i, 127);
|
|
}
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::pcmKeyOn(int chan, int note, int velo) {
|
|
if (chan < 0x40 || chan > 0x47)
|
|
return 1;
|
|
|
|
if (note & 0x80 || velo & 0x80)
|
|
return 3;
|
|
|
|
chan -= 0x40;
|
|
uint8 noteT = note;
|
|
TownsAudio_PCMChannel *p = _pcmChan[chan];
|
|
|
|
if (p->_reserved || p->_keyPressed)
|
|
return 2;
|
|
|
|
TownsAudio_WaveTable *w = _waveTables;
|
|
int res = p->initInstrument(noteT, w, _numWaveTables);
|
|
if (res)
|
|
return res;
|
|
|
|
p->loadDataFromWaveTable(w);
|
|
p->keyOn(noteT, velo, w);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::pcmKeyOff(int chan) {
|
|
if (chan < 0x40 || chan > 0x47)
|
|
return 1;
|
|
|
|
chan -= 0x40;
|
|
_pcmChan[chan]->keyOff();
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::pcmChanOff(int chan) {
|
|
if (chan < 0x40 || chan > 0x47)
|
|
return 1;
|
|
|
|
chan -= 0x40;
|
|
_pcmChan[chan]->_keyPressed = _pcmChan[chan]->_activeEffect = _pcmChan[chan]->_activeKey = false;
|
|
_pcmChan[chan]->deactivate();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::pcmSetPanPos(int chan, int mode) {
|
|
if (chan > 0x47)
|
|
return 1;
|
|
if (mode & 0x80)
|
|
return 3;
|
|
|
|
chan -= 0x40;
|
|
uint8 blc = 0x77;
|
|
|
|
if (mode > 64) {
|
|
mode -= 64;
|
|
blc = ((blc ^ (mode >> 3)) + (mode << 4)) & 0xff;
|
|
} else if (mode < 64) {
|
|
mode = (mode >> 3) ^ 7;
|
|
blc = ((119 + mode) ^ (mode << 4)) & 0xff;
|
|
}
|
|
|
|
_pcmChan[chan]->setBalance(blc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::pcmSetInstrument(int chan, int instrId) {
|
|
if (chan > 0x47)
|
|
return 1;
|
|
if (instrId > 31)
|
|
return 3;
|
|
chan -= 0x40;
|
|
_pcmChan[chan]->setInstrument(&_pcmInstruments[instrId * 128]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::pcmLoadInstrument(int instrId, const uint8 *data) {
|
|
if (instrId > 31)
|
|
return 3;
|
|
assert(data);
|
|
memcpy(&_pcmInstruments[instrId * 128], data, 128);
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::pcmSetPitch(int chan, int pitch) {
|
|
if (chan > 0x47)
|
|
return 1;
|
|
|
|
if (pitch < -8192 || pitch > 8191)
|
|
return 3;
|
|
|
|
chan -= 0x40;
|
|
uint32 pts = 0x4000;
|
|
|
|
if (pitch < 0)
|
|
pts = (0x20000000 / (-pitch + 0x2001)) >> 2;
|
|
else if (pitch > 0)
|
|
pts = (((pitch + 0x2001) << 16) / 0x2000) >> 2;
|
|
|
|
_pcmChan[chan]->setPitch(pts);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int TownsAudioInterfaceInternal::pcmSetLevel(int chan, int lvl) {
|
|
if (chan > 0x47)
|
|
return 1;
|
|
|
|
if (lvl & 0x80)
|
|
return 3;
|
|
|
|
chan -= 0x40;
|
|
_pcmChan[chan]->setLevel(lvl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void TownsAudioInterfaceInternal::updateOutputVolume() {
|
|
// Avoid calls to g_system->getAudioCDManager() functions from the main thread
|
|
// since this can cause mutex lockups.
|
|
_updateOutputVol = true;
|
|
}
|
|
|
|
void TownsAudioInterfaceInternal::updateOutputVolumeInternal() {
|
|
if (!_ready)
|
|
return;
|
|
|
|
// FM Towns seems to support volumes of 0 - 63 for each channel.
|
|
// We recalculate sane values for our 0 to 255 volume range and
|
|
// balance values for our -128 to 127 volume range
|
|
|
|
// CD-AUDIO
|
|
uint32 maxVol = MAX(_outputLevel[12] * (_outputMute[12] ^ 1), _outputLevel[13] * (_outputMute[13] ^ 1));
|
|
|
|
int volume = (int)(((powf(maxVol, 1.5f) * 255.0f) / powf(63.0f, 1.5f)));
|
|
int balance = maxVol ? (int)( ( ((int)_outputLevel[13] * (_outputMute[13] ^ 1) - _outputLevel[12] * (_outputMute[12] ^ 1)) * 127) / (float)maxVol) : 0;
|
|
|
|
g_system->getAudioCDManager()->setVolume(volume);
|
|
g_system->getAudioCDManager()->setBalance(balance);
|
|
|
|
_updateOutputVol = false;
|
|
}
|
|
|
|
TownsAudioInterfaceInternal *TownsAudioInterfaceInternal::_refInstance = nullptr;
|
|
|
|
int TownsAudioInterfaceInternal::_refCount = 0;
|
|
|
|
const uint8 TownsAudioInterfaceInternal::_chanFlags[] = {
|
|
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
|
|
};
|
|
|
|
const uint16 TownsAudioInterfaceInternal::_frequency[] = {
|
|
0x028C, 0x02B4, 0x02DC, 0x030A, 0x0338, 0x0368, 0x039C, 0x03D4, 0x040E, 0x044A, 0x048C, 0x04D0
|
|
};
|
|
|
|
const uint8 TownsAudioInterfaceInternal::_carrier[] = {
|
|
0x10, 0x10, 0x10, 0x10, 0x30, 0x70, 0x70, 0xF0
|
|
};
|
|
|
|
const uint8 TownsAudioInterfaceInternal::_fmDefaultInstrument[] = {
|
|
0x45, 0x4C, 0x45, 0x50, 0x49, 0x41, 0x4E, 0x4F, 0x01, 0x0A, 0x02, 0x01,
|
|
0x1E, 0x32, 0x05, 0x00, 0x9C, 0xDC, 0x9C, 0xDC, 0x07, 0x03, 0x14, 0x08,
|
|
0x00, 0x03, 0x05, 0x05, 0x55, 0x45, 0x27, 0xA7, 0x04, 0xC0, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
TownsAudio_PCMChannel::TownsAudio_PCMChannel() : PCMChannel_Base(), _extData(nullptr) {
|
|
clear();
|
|
}
|
|
|
|
TownsAudio_PCMChannel::~TownsAudio_PCMChannel() {
|
|
clear();
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::clear() {
|
|
PCMChannel_Base::clear();
|
|
|
|
_curInstrument = nullptr;
|
|
_note = _level = _velo = 0;
|
|
|
|
_stepNote = 0x4000;
|
|
_stepPitch = 0x4000;
|
|
|
|
_envTotalLevel = _envAttackRate = _envDecayRate = _envSustainLevel = _envSustainRate = _envReleaseRate = 0;
|
|
_envStep = _envCurrentLevel = 0;
|
|
|
|
_envState = kEnvReady;
|
|
|
|
_activeKey = _activeEffect = _keyPressed = _reserved = false;
|
|
|
|
delete[] _extData;
|
|
_extData = nullptr;
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::loadDataFromWaveTable(TownsAudio_WaveTable *w) {
|
|
setData(w->data, w->size << 11);
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::loadData(uint8 *buffer, uint32 size) {
|
|
delete[] _extData;
|
|
_extData = new int8[size];
|
|
int8 *src = (int8*)buffer;
|
|
int8 *dst = _extData;
|
|
for (uint32 i = 0; i < size; i++)
|
|
*dst++ = *src & 0x80 ? (*src++ & 0x7f) : -*src++;
|
|
setData(_extData, size << 11);
|
|
}
|
|
|
|
int TownsAudio_PCMChannel::initInstrument(uint8 ¬e, TownsAudio_WaveTable *&tables, int numTables) {
|
|
int i = 0;
|
|
for (; i < 8; i++) {
|
|
if (note <= _curInstrument[16 + 2 * i])
|
|
break;
|
|
}
|
|
|
|
if (i == 8)
|
|
return 8;
|
|
|
|
uint8 *d = &_curInstrument[(i << 3) + 64];
|
|
_envTotalLevel = d[0];
|
|
_envAttackRate = d[1];
|
|
_envDecayRate = d[2];
|
|
_envSustainLevel = d[3];
|
|
_envSustainRate = d[4];
|
|
_envReleaseRate = d[5];
|
|
_envStep = 0;
|
|
note += d[6];
|
|
|
|
int32 id = (int32)READ_LE_UINT32(&_curInstrument[i * 4 + 32]);
|
|
|
|
for (i = 0; i < numTables; i++) {
|
|
if (id == tables[i].id)
|
|
break;
|
|
}
|
|
|
|
if (i == numTables)
|
|
return 9;
|
|
|
|
tables = &tables[i];
|
|
return 0;
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::keyOn(uint8 note, uint8 velo, TownsAudio_WaveTable *w) {
|
|
setupLoop(w->loopLen ? w->loopStart : w->size, w->loopLen);
|
|
setNote(note, w, _reserved);
|
|
setVelo(velo);
|
|
|
|
if (_reserved)
|
|
_activeEffect = true;
|
|
else
|
|
_keyPressed = _activeKey = true;
|
|
|
|
activate();
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::keyOff() {
|
|
_keyPressed = false;
|
|
envRelease();
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::updateEnvelopeGenerator() {
|
|
if (!_envCurrentLevel) {
|
|
_activeKey = false;
|
|
_envState = kEnvReady;
|
|
}
|
|
|
|
if (!_activeKey)
|
|
return;
|
|
|
|
switch (_envState) {
|
|
case kEnvAttacking:
|
|
if (((_envCurrentLevel + _envStep) >> 8) > _envTotalLevel) {
|
|
envDecay();
|
|
return;
|
|
} else {
|
|
_envCurrentLevel += _envStep;
|
|
}
|
|
break;
|
|
|
|
case kEnvDecaying:
|
|
if (((_envCurrentLevel - _envStep) >> 8) < _envSustainLevel) {
|
|
envSustain();
|
|
return;
|
|
} else {
|
|
_envCurrentLevel -= _envStep;
|
|
}
|
|
break;
|
|
|
|
case kEnvSustaining:
|
|
case kEnvReleasing:
|
|
_envCurrentLevel -= _envStep;
|
|
if (_envCurrentLevel <= 0)
|
|
_envCurrentLevel = 0;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
setVolume((_envCurrentLevel >> 8) << 1);
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::setInstrument(uint8 *instr) {
|
|
_curInstrument = instr;
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::setLevel(uint8 lvl) {
|
|
if (_reserved) {
|
|
_velo = lvl;
|
|
setVolume(lvl << 1);
|
|
} else {
|
|
int32 t = _envStep * lvl;
|
|
if (_level)
|
|
t /= _level;
|
|
_envStep = t;
|
|
t = _envCurrentLevel * lvl;
|
|
if (_level)
|
|
t /= _level;
|
|
_envCurrentLevel = t;
|
|
_level = lvl;
|
|
setVolume(_envCurrentLevel >> 8);
|
|
}
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::setPitch(uint32 pt) {
|
|
_stepPitch = pt & 0xffff;
|
|
uint16 step = (_stepNote * _stepPitch) >> 14;
|
|
|
|
// if (_pcmChanUnkFlag & _chanFlags[chan])
|
|
// unk[chan] = (((p->step * 1000) << 11) / 98) / 20833;
|
|
/*else*/
|
|
setRate(_activeEffect && (step > 2048) ? 2048 : step);
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::setBalance(uint8 blc) {
|
|
setPanPos(blc);
|
|
}
|
|
|
|
bool TownsAudio_PCMChannel::isPlaying() const {
|
|
return _activeKey || _activeEffect;
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::stopInternal() {
|
|
_activeKey = _activeEffect = false;
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::setNote(uint8 note, TownsAudio_WaveTable *w, bool stepLimit) {
|
|
_note = note;
|
|
int8 diff = _note - w->baseNote;
|
|
uint16 r = w->rate + w->rateOffs;
|
|
uint16 bl = 0;
|
|
uint32 s = 0;
|
|
|
|
if (diff < 0) {
|
|
diff *= -1;
|
|
bl = diff % 12;
|
|
diff /= 12;
|
|
s = (r >> diff);
|
|
if (bl)
|
|
s = (s * _pcmPhase2[bl - 1]) >> 16;
|
|
|
|
} else if (diff > 0) {
|
|
bl = diff % 12;
|
|
diff /= 12;
|
|
s = (r << diff);
|
|
if (bl)
|
|
s += ((s * _pcmPhase1[bl - 1]) >> 16);
|
|
|
|
} else {
|
|
s = r;
|
|
}
|
|
|
|
_stepNote = s & 0xffff;
|
|
uint16 step = (s * _stepPitch) >> 14;
|
|
setRate(stepLimit && (step > 2048) ? 2048 : step);
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::setVelo(uint8 velo) {
|
|
if (_reserved) {
|
|
_velo = velo;
|
|
setVolume(velo << 1);
|
|
} else {
|
|
_velo = velo;
|
|
uint32 lvl = _level * _velo;
|
|
_envTotalLevel = ((_envTotalLevel * lvl) >> 14) & 0xff;
|
|
_envSustainLevel = ((_envSustainLevel * lvl) >> 14) & 0xff;
|
|
envAttack();
|
|
setVolume((_envCurrentLevel >> 8) << 1);
|
|
}
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::envAttack() {
|
|
_envState = kEnvAttacking;
|
|
int16 t = _envTotalLevel << 8;
|
|
if (_envAttackRate == 127) {
|
|
_envCurrentLevel = _envStep = 0;
|
|
} else if (_envAttackRate) {
|
|
_envStep = t / _envAttackRate;
|
|
_envCurrentLevel = 1;
|
|
} else {
|
|
_envCurrentLevel = t;
|
|
envDecay();
|
|
}
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::envDecay() {
|
|
_envState = kEnvDecaying;
|
|
int16 t = _envTotalLevel - _envSustainLevel;
|
|
if (t < 0 || _envDecayRate == 127) {
|
|
_envStep = 0;
|
|
} else if (_envDecayRate) {
|
|
_envStep = (t << 8) / _envDecayRate;
|
|
} else {
|
|
_envCurrentLevel = _envSustainLevel << 8;
|
|
envSustain();
|
|
}
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::envSustain() {
|
|
_envState = kEnvSustaining;
|
|
if (_envSustainLevel && _envSustainRate)
|
|
_envStep = (_envSustainRate == 127) ? 0 : (_envCurrentLevel / _envSustainRate) >> 1;
|
|
else
|
|
_envStep = _envCurrentLevel = 1;
|
|
}
|
|
|
|
void TownsAudio_PCMChannel::envRelease() {
|
|
_envState = kEnvReleasing;
|
|
if (_envReleaseRate == 127)
|
|
_envStep = 0;
|
|
else if (_envReleaseRate)
|
|
_envStep = _envCurrentLevel / _envReleaseRate;
|
|
else
|
|
_envStep = _envCurrentLevel = 1;
|
|
}
|
|
|
|
const uint16 TownsAudio_PCMChannel::_pcmPhase1[] = {
|
|
0x0F37, 0x1F58, 0x306E, 0x4288, 0x55B6, 0x6A08, 0x7F8F, 0x965E, 0xAE88, 0xC882, 0xE341
|
|
};
|
|
|
|
const uint16 TownsAudio_PCMChannel::_pcmPhase2[] = {
|
|
0xF1A0, 0xE411, 0xD744, 0xCB2F, 0xBFC7, 0xB504, 0xAAE2, 0xA144, 0x9827, 0x8FAC, 0x879B
|
|
};
|
|
|
|
TownsAudio_WaveTable::TownsAudio_WaveTable() {
|
|
data = nullptr;
|
|
clear();
|
|
}
|
|
|
|
TownsAudio_WaveTable::~TownsAudio_WaveTable() {
|
|
clear();
|
|
}
|
|
|
|
void TownsAudio_WaveTable::readHeader(const uint8 *buffer) {
|
|
memcpy(name, buffer, 8);
|
|
name[8] = 0;
|
|
id = READ_LE_UINT32(&buffer[8]);
|
|
size = READ_LE_UINT32(&buffer[12]);
|
|
loopStart = READ_LE_UINT32(&buffer[16]);
|
|
loopLen = READ_LE_UINT32(&buffer[20]);
|
|
rate = READ_LE_UINT16(&buffer[24]);
|
|
rateOffs = READ_LE_UINT16(&buffer[26]);
|
|
baseNote = READ_LE_UINT32(&buffer[28]);
|
|
}
|
|
|
|
void TownsAudio_WaveTable::readData(const uint8 *buffer) {
|
|
if (!size)
|
|
return;
|
|
|
|
delete[] data;
|
|
data = new int8[size];
|
|
|
|
const int8 *src = (const int8 *)buffer;
|
|
int8 *dst = data;
|
|
for (uint32 i = 0; i < size; i++)
|
|
*dst++ = *src & 0x80 ? (*src++ & 0x7f) : -*src++;
|
|
}
|
|
|
|
void TownsAudio_WaveTable::clear() {
|
|
name[0] = name[8] = 0;
|
|
id = -1;
|
|
size = 0;
|
|
loopStart = 0;
|
|
loopLen = 0;
|
|
rate = 0;
|
|
rateOffs = 0;
|
|
baseNote = 0;
|
|
delete[] data;
|
|
data = nullptr;
|
|
}
|
|
|
|
TownsAudioInterface::TownsAudioInterface(Audio::Mixer *mixer, TownsAudioInterfacePluginDriver *driver, bool externalMutex) {
|
|
_intf = TownsAudioInterfaceInternal::addNewRef(mixer, this, driver, externalMutex);
|
|
}
|
|
|
|
TownsAudioInterface::~TownsAudioInterface() {
|
|
TownsAudioInterfaceInternal::releaseRef(this);
|
|
_intf = nullptr;
|
|
}
|
|
|
|
bool TownsAudioInterface::init() {
|
|
return _intf->init();
|
|
}
|
|
|
|
TownsAudioInterface::ErrorCode TownsAudioInterface::callback(int command, ...) {
|
|
va_list args;
|
|
va_start(args, command);
|
|
|
|
int res = _intf->processCommand(command, args);
|
|
|
|
va_end(args);
|
|
return (TownsAudioInterface::ErrorCode)res;
|
|
}
|
|
|
|
void TownsAudioInterface::setMusicVolume(int volume) {
|
|
_intf->setMusicVolume(volume);
|
|
}
|
|
|
|
void TownsAudioInterface::setSoundEffectVolume(int volume) {
|
|
_intf->setSoundEffectVolume(volume);
|
|
}
|
|
|
|
void TownsAudioInterface::setSoundEffectChanMask(int mask) {
|
|
_intf->setSoundEffectChanMask(mask);
|
|
}
|