SegaPCM: new real emulation core

This commit is contained in:
tildearrow 2023-02-10 02:01:23 -05:00
parent 0d424c7962
commit 6036366f38
9 changed files with 144 additions and 169 deletions

View file

@ -23,47 +23,25 @@
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
//#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} #define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
//#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } #define chWrite(c,a,v) rWrite(((c)<<3)+(a),v)
void DivPlatformSegaPCM::acquire(short** buf, size_t len) { void DivPlatformSegaPCM::acquire(short** buf, size_t len) {
static int os[2]; static int os[2];
for (size_t h=0; h<len; h++) { for (size_t h=0; h<len; h++) {
os[0]=0; os[1]=0; while (!writes.empty()) {
// do a PCM cycle QueuedWrite w=writes.front();
pcmL=0; pcmR=0; pcm.write(w.addr,w.val);
for (int i=0; i<16; i++) { regPool[w.addr&0xff]=w.val;
if (chan[i].pcm.sample>=0 && chan[i].pcm.sample<parent->song.sampleLen) { writes.pop();
DivSample* s=parent->getSample(chan[i].pcm.sample);
if (s->samples<=0) {
chan[i].pcm.sample=-1;
oscBuf[i]->data[oscBuf[i]->needle++]=0;
continue;
}
if (!isMuted[i]) {
oscBuf[i]->data[oscBuf[i]->needle++]=s->data8[chan[i].pcm.pos>>8]*(chan[i].chVolL+chan[i].chVolR)>>1;
pcmL+=(s->data8[chan[i].pcm.pos>>8]*chan[i].chVolL);
pcmR+=(s->data8[chan[i].pcm.pos>>8]*chan[i].chVolR);
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=0;
}
chan[i].pcm.pos+=chan[i].pcm.freq;
if (s->isLoopable() && chan[i].pcm.pos>=((unsigned int)s->loopEnd<<8)) {
chan[i].pcm.pos=s->loopStart<<8;
} else if (chan[i].pcm.pos>=(s->samples<<8)) {
chan[i].pcm.sample=-1;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=0;
}
} }
os[0]=pcmL; pcm.sound_stream_update(os);
if (os[0]<-32768) os[0]=-32768; if (os[0]<-32768) os[0]=-32768;
if (os[0]>32767) os[0]=32767; if (os[0]>32767) os[0]=32767;
os[1]=pcmR;
if (os[1]<-32768) os[1]=-32768; if (os[1]<-32768) os[1]=-32768;
if (os[1]>32767) os[1]=32767; if (os[1]>32767) os[1]=32767;
@ -81,10 +59,8 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
chan[i].outVol=(chan[i].vol*MIN(chan[i].macroVolMul,chan[i].std.vol.val))/chan[i].macroVolMul; chan[i].outVol=(chan[i].vol*MIN(chan[i].macroVolMul,chan[i].std.vol.val))/chan[i].macroVolMul;
chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127; chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127;
chan[i].chVolR=(chan[i].outVol*chan[i].chPanR)/127; chan[i].chVolR=(chan[i].outVol*chan[i].chPanR)/127;
if (dumpWrites) { rWrite(2+(i<<3),chan[i].chVolL);
addWrite(0x10002+(i<<3),chan[i].chVolL); rWrite(3+(i<<3),chan[i].chVolR);
addWrite(0x10003+(i<<3),chan[i].chVolR);
}
} }
} }
@ -100,17 +76,13 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
if (parent->song.newSegaPCM) if (chan[i].std.panL.had) { if (parent->song.newSegaPCM) if (chan[i].std.panL.had) {
chan[i].chPanL=chan[i].std.panL.val&127; chan[i].chPanL=chan[i].std.panL.val&127;
chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127; chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127;
if (dumpWrites) { rWrite(2+(i<<3),chan[i].chVolL);
addWrite(0x10002+(i<<3),chan[i].chVolL);
}
} }
if (parent->song.newSegaPCM) if (chan[i].std.panR.had) { if (parent->song.newSegaPCM) if (chan[i].std.panR.had) {
chan[i].chPanR=chan[i].std.panR.val&127; chan[i].chPanR=chan[i].std.panR.val&127;
chan[i].chVolR=(chan[i].outVol*chan[i].chPanR)/127; chan[i].chVolR=(chan[i].outVol*chan[i].chPanR)/127;
if (dumpWrites) { rWrite(3+(i<<3),chan[i].chVolR);
addWrite(0x10003+(i<<3),chan[i].chVolR);
}
} }
if (chan[i].std.pitch.had) { if (chan[i].std.pitch.had) {
@ -145,56 +117,48 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
off=(double)s->centerRate/8363.0; off=(double)s->centerRate/8363.0;
} }
chan[i].pcm.freq=MIN(255,(15625+(off*parent->song.tuning*pow(2.0,double(chan[i].freq+256)/(64.0*12.0)))*255)/31250)+chan[i].pitch2; chan[i].pcm.freq=MIN(255,(15625+(off*parent->song.tuning*pow(2.0,double(chan[i].freq+256)/(64.0*12.0)))*255)/31250)+chan[i].pitch2;
if (dumpWrites) { rWrite(7+(i<<3),chan[i].pcm.freq);
addWrite(0x10007+(i<<3),chan[i].pcm.freq);
}
} }
chan[i].freqChanged=false; chan[i].freqChanged=false;
if (chan[i].keyOn || chan[i].keyOff) { if (chan[i].keyOn || chan[i].keyOff) {
if (chan[i].keyOn && !chan[i].keyOff) { if (chan[i].keyOn && !chan[i].keyOff) {
if (dumpWrites) { rWrite(0x86+(i<<3),3);
addWrite(0x10086+(i<<3),3);
}
chan[i].pcm.pos=0; chan[i].pcm.pos=0;
if (chan[i].furnacePCM) { if (chan[i].furnacePCM) {
if (dumpWrites) { // Sega PCM writes DivSample* s=parent->getSample(chan[i].pcm.sample);
DivSample* s=parent->getSample(chan[i].pcm.sample); int loopStart=s->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT);
int loopStart=s->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT); int actualLength=(s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT));
int actualLength=(s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)); if (actualLength>0xfeff) actualLength=0xfeff;
if (actualLength>0xfeff) actualLength=0xfeff; rWrite(0x86+(i<<3),3+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
addWrite(0x10086+(i<<3),3+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3)); rWrite(0x84+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample])&0xff);
addWrite(0x10084+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample])&0xff); rWrite(0x85+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample]>>8)&0xff);
addWrite(0x10085+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample]>>8)&0xff); rWrite(6+(i<<3),MIN(255,((sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+actualLength-1)>>8));
addWrite(0x10006+(i<<3),MIN(255,((sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+actualLength-1)>>8)); if (loopStart<0 || loopStart>=actualLength) {
if (loopStart<0 || loopStart>=actualLength) { rWrite(0x86+(i<<3),2+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
addWrite(0x10086+(i<<3),2+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3)); } else {
} else { int loopPos=(sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+loopStart+sampleLoopOff[chan[i].pcm.sample];
int loopPos=(sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+loopStart+s->loopOffP; rWrite(4+(i<<3),loopPos&0xff);
addWrite(0x10004+(i<<3),loopPos&0xff); rWrite(5+(i<<3),(loopPos>>8)&0xff);
addWrite(0x10005+(i<<3),(loopPos>>8)&0xff); rWrite(0x86+(i<<3),((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
addWrite(0x10086+(i<<3),((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
}
} }
} else { } else {
if (dumpWrites) { // Sega PCM writes DivSample* s=parent->getSample(chan[i].pcm.sample);
DivSample* s=parent->getSample(chan[i].pcm.sample); int loopStart=s->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT);
int loopStart=s->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT); int actualLength=(s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT));
int actualLength=(s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)); if (actualLength>0xfeff) actualLength=0xfeff;
if (actualLength>65536) actualLength=65536; rWrite(0x86+(i<<3),3+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
addWrite(0x10086+(i<<3),3+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3)); rWrite(0x84+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample])&0xff);
addWrite(0x10084+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample])&0xff); rWrite(0x85+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample]>>8)&0xff);
addWrite(0x10085+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample]>>8)&0xff); rWrite(6+(i<<3),MIN(255,((sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+actualLength-1)>>8));
addWrite(0x10006+(i<<3),MIN(255,((sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+actualLength-1)>>8)); if (loopStart<0 || loopStart>=actualLength) {
if (loopStart<0 || loopStart>=actualLength) { rWrite(0x86+(i<<3),2+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
addWrite(0x10086+(i<<3),2+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3)); } else {
} else { int loopPos=(sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+loopStart+sampleLoopOff[chan[i].pcm.sample];
int loopPos=(sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+loopStart+s->loopOffP; rWrite(4+(i<<3),loopPos&0xff);
addWrite(0x10004+(i<<3),loopPos&0xff); rWrite(5+(i<<3),(loopPos>>8)&0xff);
addWrite(0x10005+(i<<3),(loopPos>>8)&0xff); rWrite(0x86+(i<<3),((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
addWrite(0x10086+(i<<3),((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
}
addWrite(0x10007+(i<<3),chan[i].pcm.freq);
} }
rWrite(7+(i<<3),chan[i].pcm.freq);
} }
} }
chan[i].keyOn=false; chan[i].keyOn=false;
@ -206,6 +170,7 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
void DivPlatformSegaPCM::muteChannel(int ch, bool mute) { void DivPlatformSegaPCM::muteChannel(int ch, bool mute) {
isMuted[ch]=mute; isMuted[ch]=mute;
pcm.mute(ch,mute);
} }
int DivPlatformSegaPCM::dispatch(DivCommand c) { int DivPlatformSegaPCM::dispatch(DivCommand c) {
@ -219,9 +184,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
if (c.value!=DIV_NOTE_NULL) chan[c.chan].pcm.sample=ins->amiga.getSample(c.value); if (c.value!=DIV_NOTE_NULL) chan[c.chan].pcm.sample=ins->amiga.getSample(c.value);
if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) { if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) {
chan[c.chan].pcm.sample=-1; chan[c.chan].pcm.sample=-1;
if (dumpWrites) { rWrite(0x86+(c.chan<<3),3);
addWrite(0x10086+(c.chan<<3),3);
}
chan[c.chan].macroInit(NULL); chan[c.chan].macroInit(NULL);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol; chan[c.chan].outVol=chan[c.chan].vol;
@ -245,9 +208,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
chan[c.chan].pcm.sample=12*sampleBank+chan[c.chan].note%12; chan[c.chan].pcm.sample=12*sampleBank+chan[c.chan].note%12;
if (chan[c.chan].pcm.sample>=parent->song.sampleLen) { if (chan[c.chan].pcm.sample>=parent->song.sampleLen) {
chan[c.chan].pcm.sample=-1; chan[c.chan].pcm.sample=-1;
if (dumpWrites) { rWrite(0x86+(c.chan<<3),3);
addWrite(0x10086+(c.chan<<3),3);
}
break; break;
} }
chan[c.chan].pcm.freq=MIN(255,(parent->getSample(chan[c.chan].pcm.sample)->rate*255)/31250); chan[c.chan].pcm.freq=MIN(255,(parent->getSample(chan[c.chan].pcm.sample)->rate*255)/31250);
@ -259,9 +220,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
} }
case DIV_CMD_NOTE_OFF: case DIV_CMD_NOTE_OFF:
chan[c.chan].pcm.sample=-1; chan[c.chan].pcm.sample=-1;
if (dumpWrites) { rWrite(0x86+(c.chan<<3),3);
addWrite(0x10086+(c.chan<<3),3);
}
chan[c.chan].keyOff=true; chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false; chan[c.chan].keyOn=false;
chan[c.chan].active=false; chan[c.chan].active=false;
@ -288,10 +247,8 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
chan[c.chan].chVolL=c.value; chan[c.chan].chVolL=c.value;
chan[c.chan].chVolR=c.value; chan[c.chan].chVolR=c.value;
} }
if (dumpWrites) { rWrite(2+(c.chan<<3),chan[c.chan].chVolL);
addWrite(0x10002+(c.chan<<3),chan[c.chan].chVolL); rWrite(3+(c.chan<<3),chan[c.chan].chVolR);
addWrite(0x10003+(c.chan<<3),chan[c.chan].chVolR);
}
break; break;
} }
case DIV_CMD_GET_VOLUME: { case DIV_CMD_GET_VOLUME: {
@ -314,10 +271,8 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
chan[c.chan].chVolL=c.value>>1; chan[c.chan].chVolL=c.value>>1;
chan[c.chan].chVolR=c.value2>>1; chan[c.chan].chVolR=c.value2>>1;
} }
if (dumpWrites) { rWrite(2+(c.chan<<3),chan[c.chan].chVolL);
addWrite(0x10002+(c.chan<<3),chan[c.chan].chVolL); rWrite(3+(c.chan<<3),chan[c.chan].chVolR);
addWrite(0x10003+(c.chan<<3),chan[c.chan].chVolR);
}
break; break;
} }
case DIV_CMD_PITCH: { case DIV_CMD_PITCH: {
@ -381,9 +336,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
break; break;
case DIV_CMD_SAMPLE_FREQ: case DIV_CMD_SAMPLE_FREQ:
chan[c.chan].pcm.freq=c.value; chan[c.chan].pcm.freq=c.value;
if (dumpWrites) { rWrite(7+(c.chan<<3),chan[c.chan].pcm.freq);
addWrite(0x10007+(c.chan<<3),chan[c.chan].pcm.freq);
}
break; break;
default: default:
//printf("WARNING: unimplemented command %d\n",c.cmd); //printf("WARNING: unimplemented command %d\n",c.cmd);
@ -395,6 +348,10 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
void DivPlatformSegaPCM::forceIns() { void DivPlatformSegaPCM::forceIns() {
for (int i=0; i<16; i++) { for (int i=0; i<16; i++) {
chan[i].insChanged=true; chan[i].insChanged=true;
rWrite(2+(i<<3),chan[i].chVolL);
rWrite(3+(i<<3),chan[i].chVolR);
rWrite(7+(i<<3),chan[i].pcm.freq);
} }
} }
@ -425,7 +382,7 @@ DivDispatchOscBuffer* DivPlatformSegaPCM::getOscBuffer(int ch) {
} }
unsigned char* DivPlatformSegaPCM::getRegisterPool() { unsigned char* DivPlatformSegaPCM::getRegisterPool() {
return regPool; return pcm.get_ram();
} }
int DivPlatformSegaPCM::getRegisterPoolSize() { int DivPlatformSegaPCM::getRegisterPoolSize() {
@ -433,11 +390,29 @@ int DivPlatformSegaPCM::getRegisterPoolSize() {
} }
void DivPlatformSegaPCM::poke(unsigned int addr, unsigned short val) { void DivPlatformSegaPCM::poke(unsigned int addr, unsigned short val) {
//immWrite(addr,val); rWrite(addr,val);
} }
void DivPlatformSegaPCM::poke(std::vector<DivRegWrite>& wlist) { void DivPlatformSegaPCM::poke(std::vector<DivRegWrite>& wlist) {
//for (DivRegWrite& i: wlist) immWrite(i.addr,i.val); for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
}
const void* DivPlatformSegaPCM::getSampleMem(int index) {
return index == 0 ? sampleMem : NULL;
}
size_t DivPlatformSegaPCM::getSampleMemCapacity(int index) {
return index == 0 ? 16777216 : 0;
}
size_t DivPlatformSegaPCM::getSampleMemUsage(int index) {
return index == 0 ? sampleMemLen : 0;
}
bool DivPlatformSegaPCM::isSampleLoaded(int index, int sample) {
if (index!=0) return false;
if (sample<0 || sample>255) return false;
return sampleLoaded[sample];
} }
void DivPlatformSegaPCM::reset() { void DivPlatformSegaPCM::reset() {
@ -457,17 +432,20 @@ void DivPlatformSegaPCM::reset() {
sampleBank=0; sampleBank=0;
delay=0; delay=0;
if (dumpWrites) { pcm.device_start();
for (int i=0; i<16; i++) {
addWrite(0x10086+(i<<3),3); for (int i=0; i<16; i++) {
addWrite(0x10002+(i<<3),0x7f); rWrite(0x86+(i<<3),3);
addWrite(0x10003+(i<<3),0x7f); rWrite(2+(i<<3),0x7f);
} rWrite(3+(i<<3),0x7f);
} }
} }
void DivPlatformSegaPCM::renderSamples(int sysID) { void DivPlatformSegaPCM::renderSamples(int sysID) {
size_t memPos=0; size_t memPos=0;
memset(sampleMem,0,16777216);
memset(sampleLoaded,0,256*sizeof(bool));
for (int i=0; i<parent->song.sampleLen; i++) { for (int i=0; i<parent->song.sampleLen; i++) {
DivSample* sample=parent->getSample(i); DivSample* sample=parent->getSample(i);
@ -477,6 +455,7 @@ void DivPlatformSegaPCM::reset() {
memPos=(memPos+0xffff)&0xff0000; memPos=(memPos+0xffff)&0xff0000;
} }
logV("- sample %d will be at %x with length %x",i,memPos,alignedSize); logV("- sample %d will be at %x with length %x",i,memPos,alignedSize);
sampleLoaded[i]=true;
if (memPos>=16777216) break; if (memPos>=16777216) break;
sampleOffSegaPCM[i]=memPos; sampleOffSegaPCM[i]=memPos;
unsigned int readPos=0; unsigned int readPos=0;
@ -484,20 +463,21 @@ void DivPlatformSegaPCM::reset() {
if (readPos>=(unsigned int)sample->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)) { if (readPos>=(unsigned int)sample->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)) {
if (sample->isLoopable()) { if (sample->isLoopable()) {
readPos=sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT); readPos=sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT);
memPos++; sampleMem[memPos++]=((unsigned char)sample->data8[readPos]+0x80);
} else { } else {
memPos++; sampleMem[memPos++]=0x80;
} }
} else { } else {
memPos++; sampleMem[memPos++]=((unsigned char)sample->data8[readPos]+0x80);
} }
readPos++; readPos++;
if (memPos>=16777216) break; if (memPos>=16777216) break;
} }
sample->loopOffP=readPos-sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT); sampleLoopOff[i]=readPos-sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT);
if (memPos>=16777216) break; if (memPos>=16777216) break;
} }
} sampleMemLen=memPos;
}
void DivPlatformSegaPCM::setFlags(const DivConfig& flags) { void DivPlatformSegaPCM::setFlags(const DivConfig& flags) {
chipClock=8000000.0; chipClock=8000000.0;
@ -520,6 +500,11 @@ int DivPlatformSegaPCM::init(DivEngine* p, int channels, int sugRate, const DivC
isMuted[i]=false; isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer; oscBuf[i]=new DivDispatchOscBuffer;
} }
sampleMem=new unsigned char[16777216];
pcm.set_bank(segapcm_device::BANK_12M|segapcm_device::BANK_MASKF8);
pcm.set_read([this](unsigned int addr) -> unsigned char {
return sampleMem[addr&0xffffff];
});
setFlags(flags); setFlags(flags);
reset(); reset();
@ -530,6 +515,7 @@ void DivPlatformSegaPCM::quit() {
for (int i=0; i<16; i++) { for (int i=0; i<16; i++) {
delete oscBuf[i]; delete oscBuf[i];
} }
delete sampleMem;
} }
DivPlatformSegaPCM::~DivPlatformSegaPCM() { DivPlatformSegaPCM::~DivPlatformSegaPCM() {

View file

@ -53,6 +53,8 @@ class DivPlatformSegaPCM: public DivDispatch {
}; };
Channel chan[16]; Channel chan[16];
DivDispatchOscBuffer* oscBuf[16]; DivDispatchOscBuffer* oscBuf[16];
unsigned char* sampleMem;
size_t sampleMemLen;
struct QueuedWrite { struct QueuedWrite {
unsigned short addr; unsigned short addr;
unsigned char val; unsigned char val;
@ -74,6 +76,8 @@ class DivPlatformSegaPCM: public DivDispatch {
short pendingWrites[256]; short pendingWrites[256];
unsigned int sampleOffSegaPCM[256]; unsigned int sampleOffSegaPCM[256];
unsigned int sampleLoopOff[256];
bool sampleLoaded[256];
friend void putDispatchChip(void*,int); friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int); friend void putDispatchChan(void*,int,int);
@ -97,6 +101,10 @@ class DivPlatformSegaPCM: public DivDispatch {
int getOutputCount(); int getOutputCount();
void poke(unsigned int addr, unsigned short val); void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist); void poke(std::vector<DivRegWrite>& wlist);
const void* getSampleMem(int index=0);
size_t getSampleMemCapacity(int index=0);
size_t getSampleMemUsage(int index=0);
bool isSampleLoaded(int index, int sample);
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit(); void quit();
~DivPlatformSegaPCM(); ~DivPlatformSegaPCM();

View file

@ -16,6 +16,7 @@ segapcm_device::segapcm_device()
: m_bankshift(12) : m_bankshift(12)
, m_bankmask(0x70) , m_bankmask(0x70)
{ {
memset(m_muted,0,16*sizeof(bool));
} }
@ -94,8 +95,13 @@ void segapcm_device::sound_stream_update(int* outputs)
v = read_byte(offset + (addr >> 8)) - 0x80; v = read_byte(offset + (addr >> 8)) - 0x80;
/* apply panning and advance */ /* apply panning and advance */
lastOut[ch][0]=v * (regs[2] & 0x7f); if (m_muted[ch]) {
lastOut[ch][1]=v * (regs[3] & 0x7f); lastOut[ch][0]=0;
lastOut[ch][1]=0;
} else {
lastOut[ch][0]=v * (regs[2] & 0x7f);
lastOut[ch][1]=v * (regs[3] & 0x7f);
}
outputs[0]+=lastOut[ch][0]; outputs[0]+=lastOut[ch][0];
outputs[1]+=lastOut[ch][1]; outputs[1]+=lastOut[ch][1];
addr = (addr + regs[7]) & 0xffffff; addr = (addr + regs[7]) & 0xffffff;
@ -123,3 +129,11 @@ uint8_t segapcm_device::read(unsigned int offset)
{ {
return m_ram[offset & 0x07ff]; return m_ram[offset & 0x07ff];
} }
uint8_t* segapcm_device::get_ram() {
return m_ram;
}
void segapcm_device::mute(int ch, bool doMute) {
m_muted[ch&15]=doMute;
}

View file

@ -7,6 +7,7 @@
#ifndef MAMESOUND_SEGAPCM_H #ifndef MAMESOUND_SEGAPCM_H
#define MAMESOUND_SEGAPCM_H #define MAMESOUND_SEGAPCM_H
#include <stdint.h>
#include <functional> #include <functional>
//************************************************************************** //**************************************************************************
@ -22,7 +23,7 @@ public:
static constexpr int BANK_MASKF = 0xf0 << 16; static constexpr int BANK_MASKF = 0xf0 << 16;
static constexpr int BANK_MASKF8 = 0xf8 << 16; static constexpr int BANK_MASKF8 = 0xf8 << 16;
short lastOut[16][2]; short lastOut[16][2];
segapcm_device(); segapcm_device();
@ -32,6 +33,8 @@ public:
void write(unsigned int offset, uint8_t data); void write(unsigned int offset, uint8_t data);
uint8_t read(unsigned int offset); uint8_t read(unsigned int offset);
uint8_t* get_ram();
void mute(int ch, bool doMute);
// device-level overrides // device-level overrides
void device_start(); void device_start();
@ -42,6 +45,7 @@ public:
private: private:
uint8_t m_ram[0x800]; uint8_t m_ram[0x800];
uint8_t m_low[16]; uint8_t m_low[16];
bool m_muted[16];
int m_bankshift; int m_bankshift;
int m_bankmask; int m_bankmask;
std::function<unsigned char(unsigned int)> read_byte; std::function<unsigned char(unsigned int)> read_byte;

View file

@ -95,8 +95,7 @@ struct DivSampleHistory {
struct DivSample { struct DivSample {
String name; String name;
// TODO: get rid of loopOffP int rate, centerRate, loopStart, loopEnd;
int rate, centerRate, loopStart, loopEnd, loopOffP;
// valid values are: // valid values are:
// - 0: ZX Spectrum overlay drum (1-bit) // - 0: ZX Spectrum overlay drum (1-bit)
// - 1: 1-bit NES DPCM (1-bit) // - 1: 1-bit NES DPCM (1-bit)
@ -306,7 +305,6 @@ struct DivSample {
centerRate(8363), centerRate(8363),
loopStart(-1), loopStart(-1),
loopEnd(-1), loopEnd(-1),
loopOffP(0),
depth(DIV_SAMPLE_DEPTH_16BIT), depth(DIV_SAMPLE_DEPTH_16BIT),
loop(false), loop(false),
brrEmphasis(true), brrEmphasis(true),

View file

@ -1085,7 +1085,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
DivDispatch* writeADPCM_OPNA[2]={NULL,NULL}; DivDispatch* writeADPCM_OPNA[2]={NULL,NULL};
DivDispatch* writeADPCM_OPNB[2]={NULL,NULL}; DivDispatch* writeADPCM_OPNB[2]={NULL,NULL};
DivDispatch* writeADPCM_Y8950[2]={NULL,NULL}; DivDispatch* writeADPCM_Y8950[2]={NULL,NULL};
int writeSegaPCM=0; DivDispatch* writeSegaPCM[2]={NULL,NULL};
DivDispatch* writeX1010[2]={NULL,NULL}; DivDispatch* writeX1010[2]={NULL,NULL};
DivDispatch* writeQSound[2]={NULL,NULL}; DivDispatch* writeQSound[2]={NULL,NULL};
DivDispatch* writeES5506[2]={NULL,NULL}; DivDispatch* writeES5506[2]={NULL,NULL};
@ -1177,12 +1177,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
hasSegaPCM=4000000; hasSegaPCM=4000000;
CHIP_VOL(4,0.67); CHIP_VOL(4,0.67);
willExport[i]=true; willExport[i]=true;
writeSegaPCM=1; writeSegaPCM[0]=disCont[i].dispatch;
} else if (!(hasSegaPCM&0x40000000)) { } else if (!(hasSegaPCM&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(4,0.67); CHIP_VOL_SECOND(4,0.67);
willExport[i]=true; willExport[i]=true;
writeSegaPCM=2; writeSegaPCM[1]=disCont[i].dispatch;
hasSegaPCM|=0x40000000; hasSegaPCM|=0x40000000;
howManyChips++; howManyChips++;
} }
@ -1843,53 +1843,17 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
} }
} }
if (writeSegaPCM>0) { for (int i=0; i<2; i++) {
unsigned char* pcmMem=new unsigned char[16777216]; // SegaPCM
if (writeSegaPCM[i]!=NULL && writeSegaPCM[i]->getSampleMemUsage(0)>0) {
size_t memPos=0;
for (int i=0; i<song.sampleLen; i++) {
DivSample* sample=song.sample[i];
unsigned int alignedSize=(sample->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)+0xff)&(~0xff);
if (alignedSize>65536) alignedSize=65536;
if ((memPos&0xff0000)!=((memPos+alignedSize)&0xff0000)) {
memPos=(memPos+0xffff)&0xff0000;
}
logV("- sample %d will be at %x with length %x",i,memPos,alignedSize);
if (memPos>=16777216) break;
sampleOffSegaPCM[i]=memPos;
unsigned int readPos=0;
for (unsigned int j=0; j<alignedSize; j++) {
if (readPos>=(unsigned int)sample->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)) {
if (sample->isLoopable()) {
readPos=sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT);
pcmMem[memPos++]=((unsigned char)sample->data8[readPos]+0x80);
} else {
pcmMem[memPos++]=0x80;
}
} else {
pcmMem[memPos++]=((unsigned char)sample->data8[readPos]+0x80);
}
readPos++;
if (memPos>=16777216) break;
}
sample->loopOffP=readPos-sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT);
if (memPos>=16777216) break;
}
for (unsigned char i=0; i<writeSegaPCM; i++) {
w->writeC(0x67); w->writeC(0x67);
w->writeC(0x66); w->writeC(0x66);
w->writeC(0x80); w->writeC(0x81);
w->writeI((memPos+8)|(i*0x80000000)); w->writeI((writeSegaPCM[i]->getSampleMemUsage(0)+8)|(i*0x80000000));
w->writeI(memPos); w->writeI(writeSegaPCM[i]->getSampleMemCapacity(0));
w->writeI(0); w->writeI(0);
w->write(pcmMem,memPos); w->write(writeSegaPCM[i]->getSampleMem(0),writeSegaPCM[i]->getSampleMemUsage(0));
} }
delete[] pcmMem;
}
for (int i=0; i<2; i++) {
// ADPCM (OPNA) // ADPCM (OPNA)
if (writeADPCM_OPNA[i]!=NULL && writeADPCM_OPNA[i]->getSampleMemUsage(0)>0) { if (writeADPCM_OPNA[i]!=NULL && writeADPCM_OPNA[i]->getSampleMemUsage(0)>0) {
w->writeC(0x67); w->writeC(0x67);

View file

@ -149,6 +149,7 @@ const char* aboutLine[]={
"MAME MSM6258 core by Barry Rodewald", "MAME MSM6258 core by Barry Rodewald",
"MAME YMZ280B core by Aaron Giles", "MAME YMZ280B core by Aaron Giles",
"MAME GA20 core by Acho A. Tang and R. Belmont", "MAME GA20 core by Acho A. Tang and R. Belmont",
"MAME SegaPCM core by Hiromitsu Shioya and Olivier Galibert",
"SAASound by Dave Hooper and Simon Owen", "SAASound by Dave Hooper and Simon Owen",
"SameBoy by Lior Halphon", "SameBoy by Lior Halphon",
"Mednafen PCE, WonderSwan, T6W28 and Virtual Boy audio cores", "Mednafen PCE, WonderSwan, T6W28 and Virtual Boy audio cores",

View file

@ -183,7 +183,6 @@ void FurnaceGUI::drawDebug() {
ImGui::Text("centerRate: %d",sample->centerRate); ImGui::Text("centerRate: %d",sample->centerRate);
ImGui::Text("loopStart: %d",sample->loopStart); ImGui::Text("loopStart: %d",sample->loopStart);
ImGui::Text("loopEnd: %d", sample->loopEnd); ImGui::Text("loopEnd: %d", sample->loopEnd);
ImGui::Text("loopOffP: %d",sample->loopOffP);
ImGui::Text(sample->loop?"loop: Enabled":"loop: Disabled"); ImGui::Text(sample->loop?"loop: Enabled":"loop: Disabled");
if (sampleLoopModes[sample->loopMode]!=NULL) { if (sampleLoopModes[sample->loopMode]!=NULL) {
ImGui::Text("loopMode: %d (%s)",(unsigned char)sample->loopMode,sampleLoopModes[sample->loopMode]); ImGui::Text("loopMode: %d (%s)",(unsigned char)sample->loopMode,sampleLoopModes[sample->loopMode]);

View file

@ -185,6 +185,7 @@ TAParamResult pVersion(String) {
printf("- MAME MSM6258 core by Barry Rodewald (BSD 3-clause)\n"); printf("- MAME MSM6258 core by Barry Rodewald (BSD 3-clause)\n");
printf("- MAME YMZ280B core by Aaron Giles (BSD 3-clause)\n"); printf("- MAME YMZ280B core by Aaron Giles (BSD 3-clause)\n");
printf("- MAME GA20 core by Acho A. Tang and R. Belmont (BSD 3-clause)\n"); printf("- MAME GA20 core by Acho A. Tang and R. Belmont (BSD 3-clause)\n");
printf("- MAME SegaPCM core by Hiromitsu Shioya and Olivier Galibert (BSD 3-clause)\n");
printf("- QSound core by superctr (BSD 3-clause)\n"); printf("- QSound core by superctr (BSD 3-clause)\n");
printf("- VICE VIC-20 by Rami Rasanen and viznut (GPLv2)\n"); printf("- VICE VIC-20 by Rami Rasanen and viznut (GPLv2)\n");
printf("- VERA core by Frank van den Hoef (BSD 2-clause)\n"); printf("- VERA core by Frank van den Hoef (BSD 2-clause)\n");