WIP add GBA system
This commit is contained in:
parent
892ee12d91
commit
f3705fb435
|
@ -717,6 +717,7 @@ src/engine/platform/c140.cpp
|
|||
src/engine/platform/esfm.cpp
|
||||
src/engine/platform/powernoise.cpp
|
||||
src/engine/platform/dave.cpp
|
||||
src/engine/platform/gbadma.cpp
|
||||
src/engine/platform/pcmdac.cpp
|
||||
src/engine/platform/dummy.cpp
|
||||
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
#include "platform/k053260.h"
|
||||
#include "platform/ted.h"
|
||||
#include "platform/c140.h"
|
||||
#include "platform/gbadma.h"
|
||||
#include "platform/pcmdac.h"
|
||||
#include "platform/esfm.h"
|
||||
#include "platform/powernoise.h"
|
||||
|
@ -644,6 +645,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
dispatch=new DivPlatformC140;
|
||||
((DivPlatformC140*)dispatch)->set219(true);
|
||||
break;
|
||||
case DIV_SYSTEM_GBA_DMA:
|
||||
dispatch=new DivPlatformGBADMA;
|
||||
break;
|
||||
case DIV_SYSTEM_PCM_DAC:
|
||||
dispatch=new DivPlatformPCMDAC;
|
||||
break;
|
||||
|
|
|
@ -982,7 +982,8 @@ void DivEngine::delUnusedSamples() {
|
|||
i->type==DIV_INS_GA20 ||
|
||||
i->type==DIV_INS_K053260 ||
|
||||
i->type==DIV_INS_C140 ||
|
||||
i->type==DIV_INS_C219) {
|
||||
i->type==DIV_INS_C219 ||
|
||||
i->type==DIV_INS_GBA_DMA) {
|
||||
if (i->amiga.initSample>=0 && i->amiga.initSample<song.sampleLen) {
|
||||
isUsed[i->amiga.initSample]=true;
|
||||
}
|
||||
|
|
|
@ -1064,6 +1064,10 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
|
|||
break;
|
||||
case DIV_INS_DAVE:
|
||||
break;
|
||||
case DIV_INS_GBA_DMA:
|
||||
featureSM=true;
|
||||
featureSL=true;
|
||||
break;
|
||||
case DIV_INS_MAX:
|
||||
break;
|
||||
case DIV_INS_NULL:
|
||||
|
|
|
@ -89,6 +89,7 @@ enum DivInstrumentType: unsigned short {
|
|||
DIV_INS_POWERNOISE=56,
|
||||
DIV_INS_POWERNOISE_SLOPE=57,
|
||||
DIV_INS_DAVE=58,
|
||||
DIV_INS_GBA_DMA=59,
|
||||
DIV_INS_MAX,
|
||||
DIV_INS_NULL
|
||||
};
|
||||
|
@ -378,7 +379,7 @@ struct DivInstrumentSTD {
|
|||
|
||||
struct DivInstrumentGB {
|
||||
unsigned char envVol, envDir, envLen, soundLen, hwSeqLen;
|
||||
bool softEnv, alwaysInit;
|
||||
bool softEnv, alwaysInit, doubleWave; // TODO file save/load of doubleWave
|
||||
enum HWSeqCommands: unsigned char {
|
||||
DIV_GB_HWCMD_ENVELOPE=0,
|
||||
DIV_GB_HWCMD_SWEEP,
|
||||
|
@ -406,7 +407,8 @@ struct DivInstrumentGB {
|
|||
soundLen(64),
|
||||
hwSeqLen(0),
|
||||
softEnv(false),
|
||||
alwaysInit(false) {
|
||||
alwaysInit(false),
|
||||
doubleWave(false) {
|
||||
memset(hwSeq,0,256*sizeof(HWSeqCommandGB));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -64,13 +64,15 @@ const char** DivPlatformGB::getRegisterSheet() {
|
|||
|
||||
void DivPlatformGB::acquire(short** buf, size_t len) {
|
||||
for (size_t i=0; i<len; i++) {
|
||||
if (!writes.empty()) {
|
||||
QueuedWrite& w=writes.front();
|
||||
GB_apu_write(gb,w.addr,w.val);
|
||||
writes.pop();
|
||||
}
|
||||
for (int j=0; j<(1<<(outDepth-6)); j++) {
|
||||
if (!writes.empty()) {
|
||||
QueuedWrite& w=writes.front();
|
||||
GB_apu_write(gb,w.addr,w.val);
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
GB_advance_cycles(gb,16);
|
||||
GB_advance_cycles(gb,16);
|
||||
}
|
||||
buf[0][i]=gb->apu_output.final_sample.left;
|
||||
buf[1][i]=gb->apu_output.final_sample.right;
|
||||
|
||||
|
@ -81,17 +83,41 @@ void DivPlatformGB::acquire(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
void DivPlatformGB::updateWave() {
|
||||
rWrite(0x1a,0);
|
||||
for (int i=0; i<16; i++) {
|
||||
int nibble1=ws.output[((i<<1)+antiClickWavePos)&31];
|
||||
int nibble2=ws.output[((1+(i<<1))+antiClickWavePos)&31];
|
||||
if (invertWave) {
|
||||
nibble1^=15;
|
||||
nibble2^=15;
|
||||
if (doubleWave) {
|
||||
rWrite(0x1a,0x40); // select 1 -> write to bank 0
|
||||
for (int i=0; i<16; i++) {
|
||||
int nibble1=ws.output[((i<<1)+antiClickWavePos)&63];
|
||||
int nibble2=ws.output[((1+(i<<1))+antiClickWavePos)&63];
|
||||
if (invertWave) {
|
||||
nibble1^=15;
|
||||
nibble2^=15;
|
||||
}
|
||||
rWrite(0x30+i,(nibble1<<4)|nibble2);
|
||||
}
|
||||
rWrite(0x30+i,(nibble1<<4)|nibble2);
|
||||
rWrite(0x1a,0); // select 0 -> write to bank 1
|
||||
for (int i=0; i<16; i++) {
|
||||
int nibble1=ws.output[((32+(i<<1))+antiClickWavePos)&63];
|
||||
int nibble2=ws.output[((33+(i<<1))+antiClickWavePos)&63];
|
||||
if (invertWave) {
|
||||
nibble1^=15;
|
||||
nibble2^=15;
|
||||
}
|
||||
rWrite(0x30+i,(nibble1<<4)|nibble2);
|
||||
}
|
||||
antiClickWavePos&=63;
|
||||
} else {
|
||||
rWrite(0x1a,extendWave?0x40:0);
|
||||
for (int i=0; i<16; i++) {
|
||||
int nibble1=ws.output[((i<<1)+antiClickWavePos)&31];
|
||||
int nibble2=ws.output[((1+(i<<1))+antiClickWavePos)&31];
|
||||
if (invertWave) {
|
||||
nibble1^=15;
|
||||
nibble2^=15;
|
||||
}
|
||||
rWrite(0x30+i,(nibble1<<4)|nibble2);
|
||||
}
|
||||
antiClickWavePos&=31;
|
||||
}
|
||||
antiClickWavePos&=31;
|
||||
}
|
||||
|
||||
static unsigned char chanMuteMask[4]={
|
||||
|
@ -112,6 +138,13 @@ static unsigned char gbVolMap[16]={
|
|||
0x20, 0x20, 0x20, 0x20
|
||||
};
|
||||
|
||||
static unsigned char gbVolMapEx[16]={
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x60, 0x60, 0x60, 0x60,
|
||||
0x40, 0x40, 0x40, 0x40,
|
||||
0xa0, 0xa0, 0x20, 0x20
|
||||
};
|
||||
|
||||
static unsigned char noiseTable[256]={
|
||||
0,
|
||||
0xf7, 0xf6, 0xf5, 0xf4,
|
||||
|
@ -156,7 +189,7 @@ void DivPlatformGB::tick(bool sysTick) {
|
|||
if (chan[i].outVol<0) chan[i].outVol=0;
|
||||
|
||||
if (i==2) {
|
||||
rWrite(16+i*5+2,gbVolMap[chan[i].outVol]);
|
||||
rWrite(16+i*5+2,(extendWave?gbVolMapEx:gbVolMap)[chan[i].outVol]);
|
||||
chan[i].soundLen=64;
|
||||
} else {
|
||||
chan[i].envLen=0;
|
||||
|
@ -188,7 +221,7 @@ void DivPlatformGB::tick(bool sysTick) {
|
|||
rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(chan[i].soundLen&63)));
|
||||
} else if (!chan[i].softEnv) {
|
||||
if (parent->song.waveDutyIsVol) {
|
||||
rWrite(16+i*5+2,gbVolMap[(chan[i].std.duty.val&3)<<2]);
|
||||
rWrite(16+i*5+2,(extendWave?gbVolMapEx:gbVolMap)[(chan[i].std.duty.val&3)<<2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -301,8 +334,8 @@ void DivPlatformGB::tick(bool sysTick) {
|
|||
if (chan[i].keyOn) {
|
||||
if (i==2) { // wave
|
||||
rWrite(16+i*5,0x00);
|
||||
rWrite(16+i*5,0x80);
|
||||
rWrite(16+i*5+2,gbVolMap[chan[i].outVol]);
|
||||
rWrite(16+i*5,doubleWave?0xa0:0x80);
|
||||
rWrite(16+i*5+2,(extendWave?gbVolMapEx:gbVolMap)[chan[i].outVol]);
|
||||
} else {
|
||||
rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(chan[i].soundLen&63)));
|
||||
rWrite(16+i*5+2,((chan[i].envVol<<4))|(chan[i].envLen&7)|((chan[i].envDir&1)<<3));
|
||||
|
@ -379,11 +412,16 @@ int DivPlatformGB::dispatch(DivCommand c) {
|
|||
chan[c.chan].softEnv=ins->gb.softEnv;
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (c.chan==2) {
|
||||
doubleWave=extendWave&&ins->gb.doubleWave;
|
||||
if (chan[c.chan].wave<0) {
|
||||
chan[c.chan].wave=0;
|
||||
ws.changeWave1(chan[c.chan].wave);
|
||||
}
|
||||
ws.init(ins,32,15,chan[c.chan].insChanged);
|
||||
ws.init(ins,doubleWave?64:32,15,chan[c.chan].insChanged);
|
||||
if (doubleWave!=lastDoubleWave) {
|
||||
ws.changeWave1(chan[c.chan].wave);
|
||||
lastDoubleWave=doubleWave;
|
||||
}
|
||||
}
|
||||
if ((chan[c.chan].insChanged || ins->gb.alwaysInit) && !chan[c.chan].softEnv) {
|
||||
if (!chan[c.chan].soManyHacksToMakeItDefleCompatible && c.chan!=2) {
|
||||
|
@ -447,7 +485,7 @@ int DivPlatformGB::dispatch(DivCommand c) {
|
|||
chan[c.chan].vol=c.value;
|
||||
chan[c.chan].outVol=c.value;
|
||||
if (c.chan==2) {
|
||||
rWrite(16+c.chan*5+2,gbVolMap[chan[c.chan].outVol]);
|
||||
rWrite(16+c.chan*5+2,(extendWave?gbVolMapEx:gbVolMap)[chan[c.chan].outVol]);
|
||||
}
|
||||
if (!chan[c.chan].softEnv) {
|
||||
chan[c.chan].envVol=chan[c.chan].vol;
|
||||
|
@ -619,6 +657,8 @@ void DivPlatformGB::reset() {
|
|||
|
||||
antiClickPeriodCount=0;
|
||||
antiClickWavePos=0;
|
||||
doubleWave=false;
|
||||
lastDoubleWave=false;
|
||||
}
|
||||
|
||||
int DivPlatformGB::getPortaFloor(int ch) {
|
||||
|
@ -665,6 +705,8 @@ void DivPlatformGB::poke(std::vector<DivRegWrite>& wlist) {
|
|||
|
||||
void DivPlatformGB::setFlags(const DivConfig& flags) {
|
||||
antiClickEnabled=!flags.getBool("noAntiClick",false);
|
||||
extendWave=flags.getBool("extendWave",false);
|
||||
outDepth=flags.getInt("dacDepth",9);
|
||||
switch (flags.getInt("chipType",0)) {
|
||||
case 0:
|
||||
model=GB_MODEL_DMG_B;
|
||||
|
@ -676,7 +718,7 @@ void DivPlatformGB::setFlags(const DivConfig& flags) {
|
|||
model=GB_MODEL_CGB_E;
|
||||
break;
|
||||
case 3:
|
||||
model=GB_MODEL_AGB;
|
||||
model=extendWave?GB_MODEL_AGB_NATIVE:GB_MODEL_AGB;
|
||||
break;
|
||||
}
|
||||
invertWave=flags.getBool("invertWave",true);
|
||||
|
@ -684,7 +726,7 @@ void DivPlatformGB::setFlags(const DivConfig& flags) {
|
|||
|
||||
chipClock=4194304;
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/16;
|
||||
rate=chipClock>>(outDepth-2);
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,10 @@ class DivPlatformGB: public DivDispatch {
|
|||
bool antiClickEnabled;
|
||||
bool invertWave;
|
||||
bool enoughAlready;
|
||||
bool extendWave;
|
||||
bool doubleWave;
|
||||
bool lastDoubleWave;
|
||||
int outDepth;
|
||||
unsigned char lastPan;
|
||||
DivWaveSynth ws;
|
||||
struct QueuedWrite {
|
||||
|
|
445
src/engine/platform/gbadma.cpp
Normal file
445
src/engine/platform/gbadma.cpp
Normal file
|
@ -0,0 +1,445 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include "gbadma.h"
|
||||
#include "../engine.h"
|
||||
#include "../filter.h"
|
||||
#include <math.h>
|
||||
|
||||
#define CHIP_DIVIDER 16
|
||||
|
||||
void DivPlatformGBADMA::acquire(short** buf, size_t len) {
|
||||
// HLE for now
|
||||
int outL[2]={0,0};
|
||||
int outR[2]={0,0};
|
||||
for (size_t h=0; h<len; h++) {
|
||||
// internal mixing is always 10-bit
|
||||
for (int i=0; i<2; i++) {
|
||||
bool newSamp=h==0;
|
||||
if (chan[i].active && (chan[i].useWave || (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen))) {
|
||||
chan[i].audSub+=(1<<outDepth);
|
||||
if (chan[i].useWave) {
|
||||
if (chan[i].audPos<(int)chan[i].audLen) {
|
||||
chan[i].audDat=chan[i].ws.output[chan[i].audPos]-0x80;
|
||||
} else {
|
||||
chan[i].audDat=0;
|
||||
}
|
||||
newSamp=true;
|
||||
if (chan[i].audSub>=chan[i].freq) {
|
||||
int posInc=chan[i].audSub/chan[i].freq;
|
||||
chan[i].audSub-=chan[i].freq*posInc;
|
||||
chan[i].audPos+=posInc;
|
||||
chan[i].dmaCount+=posInc;
|
||||
if (chan[i].dmaCount>=16 && chan[i].audPos>=(int)chan[i].audLen) {
|
||||
chan[i].audPos%=chan[i].audLen;
|
||||
}
|
||||
chan[i].dmaCount&=15;
|
||||
}
|
||||
} else {
|
||||
DivSample* s=parent->getSample(chan[i].sample);
|
||||
if (s->samples>0) {
|
||||
if (chan[i].audPos>=0 && chan[i].audPos<(int)s->samples) {
|
||||
chan[i].audDat=s->data8[chan[i].audPos];
|
||||
} else {
|
||||
chan[i].audDat=0;
|
||||
}
|
||||
newSamp=true;
|
||||
if (chan[i].audSub>=chan[i].freq) {
|
||||
int posInc=chan[i].audSub/chan[i].freq;
|
||||
chan[i].audSub-=chan[i].freq*posInc;
|
||||
chan[i].audPos+=posInc;
|
||||
chan[i].dmaCount+=posInc;
|
||||
if (s->isLoopable()) {
|
||||
if (chan[i].dmaCount>=16 && chan[i].audPos>=s->loopEnd) {
|
||||
int loopPos=chan[i].audPos-s->loopStart;
|
||||
chan[i].audPos=(loopPos%(s->loopEnd-s->loopStart))+s->loopStart;
|
||||
}
|
||||
} else if (chan[i].audPos>=(int)s->samples) {
|
||||
chan[i].sample=-1;
|
||||
}
|
||||
chan[i].dmaCount&=15;
|
||||
}
|
||||
} else {
|
||||
chan[i].sample=-1;
|
||||
chan[i].audSub=0;
|
||||
chan[i].audPos=0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
chan[i].audDat=0;
|
||||
}
|
||||
if (!isMuted[i] && newSamp) {
|
||||
int out=chan[i].audDat*(chan[i].vol*chan[i].envVol/2)<<1;
|
||||
outL[i]=(chan[i].pan&2)?out:0;
|
||||
outR[i]=(chan[i].pan&1)?out:0;
|
||||
}
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(short)((outL[i]+outR[i])<<5);
|
||||
}
|
||||
int l=outL[0]+outL[1];
|
||||
int r=outR[0]+outR[1];
|
||||
l=(l>>(10-outDepth))<<(16-outDepth);
|
||||
r=(r>>(10-outDepth))<<(16-outDepth);
|
||||
if (l<-32768) l=-32768;
|
||||
if (l>32767) l=32767;
|
||||
if (r<-32768) r=-32768;
|
||||
if (r>32767) r=32767;
|
||||
buf[0][h]=(short)l;
|
||||
buf[1][h]=(short)r;
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformGBADMA::tick(bool sysTick) {
|
||||
for (int i=0; i<2; i++) {
|
||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA);
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].envVol=chan[i].std.vol.val;
|
||||
if (ins->type==DIV_INS_AMIGA) chan[i].envVol/=32;
|
||||
}
|
||||
if (NEW_ARP_STRAT) {
|
||||
chan[i].handleArp();
|
||||
} else if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].useWave && 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;
|
||||
chan[i].ws.changeWave1(chan[i].wave);
|
||||
if (!chan[i].keyOff) chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
if (chan[i].useWave && chan[i].active) {
|
||||
chan[i].ws.tick();
|
||||
}
|
||||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
if (chan[0].std.panL.had) {
|
||||
chan[0].pan=(chan[0].pan&~2)|(chan[0].std.panL.val>0?2:0);
|
||||
}
|
||||
if (chan[0].std.panR.had) {
|
||||
chan[0].pan=(chan[0].pan&~1)|(chan[0].std.panR.val>0?1:0);
|
||||
}
|
||||
} else {
|
||||
if (chan[i].std.panL.had) {
|
||||
chan[i].pan=chan[i].std.panL.val&3;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) {
|
||||
chan[i].audPos=0;
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
double off=1.0;
|
||||
if (!chan[i].useWave && chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[i].sample);
|
||||
off=(s->centerRate>=1)?(8363.0/(double)s->centerRate):1.0;
|
||||
}
|
||||
chan[i].freq=off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
|
||||
// emulate prescaler rounding
|
||||
if (chan[i].freq<65536) {
|
||||
if (chan[i].freq<1) chan[i].freq=1;
|
||||
} else if (chan[i].freq<65536*64) {
|
||||
chan[i].freq=chan[i].freq&~63;
|
||||
} else if (chan[i].freq<65536*256) {
|
||||
chan[i].freq=chan[i].freq&~255;
|
||||
} else {
|
||||
chan[i].freq=chan[i].freq&~1024;
|
||||
if (chan[i].freq>65536*1024) chan[i].freq=65536*1024;
|
||||
}
|
||||
if (chan[i].keyOn) {
|
||||
if (!chan[i].std.vol.had) {
|
||||
chan[i].envVol=2;
|
||||
}
|
||||
chan[i].keyOn=false;
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
chan[i].keyOff=false;
|
||||
}
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformGBADMA::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||
if (ins->amiga.useWave) {
|
||||
chan[c.chan].useWave=true;
|
||||
chan[c.chan].audLen=ins->amiga.waveLen+1;
|
||||
if (chan[c.chan].insChanged) {
|
||||
if (chan[c.chan].wave<0) {
|
||||
chan[c.chan].wave=0;
|
||||
chan[c.chan].ws.setWidth(chan[c.chan].audLen);
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
}
|
||||
chan[c.chan].useWave=false;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
}
|
||||
if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
|
||||
chan[c.chan].sample=-1;
|
||||
}
|
||||
if (chan[c.chan].setPos) {
|
||||
chan[c.chan].setPos=false;
|
||||
} else {
|
||||
chan[c.chan].audPos=0;
|
||||
}
|
||||
chan[c.chan].audSub=0;
|
||||
chan[c.chan].audDat=0;
|
||||
chan[c.chan].dmaCount=0;
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].envVol=2;
|
||||
}
|
||||
if (chan[c.chan].useWave) {
|
||||
chan[c.chan].ws.init(ins,chan[c.chan].audLen,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);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
chan[c.chan].ins=c.value;
|
||||
chan[c.chan].insChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_VOLUME:
|
||||
if (chan[c.chan].vol!=c.value) {
|
||||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].envVol=2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_GET_VOLUME:
|
||||
return chan[c.chan].vol;
|
||||
break;
|
||||
case DIV_CMD_PANNING:
|
||||
chan[c.chan].pan=0;
|
||||
chan[c.chan].pan|=(c.value>0)?2:0;
|
||||
chan[c.chan].pan|=(c.value2>0)?1:0;
|
||||
break;
|
||||
case DIV_CMD_PITCH:
|
||||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_WAVE:
|
||||
if (!chan[c.chan].useWave) break;
|
||||
chan[c.chan].wave=c.value;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value2);
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
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) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
if (chan[c.chan].active && c.value2) {
|
||||
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
|
||||
}
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_SAMPLE_POS:
|
||||
if (chan[c.chan].useWave) break;
|
||||
chan[c.chan].audPos=c.value;
|
||||
chan[c.chan].setPos=true;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 2;
|
||||
break;
|
||||
case DIV_CMD_MACRO_OFF:
|
||||
chan[c.chan].std.mask(c.value,true);
|
||||
break;
|
||||
case DIV_CMD_MACRO_ON:
|
||||
chan[c.chan].std.mask(c.value,false);
|
||||
break;
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformGBADMA::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
}
|
||||
|
||||
void DivPlatformGBADMA::forceIns() {
|
||||
for (int i=0; i<2; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
chan[i].audPos=0;
|
||||
chan[i].sample=-1;
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformGBADMA::getChanState(int ch) {
|
||||
return &chan;
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformGBADMA::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
void DivPlatformGBADMA::reset() {
|
||||
for (int i=0; i<2; i++) {
|
||||
chan[i]=DivPlatformGBADMA::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
chan[i].ws.setEngine(parent);
|
||||
chan[i].ws.init(NULL,32,255);
|
||||
chan[i].audDat=0;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformGBADMA::getOutputCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
DivMacroInt* DivPlatformGBADMA::getChanMacroInt(int ch) {
|
||||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
unsigned short DivPlatformGBADMA::getPan(int ch) {
|
||||
return ((chan[ch].pan&2)<<7)|(chan[ch].pan&1);
|
||||
}
|
||||
|
||||
DivSamplePos DivPlatformGBADMA::getSamplePos(int ch) {
|
||||
if (ch>=2) return DivSamplePos();
|
||||
return DivSamplePos(
|
||||
chan[ch].sample,
|
||||
chan[ch].audPos,
|
||||
chan[ch].freq
|
||||
);
|
||||
}
|
||||
|
||||
void DivPlatformGBADMA::notifyInsChange(int ins) {
|
||||
for (int i=0; i<2; i++) {
|
||||
if (chan[i].ins==ins) {
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformGBADMA::notifyWaveChange(int wave) {
|
||||
for (int i=0; i<2; i++) {
|
||||
if (chan[i].useWave && chan[i].wave==wave) {
|
||||
chan[i].ws.changeWave1(wave);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformGBADMA::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<2; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformGBADMA::setFlags(const DivConfig& flags) {
|
||||
outDepth=flags.getInt("dacDepth",9);
|
||||
chipClock=1<<24;
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock>>outDepth;
|
||||
for (int i=0; i<2; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformGBADMA::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
for (int i=0; i<2; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 2;
|
||||
}
|
||||
|
||||
void DivPlatformGBADMA::quit() {
|
||||
for (int i=0; i<2; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
}
|
83
src/engine/platform/gbadma.h
Normal file
83
src/engine/platform/gbadma.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _GBA_DMA_H
|
||||
#define _GBA_DMA_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include "../waveSynth.h"
|
||||
|
||||
class DivPlatformGBADMA: public DivDispatch {
|
||||
struct Channel: public SharedChannel<int> {
|
||||
unsigned int audLoc;
|
||||
unsigned short audLen;
|
||||
int audDat;
|
||||
int audPos;
|
||||
int audSub;
|
||||
int dmaCount;
|
||||
int sample, wave;
|
||||
int pan;
|
||||
bool useWave, setPos;
|
||||
int envVol;
|
||||
DivWaveSynth ws;
|
||||
Channel():
|
||||
SharedChannel<int>(2),
|
||||
audLoc(0),
|
||||
audLen(0),
|
||||
audDat(0),
|
||||
audPos(0),
|
||||
audSub(0),
|
||||
dmaCount(0),
|
||||
sample(-1),
|
||||
wave(-1),
|
||||
pan(3),
|
||||
useWave(false),
|
||||
setPos(false),
|
||||
envVol(2) {}
|
||||
};
|
||||
Channel chan[2];
|
||||
DivDispatchOscBuffer* oscBuf[2];
|
||||
bool isMuted[2];
|
||||
int outDepth;
|
||||
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
void acquire(short** buf, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
int getOutputCount();
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
unsigned short getPan(int chan);
|
||||
DivSamplePos getSamplePos(int ch);
|
||||
void setFlags(const DivConfig& flags);
|
||||
void notifyInsChange(int ins);
|
||||
void notifyWaveChange(int wave);
|
||||
void notifyInsDeletion(void* ins);
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -668,15 +668,17 @@ void GB_apu_run(GB_gameboy_t *gb)
|
|||
if (gb->apu.is_active[GB_WAVE]) {
|
||||
uint8_t cycles_left = cycles;
|
||||
while (unlikely(cycles_left > gb->apu.wave_channel.sample_countdown)) {
|
||||
uint8_t base = (!gb->apu.wave_channel.double_length && gb->apu.wave_channel.bank_select) ? 32 : 0;
|
||||
cycles_left -= gb->apu.wave_channel.sample_countdown + 1;
|
||||
gb->apu.wave_channel.sample_countdown = gb->apu.wave_channel.sample_length ^ 0x7FF;
|
||||
gb->apu.wave_channel.current_sample_index++;
|
||||
gb->apu.wave_channel.current_sample_index &= 0x1F;
|
||||
gb->apu.wave_channel.current_sample_index &= gb->apu.wave_channel.double_length ? 0x3F : 0x1F;
|
||||
gb->apu.wave_channel.current_sample =
|
||||
gb->apu.wave_channel.wave_form[gb->apu.wave_channel.current_sample_index];
|
||||
update_sample(gb, GB_WAVE,
|
||||
gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift,
|
||||
cycles - cycles_left);
|
||||
gb->apu.wave_channel.wave_form[base + gb->apu.wave_channel.current_sample_index];
|
||||
int8_t sample = gb->apu.wave_channel.force_3 ?
|
||||
(gb->apu.wave_channel.current_sample * 3) >> 2 :
|
||||
gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift;
|
||||
update_sample(gb, GB_WAVE, sample, cycles - cycles_left);
|
||||
gb->apu.wave_channel.wave_form_just_read = true;
|
||||
}
|
||||
if (cycles_left) {
|
||||
|
@ -738,8 +740,10 @@ void GB_apu_init(GB_gameboy_t *gb)
|
|||
memset(&gb->apu, 0, sizeof(gb->apu));
|
||||
/* Restore the wave form */
|
||||
for (unsigned reg = GB_IO_WAV_START; reg <= GB_IO_WAV_END; reg++) {
|
||||
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2] = gb->io_registers[reg] >> 4;
|
||||
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2 + 1] = gb->io_registers[reg] & 0xF;
|
||||
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2] = gb->io_registers[reg] >> 4;
|
||||
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2 + 1] = gb->io_registers[reg] & 0xF;
|
||||
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2 + 32] = gb->io_registers[reg] >> 4;
|
||||
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2 + 33] = gb->io_registers[reg] & 0xF;
|
||||
}
|
||||
gb->apu.lf_div = 1;
|
||||
/* APU glitch: When turning the APU on while DIV's bit 4 (or 5 in double speed mode) is on,
|
||||
|
@ -1160,14 +1164,24 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
|||
gb->apu.is_active[GB_WAVE] = false;
|
||||
update_sample(gb, GB_WAVE, 0, 0);
|
||||
}
|
||||
if (gb->model==GB_MODEL_AGB_NATIVE) {
|
||||
gb->apu.wave_channel.bank_select = value & 0x40;
|
||||
gb->apu.wave_channel.double_length = value & 0x20;
|
||||
}
|
||||
break;
|
||||
case GB_IO_NR31:
|
||||
gb->apu.wave_channel.pulse_length = (0x100 - value);
|
||||
break;
|
||||
case GB_IO_NR32:
|
||||
gb->apu.wave_channel.shift = (uint8_t[]){4, 0, 1, 2}[(value >> 5) & 3];
|
||||
if (gb->model==GB_MODEL_AGB_NATIVE) {
|
||||
gb->apu.wave_channel.force_3 = value & 0x80;
|
||||
}
|
||||
if (gb->apu.is_active[GB_WAVE]) {
|
||||
update_sample(gb, GB_WAVE, gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift, 0);
|
||||
int8_t sample = gb->apu.wave_channel.force_3 ?
|
||||
(gb->apu.wave_channel.current_sample * 3) >> 2 :
|
||||
gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift;
|
||||
update_sample(gb, GB_WAVE, sample, 0);
|
||||
}
|
||||
break;
|
||||
case GB_IO_NR33:
|
||||
|
@ -1209,9 +1223,10 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
|||
}
|
||||
if (!gb->apu.is_active[GB_WAVE]) {
|
||||
gb->apu.is_active[GB_WAVE] = true;
|
||||
update_sample(gb, GB_WAVE,
|
||||
gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift,
|
||||
0);
|
||||
int8_t sample = gb->apu.wave_channel.force_3 ?
|
||||
(gb->apu.wave_channel.current_sample * 3) >> 2 :
|
||||
gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift;
|
||||
update_sample(gb, GB_WAVE, sample, 0);
|
||||
}
|
||||
gb->apu.wave_channel.sample_countdown = (gb->apu.wave_channel.sample_length ^ 0x7FF) + 3;
|
||||
gb->apu.wave_channel.current_sample_index = 0;
|
||||
|
@ -1411,8 +1426,13 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
|||
|
||||
default:
|
||||
if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END) {
|
||||
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2] = value >> 4;
|
||||
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2 + 1] = value & 0xF;
|
||||
uint8_t base = 0;
|
||||
if (gb->model == GB_MODEL_AGB_NATIVE &&
|
||||
(!gb->apu.global_enable || !gb->apu.wave_channel.bank_select)) {
|
||||
base = 32;
|
||||
}
|
||||
gb->apu.wave_channel.wave_form[base + (reg - GB_IO_WAV_START) * 2] = value >> 4;
|
||||
gb->apu.wave_channel.wave_form[base + (reg - GB_IO_WAV_START) * 2 + 1] = value & 0xF;
|
||||
}
|
||||
}
|
||||
gb->io_registers[reg] = value;
|
||||
|
|
|
@ -93,12 +93,15 @@ typedef struct
|
|||
uint8_t shift; // NR32
|
||||
uint16_t sample_length; // NR33, NR34, in APU ticks
|
||||
bool length_enabled; // NR34
|
||||
bool double_length; // NR30
|
||||
bool bank_select; // NR30
|
||||
bool force_3; // NR32
|
||||
|
||||
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
|
||||
uint8_t current_sample_index;
|
||||
uint8_t current_sample; // Current sample before shifting.
|
||||
|
||||
int8_t wave_form[32];
|
||||
int8_t wave_form[64];
|
||||
bool wave_form_just_read;
|
||||
} wave_channel;
|
||||
|
||||
|
|
|
@ -114,6 +114,7 @@ typedef enum {
|
|||
// GB_MODEL_CGB_D = 0x204,
|
||||
GB_MODEL_CGB_E = 0x205,
|
||||
GB_MODEL_AGB = 0x206,
|
||||
GB_MODEL_AGB_NATIVE = 0x226,
|
||||
} GB_model_t;
|
||||
|
||||
enum {
|
||||
|
|
|
@ -118,6 +118,7 @@ enum DivSystem {
|
|||
DIV_SYSTEM_T6W28,
|
||||
DIV_SYSTEM_K007232,
|
||||
DIV_SYSTEM_GA20,
|
||||
DIV_SYSTEM_GBA_DMA,
|
||||
DIV_SYSTEM_PCM_DAC,
|
||||
DIV_SYSTEM_PONG,
|
||||
DIV_SYSTEM_DUMMY,
|
||||
|
|
|
@ -2001,6 +2001,16 @@ void DivEngine::registerSystems() {
|
|||
},
|
||||
{}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_GBA_DMA]=new DivSysDef(
|
||||
"Game Boy Advance DMA Sound", NULL, 0xfe, 0, 2, false, true, 0, false, 1U<<DIV_SAMPLE_DEPTH_8BIT, 0, 0,
|
||||
"additional PCM FIFO channels in Game Boy Advance driven directly by its DMA hardware.",
|
||||
{"PCM 1", "PCM 2"},
|
||||
{"P1", "P2"},
|
||||
{DIV_CH_PCM, DIV_CH_PCM},
|
||||
{DIV_INS_GBA_DMA, DIV_INS_GBA_DMA},
|
||||
{DIV_INS_AMIGA, DIV_INS_AMIGA}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_DAVE]=new DivSysDef(
|
||||
"Dave", NULL, 0xd5, 0, 6, false, true, 0, false, 1U<<DIV_SAMPLE_DEPTH_8BIT, 0, 0,
|
||||
|
|
|
@ -1540,7 +1540,8 @@ void FurnaceGUI::doAction(int what) {
|
|||
i==DIV_INS_GA20 ||
|
||||
i==DIV_INS_K053260 ||
|
||||
i==DIV_INS_C140 ||
|
||||
i==DIV_INS_C219) {
|
||||
i==DIV_INS_C219 ||
|
||||
i==DIV_INS_GBA_DMA) {
|
||||
makeInsTypeList.push_back(i);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -295,6 +295,7 @@ enum FurnaceGUIColors {
|
|||
GUI_COLOR_INSTR_POWERNOISE,
|
||||
GUI_COLOR_INSTR_POWERNOISE_SLOPE,
|
||||
GUI_COLOR_INSTR_DAVE,
|
||||
GUI_COLOR_INSTR_GBA_DMA,
|
||||
GUI_COLOR_INSTR_UNKNOWN,
|
||||
|
||||
GUI_COLOR_CHANNEL_BG,
|
||||
|
|
|
@ -179,6 +179,7 @@ const char* insTypes[DIV_INS_MAX+1][3]={
|
|||
{"PowerNoise (noise)",ICON_FUR_NOISE,ICON_FUR_INS_POWERNOISE},
|
||||
{"PowerNoise (slope)",ICON_FUR_SAW,ICON_FUR_INS_POWERNOISE_SAW},
|
||||
{"Dave",ICON_FA_BAR_CHART,ICON_FUR_INS_DAVE},
|
||||
{"Game Boy Advance DMA",ICON_FA_VOLUME_UP,ICON_FA_QUESTION}, // TODO
|
||||
{NULL,ICON_FA_QUESTION,ICON_FA_QUESTION}
|
||||
};
|
||||
|
||||
|
@ -992,6 +993,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
|
|||
D(GUI_COLOR_INSTR_POWERNOISE,"",ImVec4(1.0f,1.0f,0.8f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_POWERNOISE_SLOPE,"",ImVec4(1.0f,0.6f,0.3f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_DAVE,"",ImVec4(0.7f,0.7f,0.8f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_GBA_DMA,"",ImVec4(0.6f,0.4f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)),
|
||||
|
||||
D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)),
|
||||
|
@ -1229,6 +1231,7 @@ const int availableSystems[]={
|
|||
DIV_SYSTEM_TED,
|
||||
DIV_SYSTEM_C140,
|
||||
DIV_SYSTEM_C219,
|
||||
DIV_SYSTEM_GBA_DMA,
|
||||
DIV_SYSTEM_PCM_DAC,
|
||||
DIV_SYSTEM_ESFM,
|
||||
DIV_SYSTEM_PONG,
|
||||
|
@ -1347,6 +1350,7 @@ const int chipsSample[]={
|
|||
DIV_SYSTEM_K053260,
|
||||
DIV_SYSTEM_C140,
|
||||
DIV_SYSTEM_C219,
|
||||
DIV_SYSTEM_GBA_DMA,
|
||||
0 // don't remove this last one!
|
||||
};
|
||||
|
||||
|
|
|
@ -2525,14 +2525,15 @@ void FurnaceGUI::insTabSample(DivInstrument* ins) {
|
|||
ImGui::EndCombo();
|
||||
}
|
||||
// Wavetable
|
||||
if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SNES) {
|
||||
if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SNES || ins->type==DIV_INS_GBA_DMA) {
|
||||
const char* useWaveText=ins->type==DIV_INS_AMIGA?"Use wavetable (Amiga/Generic DAC only)":"Use wavetable";
|
||||
ImGui::BeginDisabled(ins->amiga.useNoteMap);
|
||||
P(ImGui::Checkbox("Use wavetable (Amiga/SNES/Generic DAC only)",&ins->amiga.useWave));
|
||||
P(ImGui::Checkbox(useWaveText,&ins->amiga.useWave));
|
||||
if (ins->amiga.useWave) {
|
||||
int len=ins->amiga.waveLen+1;
|
||||
int origLen=len;
|
||||
if (ImGui::InputInt("Width",&len,2,16)) {
|
||||
if (ins->type==DIV_INS_SNES) {
|
||||
if (ins->type==DIV_INS_SNES || ins->type==DIV_INS_GBA_DMA) {
|
||||
if (len<16) len=16;
|
||||
if (len>256) len=256;
|
||||
if (len>origLen) {
|
||||
|
@ -5395,6 +5396,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
if (ins->type==DIV_INS_GB) if (ImGui::BeginTabItem("Game Boy")) {
|
||||
P(ImGui::Checkbox("Use software envelope",&ins->gb.softEnv));
|
||||
P(ImGui::Checkbox("Initialize envelope on every note",&ins->gb.alwaysInit));
|
||||
P(ImGui::Checkbox("Double wave length (GBA only)",&ins->gb.doubleWave));
|
||||
|
||||
ImGui::BeginDisabled(ins->gb.softEnv);
|
||||
if (ImGui::BeginTable("GBParams",2)) {
|
||||
|
@ -6021,7 +6023,8 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ins->type==DIV_INS_GA20 ||
|
||||
ins->type==DIV_INS_K053260 ||
|
||||
ins->type==DIV_INS_C140 ||
|
||||
ins->type==DIV_INS_C219) {
|
||||
ins->type==DIV_INS_C219 ||
|
||||
ins->type==DIV_INS_GBA_DMA) {
|
||||
insTabSample(ins);
|
||||
}
|
||||
if (ins->type==DIV_INS_N163) if (ImGui::BeginTabItem("Namco 163")) {
|
||||
|
@ -6456,6 +6459,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
if (ins->type==DIV_INS_GB ||
|
||||
(ins->type==DIV_INS_AMIGA && ins->amiga.useWave) ||
|
||||
(ins->type==DIV_INS_GBA_DMA && ins->amiga.useWave) ||
|
||||
(ins->type==DIV_INS_X1_010 && !ins->amiga.useSample) ||
|
||||
ins->type==DIV_INS_N163 ||
|
||||
ins->type==DIV_INS_FDS ||
|
||||
|
@ -6500,6 +6504,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
wavePreviewHeight=255;
|
||||
break;
|
||||
case DIV_INS_AMIGA:
|
||||
case DIV_INS_GBA_DMA:
|
||||
wavePreviewLen=ins->amiga.waveLen+1;
|
||||
wavePreviewHeight=255;
|
||||
break;
|
||||
|
@ -6771,7 +6776,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
if (ins->type==DIV_INS_QSOUND) {
|
||||
volMax=16383;
|
||||
}
|
||||
if (ins->type==DIV_INS_POKEMINI) {
|
||||
if (ins->type==DIV_INS_POKEMINI || ins->type==DIV_INS_GBA_DMA) {
|
||||
volMax=2;
|
||||
}
|
||||
|
||||
|
@ -6830,7 +6835,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ins->type==DIV_INS_PET || ins->type==DIV_INS_SEGAPCM ||
|
||||
ins->type==DIV_INS_FM || ins->type==DIV_INS_K007232 || ins->type==DIV_INS_GA20 ||
|
||||
ins->type==DIV_INS_SM8521 || ins->type==DIV_INS_PV1000 || ins->type==DIV_INS_K053260 ||
|
||||
ins->type==DIV_INS_C140) {
|
||||
ins->type==DIV_INS_C140 || ins->type==DIV_INS_GBA_DMA) {
|
||||
dutyMax=0;
|
||||
}
|
||||
if (ins->type==DIV_INS_VBOY) {
|
||||
|
@ -7045,7 +7050,8 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ins->type==DIV_INS_VERA ||
|
||||
ins->type==DIV_INS_ADPCMA ||
|
||||
ins->type==DIV_INS_ADPCMB ||
|
||||
ins->type==DIV_INS_ESFM) {
|
||||
ins->type==DIV_INS_ESFM ||
|
||||
ins->type==DIV_INS_GBA_DMA) {
|
||||
panMax=2;
|
||||
panSingle=true;
|
||||
}
|
||||
|
@ -7201,7 +7207,8 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ins->type==DIV_INS_ESFM ||
|
||||
ins->type==DIV_INS_POWERNOISE ||
|
||||
ins->type==DIV_INS_POWERNOISE_SLOPE ||
|
||||
ins->type==DIV_INS_DAVE) {
|
||||
ins->type==DIV_INS_DAVE ||
|
||||
ins->type==DIV_INS_GBA_DMA) {
|
||||
macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
|
||||
}
|
||||
if (ex1Max>0) {
|
||||
|
|
|
@ -130,6 +130,16 @@ void FurnaceGUI::initSystemPresets() {
|
|||
CH(DIV_SYSTEM_GB, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Game Boy Advance (no software mixing)", {
|
||||
CH(DIV_SYSTEM_GB, 1.0f, 0,
|
||||
"chipType=3\n"
|
||||
"extendWave=true\n"
|
||||
"dacDepth=9\n"
|
||||
),
|
||||
CH(DIV_SYSTEM_GBA_DMA, 0.5f, 0, "dacDepth=9"),
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Neo Geo Pocket", {
|
||||
CH(DIV_SYSTEM_T6W28, 1.0f, 0, ""),
|
||||
|
|
|
@ -379,6 +379,20 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (sample->samples>129024) {
|
||||
SAMPLE_WARN(warnLength,"MSM6295: maximum bankswitched sample length is 129024");
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_GBA_DMA:
|
||||
if (sample->loop) {
|
||||
if (sample->loopStart&3) {
|
||||
SAMPLE_WARN(warnLoopStart,"GBA DMA: loop start must be a multiple of 4");
|
||||
}
|
||||
if ((sample->loopEnd-sample->loopStart)&15) {
|
||||
SAMPLE_WARN(warnLoopEnd,"GBA DMA: loop length must be a multiple of 16");
|
||||
}
|
||||
}
|
||||
if (sample->samples&15) {
|
||||
SAMPLE_WARN(warnLength,"GBA DMA: sample length will be padded to multiple of 16");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -3551,6 +3551,7 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_POWERNOISE,"PowerNoise (noise)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_POWERNOISE_SLOPE,"PowerNoise (slope)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_DAVE,"Dave");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_GBA_DMA,"GBA DMA");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown");
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
|
|
@ -329,6 +329,8 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
|||
bool noAntiClick=flags.getBool("noAntiClick",false);
|
||||
bool invertWave=flags.getBool("invertWave",true);
|
||||
bool enoughAlready=flags.getBool("enoughAlready",false);
|
||||
bool extendWave=flags.getBool("extendWave",false);
|
||||
int dacDepth=flags.getInt("dacDepth",6);
|
||||
|
||||
if (ImGui::Checkbox("Disable anti-click",&noAntiClick)) {
|
||||
altered=true;
|
||||
|
@ -352,6 +354,22 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
|||
altered=true;
|
||||
}
|
||||
ImGui::Unindent();
|
||||
ImGui::Text("Game Boy Advance:");
|
||||
ImGui::Indent();
|
||||
ImGui::BeginDisabled(chipType!=3);
|
||||
if (ImGui::Checkbox("Wave channel extension",&extendWave)) {
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("note: not supported by the VGM format!\nallows wave channel to have double length and 75%% volume");
|
||||
}
|
||||
if (CWSliderInt("DAC bit depth (reduces output rate)",&dacDepth,6,9)) {
|
||||
if (dacDepth<6) dacDepth=6;
|
||||
if (dacDepth>9) dacDepth=9;
|
||||
altered=true;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::Unindent();
|
||||
ImGui::Text("Wave channel orientation:");
|
||||
if (chipType==3) {
|
||||
ImGui::Indent();
|
||||
|
@ -381,11 +399,33 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
|||
}
|
||||
|
||||
if (altered) {
|
||||
if (chipType!=3) {
|
||||
extendWave=false;
|
||||
dacDepth=6;
|
||||
}
|
||||
e->lockSave([&]() {
|
||||
flags.set("chipType",chipType);
|
||||
flags.set("noAntiClick",noAntiClick);
|
||||
flags.set("invertWave",invertWave);
|
||||
flags.set("enoughAlready",enoughAlready);
|
||||
flags.set("extendWave",extendWave);
|
||||
flags.set("dacDepth",dacDepth);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_GBA_DMA: {
|
||||
int dacDepth=flags.getInt("dacDepth",6);
|
||||
|
||||
if (CWSliderInt("DAC bit depth (reduces output rate)",&dacDepth,6,9)) {
|
||||
if (dacDepth<6) dacDepth=6;
|
||||
if (dacDepth>9) dacDepth=9;
|
||||
altered=true;
|
||||
}
|
||||
|
||||
if (altered) {
|
||||
e->lockSave([&]() {
|
||||
flags.set("dacDepth",dacDepth);
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue