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_IPOD,
DIV_ROM_GRUB,
DIV_ROM_KMM,
DIV_ROM_MAX
};

View file

@ -23,6 +23,7 @@
#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 chDWrite(c,a,v) regPool[(c)*16+(a)]=v;
#define CHIP_FREQBASE 67108864
@ -32,9 +33,14 @@ const char* regCheatSheetKurumitsu8L[]={
"CHxPanR", "02+x*10",
"CHxMod", "03+x*10",
"CHxFreq", "04+x*10",
"CHxEcho", "07+x*10",
"CHxAcc", "08+x*10",
"CHxLoopS", "0C+x*10",
"CHxLoopE", "0E+x*10",
"CH7Bank", "73",
"CH7LoopE", "7C",
"CH7LoopL", "7E",
"EchoFB", "80",
"EchoVolL", "81",
"EchoVolR", "82",
NULL
};
@ -44,55 +50,78 @@ const char** DivPlatformKurumitsu8L::getRegisterSheet() {
void DivPlatformKurumitsu8L::acquire(short** buf, size_t len) {
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++) {
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++) {
int outAL=0;
int outAR=0;
int outE=((int)echoMem[echoPtr]*echoFB)<<11;
int outAL=((int)echoMem[echoPtr]*echoL)<<11;
int outAR=((int)echoMem[echoPtr]*echoR)<<11;
int modData=0;
for (int i=0; i<8; i++) {
int outL=0;
int outR=0;
unsigned char vol=chRead(i,0);
unsigned char panL=chRead(i,1);
unsigned char panR=chRead(i,2);
unsigned char fb=chRead(i,3)&0xf;
unsigned char mod=(chRead(i,3)>>4)&0xf;
signed char panL=chRead(i,1);
signed char panR=chRead(i,2);
signed char echo=chRead(i,7);
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);
int fbData=chRead(i,13)|((unsigned)chRead(i,14)<<8);
if (fbData>=0x8000) fbData-=0x10000;
int fbData=0;
unsigned phase_modded=phase;
phase_modded+=(fbData<<8)>>fb;
phase_modded+=(modData<<8)>>mod;
phase+=freq;
int data=0;
if (i<7) {
unsigned char fb=chRead(i,3)&0xf;
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];
int out=(data>>1)+vol;
out=volTab[out&0x1f]>>(out>>5);
if (data&1) out=-out;
fbData=out;
modData=out;
outAL+=out*panL;
outAR+=out*panR;
oscBuf[i]->putSample(h,(out*((int)panL+panR))>>9);
chWrite(i,8,phase&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);
chDWrite(i,8,phase&0xff);
chDWrite(i,9,(phase>>8)&0xff);
chDWrite(i,10,(phase>>16)&0xff);
chDWrite(i,11,(phase>>24)&0xff);
if (i<7) {
fbData=out;
modData=out;
chDWrite(i,12,fbData&0xff);
chDWrite(i,13,(fbData>>8)&0xff);
chDWrite(i,14,(fbData>>16)&0xff);
chDWrite(i,15,(fbData>>24)&0xff);
}
}
buf[0][h]=outAL>>11;
buf[1][h]=outAR>>11;
buf[0][h]=outAL>>10;
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++) {
@ -101,36 +130,38 @@ void DivPlatformKurumitsu8L::acquire(short** buf, size_t len) {
}
void DivPlatformKurumitsu8L::updateWave(int ch) {
if (ch>=7) return;
for (int i=0; i<256; i++) {
int data=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;
wtMem[ch][i]=chan[ch].ws.output[i]^128;
}
}
void DivPlatformKurumitsu8L::writePan(int ch) {
if (!isMuted[ch] && chan[ch].active) {
chWrite(ch,1,VOL_SCALE_LINEAR(chan[ch].panL,chan[ch].vol,255));
chWrite(ch,2,VOL_SCALE_LINEAR(chan[ch].panR,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,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) {
for (int i=0; i<8; i++) {
bool panChanged=false;
bool echoChanged=false;
chan[i].std.next();
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) {
chWrite(i,0,~chan[i].outVol&255);
}
@ -143,6 +174,10 @@ void DivPlatformKurumitsu8L::tick(bool sysTick) {
}
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].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) {
chan[i].wave=chan[i].std.wave.val;
@ -168,21 +203,38 @@ void DivPlatformKurumitsu8L::tick(bool sysTick) {
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1) {
chWrite(i,8,0);
chWrite(i,9,0);
chWrite(i,10,0);
chWrite(i,11,0);
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
if (i==7) {
chan[i].audPos=0;
chan[i].setPos=true;
} 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;
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;
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].ws.tick() || (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1)) {
updateWave(i);
@ -190,12 +242,16 @@ void DivPlatformKurumitsu8L::tick(bool sysTick) {
}
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);
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>8388608) chan[i].freq=8388608;
chWrite(i,4,chan[i].freq&0xff);
chWrite(i,5,(chan[i].freq>>8)&0xff);
chWrite(i,6,(chan[i].freq>>16)&0xff);
chWrite(i,7,(chan[i].freq>>24)&0xff);
chan[i].freqChanged=false;
}
if (chan[i].keyOn || chan[i].keyOff) {
@ -204,24 +260,65 @@ void DivPlatformKurumitsu8L::tick(bool sysTick) {
}
if (!isMuted[i] && chan[i].active) {
chWrite(i,0,~chan[i].outVol&255);
panChanged=true;
} else {
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 (panChanged) writePan(i);
if (echoChanged) writeEcho(i);
}
}
int DivPlatformKurumitsu8L::dispatch(DivCommand c) {
switch (c.cmd) {
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) {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
chan[c.chan].freqChanged=true;
@ -236,15 +333,11 @@ int DivPlatformKurumitsu8L::dispatch(DivCommand c) {
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;
break;
}
case DIV_CMD_NOTE_OFF:
chan[c.chan].sample=-1;
chan[c.chan].active=false;
chan[c.chan].keyOff=true;
chan[c.chan].macroInit(NULL);
@ -268,6 +361,11 @@ int DivPlatformKurumitsu8L::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLUME:
return chan[c.chan].vol;
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:
chan[c.chan].pitch=c.value;
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);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:
chan[c.chan].audPos=c.value;
chan[c.chan].setPos=true;
break;
case DIV_CMD_FM_FB: {
if (c.chan>=7) break;
unsigned char val=chRead(c.chan,3)&0xf0;
chWrite(c.chan,3,val|(~c.value&0xf));
break;
}
case DIV_CMD_FM_PM_DEPTH: {
if (c.chan>=7) break;
unsigned char val=chRead(c.chan,3)&0x0f;
chWrite(c.chan,3,val|((~c.value&0xf)<<4));
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:
return 255;
return 127;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
@ -351,15 +468,15 @@ void DivPlatformKurumitsu8L::muteChannel(int ch, bool mute) {
if (!mute && chan[ch].active) {
chWrite(ch,0,~chan[ch].outVol&255);
writePan(ch);
writeEcho(ch);
} else {
chWrite(ch,0,255);
chWrite(ch,1,0);
chWrite(ch,2,0);
}
}
void DivPlatformKurumitsu8L::forceIns() {
for (int i=0; i<8; i++) {
chan[i].sample=-1;
chan[i].insChanged=true;
chan[i].freqChanged=true;
updateWave(i);
@ -379,17 +496,27 @@ unsigned short DivPlatformKurumitsu8L::getPan(int ch) {
}
void DivPlatformKurumitsu8L::getPaired(int ch, std::vector<DivChannelPair>& ret) {
if (ch==0 || ch==7) return;
unsigned char mod=~(chRead(ch,3)>>4)&0xf;
if (mod>0) {
sprintf(modLabel[ch],"%d",mod);
if (ch==0){
ret.push_back(DivChannelPair(modLabel[ch],7));
} else {
ret.push_back(DivChannelPair(modLabel[ch],ch-1));
}
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) {
return oscBuf[ch];
}
@ -399,23 +526,94 @@ unsigned char* DivPlatformKurumitsu8L::getRegisterPool() {
}
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() {
memset(regPool,0,sizeof(regPool));
memset(wtMem,0xff,sizeof(wtMem));
memset(echoMem,0,sizeof(echoMem));
echoPtr=0;
if (dumpWrites) {
addWrite(0xffffffff,0);
}
for (int i=0; i<8; i++) {
chan[i]=DivPlatformKurumitsu8L::Channel();
chan[i].std.setEngine(parent);
chan[i].ws.setEngine(parent);
chan[i].ws.init(NULL,256,255,false);
chWrite(i,3,0xff); // set mod to min
}
modData=0;
if (dumpWrites) {
addWrite(0xffffffff,0);
chWrite(i,0,0xff); // set env to min
if (i<7) chWrite(i,3,0xff); // set mod to min
}
chWrite(8,0,initEchoFeedback);
chWrite(8,1,initEchoVolL);
chWrite(8,2,initEchoVolR);
}
int DivPlatformKurumitsu8L::getOutputCount() {
@ -449,20 +647,25 @@ int DivPlatformKurumitsu8L::mapVelocity(int ch, float vel) {
}
void DivPlatformKurumitsu8L::setFlags(const DivConfig& flags) {
chipClock=6144000;
chipClock=3072000;
CHECK_CUSTOM_CLOCK;
rate=chipClock/64;
for (int i=0; i<8; i++) {
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) {
chWrite((addr>>4)&7,addr&0xf,val);
if (addr < getRegisterPoolSize()) chWrite(addr>>4,addr&0xf,val);
}
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) {
@ -474,6 +677,28 @@ int DivPlatformKurumitsu8L::init(DivEngine* p, int channels, int sugRate, const
isMuted[i]=false;
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);
reset();
return 8;

View file

@ -26,26 +26,48 @@
class DivPlatformKurumitsu8L: public DivDispatch {
struct Channel: public SharedChannel<int> {
unsigned char panL;
unsigned char panR;
signed short wave;
unsigned char panL, panR;
unsigned char echo;
unsigned int audPos;
int sample, wave;
bool invertL, invertR;
bool setPos, isAmiga;
DivWaveSynth ws;
Channel():
SharedChannel<int>(255),
panL(255),
panR(255),
wave(-1) {}
SharedChannel<int>(127),
panL(127),
panR(127),
echo(0),
audPos(0),
sample(-1),
wave(-1),
invertL(false),
invertR(false),
setPos(false),
isAmiga(false) {}
};
Channel chan[8];
DivDispatchOscBuffer* oscBuf[8];
bool isMuted[8];
unsigned int sampleOff[256];
bool sampleLoaded[256];
unsigned char regPool[8*16];
unsigned char wtMem[8][256];
unsigned char regPool[8*16+3];
signed char wtMem[7][256];
signed char echoMem[4096];
int echoPtr;
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 writePan(int ch);
void writeEcho(int ch);
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
public:
@ -55,6 +77,7 @@ class DivPlatformKurumitsu8L: public DivDispatch {
DivMacroInt* getChanMacroInt(int ch);
unsigned short getPan(int chan);
void getPaired(int ch, std::vector<DivChannelPair>& ret);
DivSamplePos getSamplePos(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
@ -71,6 +94,12 @@ class DivPlatformKurumitsu8L: public DivDispatch {
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
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);
void quit();
~DivPlatformKurumitsu8L();

View file

@ -2343,17 +2343,22 @@ void DivEngine::registerSystems() {
);
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:"),
{_("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"},
{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_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")}},
{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:
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(_("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(_("Panning (left)"),&ins->std.panLMacro,0,255,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 (left)"),&ins->std.panLMacro,0,127,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL));
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(_("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(_("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;
case DIV_INS_MAX:
case DIV_INS_NULL:
@ -8744,7 +8746,8 @@ void FurnaceGUI::drawInsEdit() {
ins->type==DIV_INS_PCE ||
ins->type==DIV_INS_X1_010 ||
ins->type==DIV_INS_SWAN ||
ins->type==DIV_INS_VRC6) {
ins->type==DIV_INS_VRC6 ||
ins->type==DIV_INS_KURUMITSU_8L) {
insTabSample(ins);
}
if (ins->type>=DIV_INS_MAX) {

View file

@ -432,6 +432,17 @@ void FurnaceGUI::drawSampleEdit() {
SAMPLE_WARN(warnLength,_("Supervision: maximum sample length is 8192"));
}
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:
break;
}

View file

@ -2714,6 +2714,38 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
}
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_PET:
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_POWERNOISE:
case DIV_SYSTEM_UPD1771C:
case DIV_SYSTEM_KURUMITSU_8L:
break;
case DIV_SYSTEM_YMU759:
case DIV_SYSTEM_ESFM: