diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp new file mode 100644 index 00000000..ce73c3a9 --- /dev/null +++ b/src/engine/platform/amiga.cpp @@ -0,0 +1,254 @@ +#include "amiga.h" +#include "../engine.h" +#include + +#define FREQ_BASE 1712.0f +#define AMIGA_DIVIDER 8 + +void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t len) { + for (size_t h=start; hsong.sample[chan[i].dacSample]; + if (s->depth==8) { + chan[i].audOut=s->rendData[chan[i].audPos]; + } else { + chan[i].audOut=s->rendData[chan[i].audPos]>>8; + } + if (chan[i].audPos>=s->rendLength) { + chan[i].audSample=-1; + } + chan[i].audSub+=chan[i].freq; + } + } + } + bufL[h]=(chan[0].audOut*chan[0].outVol)+; + (chan[3].audOut*chan[3].outVol); + bufR[h]=(chan[1].audOut*chan[1].outVol)+; + (chan[2].audOut*chan[2].outVol); + } +} + +void DivPlatformAmiga::tick() { + for (int i=0; i<4; i++) { + chan[i].std.next(); + if (chan[i].std.hadVol) { + chan[i].outVol=(chan[i].vol*chan[i].std.vol)>>6; + } + if (chan[i].std.hadArp) { + if (!chan[i].inPorta) { + if (chan[i].std.arpMode) { + chan[i].baseFreq=round(FREQ_BASE/pow(2.0f,((float)(chan[i].std.arp)/12.0f))); + } else { + chan[i].baseFreq=round(FREQ_BASE/pow(2.0f,((float)(chan[i].note+chan[i].std.arp-12)/12.0f))); + } + } + chan[i].freqChanged=true; + } else { + if (chan[i].std.arpMode && chan[i].std.finishedArp) { + chan[i].baseFreq=round(FREQ_BASE/pow(2.0f,((float)(chan[i].note)/12.0f))); + chan[i].freqChanged=true; + } + } + if (chan[i].std.hadWave) { + if (chan[i].wave!=chan[i].std.wave) { + chan[i].wave=chan[i].std.wave; + updateWave(i); + if (!chan[i].keyOff) chan[i].keyOn=true; + } + } + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { + //DivInstrument* ins=parent->getIns(chan[i].ins); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); + if (chan[i].freq>4095) chan[i].freq=4095; + if (chan[i].note>0x5d) chan[i].freq=0x01; + if (chan[i].keyOn) { + if (chan[i].wave<0) { + chan[i].wave=0; + updateWave(i); + } + } + if (chan[i].keyOff) { + } + if (chan[i].keyOn) chan[i].keyOn=false; + if (chan[i].keyOff) chan[i].keyOff=false; + chan[i].freqChanged=false; + } + } +} + +int DivPlatformAmiga::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: + if (chan[c.chan].pcm) { + chan[c.chan].dacSample=12*sampleBank+c.value%12; + if (chan[c.chan].dacSample>=parent->song.sampleLen) { + chan[c.chan].dacSample=-1; + break; + } + chan[c.chan].dacPos=0; + chan[c.chan].dacPeriod=0; + chan[c.chan].dacRate=1789773/parent->song.sample[chan[c.chan].dacSample]->rate; + break; + } + chan[c.chan].baseFreq=round(FREQ_BASE/pow(2.0f,((float)c.value/12.0f))); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + chan[c.chan].active=true; + chan[c.chan].keyOn=true; + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + break; + case DIV_CMD_NOTE_OFF: + chan[c.chan].dacSample=-1; + chan[c.chan].pcm=false; + chan[c.chan].active=false; + chan[c.chan].keyOff=true; + chan[c.chan].std.init(NULL); + break; + case DIV_CMD_INSTRUMENT: + if (chan[c.chan].ins!=c.value) { + chan[c.chan].ins=c.value; + } + break; + case DIV_CMD_VOLUME: + if (chan[c.chan].vol!=c.value) { + chan[c.chan].vol=c.value; + if (!chan[c.chan].std.hasVol) { + chan[c.chan].outVol=c.value; + } + } + break; + case DIV_CMD_GET_VOLUME: + if (chan[c.chan].std.hasVol) { + return chan[c.chan].vol; + } + return chan[c.chan].outVol; + break; + case DIV_CMD_PITCH: + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=true; + break; + case DIV_CMD_WAVE: + chan[c.chan].wave=c.value; + updateWave(c.chan); + chan[c.chan].keyOn=true; + break; + case DIV_CMD_NOTE_PORTA: { + int destFreq=round(FREQ_BASE/pow(2.0f,((float)c.value2/12.0f))); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value; + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } + case DIV_CMD_STD_NOISE_MODE: + chan[c.chan].noise=c.value; + break; + case DIV_CMD_SAMPLE_MODE: + chan[c.chan].pcm=c.value; + break; + case DIV_CMD_SAMPLE_BANK: + sampleBank=c.value; + if (sampleBank>(parent->song.sample.size()/12)) { + sampleBank=parent->song.sample.size()/12; + } + break; + case DIV_CMD_PANNING: { + chan[c.chan].pan=c.value; + break; + } + case DIV_CMD_LEGATO: + chan[c.chan].baseFreq=round(FREQ_BASE/pow(2.0f,((float)(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp-12):(0)))/12.0f))); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + break; + case DIV_CMD_PRE_PORTA: + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].inPorta=c.value; + break; + case DIV_CMD_GET_VOLMAX: + return 31; + break; + case DIV_ALWAYS_SET_VOLUME: + return 1; + break; + default: + break; + } + return 1; +} + +void DivPlatformAmiga::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; +} + +void DivPlatformAmiga::forceIns() { + for (int i=0; i<4; i++) { + chan[i].insChanged=true; + chan[i].freqChanged=true; + } +} + +void DivPlatformAmiga::reset() { + for (int i=0; i<4; i++) { + chan[i]=DivPlatformAmiga::Channel(); + } +} + +bool DivPlatformAmiga::isStereo() { + return true; +} + +bool DivPlatformAmiga::keyOffAffectsArp(int ch) { + return true; +} + +void DivPlatformAmiga::notifyInsDeletion(void* ins) { + for (int i=0; i<4; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + +void DivPlatformAmiga::setPAL(bool pal) { + if (pal) { + rate=3546895/AMIGA_DIVIDER; + } else { + rate=3579545/AMIGA_DIVIDER; + } +} + +int DivPlatformAmiga::init(DivEngine* p, int channels, int sugRate, bool pal) { + parent=p; + skipRegisterWrites=false; + for (int i=0; i<4; i++) { + isMuted[i]=false; + } + setPAL(pal); + reset(); + return 6; +} + +void DivPlatformAmiga::quit() { + delete pce; +} + +DivPlatformAmiga::~DivPlatformAmiga() { +} diff --git a/src/engine/platform/amiga.h b/src/engine/platform/amiga.h new file mode 100644 index 00000000..1e956a2e --- /dev/null +++ b/src/engine/platform/amiga.h @@ -0,0 +1,58 @@ +#ifndef _PCE_H +#define _PCE_H + +#include "../dispatch.h" +#include +#include "../macroInt.h" + +class DivPlatformAmiga: public DivDispatch { + struct Channel { + int freq, baseFreq, pitch; + unsigned int audLoc; + unsigned short audLen; + unsigned int audPos; + signed char audDat; + int sample; + unsigned char ins, note; + bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; + signed char vol, outVol; + DivMacroInt std; + Channel(): + freq(0), + baseFreq(0), + pitch(0), + audLoc(0), + audLen(0), + audPos(0), + audDat(0), + sample(-1), + ins(-1), + note(0), + active(false), + insChanged(true), + freqChanged(false), + keyOn(false), + keyOff(false), + inPorta(false), + vol(64), + outVol(64) {} + }; + Channel chan[4]; + bool isMuted[4]; + + public: + void acquire(short* bufL, short* bufR, size_t start, size_t len); + int dispatch(DivCommand c); + void reset(); + void forceIns(); + void tick(); + void muteChannel(int ch, bool mute); + bool isStereo(); + bool keyOffAffectsArp(int ch); + void setPAL(bool pal); + void notifyInsDeletion(void* ins); + int init(DivEngine* parent, int channels, int sugRate, bool pal); + void quit(); +}; + +#endif