diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 240b9c12b..69264c19f 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -829,6 +829,20 @@ bool DivEngine::save(FILE* f) { return true; } +// ADPCM code attribution: https://wiki.neogeodev.org/index.php?title=ADPCM_codecs + +static short adSteps[49]={ + 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, + 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, + 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, + 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 +}; + +static int adStepSeek[16]={ + -1, -1, -1, -1, 2, 5, 7, 9, -1, -1, -1, -1, 2, 5, 7, 9 +}; + static double samplePitches[11]={ 0.1666666666, 0.2, 0.25, 0.333333333, 0.5, 1, @@ -836,11 +850,31 @@ static double samplePitches[11]={ }; void DivEngine::renderSamples() { + if (jediTable==NULL) { + int step=0; + int nib=0; + jediTable=new int[16*49]; + for (step=0; step<49; step++) { + for (nib=0; nib<16; nib++) { + int value=(2*(nib&0x07)+1)*adSteps[step]/8; + jediTable[step*16+nib]=((nib&0x08)!=0)?-value:value; + } + } + } + for (int i=0; irendLength!=0) delete[] s->rendData; + if (s->rendLength!=0) { + delete[] s->rendData; + delete[] s->adpcmRendData; + } s->rendLength=(double)s->length/samplePitches[s->pitch]; s->rendData=new short[s->rendLength]; + size_t adpcmLen=((s->rendLength>>1)+255)&0xffffff00; + s->adpcmRendData=new unsigned char[adpcmLen]; + memset(s->adpcmRendData,0,adpcmLen); + + // step 1: render to PCM int k=0; float mult=(float)(s->vol+100)/150.0f; for (double j=0; jlength; j+=samplePitches[s->pitch]) { @@ -855,6 +889,67 @@ void DivEngine::renderSamples() { s->rendData[k++]=fmin(fmax(next,-32768),32767); } } + + // step 2: render to ADPCM + int acc=0; + int decstep=0; + int diff=0; + int step=0; + int predsample=0; + int index=0; + int prevsample=0; + int previndex=0; + for (int j=0; jrendLength; j++) { + unsigned char encoded=0; + int tempstep=0; + + predsample=prevsample; + index=previndex; + step=adSteps[index]; + + short sample=(s->depth==16)?(s->rendData[j]>>4):(s->rendData[j]<<4); + diff=sample-predsample; + if (diff>=0) { + encoded=0; + } else { + encoded=8; + diff=-diff; + } + + tempstep=step; + if (diff>=tempstep) { + encoded|=4; + diff-=tempstep; + } + tempstep>>=1; + if (diff>=tempstep) { + encoded|=2; + diff-=tempstep; + } + tempstep>>=1; + if (diff>=tempstep) encoded|=1; + + acc+=jediTable[decstep+encoded]; + acc&=0xfff; + if (acc&0x800) acc|=~0xfff; + decstep+=adSteps[encoded&7]*16; + if (decstep<0) decstep=0; + if (decstep>48*16) decstep=48*16; + predsample=(short)acc; + + index+= adStepSeek[encoded]; + if (index<0) index=0; + if (index>48) index=48; + + prevsample=predsample; + previndex=index; + + if (j&1) { + s->adpcmRendData[j>>1]|=encoded; + } else { + s->adpcmRendData[j>>1]=encoded<<4; + } + } } } diff --git a/src/engine/engine.h b/src/engine/engine.h index 2c691f5a3..9cbb68958 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -82,6 +82,8 @@ class DivEngine { size_t totalProcessed; + private: int* jediTable; + int dispatchCmd(DivCommand c); void processRow(int i, bool afterDelay); void nextOrder(); @@ -139,6 +141,8 @@ class DivEngine { audioEngine(DIV_AUDIO_SDL), bbInLen(0), temp{0,0}, - prevSample{0,0} {} + prevSample{0,0}, + totalProcessed(0), + jediTable(NULL) {} }; #endif diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index b1426c0fa..a0d75cbe5 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -5,8 +5,8 @@ #include "ym2610shared.h" -#define FM_FREQ_BASE 624.0f -#define PSG_FREQ_BASE 7576.0f +#define FM_FREQ_BASE 622.0f +#define PSG_FREQ_BASE 7640.0f static unsigned char konOffs[4]={ 1, 2, 5, 6 @@ -66,6 +66,9 @@ void DivPlatformYM2610::tick() { chan[i].freqChanged=true; } } + if (chan[i].std.hadDuty) { + rWrite(0x06,31-chan[i].std.duty); + } if (chan[i].std.hadWave) { chan[i].psgMode&=4; chan[i].psgMode|=(chan[i].std.wave+1)&3; @@ -130,19 +133,19 @@ void DivPlatformYM2610::tick() { } int DivPlatformYM2610::octave(int freq) { - if (freq>=82432) { + if (freq>=FM_FREQ_BASE*128) { return 128; - } else if (freq>=41216) { + } else if (freq>=FM_FREQ_BASE*64) { return 64; - } else if (freq>=20608) { + } else if (freq>=FM_FREQ_BASE*32) { return 32; - } else if (freq>=10304) { + } else if (freq>=FM_FREQ_BASE*16) { return 16; - } else if (freq>=5152) { + } else if (freq>=FM_FREQ_BASE*8) { return 8; - } else if (freq>=2576) { + } else if (freq>=FM_FREQ_BASE*4) { return 4; - } else if (freq>=1288) { + } else if (freq>=FM_FREQ_BASE*2) { return 2; } else { return 1; @@ -151,19 +154,19 @@ int DivPlatformYM2610::octave(int freq) { } int DivPlatformYM2610::toFreq(int freq) { - if (freq>=82432) { + if (freq>=FM_FREQ_BASE*128) { return 0x3800|((freq>>7)&0x7ff); - } else if (freq>=41216) { + } else if (freq>=FM_FREQ_BASE*64) { return 0x3000|((freq>>6)&0x7ff); - } else if (freq>=20608) { + } else if (freq>=FM_FREQ_BASE*32) { return 0x2800|((freq>>5)&0x7ff); - } else if (freq>=10304) { + } else if (freq>=FM_FREQ_BASE*16) { return 0x2000|((freq>>4)&0x7ff); - } else if (freq>=5152) { + } else if (freq>=FM_FREQ_BASE*8) { return 0x1800|((freq>>3)&0x7ff); - } else if (freq>=2576) { + } else if (freq>=FM_FREQ_BASE*4) { return 0x1000|((freq>>2)&0x7ff); - } else if (freq>=1288) { + } else if (freq>=FM_FREQ_BASE*2) { return 0x800|((freq>>1)&0x7ff); } else { return freq&0x7ff; @@ -173,6 +176,24 @@ int DivPlatformYM2610::toFreq(int freq) { int DivPlatformYM2610::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { + if (c.chan>6) { // ADPCM + if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) { + writes.emplace(0x100,0x80|(1<<(c.chan-7))); + writes.emplace(0x110+c.chan-7,0); + writes.emplace(0x118+c.chan-7,0); + writes.emplace(0x120+c.chan-7,0); + writes.emplace(0x128+c.chan-7,0); + break; + } + writes.emplace(0x110+c.chan-7,0); + writes.emplace(0x118+c.chan-7,c.value%12); + int sampleLen=(parent->song.sample[12*sampleBank+c.value%12]->rendLength+255)>>8; + writes.emplace(0x120+c.chan-7,sampleLen&0xff); + writes.emplace(0x128+c.chan-7,(c.value%12)+(sampleLen>>8)); + writes.emplace(0x108+c.chan-7,0xff); + writes.emplace(0x100,0x00|(1<<(c.chan-7))); + break; + } DivInstrument* ins=parent->getIns(chan[c.chan].ins); if (c.chan>3) { // PSG @@ -230,7 +251,13 @@ int DivPlatformYM2610::dispatch(DivCommand c) { case DIV_CMD_VOLUME: { chan[c.chan].vol=c.value; DivInstrument* ins=parent->getIns(chan[c.chan].ins); - if (c.chan>3) break; + if (c.chan>3) { + if (!chan[c.chan].std.hasVol) { + chan[c.chan].outVol=c.value; + } + rWrite(0x04+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2)); + break; + } for (int i=0; i<4; i++) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; DivInstrumentFM::Operator op=ins->fm.op[i]; @@ -332,6 +359,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { if (sampleBank>(parent->song.sample.size()/12)) { sampleBank=parent->song.sample.size()/12; } + iface.sampleBank=sampleBank; break; case DIV_CMD_LEGATO: { if (c.chan>3) { // PSG @@ -382,6 +410,10 @@ int DivPlatformYM2610::dispatch(DivCommand c) { break; } + case DIV_CMD_STD_NOISE_FREQ: + if (c.chan<4 || c.chan>6) break; + rWrite(0x06,31-c.value); + break; case DIV_CMD_AY_ENVELOPE_SET: if (c.chan<4 || c.chan>6) break; rWrite(0x0d,c.value>>4); @@ -390,15 +422,21 @@ int DivPlatformYM2610::dispatch(DivCommand c) { } else { chan[c.chan].psgMode&=~4; } + rWrite(0x04+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2)); break; case DIV_CMD_AY_ENVELOPE_LOW: if (c.chan<4 || c.chan>6) break; - writes.emplace(0x0b,c.value); - writes.emplace(0x0c,0); + ayEnvPeriod&=0xff00; + ayEnvPeriod|=c.value; + writes.emplace(0x0b,ayEnvPeriod); + writes.emplace(0x0c,ayEnvPeriod>>8); break; case DIV_CMD_AY_ENVELOPE_HIGH: if (c.chan<4 || c.chan>6) break; - writes.emplace(0x0c,c.value); + ayEnvPeriod&=0xff; + ayEnvPeriod|=c.value<<8; + writes.emplace(0x0b,ayEnvPeriod); + writes.emplace(0x0c,ayEnvPeriod>>8); break; case DIV_ALWAYS_SET_VOLUME: return 0; @@ -438,6 +476,8 @@ int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, bool pal) { } else { rate=500000; } + iface.parent=parent; + iface.sampleBank=0; fm=new ymfm::ym2610(iface); fm->reset(); for (int i=0; i<4; i++) { @@ -462,6 +502,7 @@ int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, bool pal) { dacRate=0; dacSample=-1; sampleBank=0; + ayEnvPeriod=0; delay=0; @@ -469,5 +510,8 @@ int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, bool pal) { // LFO writes.emplace(0x22,0x08); + + // PCM volume + writes.emplace(0x101,0x3f); return 10; } diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index 6bf1c00f9..4df404bda 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -7,8 +7,11 @@ class DivYM2610Interface: public ymfm::ymfm_interface { public: + DivEngine* parent; + int sampleBank; uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address); void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data); + DivYM2610Interface(): parent(NULL), sampleBank(0) {} }; class DivPlatformYM2610: public DivDispatch { @@ -50,6 +53,7 @@ class DivPlatformYM2610: public DivDispatch { short oldWrites[512]; short pendingWrites[512]; + unsigned short ayEnvPeriod; int octave(int freq); int toFreq(int freq); diff --git a/src/engine/platform/ym2610Interface.cpp b/src/engine/platform/ym2610Interface.cpp index ff1822ad5..1e4164be6 100644 --- a/src/engine/platform/ym2610Interface.cpp +++ b/src/engine/platform/ym2610Interface.cpp @@ -1,10 +1,13 @@ +#include "sound/ym2610/ymfm.h" #include "ym2610.h" +#include "../engine.h" uint8_t DivYM2610Interface::ymfm_external_read(ymfm::access_class type, uint32_t address) { - return 0; + //printf("wants to read from %x\n",address); + if (type!=ymfm::ACCESS_ADPCM_A) return 0; + if (12*sampleBank+(address>>16)>=parent->song.sampleLen) return 0; + return parent->song.sample[12*sampleBank+(address>>16)]->adpcmRendData[(address&0xffff)]; } void DivYM2610Interface::ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) { -} - - +} \ No newline at end of file diff --git a/src/engine/sample.h b/src/engine/sample.h index c1cac9627..fc0daeb1e 100644 --- a/src/engine/sample.h +++ b/src/engine/sample.h @@ -6,6 +6,7 @@ struct DivSample { short* data; int rendLength; short* rendData; + unsigned char* adpcmRendData; DivSample(): name(""), @@ -16,5 +17,6 @@ struct DivSample { depth(16), data(NULL), rendLength(0), - rendData(NULL) {} + rendData(NULL), + adpcmRendData(NULL) {} };