2021-05-26 04:17:12 -04:00
|
|
|
#include "gb.h"
|
|
|
|
#include "../engine.h"
|
|
|
|
#include <math.h>
|
|
|
|
|
2021-05-26 18:43:14 -04:00
|
|
|
//#define rWrite(a,v) pendingWrites[a]=v;
|
|
|
|
#define rWrite(a,v) GB_apu_write(gb,a,v);
|
|
|
|
|
|
|
|
#define FREQ_BASE 7943.75f
|
|
|
|
|
2021-05-26 04:17:12 -04:00
|
|
|
void DivPlatformGB::acquire(int& l, int& r) {
|
2021-05-26 18:43:14 -04:00
|
|
|
gb->apu.apu_cycles=4;
|
2021-05-26 04:17:12 -04:00
|
|
|
GB_apu_run(gb);
|
2021-05-26 18:43:14 -04:00
|
|
|
l=(gb->apu_output.current_sample[0].left+
|
|
|
|
gb->apu_output.current_sample[1].left+
|
|
|
|
gb->apu_output.current_sample[2].left+
|
|
|
|
gb->apu_output.current_sample[3].left)<<6;
|
|
|
|
r=(gb->apu_output.current_sample[0].right+
|
|
|
|
gb->apu_output.current_sample[1].right+
|
|
|
|
gb->apu_output.current_sample[2].right+
|
|
|
|
gb->apu_output.current_sample[3].right)<<6;
|
2021-05-26 04:17:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void DivPlatformGB::tick() {
|
|
|
|
for (int i=0; i<4; i++) {
|
|
|
|
chan[i].std.next();
|
|
|
|
if (chan[i].std.hadArp) {
|
|
|
|
if (chan[i].std.arpMode) {
|
2021-05-26 18:43:14 -04:00
|
|
|
chan[i].baseFreq=round(FREQ_BASE/pow(2.0f,((float)(chan[i].std.arp)/12.0f)));
|
2021-05-26 04:17:12 -04:00
|
|
|
} else {
|
2021-05-26 18:43:14 -04:00
|
|
|
chan[i].baseFreq=round(FREQ_BASE/pow(2.0f,((float)(chan[i].note+chan[i].std.arp-12)/12.0f)));
|
2021-05-26 04:17:12 -04:00
|
|
|
}
|
|
|
|
chan[i].freqChanged=true;
|
|
|
|
}
|
|
|
|
if (chan[i].std.hadDuty) {
|
2021-05-26 18:43:14 -04:00
|
|
|
chan[i].duty=chan[i].std.duty;
|
|
|
|
rWrite(16+i*5+1,(chan[i].duty&3)<<6);
|
2021-05-26 04:17:12 -04:00
|
|
|
}
|
|
|
|
if (chan[i].freqChanged) {
|
|
|
|
chan[i].freq=(chan[i].baseFreq*(ONE_SEMITONE-chan[i].pitch))/ONE_SEMITONE;
|
|
|
|
if (chan[i].note>0x5d) chan[i].freq=0x01;
|
2021-05-26 18:43:14 -04:00
|
|
|
if (i==0 || i==1) {
|
|
|
|
if (chan[i].keyOn) {
|
|
|
|
DivInstrument* ins=parent->getIns(chan[i].ins);
|
|
|
|
rWrite(16+i*5+2,((chan[i].vol*ins->gb.envVol)&0xf0)|(ins->gb.envLen&7)|((ins->gb.envDir&1)<<3));
|
|
|
|
}
|
|
|
|
rWrite(16+i*5+3,(2048-chan[i].freq)&0xff);
|
|
|
|
rWrite(16+i*5+4,(((2048-chan[i].freq)>>8)&7)|(chan[i].keyOn?0x80:0x00));
|
|
|
|
if (chan[i].keyOn) chan[i].keyOn=false;
|
|
|
|
}
|
2021-05-26 04:17:12 -04:00
|
|
|
chan[i].freqChanged=false;
|
|
|
|
}
|
|
|
|
}
|
2021-05-26 18:43:14 -04:00
|
|
|
|
|
|
|
for (int i=0; i<64; i++) {
|
|
|
|
if (pendingWrites[i]!=oldWrites[i]) {
|
|
|
|
GB_apu_write(gb,i,pendingWrites[i]&0xff);
|
|
|
|
oldWrites[i]=pendingWrites[i];
|
2021-05-26 04:17:12 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int DivPlatformGB::dispatch(DivCommand c) {
|
|
|
|
switch (c.cmd) {
|
|
|
|
case DIV_CMD_NOTE_ON:
|
2021-05-26 18:43:14 -04:00
|
|
|
chan[c.chan].baseFreq=round(FREQ_BASE/pow(2.0f,((float)c.value/12.0f)));
|
2021-05-26 04:17:12 -04:00
|
|
|
chan[c.chan].freqChanged=true;
|
|
|
|
chan[c.chan].note=c.value;
|
|
|
|
chan[c.chan].active=true;
|
2021-05-26 18:43:14 -04:00
|
|
|
chan[c.chan].keyOn=true;
|
2021-05-26 04:17:12 -04:00
|
|
|
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
|
|
|
|
break;
|
|
|
|
case DIV_CMD_NOTE_OFF:
|
|
|
|
chan[c.chan].active=false;
|
2021-05-26 18:43:14 -04:00
|
|
|
chan[c.chan].keyOff=true;
|
2021-05-26 04:17:12 -04:00
|
|
|
chan[c.chan].std.init(NULL);
|
|
|
|
break;
|
|
|
|
case DIV_CMD_INSTRUMENT:
|
|
|
|
chan[c.chan].ins=c.value;
|
|
|
|
break;
|
|
|
|
case DIV_CMD_VOLUME:
|
|
|
|
if (chan[c.chan].vol!=c.value) {
|
|
|
|
chan[c.chan].vol=c.value;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DIV_CMD_GET_VOLUME:
|
2021-05-26 18:43:14 -04:00
|
|
|
return chan[c.chan].vol;
|
2021-05-26 04:17:12 -04:00
|
|
|
break;
|
|
|
|
case DIV_CMD_PITCH:
|
|
|
|
chan[c.chan].pitch=c.value;
|
|
|
|
chan[c.chan].freqChanged=true;
|
|
|
|
break;
|
|
|
|
case DIV_CMD_NOTE_PORTA: {
|
2021-05-26 18:43:14 -04:00
|
|
|
int destFreq=round(FREQ_BASE/pow(2.0f,((float)c.value2/12.0f)));
|
2021-05-26 04:17:12 -04:00
|
|
|
bool return2=false;
|
|
|
|
if (destFreq>chan[c.chan].baseFreq) {
|
|
|
|
chan[c.chan].baseFreq+=c.value;
|
|
|
|
if (chan[c.chan].baseFreq>=destFreq) {
|
|
|
|
chan[c.chan].baseFreq=destFreq;
|
|
|
|
return2=true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
chan[c.chan].baseFreq-=c.value;
|
|
|
|
if (chan[c.chan].baseFreq<=destFreq) {
|
|
|
|
chan[c.chan].baseFreq=destFreq;
|
|
|
|
return2=true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
chan[c.chan].freqChanged=true;
|
|
|
|
if (return2) return 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DIV_CMD_STD_NOISE_MODE:
|
|
|
|
snNoiseMode=(c.value&1)|((c.value&16)>>3);
|
|
|
|
updateSNMode=true;
|
|
|
|
break;
|
|
|
|
case DIV_CMD_LEGATO:
|
2021-05-26 18:43:14 -04:00
|
|
|
chan[c.chan].baseFreq=round(FREQ_BASE/pow(2.0f,((float)c.value/12.0f)));
|
2021-05-26 04:17:12 -04:00
|
|
|
chan[c.chan].freqChanged=true;
|
|
|
|
chan[c.chan].note=c.value;
|
|
|
|
break;
|
|
|
|
case DIV_CMD_PRE_PORTA:
|
|
|
|
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
|
|
|
|
break;
|
|
|
|
case DIV_CMD_GET_VOLMAX:
|
|
|
|
return 15;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int DivPlatformGB::init(DivEngine* p, int channels, int sugRate) {
|
|
|
|
parent=p;
|
2021-05-26 18:43:14 -04:00
|
|
|
rate=2097152;
|
2021-05-26 04:17:12 -04:00
|
|
|
gb=new GB_gameboy_t;
|
|
|
|
memset(gb,0,sizeof(GB_gameboy_t));
|
2021-05-26 18:43:14 -04:00
|
|
|
gb->model=GB_MODEL_DMG_B;
|
2021-05-26 04:17:12 -04:00
|
|
|
GB_apu_init(gb);
|
|
|
|
GB_set_sample_rate(gb,rate);
|
2021-05-26 18:43:14 -04:00
|
|
|
for (int i=0; i<64; i++) {
|
|
|
|
oldWrites[i]=-1;
|
|
|
|
pendingWrites[i]=-1;
|
|
|
|
}
|
|
|
|
// enable all channels
|
|
|
|
GB_apu_write(gb,0x26,0x80);
|
|
|
|
GB_apu_write(gb,0x25,0xff);
|
2021-05-26 04:17:12 -04:00
|
|
|
snNoiseMode=3;
|
|
|
|
updateSNMode=false;
|
|
|
|
return 4;
|
|
|
|
}
|