more neo geo stuff
This commit is contained in:
parent
442180956c
commit
e365aa4bdb
|
@ -829,6 +829,20 @@ bool DivEngine::save(FILE* f) {
|
||||||
return true;
|
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]={
|
static double samplePitches[11]={
|
||||||
0.1666666666, 0.2, 0.25, 0.333333333, 0.5,
|
0.1666666666, 0.2, 0.25, 0.333333333, 0.5,
|
||||||
1,
|
1,
|
||||||
|
@ -836,11 +850,31 @@ static double samplePitches[11]={
|
||||||
};
|
};
|
||||||
|
|
||||||
void DivEngine::renderSamples() {
|
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++) {
|
for (int i=0; i<song.sampleLen; i++) {
|
||||||
DivSample* s=song.sample[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->rendLength=(double)s->length/samplePitches[s->pitch];
|
||||||
s->rendData=new short[s->rendLength];
|
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;
|
int k=0;
|
||||||
float mult=(float)(s->vol+100)/150.0f;
|
float mult=(float)(s->vol+100)/150.0f;
|
||||||
for (double j=0; j<s->length; j+=samplePitches[s->pitch]) {
|
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);
|
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;
|
size_t totalProcessed;
|
||||||
|
|
||||||
|
private: int* jediTable;
|
||||||
|
|
||||||
int dispatchCmd(DivCommand c);
|
int dispatchCmd(DivCommand c);
|
||||||
void processRow(int i, bool afterDelay);
|
void processRow(int i, bool afterDelay);
|
||||||
void nextOrder();
|
void nextOrder();
|
||||||
|
@ -139,6 +141,8 @@ class DivEngine {
|
||||||
audioEngine(DIV_AUDIO_SDL),
|
audioEngine(DIV_AUDIO_SDL),
|
||||||
bbInLen(0),
|
bbInLen(0),
|
||||||
temp{0,0},
|
temp{0,0},
|
||||||
prevSample{0,0} {}
|
prevSample{0,0},
|
||||||
|
totalProcessed(0),
|
||||||
|
jediTable(NULL) {}
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
|
|
||||||
#include "ym2610shared.h"
|
#include "ym2610shared.h"
|
||||||
|
|
||||||
#define FM_FREQ_BASE 624.0f
|
#define FM_FREQ_BASE 622.0f
|
||||||
#define PSG_FREQ_BASE 7576.0f
|
#define PSG_FREQ_BASE 7640.0f
|
||||||
|
|
||||||
static unsigned char konOffs[4]={
|
static unsigned char konOffs[4]={
|
||||||
1, 2, 5, 6
|
1, 2, 5, 6
|
||||||
|
@ -66,6 +66,9 @@ void DivPlatformYM2610::tick() {
|
||||||
chan[i].freqChanged=true;
|
chan[i].freqChanged=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (chan[i].std.hadDuty) {
|
||||||
|
rWrite(0x06,31-chan[i].std.duty);
|
||||||
|
}
|
||||||
if (chan[i].std.hadWave) {
|
if (chan[i].std.hadWave) {
|
||||||
chan[i].psgMode&=4;
|
chan[i].psgMode&=4;
|
||||||
chan[i].psgMode|=(chan[i].std.wave+1)&3;
|
chan[i].psgMode|=(chan[i].std.wave+1)&3;
|
||||||
|
@ -130,19 +133,19 @@ void DivPlatformYM2610::tick() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int DivPlatformYM2610::octave(int freq) {
|
int DivPlatformYM2610::octave(int freq) {
|
||||||
if (freq>=82432) {
|
if (freq>=FM_FREQ_BASE*128) {
|
||||||
return 128;
|
return 128;
|
||||||
} else if (freq>=41216) {
|
} else if (freq>=FM_FREQ_BASE*64) {
|
||||||
return 64;
|
return 64;
|
||||||
} else if (freq>=20608) {
|
} else if (freq>=FM_FREQ_BASE*32) {
|
||||||
return 32;
|
return 32;
|
||||||
} else if (freq>=10304) {
|
} else if (freq>=FM_FREQ_BASE*16) {
|
||||||
return 16;
|
return 16;
|
||||||
} else if (freq>=5152) {
|
} else if (freq>=FM_FREQ_BASE*8) {
|
||||||
return 8;
|
return 8;
|
||||||
} else if (freq>=2576) {
|
} else if (freq>=FM_FREQ_BASE*4) {
|
||||||
return 4;
|
return 4;
|
||||||
} else if (freq>=1288) {
|
} else if (freq>=FM_FREQ_BASE*2) {
|
||||||
return 2;
|
return 2;
|
||||||
} else {
|
} else {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -151,19 +154,19 @@ int DivPlatformYM2610::octave(int freq) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int DivPlatformYM2610::toFreq(int freq) {
|
int DivPlatformYM2610::toFreq(int freq) {
|
||||||
if (freq>=82432) {
|
if (freq>=FM_FREQ_BASE*128) {
|
||||||
return 0x3800|((freq>>7)&0x7ff);
|
return 0x3800|((freq>>7)&0x7ff);
|
||||||
} else if (freq>=41216) {
|
} else if (freq>=FM_FREQ_BASE*64) {
|
||||||
return 0x3000|((freq>>6)&0x7ff);
|
return 0x3000|((freq>>6)&0x7ff);
|
||||||
} else if (freq>=20608) {
|
} else if (freq>=FM_FREQ_BASE*32) {
|
||||||
return 0x2800|((freq>>5)&0x7ff);
|
return 0x2800|((freq>>5)&0x7ff);
|
||||||
} else if (freq>=10304) {
|
} else if (freq>=FM_FREQ_BASE*16) {
|
||||||
return 0x2000|((freq>>4)&0x7ff);
|
return 0x2000|((freq>>4)&0x7ff);
|
||||||
} else if (freq>=5152) {
|
} else if (freq>=FM_FREQ_BASE*8) {
|
||||||
return 0x1800|((freq>>3)&0x7ff);
|
return 0x1800|((freq>>3)&0x7ff);
|
||||||
} else if (freq>=2576) {
|
} else if (freq>=FM_FREQ_BASE*4) {
|
||||||
return 0x1000|((freq>>2)&0x7ff);
|
return 0x1000|((freq>>2)&0x7ff);
|
||||||
} else if (freq>=1288) {
|
} else if (freq>=FM_FREQ_BASE*2) {
|
||||||
return 0x800|((freq>>1)&0x7ff);
|
return 0x800|((freq>>1)&0x7ff);
|
||||||
} else {
|
} else {
|
||||||
return freq&0x7ff;
|
return freq&0x7ff;
|
||||||
|
@ -173,6 +176,24 @@ int DivPlatformYM2610::toFreq(int freq) {
|
||||||
int DivPlatformYM2610::dispatch(DivCommand c) {
|
int DivPlatformYM2610::dispatch(DivCommand c) {
|
||||||
switch (c.cmd) {
|
switch (c.cmd) {
|
||||||
case DIV_CMD_NOTE_ON: {
|
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);
|
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||||
|
|
||||||
if (c.chan>3) { // PSG
|
if (c.chan>3) { // PSG
|
||||||
|
@ -230,7 +251,13 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
||||||
case DIV_CMD_VOLUME: {
|
case DIV_CMD_VOLUME: {
|
||||||
chan[c.chan].vol=c.value;
|
chan[c.chan].vol=c.value;
|
||||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
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++) {
|
for (int i=0; i<4; i++) {
|
||||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||||
DivInstrumentFM::Operator op=ins->fm.op[i];
|
DivInstrumentFM::Operator op=ins->fm.op[i];
|
||||||
|
@ -332,6 +359,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
||||||
if (sampleBank>(parent->song.sample.size()/12)) {
|
if (sampleBank>(parent->song.sample.size()/12)) {
|
||||||
sampleBank=parent->song.sample.size()/12;
|
sampleBank=parent->song.sample.size()/12;
|
||||||
}
|
}
|
||||||
|
iface.sampleBank=sampleBank;
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_LEGATO: {
|
case DIV_CMD_LEGATO: {
|
||||||
if (c.chan>3) { // PSG
|
if (c.chan>3) { // PSG
|
||||||
|
@ -382,6 +410,10 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
||||||
|
|
||||||
break;
|
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:
|
case DIV_CMD_AY_ENVELOPE_SET:
|
||||||
if (c.chan<4 || c.chan>6) break;
|
if (c.chan<4 || c.chan>6) break;
|
||||||
rWrite(0x0d,c.value>>4);
|
rWrite(0x0d,c.value>>4);
|
||||||
|
@ -390,15 +422,21 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
||||||
} else {
|
} else {
|
||||||
chan[c.chan].psgMode&=~4;
|
chan[c.chan].psgMode&=~4;
|
||||||
}
|
}
|
||||||
|
rWrite(0x04+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_AY_ENVELOPE_LOW:
|
case DIV_CMD_AY_ENVELOPE_LOW:
|
||||||
if (c.chan<4 || c.chan>6) break;
|
if (c.chan<4 || c.chan>6) break;
|
||||||
writes.emplace(0x0b,c.value);
|
ayEnvPeriod&=0xff00;
|
||||||
writes.emplace(0x0c,0);
|
ayEnvPeriod|=c.value;
|
||||||
|
writes.emplace(0x0b,ayEnvPeriod);
|
||||||
|
writes.emplace(0x0c,ayEnvPeriod>>8);
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_AY_ENVELOPE_HIGH:
|
case DIV_CMD_AY_ENVELOPE_HIGH:
|
||||||
if (c.chan<4 || c.chan>6) break;
|
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;
|
break;
|
||||||
case DIV_ALWAYS_SET_VOLUME:
|
case DIV_ALWAYS_SET_VOLUME:
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -438,6 +476,8 @@ int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, bool pal) {
|
||||||
} else {
|
} else {
|
||||||
rate=500000;
|
rate=500000;
|
||||||
}
|
}
|
||||||
|
iface.parent=parent;
|
||||||
|
iface.sampleBank=0;
|
||||||
fm=new ymfm::ym2610(iface);
|
fm=new ymfm::ym2610(iface);
|
||||||
fm->reset();
|
fm->reset();
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
|
@ -462,6 +502,7 @@ int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, bool pal) {
|
||||||
dacRate=0;
|
dacRate=0;
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
sampleBank=0;
|
sampleBank=0;
|
||||||
|
ayEnvPeriod=0;
|
||||||
|
|
||||||
delay=0;
|
delay=0;
|
||||||
|
|
||||||
|
@ -469,5 +510,8 @@ int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, bool pal) {
|
||||||
|
|
||||||
// LFO
|
// LFO
|
||||||
writes.emplace(0x22,0x08);
|
writes.emplace(0x22,0x08);
|
||||||
|
|
||||||
|
// PCM volume
|
||||||
|
writes.emplace(0x101,0x3f);
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,11 @@
|
||||||
|
|
||||||
class DivYM2610Interface: public ymfm::ymfm_interface {
|
class DivYM2610Interface: public ymfm::ymfm_interface {
|
||||||
public:
|
public:
|
||||||
|
DivEngine* parent;
|
||||||
|
int sampleBank;
|
||||||
uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address);
|
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);
|
void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data);
|
||||||
|
DivYM2610Interface(): parent(NULL), sampleBank(0) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class DivPlatformYM2610: public DivDispatch {
|
class DivPlatformYM2610: public DivDispatch {
|
||||||
|
@ -50,6 +53,7 @@ class DivPlatformYM2610: public DivDispatch {
|
||||||
|
|
||||||
short oldWrites[512];
|
short oldWrites[512];
|
||||||
short pendingWrites[512];
|
short pendingWrites[512];
|
||||||
|
unsigned short ayEnvPeriod;
|
||||||
|
|
||||||
int octave(int freq);
|
int octave(int freq);
|
||||||
int toFreq(int freq);
|
int toFreq(int freq);
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
|
#include "sound/ym2610/ymfm.h"
|
||||||
#include "ym2610.h"
|
#include "ym2610.h"
|
||||||
|
#include "../engine.h"
|
||||||
|
|
||||||
uint8_t DivYM2610Interface::ymfm_external_read(ymfm::access_class type, uint32_t address) {
|
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) {
|
void DivYM2610Interface::ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ struct DivSample {
|
||||||
short* data;
|
short* data;
|
||||||
int rendLength;
|
int rendLength;
|
||||||
short* rendData;
|
short* rendData;
|
||||||
|
unsigned char* adpcmRendData;
|
||||||
|
|
||||||
DivSample():
|
DivSample():
|
||||||
name(""),
|
name(""),
|
||||||
|
@ -16,5 +17,6 @@ struct DivSample {
|
||||||
depth(16),
|
depth(16),
|
||||||
data(NULL),
|
data(NULL),
|
||||||
rendLength(0),
|
rendLength(0),
|
||||||
rendData(NULL) {}
|
rendData(NULL),
|
||||||
|
adpcmRendData(NULL) {}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue