more neo geo stuff

This commit is contained in:
tildearrow 2021-12-10 04:22:13 -05:00
parent 442180956c
commit e365aa4bdb
6 changed files with 179 additions and 27 deletions

View File

@ -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; i<song.sampleLen; i++) {
DivSample* s=song.sample[i];
if (s->rendLength!=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; j<s->length; 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; j<s->rendLength; 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;
}
}
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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) {
}
}

View File

@ -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) {}
};