Kurumitsu-8L: add echo and PCM sample

This commit is contained in:
Natt Akuma 2025-04-26 14:41:22 +07:00
parent 37b2a058c3
commit 434fc96bba
7 changed files with 414 additions and 109 deletions

View file

@ -34,6 +34,7 @@ enum DivROMExportOptions {
DIV_ROM_SAP_R, DIV_ROM_SAP_R,
DIV_ROM_IPOD, DIV_ROM_IPOD,
DIV_ROM_GRUB, DIV_ROM_GRUB,
DIV_ROM_KMM,
DIV_ROM_MAX DIV_ROM_MAX
}; };

View file

@ -23,6 +23,7 @@
#define chRead(c,a) regPool[(c)*16+(a)] #define chRead(c,a) regPool[(c)*16+(a)]
#define chWrite(c,a,v) if (!skipRegisterWrites) {regPool[(c)*16+(a)]=v; if (dumpWrites) {addWrite(a,v);} } #define chWrite(c,a,v) if (!skipRegisterWrites) {regPool[(c)*16+(a)]=v; if (dumpWrites) {addWrite(a,v);} }
#define chDWrite(c,a,v) regPool[(c)*16+(a)]=v;
#define CHIP_FREQBASE 67108864 #define CHIP_FREQBASE 67108864
@ -32,9 +33,14 @@ const char* regCheatSheetKurumitsu8L[]={
"CHxPanR", "02+x*10", "CHxPanR", "02+x*10",
"CHxMod", "03+x*10", "CHxMod", "03+x*10",
"CHxFreq", "04+x*10", "CHxFreq", "04+x*10",
"CHxEcho", "07+x*10",
"CHxAcc", "08+x*10", "CHxAcc", "08+x*10",
"CHxLoopS", "0C+x*10", "CH7Bank", "73",
"CHxLoopE", "0E+x*10", "CH7LoopE", "7C",
"CH7LoopL", "7E",
"EchoFB", "80",
"EchoVolL", "81",
"EchoVolR", "82",
NULL NULL
}; };
@ -44,55 +50,78 @@ const char** DivPlatformKurumitsu8L::getRegisterSheet() {
void DivPlatformKurumitsu8L::acquire(short** buf, size_t len) { void DivPlatformKurumitsu8L::acquire(short** buf, size_t len) {
const int volTab[32]={ const int volTab[32]={
32767, 32064, 31377, 30705, 30047, 29403, 28773, 28156, 27553, 26963, 26385, 25820, 25266, 24725, 24195, 23677, 23169, 22673, 22187, 21712, 21246, 20791, 20345, 19910, 19483, 19065, 18657, 18257, 17866, 17483, 17108, 16742 65535, 64130, 62756, 61411, 60095, 58808, 57547, 56314, 55108, 53927, 52771, 51640, 50534, 49451, 48391, 47354,
46340, 45347, 44375, 43424, 42494, 41583, 40692, 39820, 38967, 38132, 37315, 36515, 35733, 34967, 34218, 33485
}; };
for (int i=0; i<8; i++) { for (int i=0; i<8; i++) {
oscBuf[i]->begin(len); oscBuf[i]->begin(len);
} }
signed char echoFB=chRead(8,0);
signed char echoL=chRead(8,1);
signed char echoR=chRead(8,2);
for (size_t h=0; h<len; h++) { for (size_t h=0; h<len; h++) {
int outAL=0; int outE=((int)echoMem[echoPtr]*echoFB)<<11;
int outAR=0; int outAL=((int)echoMem[echoPtr]*echoL)<<11;
int outAR=((int)echoMem[echoPtr]*echoR)<<11;
int modData=0;
for (int i=0; i<8; i++) { for (int i=0; i<8; i++) {
int outL=0;
int outR=0;
unsigned char vol=chRead(i,0); unsigned char vol=chRead(i,0);
unsigned char panL=chRead(i,1); signed char panL=chRead(i,1);
unsigned char panR=chRead(i,2); signed char panR=chRead(i,2);
unsigned char fb=chRead(i,3)&0xf; signed char echo=chRead(i,7);
unsigned char mod=(chRead(i,3)>>4)&0xf;
unsigned freq=chRead(i,4)|((unsigned)chRead(i,5)<<8)|((unsigned)chRead(i,6)<<16)|((unsigned)chRead(i,7)<<24); unsigned freq=chRead(i,4)|((unsigned)chRead(i,5)<<8)|((unsigned)chRead(i,6)<<16)|((unsigned)chRead(i,7)<<24);
unsigned phase=chRead(i,8)|((unsigned)chRead(i,9)<<8)|((unsigned)chRead(i,10)<<16)|((unsigned)chRead(i,11)<<24); unsigned phase=chRead(i,8)|((unsigned)chRead(i,9)<<8)|((unsigned)chRead(i,10)<<16)|((unsigned)chRead(i,11)<<24);
int fbData=chRead(i,13)|((unsigned)chRead(i,14)<<8); int fbData=0;
if (fbData>=0x8000) fbData-=0x10000;
unsigned phase_modded=phase; int data=0;
phase_modded+=(fbData<<8)>>fb; if (i<7) {
phase_modded+=(modData<<8)>>mod; unsigned char fb=chRead(i,3)&0xf;
phase+=freq; unsigned char mod=(chRead(i,3)>>4)&0xf;
fbData=chRead(i,12)|((int)chRead(i,13)<<8)|((int)chRead(i,14)<<16)|((int)chRead(i,15)<<24);
unsigned phase_modded=phase;
phase_modded+=fbData>>fb;
phase_modded+=modData>>mod;
data=wtMem[i][(phase_modded>>16)&255];
phase+=freq;
} else {
unsigned loopE=chRead(i,12)|((unsigned)chRead(i,13)<<8);
unsigned loopL=chRead(i,14)|((unsigned)chRead(i,15)<<8);
data=sampleMem[(phase>>16)|(chRead(i,3)<<16)];
int64_t newPhase=phase+(freq&0x00ffffff);
if (newPhase>=(loopE<<16)) {
newPhase-=loopL<<16;
}
phase=newPhase;
}
int out=data*(volTab[vol&0x1f]>>(vol>>5));
outAL+=out*panL/256;
outAR+=out*panR/256;
outE+=out*echo/256;
oscBuf[i]->putSample(h,out*((int)abs(panL)+abs(panR))/65536);
unsigned char data=wtMem[i][(phase_modded>>16)&255]; chDWrite(i,8,phase&0xff);
int out=(data>>1)+vol; chDWrite(i,9,(phase>>8)&0xff);
out=volTab[out&0x1f]>>(out>>5); chDWrite(i,10,(phase>>16)&0xff);
if (data&1) out=-out; chDWrite(i,11,(phase>>24)&0xff);
fbData=out; if (i<7) {
modData=out; fbData=out;
outAL+=out*panL; modData=out;
outAR+=out*panR; chDWrite(i,12,fbData&0xff);
oscBuf[i]->putSample(h,(out*((int)panL+panR))>>9); chDWrite(i,13,(fbData>>8)&0xff);
chDWrite(i,14,(fbData>>16)&0xff);
chWrite(i,8,phase&0xff); chDWrite(i,15,(fbData>>24)&0xff);
chWrite(i,9,(phase>>8)&0xff); }
chWrite(i,10,(phase>>16)&0xff);
chWrite(i,11,(phase>>24)&0xff);
chWrite(i,13,fbData&0xff);
chWrite(i,14,(fbData>>8)&0xff);
chWrite(i,15,(fbData>>16)&0xff);
} }
buf[0][h]=outAL>>11; buf[0][h]=outAL>>10;
buf[1][h]=outAR>>11; buf[1][h]=outAR>>10;
outE>>=18;
if (outE<0) outE++;
echoMem[echoPtr]=outE;
echoPtr=(echoPtr+1)&4095;
} }
for (int i=0; i<8; i++) { for (int i=0; i<8; i++) {
@ -101,36 +130,38 @@ void DivPlatformKurumitsu8L::acquire(short** buf, size_t len) {
} }
void DivPlatformKurumitsu8L::updateWave(int ch) { void DivPlatformKurumitsu8L::updateWave(int ch) {
if (ch>=7) return;
for (int i=0; i<256; i++) { for (int i=0; i<256; i++) {
int data=chan[ch].ws.output[i]-128; wtMem[ch][i]=chan[ch].ws.output[i]^128;
unsigned char val=0;
if (data<0) {
val|=1;
data=-data;
}
// convert to log attenuation format
if (data>0) {
val|=((unsigned char)MIN(roundf((7.f-log2f(data))*32.f),127.f))<<1;
} else {
val|=0xfe;
}
wtMem[ch][i]=val;
} }
} }
void DivPlatformKurumitsu8L::writePan(int ch) { void DivPlatformKurumitsu8L::writePan(int ch) {
if (!isMuted[ch] && chan[ch].active) { if (!isMuted[ch] && chan[ch].active) {
chWrite(ch,1,VOL_SCALE_LINEAR(chan[ch].panL,chan[ch].vol,255)); chWrite(ch,1,VOL_SCALE_LINEAR(chan[ch].panL,chan[ch].vol,127)*(chan[ch].invertL?-1:1));
chWrite(ch,2,VOL_SCALE_LINEAR(chan[ch].panR,chan[ch].vol,255)); chWrite(ch,2,VOL_SCALE_LINEAR(chan[ch].panR,chan[ch].vol,127)*(chan[ch].invertR?-1:1));
} else {
chWrite(ch,1,0);
chWrite(ch,2,0);
}
}
void DivPlatformKurumitsu8L::writeEcho(int ch) {
if (!isMuted[ch] && chan[ch].active) {
chWrite(ch,7,chan[ch].echo);
} else {
chWrite(ch,7,0);
} }
} }
void DivPlatformKurumitsu8L::tick(bool sysTick) { void DivPlatformKurumitsu8L::tick(bool sysTick) {
for (int i=0; i<8; i++) { for (int i=0; i<8; i++) {
bool panChanged=false; bool panChanged=false;
bool echoChanged=false;
chan[i].std.next(); chan[i].std.next();
if (chan[i].std.vol.had) { if (chan[i].std.vol.had) {
chan[i].outVol=chan[i].std.vol.val; chan[i].outVol=chan[i].std.vol.val*(chan[i].isAmiga?4:1);
if (chan[i].outVol>255) chan[i].outVol=255;
if (!isMuted[i] && chan[i].active) { if (!isMuted[i] && chan[i].active) {
chWrite(i,0,~chan[i].outVol&255); chWrite(i,0,~chan[i].outVol&255);
} }
@ -143,6 +174,10 @@ void DivPlatformKurumitsu8L::tick(bool sysTick) {
} }
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
if (chan[i].std.duty.had) {
chan[i].echo=chan[i].std.duty.val;
echoChanged=true;
}
if (chan[i].std.wave.had) { if (chan[i].std.wave.had) {
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) { if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) {
chan[i].wave=chan[i].std.wave.val; chan[i].wave=chan[i].std.wave.val;
@ -168,21 +203,38 @@ void DivPlatformKurumitsu8L::tick(bool sysTick) {
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) { if (chan[i].std.phaseReset.val==1 && chan[i].active) {
chWrite(i,8,0); if (i==7) {
chWrite(i,9,0); chan[i].audPos=0;
chWrite(i,10,0); chan[i].setPos=true;
chWrite(i,11,0); } else {
chWrite(i,8,0);
chWrite(i,9,0);
chWrite(i,10,0);
chWrite(i,11,0);
}
} }
} }
if (chan[i].std.ex1.had) { if (i<7 && chan[i].std.ex1.had) {
unsigned char val=chRead(i,3)&0xf0; unsigned char val=chRead(i,3)&0xf0;
chWrite(i,3,val|(~chan[i].std.ex1.val&0xf)); chWrite(i,3,val|(~chan[i].std.ex1.val&0xf));
} }
if (chan[i].std.ex2.had) { if (i<7 && chan[i].std.ex2.had) {
unsigned char val=chRead(i,3)&0x0f; unsigned char val=chRead(i,3)&0x0f;
chWrite(i,3,val|((~chan[i].std.ex2.val&0xf)<<4)); chWrite(i,3,val|((~chan[i].std.ex2.val&0xf)<<4));
} }
if (chan[i].std.ex3.had) {
chan[i].invertL=chan[i].std.ex3.val&2;
chan[i].invertR=chan[i].std.ex3.val&1;
panChanged=true;
}
if (chan[i].setPos) {
// force keyon
chan[i].keyOn=true;
chan[i].setPos=false;
} else {
chan[i].audPos=0;
}
if (chan[i].active) { if (chan[i].active) {
if (chan[i].ws.tick() || (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1)) { if (chan[i].ws.tick() || (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1)) {
updateWave(i); updateWave(i);
@ -190,12 +242,16 @@ void DivPlatformKurumitsu8L::tick(bool sysTick) {
} }
if (chan[i].freqChanged) { if (chan[i].freqChanged) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE);
if (i==7) {
DivSample* s=parent->getSample(chan[i].sample);
double off=(s->centerRate>=1)?((double)s->centerRate/parent->getCenterRate()):1.0;
chan[i].freq*=off/16.;
}
if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>8388608) chan[i].freq=8388608; if (chan[i].freq>8388608) chan[i].freq=8388608;
chWrite(i,4,chan[i].freq&0xff); chWrite(i,4,chan[i].freq&0xff);
chWrite(i,5,(chan[i].freq>>8)&0xff); chWrite(i,5,(chan[i].freq>>8)&0xff);
chWrite(i,6,(chan[i].freq>>16)&0xff); chWrite(i,6,(chan[i].freq>>16)&0xff);
chWrite(i,7,(chan[i].freq>>24)&0xff);
chan[i].freqChanged=false; chan[i].freqChanged=false;
} }
if (chan[i].keyOn || chan[i].keyOff) { if (chan[i].keyOn || chan[i].keyOff) {
@ -204,24 +260,65 @@ void DivPlatformKurumitsu8L::tick(bool sysTick) {
} }
if (!isMuted[i] && chan[i].active) { if (!isMuted[i] && chan[i].active) {
chWrite(i,0,~chan[i].outVol&255); chWrite(i,0,~chan[i].outVol&255);
panChanged=true;
} else { } else {
chWrite(i,0,255); chWrite(i,0,255);
chWrite(i,1,0);
chWrite(i,2,0);
panChanged=false;
} }
if (chan[i].keyOn) chan[i].keyOn=false; panChanged=true;
echoChanged=true;
if (chan[i].keyOn) {
chan[i].keyOn=false;
if (i==7) {
DivSample* s=parent->getSample(chan[i].sample);
size_t maxPos=getSampleMemCapacity();
unsigned int start, length, loop;
start=sampleOff[chan[i].sample];
if (s->isLoopable()) {
length=s->loopEnd;
loop=s->loopEnd-s->loopStart;
} else {
length=s->samples+16;
loop=16;
}
if (length>65535) length=65535;
if (chan[i].audPos>0) {
start=start+MIN(chan[i].audPos,length);
}
chWrite(i,8,0);
chWrite(i,9,0);
chWrite(i,10,start&0xff);
chWrite(i,11,(start>>8)&0xff);
chWrite(i,3,start>>16);
chWrite(i,12,(start+length)&0xff);
chWrite(i,13,((start+length)>>8)&0xff);
chWrite(i,14,loop&0xff);
chWrite(i,15,(loop>>8)&0xff);
}
}
if (chan[i].keyOff) chan[i].keyOff=false; if (chan[i].keyOff) chan[i].keyOff=false;
} }
if (panChanged) writePan(i); if (panChanged) writePan(i);
if (echoChanged) writeEcho(i);
} }
} }
int DivPlatformKurumitsu8L::dispatch(DivCommand c) { int DivPlatformKurumitsu8L::dispatch(DivCommand c) {
switch (c.cmd) { switch (c.cmd) {
case DIV_CMD_NOTE_ON: { case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_PCE); DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
chan[c.chan].isAmiga=ins->type==DIV_INS_AMIGA;
if (c.chan==7 && c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
} else {
if (chan[c.chan].wave<0) {
chan[c.chan].wave=0;
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
}
chan[c.chan].ws.init(ins,256,255,chan[c.chan].insChanged);
}
if (c.chan!=7 || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
chan[c.chan].sample=-1;
}
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
@ -236,15 +333,11 @@ int DivPlatformKurumitsu8L::dispatch(DivCommand c) {
chWrite(c.chan,0,~chan[c.chan].outVol&255); chWrite(c.chan,0,~chan[c.chan].outVol&255);
} }
} }
if (chan[c.chan].wave<0) {
chan[c.chan].wave=0;
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
}
chan[c.chan].ws.init(ins,256,255,chan[c.chan].insChanged);
chan[c.chan].insChanged=false; chan[c.chan].insChanged=false;
break; break;
} }
case DIV_CMD_NOTE_OFF: case DIV_CMD_NOTE_OFF:
chan[c.chan].sample=-1;
chan[c.chan].active=false; chan[c.chan].active=false;
chan[c.chan].keyOff=true; chan[c.chan].keyOff=true;
chan[c.chan].macroInit(NULL); chan[c.chan].macroInit(NULL);
@ -268,6 +361,11 @@ int DivPlatformKurumitsu8L::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLUME: case DIV_CMD_GET_VOLUME:
return chan[c.chan].vol; return chan[c.chan].vol;
break; break;
case DIV_CMD_SNES_INVERT:
chan[c.chan].invertL=(c.value>>4);
chan[c.chan].invertR=c.value&15;
writePan(c.chan);
break;
case DIV_CMD_PITCH: case DIV_CMD_PITCH:
chan[c.chan].pitch=c.value; chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
@ -318,18 +416,37 @@ int DivPlatformKurumitsu8L::dispatch(DivCommand c) {
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note); if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_FREQUENCY(chan[c.chan].note);
chan[c.chan].inPorta=c.value; chan[c.chan].inPorta=c.value;
break; break;
case DIV_CMD_SAMPLE_POS:
chan[c.chan].audPos=c.value;
chan[c.chan].setPos=true;
break;
case DIV_CMD_FM_FB: { case DIV_CMD_FM_FB: {
if (c.chan>=7) break;
unsigned char val=chRead(c.chan,3)&0xf0; unsigned char val=chRead(c.chan,3)&0xf0;
chWrite(c.chan,3,val|(~c.value&0xf)); chWrite(c.chan,3,val|(~c.value&0xf));
break; break;
} }
case DIV_CMD_FM_PM_DEPTH: { case DIV_CMD_FM_PM_DEPTH: {
if (c.chan>=7) break;
unsigned char val=chRead(c.chan,3)&0x0f; unsigned char val=chRead(c.chan,3)&0x0f;
chWrite(c.chan,3,val|((~c.value&0xf)<<4)); chWrite(c.chan,3,val|((~c.value&0xf)<<4));
break; break;
} }
case DIV_CMD_SNES_ECHO:
chan[c.chan].echo=c.value;
writeEcho(c.chan);
break;
case DIV_CMD_SNES_ECHO_FEEDBACK:
chWrite(8,0,c.value);
break;
case DIV_CMD_SNES_ECHO_VOL_LEFT:
chWrite(8,1,c.value);
break;
case DIV_CMD_SNES_ECHO_VOL_RIGHT:
chWrite(8,2,c.value);
break;
case DIV_CMD_GET_VOLMAX: case DIV_CMD_GET_VOLMAX:
return 255; return 127;
break; break;
case DIV_CMD_MACRO_OFF: case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true); chan[c.chan].std.mask(c.value,true);
@ -351,15 +468,15 @@ void DivPlatformKurumitsu8L::muteChannel(int ch, bool mute) {
if (!mute && chan[ch].active) { if (!mute && chan[ch].active) {
chWrite(ch,0,~chan[ch].outVol&255); chWrite(ch,0,~chan[ch].outVol&255);
writePan(ch); writePan(ch);
writeEcho(ch);
} else { } else {
chWrite(ch,0,255); chWrite(ch,0,255);
chWrite(ch,1,0);
chWrite(ch,2,0);
} }
} }
void DivPlatformKurumitsu8L::forceIns() { void DivPlatformKurumitsu8L::forceIns() {
for (int i=0; i<8; i++) { for (int i=0; i<8; i++) {
chan[i].sample=-1;
chan[i].insChanged=true; chan[i].insChanged=true;
chan[i].freqChanged=true; chan[i].freqChanged=true;
updateWave(i); updateWave(i);
@ -379,17 +496,27 @@ unsigned short DivPlatformKurumitsu8L::getPan(int ch) {
} }
void DivPlatformKurumitsu8L::getPaired(int ch, std::vector<DivChannelPair>& ret) { void DivPlatformKurumitsu8L::getPaired(int ch, std::vector<DivChannelPair>& ret) {
if (ch==0 || ch==7) return;
unsigned char mod=~(chRead(ch,3)>>4)&0xf; unsigned char mod=~(chRead(ch,3)>>4)&0xf;
if (mod>0) { if (mod>0) {
sprintf(modLabel[ch],"%d",mod); sprintf(modLabel[ch],"%d",mod);
if (ch==0){ ret.push_back(DivChannelPair(modLabel[ch],ch-1));
ret.push_back(DivChannelPair(modLabel[ch],7));
} else {
ret.push_back(DivChannelPair(modLabel[ch],ch-1));
}
} }
} }
DivSamplePos DivPlatformKurumitsu8L::getSamplePos(int ch) {
if (ch!=7 || chan[ch].sample<0 ||
chan[ch].sample>=parent->song.sampleLen || !chan[ch].active
) {
return DivSamplePos();
}
return DivSamplePos(
chan[ch].sample,
(((int)regPool[0x7a]|((int)regPool[0x7b]<<8)|((int)regPool[0x73]<<16)))-sampleOff[chan[ch].sample],
(int64_t)chan[ch].freq*rate/0x10000
);
}
DivDispatchOscBuffer* DivPlatformKurumitsu8L::getOscBuffer(int ch) { DivDispatchOscBuffer* DivPlatformKurumitsu8L::getOscBuffer(int ch) {
return oscBuf[ch]; return oscBuf[ch];
} }
@ -399,23 +526,94 @@ unsigned char* DivPlatformKurumitsu8L::getRegisterPool() {
} }
int DivPlatformKurumitsu8L::getRegisterPoolSize() { int DivPlatformKurumitsu8L::getRegisterPoolSize() {
return 16*8; return 16*8+3;
}
const void* DivPlatformKurumitsu8L::getSampleMem(int index) {
return index == 0 ? sampleMem : NULL;
}
size_t DivPlatformKurumitsu8L::getSampleMemCapacity(int index) {
return index == 0 ? 16777216 : 0;
}
size_t DivPlatformKurumitsu8L::getSampleMemUsage(int index) {
return index == 0 ? sampleMemLen : 0;
}
bool DivPlatformKurumitsu8L::isSampleLoaded(int index, int sample) {
if (index!=0) return false;
if (sample<0 || sample>255) return false;
return sampleLoaded[sample];
}
const DivMemoryComposition* DivPlatformKurumitsu8L::getMemCompo(int index) {
switch (index) {
case 0: return &romMemCompo;
case 1: return &wtMemCompo;
case 2: return &echoMemCompo;
}
return NULL;
}
void DivPlatformKurumitsu8L::renderSamples(int sysID) {
size_t maxPos=getSampleMemCapacity();
memset(sampleMem,0,maxPos);
romMemCompo.entries.clear();
romMemCompo.capacity=maxPos;
size_t memPos=0;
for (int i=0; i<parent->song.sampleLen; i++) {
DivSample* s=parent->song.sample[i];
if (!s->renderOn[0][sysID]) {
sampleOff[i]=0;
continue;
}
int length=s->isLoopable()?s->loopEnd:s->samples;
int actualLength=length;
// if it's one-shot, add 16 silent samples for looping area
// this should be enough for most cases even though the
// frequency register can make position jump by up to 256 samples
int oneShotPos=length;
if (!s->isLoopable()) actualLength+=16;
if (actualLength>65535) actualLength=65535;
if (!s->isLoopable()) oneShotPos=actualLength-16;
if (0x10000-(memPos&0xffff)<actualLength) {
memPos=(memPos+0x10000)&~0xffff;
if (memPos==maxPos) {
logW("out of Kurumitsu-8L PCM memory for sample %d!",i);
break;
}
}
sampleOff[i]=memPos;
memcpy(&sampleMem[memPos],s->data8,oneShotPos);
memPos+=actualLength;
romMemCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_SAMPLE,"PCM",i,sampleOff[i],memPos));
sampleLoaded[i]=true;
}
sampleMemLen=memPos;
romMemCompo.used=sampleMemLen;
} }
void DivPlatformKurumitsu8L::reset() { void DivPlatformKurumitsu8L::reset() {
memset(regPool,0,sizeof(regPool)); memset(regPool,0,sizeof(regPool));
memset(wtMem,0xff,sizeof(wtMem)); memset(wtMem,0xff,sizeof(wtMem));
memset(echoMem,0,sizeof(echoMem));
echoPtr=0;
if (dumpWrites) {
addWrite(0xffffffff,0);
}
for (int i=0; i<8; i++) { for (int i=0; i<8; i++) {
chan[i]=DivPlatformKurumitsu8L::Channel(); chan[i]=DivPlatformKurumitsu8L::Channel();
chan[i].std.setEngine(parent); chan[i].std.setEngine(parent);
chan[i].ws.setEngine(parent); chan[i].ws.setEngine(parent);
chan[i].ws.init(NULL,256,255,false); chan[i].ws.init(NULL,256,255,false);
chWrite(i,3,0xff); // set mod to min chWrite(i,0,0xff); // set env to min
} if (i<7) chWrite(i,3,0xff); // set mod to min
modData=0;
if (dumpWrites) {
addWrite(0xffffffff,0);
} }
chWrite(8,0,initEchoFeedback);
chWrite(8,1,initEchoVolL);
chWrite(8,2,initEchoVolR);
} }
int DivPlatformKurumitsu8L::getOutputCount() { int DivPlatformKurumitsu8L::getOutputCount() {
@ -449,20 +647,25 @@ int DivPlatformKurumitsu8L::mapVelocity(int ch, float vel) {
} }
void DivPlatformKurumitsu8L::setFlags(const DivConfig& flags) { void DivPlatformKurumitsu8L::setFlags(const DivConfig& flags) {
chipClock=6144000; chipClock=3072000;
CHECK_CUSTOM_CLOCK; CHECK_CUSTOM_CLOCK;
rate=chipClock/64; rate=chipClock/64;
for (int i=0; i<8; i++) { for (int i=0; i<8; i++) {
oscBuf[i]->setRate(rate); oscBuf[i]->setRate(rate);
} }
initEchoVolL=flags.getInt("echoVolL",0);
initEchoVolR=flags.getInt("echoVolR",0);
initEchoFeedback=flags.getInt("echoFeedback",0);
} }
void DivPlatformKurumitsu8L::poke(unsigned int addr, unsigned short val) { void DivPlatformKurumitsu8L::poke(unsigned int addr, unsigned short val) {
chWrite((addr>>4)&7,addr&0xf,val); if (addr < getRegisterPoolSize()) chWrite(addr>>4,addr&0xf,val);
} }
void DivPlatformKurumitsu8L::poke(std::vector<DivRegWrite>& wlist) { void DivPlatformKurumitsu8L::poke(std::vector<DivRegWrite>& wlist) {
for (DivRegWrite& i: wlist) chWrite((i.addr>>4)&7,i.addr&0xf,i.val); for (DivRegWrite& i: wlist) {
if (i.addr < getRegisterPoolSize()) chWrite(i.addr>>4,i.addr&0xf,i.val);
}
} }
int DivPlatformKurumitsu8L::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { int DivPlatformKurumitsu8L::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
@ -474,6 +677,28 @@ int DivPlatformKurumitsu8L::init(DivEngine* p, int channels, int sugRate, const
isMuted[i]=false; isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer; oscBuf[i]=new DivDispatchOscBuffer;
} }
sampleMem=new signed char[getSampleMemCapacity()];
sampleMemLen=0;
romMemCompo=DivMemoryComposition();
romMemCompo.name="Sample ROM";
wtMemCompo=DivMemoryComposition();
wtMemCompo.name="Wavetable RAM";
wtMemCompo.capacity=256*7;
wtMemCompo.memory=(unsigned char*)wtMem;
wtMemCompo.waveformView=DIV_MEMORY_WAVE_8BIT_SIGNED;
echoMemCompo=DivMemoryComposition();
echoMemCompo.name="Echo Buffer";
echoMemCompo.capacity=4096;
echoMemCompo.memory=(unsigned char*)echoMem;
echoMemCompo.waveformView=DIV_MEMORY_WAVE_8BIT_SIGNED;
wtMemCompo.used=256*7;
echoMemCompo.used=4096;
wtMemCompo.entries.clear();
echoMemCompo.entries.clear();
for (int i=0; i<7; i++) {
wtMemCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_WAVE_RAM, fmt::sprintf("Channel %d",i+1),-1,i*256,i*256+256));
}
echoMemCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_ECHO, "Echo Buffer",-1,0,4096));
setFlags(flags); setFlags(flags);
reset(); reset();
return 8; return 8;

View file

@ -26,26 +26,48 @@
class DivPlatformKurumitsu8L: public DivDispatch { class DivPlatformKurumitsu8L: public DivDispatch {
struct Channel: public SharedChannel<int> { struct Channel: public SharedChannel<int> {
unsigned char panL; unsigned char panL, panR;
unsigned char panR; unsigned char echo;
signed short wave; unsigned int audPos;
int sample, wave;
bool invertL, invertR;
bool setPos, isAmiga;
DivWaveSynth ws; DivWaveSynth ws;
Channel(): Channel():
SharedChannel<int>(255), SharedChannel<int>(127),
panL(255), panL(127),
panR(255), panR(127),
wave(-1) {} echo(0),
audPos(0),
sample(-1),
wave(-1),
invertL(false),
invertR(false),
setPos(false),
isAmiga(false) {}
}; };
Channel chan[8]; Channel chan[8];
DivDispatchOscBuffer* oscBuf[8]; DivDispatchOscBuffer* oscBuf[8];
bool isMuted[8]; bool isMuted[8];
unsigned int sampleOff[256];
bool sampleLoaded[256];
unsigned char regPool[8*16]; unsigned char regPool[8*16+3];
unsigned char wtMem[8][256]; signed char wtMem[7][256];
signed char echoMem[4096];
int echoPtr;
char modLabel[8][4]; char modLabel[8][4];
int modData; signed char initEchoVolL;
signed char initEchoVolR;
signed char initEchoFeedback;
signed char* sampleMem;
size_t sampleMemLen;
DivMemoryComposition romMemCompo;
DivMemoryComposition wtMemCompo;
DivMemoryComposition echoMemCompo;
void updateWave(int ch); void updateWave(int ch);
void writePan(int ch); void writePan(int ch);
void writeEcho(int ch);
friend void putDispatchChip(void*,int); friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int); friend void putDispatchChan(void*,int,int);
public: public:
@ -55,6 +77,7 @@ class DivPlatformKurumitsu8L: public DivDispatch {
DivMacroInt* getChanMacroInt(int ch); DivMacroInt* getChanMacroInt(int ch);
unsigned short getPan(int chan); unsigned short getPan(int chan);
void getPaired(int ch, std::vector<DivChannelPair>& ret); void getPaired(int ch, std::vector<DivChannelPair>& ret);
DivSamplePos getSamplePos(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan); DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool(); unsigned char* getRegisterPool();
int getRegisterPoolSize(); int getRegisterPoolSize();
@ -71,6 +94,12 @@ class DivPlatformKurumitsu8L: public DivDispatch {
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 char** getRegisterSheet(); const char** getRegisterSheet();
const void* getSampleMem(int index = 0);
size_t getSampleMemCapacity(int index = 0);
size_t getSampleMemUsage(int index = 0);
bool isSampleLoaded(int index, int sample);
const DivMemoryComposition* getMemCompo(int index);
void renderSamples(int chipID);
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();
~DivPlatformKurumitsu8L(); ~DivPlatformKurumitsu8L();

View file

@ -2343,17 +2343,22 @@ void DivEngine::registerSystems() {
); );
sysDefs[DIV_SYSTEM_KURUMITSU_8L]=new DivSysDef( sysDefs[DIV_SYSTEM_KURUMITSU_8L]=new DivSysDef(
_("Kurumitsu-8L"), NULL, 0xfe, 0, 8, false, true, 0, false, 1U<<DIV_SAMPLE_DEPTH_16BIT, 256, 256, _("Kurumitsu-8L"), NULL, 0xfe, 0, 8, false, true, 0, false, 1U<<DIV_SAMPLE_DEPTH_8BIT, 256, 256,
_(":grins:"), _(":grins:"),
{_("Channel 1"), _("Channel 2"), _("Channel 3"), _("Channel 4"), _("Channel 5"), _("Channel 6"), _("Channel 7"), _("Channel 8")}, {_("Channel 1"), _("Channel 2"), _("Channel 3"), _("Channel 4"), _("Channel 5"), _("Channel 6"), _("Channel 7"), _("Sample")},
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"},
{DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_PCM},
{DIV_INS_KURUMITSU_8L, DIV_INS_KURUMITSU_8L, DIV_INS_KURUMITSU_8L, DIV_INS_KURUMITSU_8L, DIV_INS_KURUMITSU_8L, DIV_INS_KURUMITSU_8L, DIV_INS_KURUMITSU_8L, DIV_INS_KURUMITSU_8L}, {DIV_INS_KURUMITSU_8L, DIV_INS_KURUMITSU_8L, DIV_INS_KURUMITSU_8L, DIV_INS_KURUMITSU_8L, DIV_INS_KURUMITSU_8L, DIV_INS_KURUMITSU_8L, DIV_INS_KURUMITSU_8L, DIV_INS_KURUMITSU_8L},
{DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, {},
{ {
{0x10, {DIV_CMD_WAVE, _("10xx: Set waveform")}}, {0x10, {DIV_CMD_WAVE, _("10xx: Set waveform")}},
{0x11, {DIV_CMD_FM_FB, _("11xx: Set feedback (0 to F)")}}, {0x11, {DIV_CMD_FM_FB, _("11xx: Set feedback (0 to F)")}},
{0x12, {DIV_CMD_FM_PM_DEPTH, _("12xx: Set modulation (0 to F)")}} {0x12, {DIV_CMD_FM_PM_DEPTH, _("12xx: Set modulation (0 to F)")}},
{0x13, {DIV_CMD_SNES_ECHO, _("13xx: Set channel echo input volume")}},
{0x14, {DIV_CMD_SNES_INVERT, _("14xy: Toggle invert (x: left; y: right)")}},
{0x1a, {DIV_CMD_SNES_ECHO_VOL_LEFT, _("1Axx: Set left global echo output volume")}},
{0x1b, {DIV_CMD_SNES_ECHO_VOL_RIGHT, _("1Bxx: Set right global echo output volume")}},
{0x1c, {DIV_CMD_SNES_ECHO_FEEDBACK, _("1Cxx: Set global echo feedback")}},
} }
); );

View file

@ -8689,13 +8689,15 @@ void FurnaceGUI::drawInsEdit() {
case DIV_INS_KURUMITSU_8L: case DIV_INS_KURUMITSU_8L:
macroList.push_back(FurnaceGUIMacroDesc(_("Envelope"),&ins->std.volMacro,0,255,160,uiColors[GUI_COLOR_MACRO_VOLUME])); macroList.push_back(FurnaceGUIMacroDesc(_("Envelope"),&ins->std.volMacro,0,255,160,uiColors[GUI_COLOR_MACRO_VOLUME]));
macroList.push_back(FurnaceGUIMacroDesc(_("Arpeggio"),&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.arpMacro.val)); macroList.push_back(FurnaceGUIMacroDesc(_("Arpeggio"),&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,macroHoverNote,false,NULL,true,ins->std.arpMacro.val));
macroList.push_back(FurnaceGUIMacroDesc(_("Echo Level"),&ins->std.dutyMacro,-127,127,160,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc(_("Waveform"),&ins->std.waveMacro,0,waveCount,160,uiColors[GUI_COLOR_MACRO_WAVE],false,NULL,NULL,false,NULL)); macroList.push_back(FurnaceGUIMacroDesc(_("Waveform"),&ins->std.waveMacro,0,waveCount,160,uiColors[GUI_COLOR_MACRO_WAVE],false,NULL,NULL,false,NULL));
macroList.push_back(FurnaceGUIMacroDesc(_("Panning (left)"),&ins->std.panLMacro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL)); macroList.push_back(FurnaceGUIMacroDesc(_("Panning (left)"),&ins->std.panLMacro,0,127,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL));
macroList.push_back(FurnaceGUIMacroDesc(_("Panning (right)"),&ins->std.panRMacro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc(_("Panning (right)"),&ins->std.panRMacro,0,127,160,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode)); macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode));
macroList.push_back(FurnaceGUIMacroDesc(_("Phase Reset"),&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); macroList.push_back(FurnaceGUIMacroDesc(_("Phase Reset"),&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(_("Feedback"),&ins->std.ex1Macro,0,15,160,uiColors[GUI_COLOR_MACRO_VOLUME],false,NULL,NULL,false,NULL)); macroList.push_back(FurnaceGUIMacroDesc(_("Feedback"),&ins->std.ex1Macro,0,15,160,uiColors[GUI_COLOR_MACRO_VOLUME],false,NULL,NULL,false,NULL));
macroList.push_back(FurnaceGUIMacroDesc(_("Modulation"),&ins->std.ex2Macro,0,15,160,uiColors[GUI_COLOR_MACRO_VOLUME],false,NULL,NULL,false,NULL)); macroList.push_back(FurnaceGUIMacroDesc(_("Modulation"),&ins->std.ex2Macro,0,15,160,uiColors[GUI_COLOR_MACRO_VOLUME],false,NULL,NULL,false,NULL));
macroList.push_back(FurnaceGUIMacroDesc(_("Special"),&ins->std.ex3Macro,0,2,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,minModModeBits));
break; break;
case DIV_INS_MAX: case DIV_INS_MAX:
case DIV_INS_NULL: case DIV_INS_NULL:
@ -8744,7 +8746,8 @@ void FurnaceGUI::drawInsEdit() {
ins->type==DIV_INS_PCE || ins->type==DIV_INS_PCE ||
ins->type==DIV_INS_X1_010 || ins->type==DIV_INS_X1_010 ||
ins->type==DIV_INS_SWAN || ins->type==DIV_INS_SWAN ||
ins->type==DIV_INS_VRC6) { ins->type==DIV_INS_VRC6 ||
ins->type==DIV_INS_KURUMITSU_8L) {
insTabSample(ins); insTabSample(ins);
} }
if (ins->type>=DIV_INS_MAX) { if (ins->type>=DIV_INS_MAX) {

View file

@ -432,6 +432,17 @@ void FurnaceGUI::drawSampleEdit() {
SAMPLE_WARN(warnLength,_("Supervision: maximum sample length is 8192")); SAMPLE_WARN(warnLength,_("Supervision: maximum sample length is 8192"));
} }
break; break;
case DIV_SYSTEM_KURUMITSU_8L:
if (sample->isLoopable()) {
if (sample->samples>65535) {
SAMPLE_WARN(warnLength,"Kurumitsu-8L: maximum sample length is 65535");
}
} else {
if (sample->samples>65519) {
SAMPLE_WARN(warnLength,"Kurumitsu-8L: maximum sample length is 65519");
}
}
break;
default: default:
break; break;
} }

View file

@ -2714,6 +2714,38 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
} }
break; break;
} }
case DIV_SYSTEM_KURUMITSU_8L: {
int echoVolL=flags.getInt("echoVolL",0);
int echoVolR=flags.getInt("echoVolR",0);
int echoFeedback=flags.getInt("echoFeedback",0);
ImGui::Text(_("Initial echo state:"));
if (CWSliderInt(_("Feedback##EchoFeedback"),&echoFeedback,-128,127)) {
if (echoFeedback<-128) echoFeedback=-128;
if (echoFeedback>127) echoFeedback=127;
altered=true;
} rightClickable
if (CWSliderInt(_("Left Volume##EchoVolL"),&echoVolL,-128,127)) {
if (echoVolL<-128) echoVolL=-128;
if (echoVolL>127) echoVolL=127;
altered=true;
} rightClickable
if (CWSliderInt(_("Right Volume##EchoVolL"),&echoVolR,-128,127)) {
if (echoVolR<-128) echoVolR=-128;
if (echoVolR>127) echoVolR=127;
altered=true;
} rightClickable
if (altered) {
e->lockSave([&]() {
flags.set("echoVolL",echoVolL);
flags.set("echoVolR",echoVolR);
flags.set("echoFeedback",echoFeedback);
});
}
break;
}
case DIV_SYSTEM_BUBSYS_WSG: case DIV_SYSTEM_BUBSYS_WSG:
case DIV_SYSTEM_PET: case DIV_SYSTEM_PET:
case DIV_SYSTEM_GA20: case DIV_SYSTEM_GA20:
@ -2722,7 +2754,6 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
case DIV_SYSTEM_BIFURCATOR: case DIV_SYSTEM_BIFURCATOR:
case DIV_SYSTEM_POWERNOISE: case DIV_SYSTEM_POWERNOISE:
case DIV_SYSTEM_UPD1771C: case DIV_SYSTEM_UPD1771C:
case DIV_SYSTEM_KURUMITSU_8L:
break; break;
case DIV_SYSTEM_YMU759: case DIV_SYSTEM_YMU759:
case DIV_SYSTEM_ESFM: case DIV_SYSTEM_ESFM: