more neo geo stuff
This commit is contained in:
parent
442180956c
commit
e365aa4bdb
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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) {}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue