Merge branch 'master' into ZSMv1
This commit is contained in:
commit
c3654eb67c
138 changed files with 10160 additions and 2938 deletions
|
|
@ -2000,6 +2000,14 @@ void DivEngine::recalcChans() {
|
|||
}
|
||||
|
||||
void DivEngine::reset() {
|
||||
if (output) if (output->midiOut!=NULL) {
|
||||
output->midiOut->send(TAMidiMessage(TA_MIDI_MACHINE_STOP,0,0));
|
||||
for (int i=0; i<chans; i++) {
|
||||
if (chan[i].curMidiNote>=0) {
|
||||
output->midiOut->send(TAMidiMessage(0x80|(i&15),chan[i].curMidiNote,0));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
chan[i]=DivChannelState();
|
||||
if (i<chans) chan[i].volMax=(disCont[dispatchOfChan[i]].dispatch->dispatch(DivCommand(DIV_CMD_GET_VOLMAX,dispatchChanOfChan[i]))<<8)|0xff;
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@
|
|||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev114"
|
||||
#define DIV_ENGINE_VERSION 114
|
||||
#define DIV_VERSION "0.6pre1.5"
|
||||
#define DIV_ENGINE_VERSION 116
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
|
|
|||
|
|
@ -196,6 +196,15 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
}
|
||||
*/
|
||||
|
||||
// Genesis detuned on Defle v10 and earlier
|
||||
/*if (ds.version<19 && ds.system[0]==DIV_SYSTEM_GENESIS) {
|
||||
ds.tuning=443.23;
|
||||
}*/
|
||||
// C64 detuned on Defle v11 and earlier
|
||||
/*if (ds.version<21 && (ds.system[0]==DIV_SYSTEM_C64_6581 || ds.system[0]==DIV_SYSTEM_C64_8580)) {
|
||||
ds.tuning=433.2;
|
||||
}*/
|
||||
|
||||
logI("reading module data...");
|
||||
if (ds.version>0x0c) {
|
||||
ds.subsong[0]->hilightA=reader.readC();
|
||||
|
|
@ -449,6 +458,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
ins->fm.op[j].ssgEnv=reader.readC();
|
||||
}
|
||||
}
|
||||
if (ds.version<0x12) { // before version 10 all ops were responsive to volume
|
||||
ins->fm.op[j].kvs=1;
|
||||
}
|
||||
|
||||
logD("OP%d: AM %d AR %d DAM %d DR %d DVB %d EGT %d KSL %d MULT %d RR %d SL %d SUS %d TL %d VIB %d WS %d RS %d DT %d D2R %d SSG-EG %d",j,
|
||||
ins->fm.op[j].am,
|
||||
|
|
@ -1085,6 +1097,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
if (ds.version<113) {
|
||||
ds.jumpTreatment=1;
|
||||
}
|
||||
if (ds.version<115) {
|
||||
ds.autoSystem=false;
|
||||
}
|
||||
ds.isDMF=false;
|
||||
|
||||
reader.readS(); // reserved
|
||||
|
|
@ -1512,7 +1527,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
} else {
|
||||
reader.readC();
|
||||
}
|
||||
for (int i=0; i<4; i++) {
|
||||
if (ds.version>=115) {
|
||||
ds.autoSystem=reader.readC();
|
||||
} else {
|
||||
reader.readC();
|
||||
}
|
||||
for (int i=0; i<3; i++) {
|
||||
reader.readC();
|
||||
}
|
||||
}
|
||||
|
|
@ -1549,6 +1569,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
ds.categoryJ=reader.readString();
|
||||
} else {
|
||||
ds.systemName=getSongSystemLegacyName(ds,!getConfInt("noMultiSystem",0));
|
||||
ds.autoSystem=true;
|
||||
}
|
||||
|
||||
// read subsongs
|
||||
|
|
@ -1772,14 +1793,8 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
|
||||
#ifdef TA_BIG_ENDIAN
|
||||
// convert 16-bit samples to big-endian
|
||||
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
|
||||
unsigned char* sampleBuf=(unsigned char*)sample->getCurBuf();
|
||||
size_t sampleBufLen=sample->getCurBufLen();
|
||||
for (size_t pos=0; pos<sampleBufLen; pos+=2) {
|
||||
sampleBuf[pos]^=sampleBuf[pos+1];
|
||||
sampleBuf[pos+1]^=sampleBuf[pos];
|
||||
sampleBuf[pos]^=sampleBuf[pos+1];
|
||||
}
|
||||
for (int pos=0; pos<length; pos++) {
|
||||
data[pos]=((unsigned short)data[pos]>>8)|((unsigned short)data[pos]<<8);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -3757,7 +3772,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeC(song.snNoLowPeriods);
|
||||
w->writeC(song.delayBehavior);
|
||||
w->writeC(song.jumpTreatment);
|
||||
for (int i=0; i<4; i++) {
|
||||
w->writeC(song.autoSystem);
|
||||
for (int i=0; i<3; i++) {
|
||||
w->writeC(0);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,9 +72,10 @@ void DivInstrument::putInsData(SafeWriter* w) {
|
|||
w->writeC(op.ksr);
|
||||
|
||||
w->writeC(op.enable);
|
||||
w->writeC(op.kvs);
|
||||
|
||||
// reserved
|
||||
for (int k=0; k<11; k++) {
|
||||
for (int k=0; k<10; k++) {
|
||||
w->writeC(0);
|
||||
}
|
||||
}
|
||||
|
|
@ -724,8 +725,15 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
|
|||
reader.readC();
|
||||
}
|
||||
|
||||
if (version>=115) {
|
||||
op.kvs=reader.readC();
|
||||
} else {
|
||||
op.kvs=2;
|
||||
reader.readC();
|
||||
}
|
||||
|
||||
// reserved
|
||||
for (int k=0; k<11; k++) reader.readC();
|
||||
for (int k=0; k<10; k++) reader.readC();
|
||||
}
|
||||
|
||||
// GB
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ struct DivInstrumentFM {
|
|||
bool enable;
|
||||
unsigned char am, ar, dr, mult, rr, sl, tl, dt2, rs, dt, d2r, ssgEnv;
|
||||
unsigned char dam, dvb, egt, ksl, sus, vib, ws, ksr; // YMU759/OPL/OPZ
|
||||
unsigned char kvs;
|
||||
Operator():
|
||||
enable(true),
|
||||
am(0),
|
||||
|
|
@ -108,7 +109,8 @@ struct DivInstrumentFM {
|
|||
sus(0),
|
||||
vib(0),
|
||||
ws(0),
|
||||
ksr(0) {}
|
||||
ksr(0),
|
||||
kvs(2) {}
|
||||
} op[4];
|
||||
DivInstrumentFM():
|
||||
alg(0),
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic
|
|||
}
|
||||
if (delay>0) {
|
||||
delay--;
|
||||
had=false;
|
||||
return;
|
||||
}
|
||||
if (began && source.delay>0) {
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ void DivPlatformArcade::tick(bool sysTick) {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -231,7 +231,7 @@ void DivPlatformArcade::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -289,7 +289,7 @@ void DivPlatformArcade::tick(bool sysTick) {
|
|||
}
|
||||
if (m.tl.had) {
|
||||
op.tl=127-m.tl.val;
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -390,7 +390,7 @@ int DivPlatformArcade::dispatch(DivCommand c) {
|
|||
for (int i=0; i<4; i++) {
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
DivInstrumentFM::Operator op=chan[c.chan].state.op[i];
|
||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
||||
if (KVS(c.chan,i)) {
|
||||
if (!chan[c.chan].active || chan[c.chan].insChanged) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
}
|
||||
|
|
@ -448,7 +448,7 @@ int DivPlatformArcade::dispatch(DivCommand c) {
|
|||
for (int i=0; i<4; i++) {
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
||||
if (KVS(c.chan,i)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -545,7 +545,7 @@ int DivPlatformArcade::dispatch(DivCommand c) {
|
|||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.tl=c.value2;
|
||||
if (isOutput[chan[c.chan].state.alg][c.value]) {
|
||||
if (KVS(c.chan,c.value)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -746,7 +746,7 @@ void DivPlatformArcade::forceIns() {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator op=chan[i].state.op[j];
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
|
|||
|
|
@ -115,8 +115,8 @@ void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t l
|
|||
ay->sound_stream_update(ayBuf,len);
|
||||
if (stereo) {
|
||||
for (size_t i=0; i<len; i++) {
|
||||
bufL[i+start]=ayBuf[0][i]+ayBuf[1][i];
|
||||
bufR[i+start]=ayBuf[1][i]+ayBuf[2][i];
|
||||
bufL[i+start]=ayBuf[0][i]+ayBuf[1][i]+((ayBuf[2][i]*stereoSep)>>8);
|
||||
bufR[i+start]=((ayBuf[0][i]*stereoSep)>>8)+ayBuf[1][i]+ayBuf[2][i];
|
||||
}
|
||||
} else {
|
||||
for (size_t i=0; i<len; i++) {
|
||||
|
|
@ -126,7 +126,7 @@ void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t l
|
|||
}
|
||||
for (int ch=0; ch<3; ch++) {
|
||||
for (size_t i=0; i<len; i++) {
|
||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=ayBuf[ch][i];
|
||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=ayBuf[ch][i]<<2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -659,6 +659,7 @@ void DivPlatformAY8910::setFlags(unsigned int flags) {
|
|||
ay->device_reset();
|
||||
|
||||
stereo=(flags>>6)&1;
|
||||
stereoSep=(flags>>8)&255;
|
||||
}
|
||||
|
||||
int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
int dacPos;
|
||||
int dacSample;
|
||||
unsigned char sampleBank;
|
||||
unsigned char stereoSep;
|
||||
|
||||
int delay;
|
||||
|
||||
|
|
|
|||
|
|
@ -99,8 +99,8 @@ void DivPlatformAY8930::acquire(short* bufL, short* bufR, size_t start, size_t l
|
|||
ay->sound_stream_update(ayBuf,len);
|
||||
if (stereo) {
|
||||
for (size_t i=0; i<len; i++) {
|
||||
bufL[i+start]=ayBuf[0][i]+ayBuf[1][i];
|
||||
bufR[i+start]=ayBuf[1][i]+ayBuf[2][i];
|
||||
bufL[i+start]=ayBuf[0][i]+ayBuf[1][i]+((ayBuf[2][i]*stereoSep)>>8);
|
||||
bufR[i+start]=((ayBuf[0][i]*stereoSep)>>8)+ayBuf[1][i]+ayBuf[2][i];
|
||||
}
|
||||
} else {
|
||||
for (size_t i=0; i<len; i++) {
|
||||
|
|
@ -111,7 +111,7 @@ void DivPlatformAY8930::acquire(short* bufL, short* bufR, size_t start, size_t l
|
|||
|
||||
for (int ch=0; ch<3; ch++) {
|
||||
for (size_t i=0; i<len; i++) {
|
||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=ayBuf[ch][i];
|
||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=ayBuf[ch][i]<<2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -609,6 +609,7 @@ void DivPlatformAY8930::setFlags(unsigned int flags) {
|
|||
}
|
||||
|
||||
stereo=(flags>>6)&1;
|
||||
stereoSep=(flags>>8)&255;
|
||||
}
|
||||
|
||||
int DivPlatformAY8930::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ class DivPlatformAY8930: public DivDispatch {
|
|||
DivDispatchOscBuffer* oscBuf[3];
|
||||
unsigned char regPool[32];
|
||||
unsigned char ayNoiseAnd, ayNoiseOr;
|
||||
unsigned char stereoSep;
|
||||
bool bank;
|
||||
|
||||
int delay;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ void DivPlatformBubSysWSG::acquire(short* bufL, short* bufR, size_t start, size_
|
|||
for (size_t h=start; h<start+len; h++) {
|
||||
signed int out=0;
|
||||
// K005289 part
|
||||
k005289->tick();
|
||||
k005289.tick();
|
||||
|
||||
// Wavetable part
|
||||
for (int i=0; i<2; i++) {
|
||||
|
|
@ -52,7 +52,7 @@ void DivPlatformBubSysWSG::acquire(short* bufL, short* bufR, size_t start, size_
|
|||
oscBuf[i]->data[oscBuf[i]->needle++]=0;
|
||||
continue;
|
||||
} else {
|
||||
chanOut=chan[i].waveROM[k005289->addr(i)]*(regPool[2+i]&0xf);
|
||||
chanOut=chan[i].waveROM[k005289.addr(i)]*(regPool[2+i]&0xf);
|
||||
out+=chanOut;
|
||||
if (writeOscBuf==0) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=chanOut<<7;
|
||||
|
|
@ -122,9 +122,9 @@ void DivPlatformBubSysWSG::tick(bool sysTick) {
|
|||
chan[i].freq=0x1000-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>4095) chan[i].freq=4095;
|
||||
k005289->load(i,chan[i].freq);
|
||||
k005289.load(i,chan[i].freq);
|
||||
rWrite(i,chan[i].freq);
|
||||
k005289->update(i);
|
||||
k005289.update(i);
|
||||
if (chan[i].keyOn) {
|
||||
// ???
|
||||
}
|
||||
|
|
@ -295,7 +295,7 @@ void DivPlatformBubSysWSG::reset() {
|
|||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
k005289->reset();
|
||||
k005289.reset();
|
||||
}
|
||||
|
||||
bool DivPlatformBubSysWSG::isStereo() {
|
||||
|
|
@ -347,7 +347,6 @@ int DivPlatformBubSysWSG::init(DivEngine* p, int channels, int sugRate, unsigned
|
|||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
setFlags(flags);
|
||||
k005289=new k005289_core();
|
||||
reset();
|
||||
return 2;
|
||||
}
|
||||
|
|
@ -356,7 +355,6 @@ void DivPlatformBubSysWSG::quit() {
|
|||
for (int i=0; i<2; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
delete k005289;
|
||||
}
|
||||
|
||||
DivPlatformBubSysWSG::~DivPlatformBubSysWSG() {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
#include <queue>
|
||||
#include "../macroInt.h"
|
||||
#include "../waveSynth.h"
|
||||
#include "sound/k005289/k005289.hpp"
|
||||
#include "vgsound_emu/src/k005289/k005289.hpp"
|
||||
|
||||
class DivPlatformBubSysWSG: public DivDispatch {
|
||||
struct Channel {
|
||||
|
|
@ -60,7 +60,7 @@ class DivPlatformBubSysWSG: public DivDispatch {
|
|||
bool isMuted[2];
|
||||
unsigned char writeOscBuf;
|
||||
|
||||
k005289_core* k005289;
|
||||
k005289_core k005289;
|
||||
unsigned short regPool[4];
|
||||
void updateWave(int ch);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
#include "../dispatch.h"
|
||||
#include <deque>
|
||||
|
||||
#define KVS(x,y) ((chan[x].state.op[y].kvs==2 && isOutput[chan[x].state.alg][y]) || chan[x].state.op[y].kvs==1)
|
||||
|
||||
class DivPlatformFMBase: public DivDispatch {
|
||||
protected:
|
||||
const bool isOutput[8][4]={
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -327,7 +327,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -384,7 +384,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -501,7 +501,7 @@ void DivPlatformGenesis::muteChannel(int ch, bool mute) {
|
|||
if (isMuted[ch]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[ch].state.alg][j]) {
|
||||
if (KVS(ch,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[ch].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -614,7 +614,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
||||
if (KVS(c.chan,i)) {
|
||||
if (!chan[c.chan].active || chan[c.chan].insChanged) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
}
|
||||
|
|
@ -687,7 +687,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
||||
if (KVS(c.chan,i)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -860,7 +860,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[c.chan].state.alg][c.value]) {
|
||||
if (KVS(c.chan,c.value)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -1051,7 +1051,7 @@ void DivPlatformGenesis::forceIns() {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
|||
op.tl=c.value2;
|
||||
if (isOpMuted[ch]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (isOutput[chan[2].state.alg][c.value]) {
|
||||
} else if (KVS(2,c.value)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
|
|
@ -392,7 +392,7 @@ void DivPlatformGenesisExt::muteChannel(int ch, bool mute) {
|
|||
if (isOpMuted[ch-2]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
immWrite(baseAddr+0x40,127);
|
||||
} else if (isOutput[chan[2].state.alg][ordch]) {
|
||||
} else if (KVS(2,ordch)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127));
|
||||
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127));
|
||||
} else {
|
||||
|
|
@ -526,7 +526,7 @@ void DivPlatformGenesisExt::forceIns() {
|
|||
if (i==2 && extMode) { // extended channel
|
||||
if (isOpMuted[j]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (isOutput[chan[i].state.alg][j]) {
|
||||
} else if (KVS(i,j)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
|
|
@ -535,7 +535,7 @@ void DivPlatformGenesisExt::forceIns() {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
|
|||
|
|
@ -68,9 +68,11 @@ void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t
|
|||
delay=w.delay;
|
||||
}
|
||||
} else {
|
||||
delay--;
|
||||
delay-=3;
|
||||
}
|
||||
|
||||
msm.tick();
|
||||
msm.tick();
|
||||
msm.tick();
|
||||
|
||||
bufL[h]=msm.out()<<4;
|
||||
|
|
@ -79,7 +81,7 @@ void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t
|
|||
updateOsc=0;
|
||||
// TODO: per-channel osc
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=msm.m_voice[i].m_muted?0:(msm.m_voice[i].m_out<<6);
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=msm.voice_out(i)<<6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -113,7 +115,7 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
|
|||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
rWriteDelay(0,(8<<c.chan),60); // turn off
|
||||
rWriteDelay(0,(8<<c.chan),180); // turn off
|
||||
rWrite(0,0x80|chan[c.chan].sample); // set phrase
|
||||
rWrite(0,(16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
|
||||
} else {
|
||||
|
|
@ -128,7 +130,7 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
|
|||
}
|
||||
//DivSample* s=parent->getSample(12*sampleBank+c.value%12);
|
||||
chan[c.chan].sample=12*sampleBank+c.value%12;
|
||||
rWriteDelay(0,(8<<c.chan),60); // turn off
|
||||
rWriteDelay(0,(8<<c.chan),180); // turn off
|
||||
rWrite(0,0x80|chan[c.chan].sample); // set phrase
|
||||
rWrite(0,(16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
|
||||
}
|
||||
|
|
@ -138,14 +140,14 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
|
|||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
rWriteDelay(0,(8<<c.chan),60); // turn off
|
||||
rWriteDelay(0,(8<<c.chan),180); // turn off
|
||||
chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
rWriteDelay(0,(8<<c.chan),60); // turn off
|
||||
rWriteDelay(0,(8<<c.chan),180); // turn off
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
|
|
@ -206,7 +208,7 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
|
|||
|
||||
void DivPlatformMSM6295::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
msm.m_voice[ch].m_muted=mute;
|
||||
msm.voice_mute(ch,mute);
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::forceIns() {
|
||||
|
|
@ -255,6 +257,7 @@ void DivPlatformMSM6295::reset() {
|
|||
for (int i=0; i<4; i++) {
|
||||
chan[i]=DivPlatformMSM6295::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
msm.voice_mute(i,isMuted[i]);
|
||||
}
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].vol=8;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
#include "../dispatch.h"
|
||||
#include "../macroInt.h"
|
||||
#include <queue>
|
||||
#include "sound/oki/msm6295.hpp"
|
||||
#include "vgsound_emu/src/msm6295/msm6295.hpp"
|
||||
|
||||
class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf {
|
||||
protected:
|
||||
|
|
@ -57,7 +57,7 @@ class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf {
|
|||
unsigned short addr;
|
||||
unsigned char val;
|
||||
unsigned short delay;
|
||||
QueuedWrite(unsigned short a, unsigned char v, unsigned short d=32):
|
||||
QueuedWrite(unsigned short a, unsigned char v, unsigned short d=96):
|
||||
addr(a),
|
||||
val(v),
|
||||
delay(d) {}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ void DivPlatformN163::acquire(short* bufL, short* bufR, size_t start, size_t len
|
|||
bufL[i]=bufR[i]=out;
|
||||
|
||||
if (n163.voice_cycle()==0x78) for (int i=0; i<8; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=n163.chan_out(i)<<7;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=n163.voice_out(i)<<7;
|
||||
}
|
||||
|
||||
// command queue
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
#include <queue>
|
||||
#include "../macroInt.h"
|
||||
#include "../waveSynth.h"
|
||||
#include "sound/n163/n163.hpp"
|
||||
#include "vgsound_emu/src/n163/n163.hpp"
|
||||
|
||||
class DivPlatformN163: public DivDispatch {
|
||||
struct Channel {
|
||||
|
|
|
|||
|
|
@ -320,9 +320,10 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
unsigned int dpcmAddr=parent->getSample(dacSample)->offDPCM;
|
||||
unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4;
|
||||
if (dpcmLen>255) dpcmLen=255;
|
||||
goingToLoop=parent->getSample(dacSample)->isLoopable();
|
||||
// write DPCM
|
||||
rWrite(0x4015,15);
|
||||
rWrite(0x4010,calcDPCMRate(dacRate));
|
||||
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
|
||||
rWrite(0x4012,(dpcmAddr>>6)&0xff);
|
||||
rWrite(0x4013,dpcmLen&0xff);
|
||||
rWrite(0x4015,31);
|
||||
|
|
@ -330,7 +331,7 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
}
|
||||
} else {
|
||||
if (dpcmMode) {
|
||||
rWrite(0x4010,calcDPCMRate(dacRate));
|
||||
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
|
||||
}
|
||||
}
|
||||
if (dumpWrites && !dpcmMode) addWrite(0xffff0001,dacRate);
|
||||
|
|
@ -385,9 +386,10 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
unsigned int dpcmAddr=parent->getSample(dacSample)->offDPCM;
|
||||
unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4;
|
||||
if (dpcmLen>255) dpcmLen=255;
|
||||
goingToLoop=parent->getSample(dacSample)->isLoopable();
|
||||
// write DPCM
|
||||
rWrite(0x4015,15);
|
||||
rWrite(0x4010,calcDPCMRate(dacRate));
|
||||
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
|
||||
rWrite(0x4012,(dpcmAddr>>6)&0xff);
|
||||
rWrite(0x4013,dpcmLen&0xff);
|
||||
rWrite(0x4015,31);
|
||||
|
|
@ -612,6 +614,7 @@ void DivPlatformNES::reset() {
|
|||
sampleBank=0;
|
||||
dpcmBank=0;
|
||||
dpcmMode=false;
|
||||
goingToLoop=false;
|
||||
|
||||
if (useNP) {
|
||||
nes1_NP->Reset();
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ class DivPlatformNES: public DivDispatch {
|
|||
bool dpcmMode;
|
||||
bool dacAntiClickOn;
|
||||
bool useNP;
|
||||
bool goingToLoop;
|
||||
struct NESAPU* nes;
|
||||
xgm::NES_APU* nes1_NP;
|
||||
xgm::NES_DMC* nes2_NP;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@
|
|||
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
||||
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#define KVSL(x,y) ((chan[x].state.op[orderedOpsL1[ops==4][y]].kvs==2 && isOutputL[ops==4][chan[x].state.alg][y]) || chan[x].state.op[orderedOpsL1[ops==4][y]].kvs==1)
|
||||
|
||||
#define CHIP_FREQBASE chipFreqBase
|
||||
|
||||
// N = invalid
|
||||
|
|
@ -138,6 +140,11 @@ const bool isOutputL[2][4][4]={
|
|||
|
||||
#undef N
|
||||
|
||||
const int orderedOpsL1[2][4]={
|
||||
{0, 1, 0, 1}, // 2-op
|
||||
{0, 2, 1, 3} // 4-op
|
||||
};
|
||||
|
||||
const int orderedOpsL[4]={
|
||||
0,2,1,3
|
||||
};
|
||||
|
|
@ -249,7 +256,9 @@ void DivPlatformOPL::acquire_nuked(short* bufL, short* bufR, size_t start, size_
|
|||
if (os[1]>32767) os[1]=32767;
|
||||
|
||||
bufL[h]=os[0];
|
||||
bufR[h]=os[1];
|
||||
if (oplType==3 || oplType==759) {
|
||||
bufR[h]=os[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +295,7 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) {
|
||||
if (KVSL(i,j) || i>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -404,7 +413,7 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) {
|
||||
if (KVSL(i,j) || i>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -608,7 +617,7 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
|||
if (isMuted[ch]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[ch].state.alg][i] || ch>melodicChans) {
|
||||
if (KVSL(ch,i) || ch>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -781,7 +790,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
|
||||
if (KVSL(c.chan,i) || c.chan>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -897,7 +906,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
|
||||
if (KVSL(c.chan,i) || c.chan>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -1046,7 +1055,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>melodicChans) {
|
||||
if (KVSL(c.chan,c.value) || c.chan>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -1276,7 +1285,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
|
||||
if (KVSL(c.chan,i) || c.chan>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -1293,7 +1302,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>melodicChans) {
|
||||
if (KVSL(c.chan,c.value) || c.chan>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -1370,7 +1379,7 @@ void DivPlatformOPL::forceIns() {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) {
|
||||
if (KVSL(i,j) || i>melodicChans) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
|
|
@ -1419,6 +1428,15 @@ DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) {
|
|||
|
||||
DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) {
|
||||
if (ch>=18) return NULL;
|
||||
if (oplType==3 && ch<12) {
|
||||
if (chan[ch&(~1)].fourOp) {
|
||||
if (ch&1) {
|
||||
return oscBuf[ch-1];
|
||||
} else {
|
||||
return oscBuf[ch+1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
|
|
@ -1520,7 +1538,7 @@ void DivPlatformOPL::reset() {
|
|||
}
|
||||
|
||||
bool DivPlatformOPL::isStereo() {
|
||||
return true;
|
||||
return (oplType==3 || oplType==759);
|
||||
}
|
||||
|
||||
bool DivPlatformOPL::keyOffAffectsArp(int ch) {
|
||||
|
|
|
|||
|
|
@ -243,8 +243,9 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
|||
if (chan[i].freqChanged) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)*2,chan[i].pitch2,chipClock,CHIP_FREQBASE);
|
||||
if (chan[i].fixedFreq>0) chan[i].freq=chan[i].fixedFreq;
|
||||
if (chan[i].freq>262143) chan[i].freq=262143;
|
||||
int freqt=toFreq(chan[i].freq)+chan[i].pitch2;
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>65535) chan[i].freq=65535;
|
||||
int freqt=toFreq(chan[i].freq);
|
||||
chan[i].freqL=freqt&0xff;
|
||||
if (i>=6 && properDrums) {
|
||||
immWrite(0x10+drumSlot[i],freqt&0xff);
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ void DivPlatformSCC::acquire(short* bufL, short* bufR, size_t start, size_t len)
|
|||
bufL[h]=bufR[h]=out;
|
||||
|
||||
for (int i=0; i<5; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=scc->chan_out(i)<<7;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=scc->voice_out(i)<<7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
#include <queue>
|
||||
#include "../macroInt.h"
|
||||
#include "../waveSynth.h"
|
||||
#include "sound/scc/scc.hpp"
|
||||
#include "vgsound_emu/src/scc/scc.hpp"
|
||||
|
||||
class DivPlatformSCC: public DivDispatch {
|
||||
struct Channel {
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_
|
|||
if (isMuted[i]) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=0;
|
||||
} else {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=sn_nuked.vol_table[sn_nuked.volume_out[i]];
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=sn_nuked.vol_table[sn_nuked.volume_out[i]]*3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ void DivPlatformSMS::acquire_mame(short* bufL, short* bufR, size_t start, size_t
|
|||
if (isMuted[i]) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=0;
|
||||
} else {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=sn->get_channel_output(i);
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=sn->get_channel_output(i)*3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Modifiers and Contributors for Furnace: cam900
|
||||
Konami K005289 emulation core
|
||||
|
||||
This chip is used at infamous Konami Bubble System, for part of Wavetable sound generator.
|
||||
But seriously, It is just to 2 internal 12 bit timer and address generators, rather than sound generator.
|
||||
|
||||
Everything except for internal counter and address are done by external logic, the chip is only has external address, frequency registers and its update pins.
|
||||
|
||||
Frequency calculation: Input clock / (4096 - Pitch input)
|
||||
*/
|
||||
|
||||
#include "k005289.hpp"
|
||||
|
||||
void k005289_core::tick()
|
||||
{
|
||||
for (auto & elem : m_voice)
|
||||
elem.tick();
|
||||
}
|
||||
|
||||
void k005289_core::reset()
|
||||
{
|
||||
for (auto & elem : m_voice)
|
||||
elem.reset();
|
||||
}
|
||||
|
||||
void k005289_core::voice_t::tick()
|
||||
{
|
||||
if (bitfield(++counter, 0, 12) == 0)
|
||||
{
|
||||
addr = bitfield(addr + 1, 0, 5);
|
||||
counter = freq;
|
||||
}
|
||||
}
|
||||
|
||||
void k005289_core::voice_t::reset()
|
||||
{
|
||||
addr = 0;
|
||||
pitch = 0;
|
||||
freq = 0;
|
||||
counter = 0;
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Modifiers and Contributors for Furnace: cam900
|
||||
Konami K005289 emulation core
|
||||
|
||||
See k005289.cpp for more info.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifndef _VGSOUND_EMU_K005289_HPP
|
||||
#define _VGSOUND_EMU_K005289_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace k005289
|
||||
{
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef signed short s16;
|
||||
|
||||
// get bitfield, bitfield(input, position, len)
|
||||
template<typename T> T bitfield(T in, u8 pos, u8 len = 1)
|
||||
{
|
||||
return (in >> pos) & (len ? (T(1 << len) - 1) : 1);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace k005289;
|
||||
class k005289_core
|
||||
{
|
||||
public:
|
||||
// accessors, getters, setters
|
||||
u8 addr(int voice) { return m_voice[voice & 1].addr; } // 1QA...E/2QA...E pin
|
||||
void load(int voice, u16 addr) { m_voice[voice & 1].load(addr); } // LD1/2 pin, A0...11 pin
|
||||
void update(int voice) { m_voice[voice & 1].update(); } // TG1/2 pin
|
||||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
|
||||
private:
|
||||
// k005289 voice structs
|
||||
struct voice_t
|
||||
{
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
|
||||
// accessors, getters, setters
|
||||
void load(u16 addr) { pitch = addr; } // Load pitch data (address pin)
|
||||
void update() { freq = pitch; } // Replace current frequency to lastest loaded pitch
|
||||
|
||||
// registers
|
||||
u8 addr = 0; // external address pin
|
||||
u16 pitch = 0; // pitch
|
||||
u16 freq = 0; // current frequency
|
||||
s16 counter = 0; // frequency counter
|
||||
};
|
||||
|
||||
voice_t m_voice[2];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Modifiers and Contributors for Furnace: cam900, tildearrow
|
||||
Namco 163 Sound emulation core
|
||||
|
||||
This chip is one of NES mapper with sound expansion, This one is by Namco.
|
||||
|
||||
It has 1 to 8 wavetable channels, All channel registers and waveforms are stored to internal RAM.
|
||||
4 bit Waveforms are freely allocatable, and its length is variables; its can be stores many short waveforms or few long waveforms in RAM.
|
||||
|
||||
But waveforms are needs to squash, reallocate to avoid conflict with channel register area, each channel register size is 8 bytes per channels.
|
||||
|
||||
Sound output is time division multiplexed, it's can be captured only single channels output at once. in reason, More activated channels are less sound quality.
|
||||
|
||||
Sound register layout
|
||||
|
||||
Address Bit Description
|
||||
7654 3210
|
||||
|
||||
78-7f Channel 0
|
||||
78 xxxx xxxx Channel 0 Pitch input bit 0-7
|
||||
79 xxxx xxxx Channel 0 Accumulator bit 0-7*
|
||||
7a xxxx xxxx Channel 0 Pitch input bit 8-15
|
||||
7b xxxx xxxx Channel 0 Accumulator bit 8-15*
|
||||
7c xxxx xx-- Channel 0 Waveform length, 256 - (x * 4)
|
||||
---- --xx Channel 0 Pitch input bit 16-17
|
||||
7d xxxx xxxx Channel 0 Accumulator bit 16-23*
|
||||
7e xxxx xxxx Channel 0 Waveform base offset
|
||||
xxxx xxx- RAM byte (0 to 127)
|
||||
---- ---x RAM nibble
|
||||
---- ---0 Low nibble
|
||||
---- ---1 High nibble
|
||||
7f ---- xxxx Channel 0 Volume
|
||||
|
||||
7f Number of active channels
|
||||
7f -xxx ---- Number of active channels
|
||||
-000 ---- Channel 0 activated
|
||||
-001 ---- Channel 1 activated
|
||||
-010 ---- Channel 2 activated
|
||||
...
|
||||
-110 ---- Channel 6 activated
|
||||
-111 ---- Channel 7 activated
|
||||
|
||||
70-77 Channel 1 (Optional if activated)
|
||||
68-6f Channel 2 (Optional if activated)
|
||||
...
|
||||
48-4f Channel 6 (Optional if activated)
|
||||
40-47 Channel 7 (Optional if activated)
|
||||
|
||||
Rest of RAM area are for 4 bit Waveform and/or scratchpad.
|
||||
Each waveform byte has 2 nibbles packed, fetches LSB first, MSB next.
|
||||
---- xxxx 4 bit waveform, LSB
|
||||
xxxx ---- Same as above, MSB
|
||||
|
||||
Waveform address: Waveform base offset + Bit 16 to 23 of Accumulator, 1 LSB of result is nibble select, 7 MSB of result is Byte address in RAM.
|
||||
|
||||
Frequency formula:
|
||||
Frequency: Pitch input * ((Input clock * 15 * Number of activated voices) / 65536)
|
||||
|
||||
There's to way for reduce N163 noises: reduce channel limit and demultiplex
|
||||
- Channel limit is runtime changeable and it makes some usable effects.
|
||||
- Demultiplex is used for "non-ear destroyable" emulators, but less hardware accurate. (when LPF and RF filter is not considered)
|
||||
This core is support both, You can choose output behavior
|
||||
|
||||
*/
|
||||
|
||||
#include "n163.hpp"
|
||||
#include <string.h>
|
||||
|
||||
void n163_core::tick()
|
||||
{
|
||||
if (m_multiplex)
|
||||
m_out = 0;
|
||||
// 0xe000-0xe7ff Disable sound bits (bit 6, bit 0 to 5 are CPU ROM Bank 0x8000-0x9fff select.)
|
||||
if (m_disable)
|
||||
{
|
||||
if (!m_multiplex)
|
||||
m_out = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// tick per each clock
|
||||
const u32 freq = m_ram[m_voice_cycle + 0] | (u32(m_ram[m_voice_cycle + 2]) << 8) | (bitfield<u32>(m_ram[m_voice_cycle + 4], 0, 2) << 16); // 18 bit frequency
|
||||
u32 accum = m_ram[m_voice_cycle + 1] | (u32(m_ram[m_voice_cycle + 3]) << 8) | ( u32(m_ram[m_voice_cycle + 5]) << 16); // 24 bit accumulator
|
||||
const u16 length = 256 - (m_ram[m_voice_cycle + 4] & 0xfc);
|
||||
const u8 addr = m_ram[m_voice_cycle + 6] + bitfield(accum, 16, 8);
|
||||
const s16 wave = (bitfield(m_ram[bitfield(addr, 1, 7)], bitfield(addr, 0) << 2, 4) - 8);
|
||||
const s16 volume = bitfield(m_ram[m_voice_cycle + 7], 0, 4);
|
||||
|
||||
// accumulate address
|
||||
accum = bitfield(accum + freq, 0, 24);
|
||||
if (bitfield(accum, 16, 8) >= length)
|
||||
accum = bitfield(accum, 0, 18);
|
||||
|
||||
// writeback to register
|
||||
m_ram[m_voice_cycle + 1] = bitfield(accum, 0, 8);
|
||||
m_ram[m_voice_cycle + 3] = bitfield(accum, 8, 8);
|
||||
m_ram[m_voice_cycle + 5] = bitfield(accum, 16, 8);
|
||||
|
||||
const u8 prev_voice_cycle = m_voice_cycle;
|
||||
|
||||
// update voice cycle
|
||||
bool flush = m_multiplex ? true : false;
|
||||
m_voice_cycle -= 0x8;
|
||||
if (m_voice_cycle < (0x78 - (bitfield(m_ram[0x7f], 4, 3) << 3)))
|
||||
{
|
||||
if (!m_multiplex)
|
||||
flush = true;
|
||||
m_voice_cycle = 0x78;
|
||||
}
|
||||
|
||||
// output 4 bit waveform and volume, multiplexed
|
||||
const u8 chan_index = ((0x78-prev_voice_cycle)>>3)&7;
|
||||
m_ch_out[chan_index]=wave * volume;
|
||||
m_acc += m_ch_out[chan_index];
|
||||
if (flush)
|
||||
{
|
||||
m_out = m_acc / (m_multiplex ? 1 : (bitfield(m_ram[0x7f], 4, 3) + 1));
|
||||
m_acc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void n163_core::reset()
|
||||
{
|
||||
// reset this chip
|
||||
m_disable = false;
|
||||
m_multiplex = true;
|
||||
memset(m_ram,0,sizeof(m_ram));
|
||||
m_voice_cycle = 0x78;
|
||||
m_addr_latch.reset();
|
||||
m_out = 0;
|
||||
m_acc = 0;
|
||||
memset(m_ch_out,0,sizeof(m_ch_out));
|
||||
}
|
||||
|
||||
// accessor
|
||||
void n163_core::addr_w(u8 data)
|
||||
{
|
||||
// 0xf800-0xffff Sound address, increment
|
||||
m_addr_latch.addr = bitfield(data, 0, 7);
|
||||
m_addr_latch.incr = bitfield(data, 7);
|
||||
}
|
||||
|
||||
void n163_core::data_w(u8 data, bool cpu_access)
|
||||
{
|
||||
// 0x4800-0x4fff Sound data write
|
||||
m_ram[m_addr_latch.addr] = data;
|
||||
|
||||
// address latch increment
|
||||
if (cpu_access && m_addr_latch.incr)
|
||||
m_addr_latch.addr = bitfield(m_addr_latch.addr + 1, 0, 7);
|
||||
}
|
||||
|
||||
u8 n163_core::data_r(bool cpu_access)
|
||||
{
|
||||
// 0x4800-0x4fff Sound data read
|
||||
const u8 ret = m_ram[m_addr_latch.addr];
|
||||
|
||||
// address latch increment
|
||||
if (cpu_access && m_addr_latch.incr)
|
||||
m_addr_latch.addr = bitfield(m_addr_latch.addr + 1, 0, 7);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Modifiers and Contributors for Furnace: cam900, tildearrow
|
||||
Namco 163 Sound emulation core
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifndef _VGSOUND_EMU_N163_HPP
|
||||
#define _VGSOUND_EMU_N163_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace n163
|
||||
{
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed short s16;
|
||||
|
||||
// get bitfield, bitfield(input, position, len)
|
||||
template<typename T> T bitfield(T in, u8 pos, u8 len = 1)
|
||||
{
|
||||
return (in >> pos) & (len ? (T(1 << len) - 1) : 1);
|
||||
}
|
||||
};
|
||||
|
||||
using namespace n163;
|
||||
class n163_core
|
||||
{
|
||||
public:
|
||||
// accessors, getters, setters
|
||||
void addr_w(u8 data);
|
||||
void data_w(u8 data, bool cpu_access = false);
|
||||
u8 data_r(bool cpu_access = false);
|
||||
|
||||
void set_disable(bool disable) { m_disable = disable; }
|
||||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
|
||||
// sound output pin
|
||||
s16 out() { return m_out; }
|
||||
|
||||
// get channel output
|
||||
s16 chan_out(u8 ch) { return m_ch_out[ch]; }
|
||||
|
||||
// get voice cycle
|
||||
u8 voice_cycle() { return m_voice_cycle; }
|
||||
|
||||
// register pool
|
||||
u8 reg(u8 addr) { return m_ram[addr & 0x7f]; }
|
||||
void set_multiplex(bool multiplex = true) { m_multiplex = multiplex; }
|
||||
|
||||
private:
|
||||
// Address latch
|
||||
struct addr_latch_t
|
||||
{
|
||||
addr_latch_t()
|
||||
: addr(0)
|
||||
, incr(0)
|
||||
{ }
|
||||
|
||||
void reset()
|
||||
{
|
||||
addr = 0;
|
||||
incr = 0;
|
||||
}
|
||||
|
||||
u8 addr : 7;
|
||||
u8 incr : 1;
|
||||
};
|
||||
|
||||
bool m_disable = false;
|
||||
u8 m_ram[0x80] = {0}; // internal 128 byte RAM
|
||||
u8 m_voice_cycle = 0x78; // Voice cycle for processing
|
||||
addr_latch_t m_addr_latch; // address latch
|
||||
s16 m_out = 0; // output
|
||||
s16 m_ch_out[8] = {0}; // per channel output
|
||||
// demultiplex related
|
||||
bool m_multiplex = true; // multiplex flag, but less noisy = inaccurate!
|
||||
s16 m_acc = 0; // accumulated output
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,215 +0,0 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Modifiers and Contributors for Furnace: tildearrow
|
||||
OKI MSM6295 emulation core
|
||||
|
||||
It is 4 channel ADPCM playback chip from OKI semiconductor.
|
||||
It was becomes de facto standard for ADPCM playback in arcade machine, due to cost performance.
|
||||
|
||||
The chip itself is pretty barebone: there is no "register" in chip.
|
||||
It can't control volume and pitch in currently playing channels, only stopable them.
|
||||
And volume is must be determined at playback start command.
|
||||
|
||||
Command format:
|
||||
|
||||
Playback command (2 byte):
|
||||
|
||||
Byte Bit Description
|
||||
76543210
|
||||
0 1xxxxxxx Phrase select (Header stored in ROM)
|
||||
1 x000---- Play channel 4
|
||||
0x00---- Play channel 3
|
||||
00x0---- Play channel 2
|
||||
000x---- Play channel 1
|
||||
----xxxx Volume
|
||||
----0000 0.0dB
|
||||
----0001 -3.2dB
|
||||
----0010 -6.0dB
|
||||
----0011 -9.2dB
|
||||
----0100 -12.0dB
|
||||
----0101 -14.5dB
|
||||
----0110 -18.0dB
|
||||
----0111 -20.5dB
|
||||
----1000 -24.0dB
|
||||
|
||||
Suspend command (1 byte):
|
||||
|
||||
Byte Bit Description
|
||||
76543210
|
||||
0 0x------ Suspend channel 4
|
||||
0-x----- Suspend channel 3
|
||||
0--x---- Suspend channel 2
|
||||
0---x--- Suspend channel 1
|
||||
|
||||
Frequency calculation:
|
||||
if (SS) then
|
||||
Frequency = Input clock / 165
|
||||
else then
|
||||
Frequency = Input clock / 132
|
||||
|
||||
*/
|
||||
|
||||
#include "msm6295.hpp"
|
||||
|
||||
#define CORE_DIVIDER 3
|
||||
|
||||
#define CHANNEL_DELAY (15/CORE_DIVIDER)
|
||||
#define MASTER_DELAY (33/CORE_DIVIDER)
|
||||
|
||||
void msm6295_core::tick()
|
||||
{
|
||||
// command handler
|
||||
if (m_command_pending)
|
||||
{
|
||||
if (bitfield(m_command, 7)) // play voice
|
||||
{
|
||||
if ((++m_clock) >= ((CHANNEL_DELAY * (m_ss ? 5 : 4))))
|
||||
{
|
||||
m_clock = 0;
|
||||
if (bitfield(m_next_command, 4, 4) != 0)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (bitfield(m_next_command, 4 + i))
|
||||
{
|
||||
if (!m_voice[i].m_busy)
|
||||
{
|
||||
m_voice[i].m_command = m_command;
|
||||
m_voice[i].m_volume = (bitfield(m_next_command, 0, 4) < 9) ? m_volume_table[std::min<u8>(8, bitfield(m_next_command, 0, 4))] : 0;
|
||||
}
|
||||
break; // voices aren't be playable simultaneously at once
|
||||
}
|
||||
}
|
||||
}
|
||||
m_command = 0;
|
||||
m_command_pending = false;
|
||||
}
|
||||
}
|
||||
else if (bitfield(m_next_command, 7)) // select phrase
|
||||
{
|
||||
if ((++m_clock) >= ((CHANNEL_DELAY * (m_ss ? 5 : 4))))
|
||||
{
|
||||
m_clock = 0;
|
||||
m_command = m_next_command;
|
||||
m_command_pending = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bitfield(m_next_command, 3, 4) != 0) // suspend voices
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (bitfield(m_next_command, 3 + i))
|
||||
{
|
||||
if (m_voice[i].m_busy)
|
||||
m_voice[i].m_command = m_next_command;
|
||||
}
|
||||
}
|
||||
m_next_command &= ~0x78;
|
||||
}
|
||||
m_command_pending = false;
|
||||
}
|
||||
}
|
||||
m_out = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
m_voice[i].tick();
|
||||
if (!m_voice[i].m_muted) m_out += m_voice[i].m_out;
|
||||
}
|
||||
}
|
||||
|
||||
void msm6295_core::reset()
|
||||
{
|
||||
for (auto & elem : m_voice)
|
||||
elem.reset();
|
||||
|
||||
m_command = 0;
|
||||
m_next_command = 0;
|
||||
m_command_pending = false;
|
||||
m_clock = 0;
|
||||
m_out = 0;
|
||||
}
|
||||
|
||||
void msm6295_core::voice_t::tick()
|
||||
{
|
||||
if (!m_busy)
|
||||
{
|
||||
if (bitfield(m_command, 7))
|
||||
{
|
||||
// get phrase header (stored in data memory)
|
||||
const u32 phrase = bitfield(m_command, 0, 7) << 3;
|
||||
// Start address
|
||||
m_addr = (bitfield(m_host.m_intf.read_byte(phrase | 0), 0, 2) << 16)
|
||||
| (m_host.m_intf.read_byte(phrase | 1) << 8)
|
||||
| (m_host.m_intf.read_byte(phrase | 2) << 0);
|
||||
// End address
|
||||
m_end = (bitfield(m_host.m_intf.read_byte(phrase | 3), 0, 2) << 16)
|
||||
| (m_host.m_intf.read_byte(phrase | 4) << 8)
|
||||
| (m_host.m_intf.read_byte(phrase | 5) << 0);
|
||||
m_nibble = 4; // MSB first, LSB second
|
||||
m_command = 0;
|
||||
m_busy = true;
|
||||
vox_decoder_t::reset();
|
||||
}
|
||||
m_out = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// playback
|
||||
if ((++m_clock) >= ((MASTER_DELAY * (m_host.m_ss ? 5 : 4))))
|
||||
{
|
||||
m_clock = 0;
|
||||
bool is_end = (m_command != 0);
|
||||
m_curr.decode(bitfield(m_host.m_intf.read_byte(m_addr), m_nibble, 4));
|
||||
if (m_nibble <= 0)
|
||||
{
|
||||
m_nibble = 4;
|
||||
if (++m_addr > m_end)
|
||||
is_end = true;
|
||||
}
|
||||
else
|
||||
m_nibble -= 4;
|
||||
if (is_end)
|
||||
{
|
||||
m_command = 0;
|
||||
m_busy = false;
|
||||
}
|
||||
m_out = (out() * m_volume) >> 7; // scale out to 12 bit output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void msm6295_core::voice_t::reset()
|
||||
{
|
||||
vox_decoder_t::reset();
|
||||
m_clock = 0;
|
||||
m_busy = false;
|
||||
m_command = 0;
|
||||
m_addr = 0;
|
||||
m_nibble = 0;
|
||||
m_end = 0;
|
||||
m_volume = 0;
|
||||
m_out = 0;
|
||||
}
|
||||
|
||||
// accessors
|
||||
u8 msm6295_core::busy_r()
|
||||
{
|
||||
return (m_voice[0].m_busy ? 0x01 : 0x00)
|
||||
| (m_voice[1].m_busy ? 0x02 : 0x00)
|
||||
| (m_voice[2].m_busy ? 0x04 : 0x00)
|
||||
| (m_voice[3].m_busy ? 0x08 : 0x00);
|
||||
}
|
||||
|
||||
void msm6295_core::command_w(u8 data)
|
||||
{
|
||||
if (!m_command_pending)
|
||||
{
|
||||
m_next_command = data;
|
||||
m_command_pending = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Modifiers and Contributors for Furnace: tildearrow
|
||||
OKI MSM6295 emulation core
|
||||
|
||||
See msm6295.cpp for more info.
|
||||
*/
|
||||
|
||||
#include "util.hpp"
|
||||
#include "vox.hpp"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifndef _VGSOUND_EMU_MSM6295_HPP
|
||||
#define _VGSOUND_EMU_MSM6295_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
class msm6295_core : public vox_core
|
||||
{
|
||||
friend class vgsound_emu_mem_intf; // common memory interface
|
||||
public:
|
||||
// constructor
|
||||
msm6295_core(vgsound_emu_mem_intf &intf)
|
||||
: m_voice{{*this,*this},{*this,*this},{*this,*this},{*this,*this}}
|
||||
, m_intf(intf)
|
||||
{
|
||||
}
|
||||
// accessors, getters, setters
|
||||
u8 busy_r();
|
||||
void command_w(u8 data);
|
||||
void ss_w(bool ss) { m_ss = ss; } // SS pin
|
||||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
|
||||
s32 out() { return m_out; } // built in 12 bit DAC
|
||||
|
||||
private:
|
||||
// Internal volume table, 9 step
|
||||
const s32 m_volume_table[9] = {
|
||||
32/* 0.0dB */,
|
||||
22/* -3.2dB */,
|
||||
16/* -6.0dB */,
|
||||
11/* -9.2dB */,
|
||||
8/* -12.0dB */,
|
||||
6/* -14.5dB */,
|
||||
4/* -18.0dB */,
|
||||
3/* -20.5dB */,
|
||||
2/* -24.0dB */ }; // scale out to 5 bit for optimization
|
||||
|
||||
public:
|
||||
// msm6295 voice structs
|
||||
struct voice_t : vox_decoder_t
|
||||
{
|
||||
// constructor
|
||||
voice_t(vox_core &vox, msm6295_core &host)
|
||||
: vox_decoder_t(vox)
|
||||
, m_host(host)
|
||||
{};
|
||||
|
||||
// internal state
|
||||
virtual void reset() override;
|
||||
void tick();
|
||||
|
||||
// accessors, getters, setters
|
||||
// registers
|
||||
msm6295_core &m_host;
|
||||
u16 m_clock = 0; // clock counter
|
||||
bool m_busy = false; // busy status
|
||||
bool m_muted = false; // muted
|
||||
u8 m_command = 0; // current command
|
||||
u32 m_addr = 0; // current address
|
||||
s8 m_nibble = 0; // current nibble
|
||||
u32 m_end = 0; // end address
|
||||
s32 m_volume = 0; // volume
|
||||
s32 m_out = 0; // output
|
||||
};
|
||||
voice_t m_voice[4];
|
||||
private:
|
||||
vgsound_emu_mem_intf &m_intf; // common memory interface
|
||||
|
||||
bool m_ss = false; // SS pin controls divider, input clock / 33 * (SS ? 5 : 4)
|
||||
u8 m_command = 0; // Command byte
|
||||
u8 m_next_command = 0; // Next command
|
||||
bool m_command_pending = false; // command pending flag
|
||||
u16 m_clock = 0; // clock counter
|
||||
s32 m_out = 0; // 12 bit output
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Modifiers and Contributors for Furnace: tildearrow
|
||||
Various core utilities for vgsound_emu
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef _VGSOUND_EMU_CORE_UTIL_HPP
|
||||
#define _VGSOUND_EMU_CORE_UTIL_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef signed char s8;
|
||||
typedef signed short s16;
|
||||
typedef signed int s32;
|
||||
typedef signed long long s64;
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
|
||||
const f64 PI = 3.1415926535897932384626433832795;
|
||||
|
||||
// get bitfield, bitfield(input, position, len)
|
||||
template<typename T> T bitfield(T in, u8 pos, u8 len = 1)
|
||||
{
|
||||
return (in >> pos) & (len ? (T(1 << len) - 1) : 1);
|
||||
}
|
||||
|
||||
// get sign extended value, sign_ext<type>(input, len)
|
||||
template<typename T> T sign_ext(T in, u8 len)
|
||||
{
|
||||
len = std::max<u8>(0, (8 * sizeof(T)) - len);
|
||||
return T(T(in) << len) >> len;
|
||||
}
|
||||
|
||||
// convert attenuation decibel value to gain
|
||||
inline f32 dB_to_gain(f32 attenuation)
|
||||
{
|
||||
return powf(10.0f, attenuation / 20.0f);
|
||||
}
|
||||
|
||||
class vgsound_emu_mem_intf
|
||||
{
|
||||
public:
|
||||
virtual u8 read_byte(u32 address) { return 0; }
|
||||
virtual u16 read_word(u32 address) { return 0; }
|
||||
virtual u32 read_dword(u32 address) { return 0; }
|
||||
virtual u64 read_qword(u32 address) { return 0; }
|
||||
virtual void write_byte(u32 address, u8 data) { }
|
||||
virtual void write_word(u32 address, u16 data) { }
|
||||
virtual void write_dword(u32 address, u32 data) { }
|
||||
virtual void write_qword(u32 address, u64 data) { }
|
||||
};
|
||||
|
||||
template<typename T, T InitWidth, u8 InitEdge = 0>
|
||||
struct clock_pulse_t
|
||||
{
|
||||
void reset(T init = InitWidth)
|
||||
{
|
||||
m_edge.reset();
|
||||
m_width = m_width_latch = m_counter = init;
|
||||
m_cycle = 0;
|
||||
}
|
||||
|
||||
bool tick(T width = 0)
|
||||
{
|
||||
bool carry = ((--m_counter) <= 0);
|
||||
if (carry)
|
||||
{
|
||||
if (!width)
|
||||
m_width = m_width_latch;
|
||||
else
|
||||
m_width = width; // reset width
|
||||
m_counter = m_width;
|
||||
m_cycle = 0;
|
||||
}
|
||||
else
|
||||
m_cycle++;
|
||||
|
||||
m_edge.tick(carry);
|
||||
return carry;
|
||||
}
|
||||
|
||||
void set_width(T width) { m_width = width; }
|
||||
void set_width_latch(T width) { m_width_latch = width; }
|
||||
|
||||
// Accessors
|
||||
bool current_edge() { return m_edge.m_current; }
|
||||
bool rising_edge() { return m_edge.m_rising; }
|
||||
bool falling_edge() { return m_edge.m_rising; }
|
||||
T cycle() { return m_cycle; }
|
||||
|
||||
struct edge_t
|
||||
{
|
||||
edge_t()
|
||||
: m_current(InitEdge ^ 1)
|
||||
, m_previous(InitEdge)
|
||||
, m_rising(0)
|
||||
, m_falling(0)
|
||||
, m_changed(0)
|
||||
{
|
||||
set(InitEdge);
|
||||
}
|
||||
|
||||
void tick(bool toggle)
|
||||
{
|
||||
u8 current = m_current;
|
||||
if (toggle)
|
||||
current ^= 1;
|
||||
set(current);
|
||||
}
|
||||
|
||||
void set(u8 edge)
|
||||
{
|
||||
edge &= 1;
|
||||
m_rising = m_falling = m_changed = 0;
|
||||
if (m_current != edge)
|
||||
{
|
||||
m_changed = 1;
|
||||
if (m_current && (!edge))
|
||||
m_falling = 1;
|
||||
else if ((!m_current) && edge)
|
||||
m_rising = 1;
|
||||
m_current = edge;
|
||||
}
|
||||
m_previous = m_current;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_previous = InitEdge;
|
||||
m_current = InitEdge ^ 1;
|
||||
set(InitEdge);
|
||||
}
|
||||
|
||||
u8 m_current : 1; // current edge
|
||||
u8 m_previous : 1; // previous edge
|
||||
u8 m_rising : 1; // rising edge
|
||||
u8 m_falling : 1; // falling edge
|
||||
u8 m_changed : 1; // changed flag
|
||||
};
|
||||
|
||||
edge_t m_edge;
|
||||
T m_width = InitWidth; // clock pulse width
|
||||
T m_width_latch = InitWidth; // clock pulse width latch
|
||||
T m_counter = InitWidth; // clock counter
|
||||
T m_cycle = 0; // clock cycle
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Modifiers and Contributors for Furnace: tildearrow
|
||||
Dialogic ADPCM core
|
||||
*/
|
||||
|
||||
#include "util.hpp"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifndef _VGSOUND_EMU_CORE_VOX_HPP
|
||||
#define _VGSOUND_EMU_CORE_VOX_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MODIFIED_CLAMP(x,xMin,xMax) (std::min(std::max((x),(xMin)),(xMax)))
|
||||
|
||||
class vox_core
|
||||
{
|
||||
protected:
|
||||
struct vox_decoder_t
|
||||
{
|
||||
vox_decoder_t(vox_core &vox)
|
||||
: m_curr(vox)
|
||||
, m_loop(vox)
|
||||
{ };
|
||||
|
||||
virtual void reset()
|
||||
{
|
||||
m_curr.reset();
|
||||
m_loop.reset();
|
||||
m_loop_saved = false;
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
if (!m_loop_saved)
|
||||
{
|
||||
m_loop.copy(m_curr);
|
||||
m_loop_saved = true;
|
||||
}
|
||||
}
|
||||
|
||||
void restore()
|
||||
{
|
||||
if (m_loop_saved)
|
||||
m_curr.copy(m_loop);
|
||||
}
|
||||
|
||||
s32 out() { return m_curr.m_step; }
|
||||
|
||||
struct decoder_state_t
|
||||
{
|
||||
decoder_state_t(vox_core &vox)
|
||||
: m_vox(vox)
|
||||
{ };
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_index = 0;
|
||||
m_step = 16;
|
||||
}
|
||||
|
||||
void copy(decoder_state_t src)
|
||||
{
|
||||
m_index = src.m_index;
|
||||
m_step = src.m_step;
|
||||
}
|
||||
|
||||
void decode(u8 nibble)
|
||||
{
|
||||
const u8 delta = bitfield(nibble, 0, 3);
|
||||
s16 ss = m_vox.m_step_table[m_index]; // ss(n)
|
||||
|
||||
// d(n) = (ss(n) * B2) + ((ss(n) / 2) * B1) + ((ss(n) / 4) * B0) + (ss(n) / 8)
|
||||
s16 d = ss >> 3;
|
||||
if (bitfield(delta, 2))
|
||||
d += ss;
|
||||
if (bitfield(delta, 1))
|
||||
d += (ss >> 1);
|
||||
if (bitfield(delta, 0))
|
||||
d += (ss >> 2);
|
||||
|
||||
// if (B3 = 1) then d(n) = d(n) * (-1) X(n) = X(n-1) * d(n)
|
||||
if (bitfield(nibble, 3))
|
||||
m_step = std::max(m_step - d, -2048);
|
||||
else
|
||||
m_step = std::min(m_step + d, 2047);
|
||||
|
||||
// adjust step index
|
||||
m_index = MODIFIED_CLAMP(m_index + m_vox.m_index_table[delta], 0, 48);
|
||||
}
|
||||
|
||||
vox_core &m_vox;
|
||||
s8 m_index = 0;
|
||||
s32 m_step = 16;
|
||||
};
|
||||
|
||||
decoder_state_t m_curr;
|
||||
decoder_state_t m_loop;
|
||||
bool m_loop_saved = false;
|
||||
};
|
||||
|
||||
s8 m_index_table[8] = {-1, -1, -1, -1, 2, 4, 6, 8};
|
||||
s32 m_step_table[49] = {
|
||||
16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
||||
50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143,
|
||||
157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449,
|
||||
494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,617 +0,0 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Contributor(s): Natt Akuma, James Alan Nguyen, Laurens Holst
|
||||
Modifiers and Contributors for Furnace: Natt Akuma, tildearrow, Grauw
|
||||
Konami SCC emulation core
|
||||
|
||||
Konami SCC means "Sound Creative Chip", it's actually MSX MegaROM/RAM Mapper with 5 channel Wavetable sound generator.
|
||||
|
||||
It was first appeared at 1987, F-1 Spirit and Nemesis 2/Gradius 2 for MSX. then several MSX cartridges used that until 1990, Metal Gear 2: Solid Snake.
|
||||
Even after MSX is discontinued, it was still used at some low-end arcade and amusement hardwares.
|
||||
and some Third-party MSX utilities still support this due to its market shares.
|
||||
|
||||
There's 2 SCC types:
|
||||
|
||||
K051649 (or simply known as SCC)
|
||||
This chip is used for MSX MegaROM Mapper, some arcade machines.
|
||||
Channel 4 and 5 must be share waveform, other channels has its own waveforms.
|
||||
|
||||
K052539 (also known as SCC+)
|
||||
This chip is used for MSX MegaRAM Mapper (Konami Sound Cartridges for Snatcher/SD Snatcher).
|
||||
All channels can be has its own waveforms, and also has backward compatibility mode with K051649.
|
||||
|
||||
Based on:
|
||||
https://www.msx.org/wiki/MegaROM_Mappers
|
||||
https://www.msx.org/wiki/Konami_051649
|
||||
https://www.msx.org/wiki/Konami_052539
|
||||
http://bifi.msxnet.org/msxnet/tech/scc
|
||||
http://bifi.msxnet.org/msxnet/tech/soundcartridge
|
||||
|
||||
K051649 Register Layout
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
4000-bfff MegaROM Mapper
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
4000-5fff xxxx xxxx R Bank page 0
|
||||
c000-dfff mirror of 4000-5fff
|
||||
|
||||
6000-7fff xxxx xxxx R Bank page 1
|
||||
e000-ffff mirror of 6000-7fff
|
||||
|
||||
8000-9fff xxxx xxxx R Bank page 2
|
||||
0000-1fff mirror of 8000-9fff
|
||||
|
||||
a000-bfff xxxx xxxx R Bank page 3
|
||||
2000-3fff mirror of a000-bfff
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
5000-57ff, 7000-77ff, 9000-97ff, b000-b7ff Bank select
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
5000 --xx xxxx W Bank select, Page 0
|
||||
5001-57ff Mirror of 5000
|
||||
|
||||
7000 --xx xxxx W Bank select, Page 1
|
||||
7001-77ff Mirror of 7000
|
||||
|
||||
9000 --xx xxxx W Bank select, Page 2
|
||||
--11 1111 W SCC Enable
|
||||
9001-97ff Mirror of 9000
|
||||
|
||||
b000 --xx xxxx W Bank select, Page 3
|
||||
b001-b7ff Mirror of b000
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
9800-9fff SCC register
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
9800-987f Waveform
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
9800-981f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed)
|
||||
9820-983f xxxx xxxx R/W Channel 1 ""
|
||||
9840-985f xxxx xxxx R/W Channel 2 ""
|
||||
9860-987f xxxx xxxx R/W Channel 3/4 ""
|
||||
|
||||
9880-9889 Pitch
|
||||
|
||||
9880 xxxx xxxx W Channel 0 Pitch LSB
|
||||
9881 ---- xxxx W Channel 0 Pitch MSB
|
||||
9882 xxxx xxxx W Channel 1 Pitch LSB
|
||||
9883 ---- xxxx W Channel 1 Pitch MSB
|
||||
9884 xxxx xxxx W Channel 2 Pitch LSB
|
||||
9885 ---- xxxx W Channel 2 Pitch MSB
|
||||
9886 xxxx xxxx W Channel 3 Pitch LSB
|
||||
9887 ---- xxxx W Channel 3 Pitch MSB
|
||||
9888 xxxx xxxx W Channel 4 Pitch LSB
|
||||
9889 ---- xxxx W Channel 4 Pitch MSB
|
||||
|
||||
9888-988e Volume
|
||||
|
||||
988a ---- xxxx W Channel 0 Volume
|
||||
988b ---- xxxx W Channel 1 Volume
|
||||
988c ---- xxxx W Channel 2 Volume
|
||||
988d ---- xxxx W Channel 3 Volume
|
||||
988e ---- xxxx W Channel 4 Volume
|
||||
|
||||
988f ---x ---- W Channel 4 Output enable/disable flag
|
||||
---- x--- W Channel 3 Output enable/disable flag
|
||||
---- -x-- W Channel 2 Output enable/disable flag
|
||||
---- --x- W Channel 1 Output enable/disable flag
|
||||
---- ---x W Channel 0 Output enable/disable flag
|
||||
|
||||
9890-989f Mirror of 9880-988f
|
||||
|
||||
98a0-98bf xxxx xxxx R Channel 4 Waveform
|
||||
|
||||
98e0 x--- ---- W Waveform rotate flag for channel 4
|
||||
-x-- ---- W Waveform rotate flag for all channels
|
||||
--x- ---- W Reset waveform position after pitch writes
|
||||
---- --x- W 8 bit frequency
|
||||
---- --0x W 4 bit frequency
|
||||
|
||||
98e1-98ff Mirror of 98e0
|
||||
|
||||
9900-9fff Mirror of 9800-98ff
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
K052539 Register Layout
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
4000-bfff MegaRAM Mapper
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
4000-5fff xxxx xxxx R/W Bank page 0
|
||||
c000-dfff xxxx xxxx R/W ""
|
||||
|
||||
6000-7fff xxxx xxxx R/W Bank page 1
|
||||
e000-ffff xxxx xxxx R/W ""
|
||||
|
||||
8000-9fff xxxx xxxx R/W Bank page 2
|
||||
0000-1fff xxxx xxxx R/W ""
|
||||
|
||||
a000-bfff xxxx xxxx R/W Bank page 3
|
||||
2000-3fff xxxx xxxx R/W ""
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
5000-57ff, 7000-77ff, 9000-97ff, b000-b7ff Bank select
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
5000 xxxx xxxx W Bank select, Page 0
|
||||
5001-57ff Mirror of 5000
|
||||
|
||||
7000 xxxx xxxx W Bank select, Page 1
|
||||
7001-77ff Mirror of 7000
|
||||
|
||||
9000 xxxx xxxx W Bank select, Page 2
|
||||
--11 1111 W SCC Enable (SCC Compatible mode)
|
||||
9001-97ff Mirror of 9000
|
||||
|
||||
b000 xxxx xxxx W Bank select, Page 3
|
||||
1--- ---- W SCC+ Enable (SCC+ mode)
|
||||
b001-b7ff Mirror of b000
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
bffe-bfff Mapper configuration
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
bffe --x- ---- W SCC operation mode
|
||||
--0- ---- W SCC Compatible mode
|
||||
--1- ---- W SCC+ mode
|
||||
---x ---- W RAM write/Bank select toggle for all Bank pages
|
||||
---0 ---- W Bank select enable
|
||||
---1 ---- W RAM write enable
|
||||
---0 -x-- W RAM write/Bank select toggle for Bank page 2
|
||||
---0 --x- W RAM write/Bank select toggle for Bank page 1
|
||||
---0 ---x W RAM write/Bank select toggle for Bank page 0
|
||||
bfff Mirror of bffe
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
9800-9fff SCC Compatible mode register
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
9800-987f Waveform
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
9800-981f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed)
|
||||
9820-983f xxxx xxxx R/W Channel 1 ""
|
||||
9840-985f xxxx xxxx R/W Channel 2 ""
|
||||
9860-987f xxxx xxxx R/W Channel 3/4 ""
|
||||
|
||||
9880-9889 Pitch
|
||||
|
||||
9880 xxxx xxxx W Channel 0 Pitch LSB
|
||||
9881 ---- xxxx W Channel 0 Pitch MSB
|
||||
9882 xxxx xxxx W Channel 1 Pitch LSB
|
||||
9883 ---- xxxx W Channel 1 Pitch MSB
|
||||
9884 xxxx xxxx W Channel 2 Pitch LSB
|
||||
9885 ---- xxxx W Channel 2 Pitch MSB
|
||||
9886 xxxx xxxx W Channel 3 Pitch LSB
|
||||
9887 ---- xxxx W Channel 3 Pitch MSB
|
||||
9888 xxxx xxxx W Channel 4 Pitch LSB
|
||||
9889 ---- xxxx W Channel 4 Pitch MSB
|
||||
|
||||
9888-988e Volume
|
||||
|
||||
988a ---- xxxx W Channel 0 Volume
|
||||
988b ---- xxxx W Channel 1 Volume
|
||||
988c ---- xxxx W Channel 2 Volume
|
||||
988d ---- xxxx W Channel 3 Volume
|
||||
988e ---- xxxx W Channel 4 Volume
|
||||
|
||||
988f ---x ---- W Channel 4 Output enable/disable flag
|
||||
---- x--- W Channel 3 Output enable/disable flag
|
||||
---- -x-- W Channel 2 Output enable/disable flag
|
||||
---- --x- W Channel 1 Output enable/disable flag
|
||||
---- ---x W Channel 0 Output enable/disable flag
|
||||
|
||||
9890-989f Mirror of 9880-988f
|
||||
|
||||
98a0-98bf xxxx xxxx R Channel 4 Waveform
|
||||
|
||||
98c0 -x-- ---- W Waveform rotate flag for all channels
|
||||
--x- ---- W Reset waveform position after pitch writes
|
||||
---- --x- W 8 bit frequency
|
||||
---- --0x W 4 bit frequency
|
||||
|
||||
98c1-98df Mirror of 98c0
|
||||
|
||||
9900-9fff Mirror of 9800-98ff
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
b800-bfff SCC+ mode register
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
b800-b89f Waveform
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
b800-b81f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed)
|
||||
b820-b83f xxxx xxxx R/W Channel 1 ""
|
||||
b840-b85f xxxx xxxx R/W Channel 2 ""
|
||||
b860-b87f xxxx xxxx R/W Channel 3 ""
|
||||
b880-b89f xxxx xxxx R/W Channel 3 ""
|
||||
|
||||
b8a0-b8a9 Pitch
|
||||
|
||||
b8a0 xxxx xxxx W Channel 0 Pitch LSB
|
||||
b8a1 ---- xxxx W Channel 0 Pitch MSB
|
||||
b8a2 xxxx xxxx W Channel 1 Pitch LSB
|
||||
b8a3 ---- xxxx W Channel 1 Pitch MSB
|
||||
b8a4 xxxx xxxx W Channel 2 Pitch LSB
|
||||
b8a5 ---- xxxx W Channel 2 Pitch MSB
|
||||
b8a6 xxxx xxxx W Channel 3 Pitch LSB
|
||||
b8a7 ---- xxxx W Channel 3 Pitch MSB
|
||||
b8a8 xxxx xxxx W Channel 4 Pitch LSB
|
||||
b8a9 ---- xxxx W Channel 4 Pitch MSB
|
||||
|
||||
b8a8-b8ae Volume
|
||||
|
||||
b8aa ---- xxxx W Channel 0 Volume
|
||||
b8ab ---- xxxx W Channel 1 Volume
|
||||
b8ac ---- xxxx W Channel 2 Volume
|
||||
b8ad ---- xxxx W Channel 3 Volume
|
||||
b8ae ---- xxxx W Channel 4 Volume
|
||||
|
||||
b8af ---x ---- W Channel 4 Output enable/disable flag
|
||||
---- x--- W Channel 3 Output enable/disable flag
|
||||
---- -x-- W Channel 2 Output enable/disable flag
|
||||
---- --x- W Channel 1 Output enable/disable flag
|
||||
---- ---x W Channel 0 Output enable/disable flag
|
||||
|
||||
b8b0-b8bf Mirror of b8a0-b8af
|
||||
|
||||
b8c0 -x-- ---- W Waveform rotate flag for all channels
|
||||
--x- ---- W Reset waveform position after pitch writes
|
||||
---- --x- W 8 bit frequency
|
||||
---- --0x W 4 bit frequency
|
||||
|
||||
b8c1-b8df Mirror of b8c0
|
||||
|
||||
b900-bfff Mirror of b800-b8ff
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
SCC Frequency calculation:
|
||||
if 8 bit frequency then
|
||||
Frequency = Input clock / ((bit 0 to 7 of Pitch input) + 1)
|
||||
else if 4 bit frequency then
|
||||
Frequency = Input clock / ((bit 8 to 11 of Pitch input) + 1)
|
||||
else
|
||||
Frequency = Input clock / (Pitch input + 1)
|
||||
|
||||
*/
|
||||
|
||||
#include "scc.hpp"
|
||||
#include <string.h>
|
||||
|
||||
// shared SCC features
|
||||
void scc_core::tick()
|
||||
{
|
||||
m_out = 0;
|
||||
for (auto & elem : m_voice)
|
||||
{
|
||||
elem.tick();
|
||||
m_out += elem.out;
|
||||
}
|
||||
}
|
||||
|
||||
void scc_core::voice_t::tick()
|
||||
{
|
||||
if (pitch >= 9) // or voice is halted
|
||||
{
|
||||
// update counter - Post decrement
|
||||
u16 temp = counter;
|
||||
if (m_host.m_test.freq_4bit) // 4 bit frequency mode
|
||||
{
|
||||
counter = (counter & ~0x0ff) | (bitfield(bitfield(counter, 0, 8) - 1, 0, 8) << 0);
|
||||
counter = (counter & ~0xf00) | (bitfield(bitfield(counter, 8, 4) - 1, 0, 4) << 8);
|
||||
}
|
||||
else
|
||||
counter = bitfield(counter - 1, 0, 12);
|
||||
|
||||
// handle counter carry
|
||||
bool carry = m_host.m_test.freq_8bit ? (bitfield(temp, 0, 8) == 0) :
|
||||
(m_host.m_test.freq_4bit ? (bitfield(temp, 8, 4) == 0) :
|
||||
(bitfield(temp, 0, 12) == 0));
|
||||
if (carry)
|
||||
{
|
||||
addr = bitfield(addr + 1, 0, 5);
|
||||
counter = pitch;
|
||||
}
|
||||
}
|
||||
// get output
|
||||
if (enable)
|
||||
out = (wave[addr] * volume) >> 4; // scale to 11 bit digital output
|
||||
else
|
||||
out = 0;
|
||||
}
|
||||
|
||||
void scc_core::reset()
|
||||
{
|
||||
for (auto & elem : m_voice)
|
||||
elem.reset();
|
||||
|
||||
m_test.reset();
|
||||
m_out = 0;
|
||||
memset(m_reg,0,sizeof(m_reg));
|
||||
}
|
||||
|
||||
void scc_core::voice_t::reset()
|
||||
{
|
||||
memset(wave,0,sizeof(wave));
|
||||
enable = false;
|
||||
pitch = 0;
|
||||
volume = 0;
|
||||
addr = 0;
|
||||
counter = 0;
|
||||
out = 0;
|
||||
}
|
||||
|
||||
// SCC accessors
|
||||
u8 scc_core::wave_r(bool is_sccplus, u8 address)
|
||||
{
|
||||
u8 ret = 0xff;
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
if (voice > 4)
|
||||
return ret;
|
||||
|
||||
u8 wave_addr = bitfield(address, 0, 5);
|
||||
|
||||
if (m_test.rotate) // rotate flag
|
||||
wave_addr = bitfield(wave_addr + m_voice[voice].addr, 0, 5);
|
||||
|
||||
if (!is_sccplus)
|
||||
{
|
||||
if (voice == 3) // rotate voice 3~4 flag
|
||||
{
|
||||
if (m_test.rotate4 || m_test.rotate) // rotate flag
|
||||
wave_addr = bitfield(bitfield(address, 0, 5) + m_voice[3 + m_test.rotate].addr, 0, 5);
|
||||
}
|
||||
}
|
||||
ret = m_voice[voice].wave[wave_addr];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void scc_core::wave_w(bool is_sccplus, u8 address, u8 data)
|
||||
{
|
||||
if (m_test.rotate) // write protected
|
||||
return;
|
||||
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
if (voice > 4)
|
||||
return;
|
||||
|
||||
const u8 wave_addr = bitfield(address, 0, 5);
|
||||
|
||||
if (!is_sccplus)
|
||||
{
|
||||
if (((voice >= 3) && m_test.rotate4) || (voice >= 4)) // Ignore if write protected, or voice 4
|
||||
return;
|
||||
if (voice >= 3) // voice 3, 4 shares waveform
|
||||
{
|
||||
m_voice[3].wave[wave_addr] = data;
|
||||
m_voice[4].wave[wave_addr] = data;
|
||||
}
|
||||
else
|
||||
m_voice[voice].wave[wave_addr] = data;
|
||||
}
|
||||
else
|
||||
m_voice[voice].wave[wave_addr] = data;
|
||||
}
|
||||
|
||||
void scc_core::freq_vol_enable_w(u8 address, u8 data)
|
||||
{
|
||||
const u8 voice_freq = bitfield(address, 1, 3);
|
||||
const u8 voice_reg = bitfield(address, 0, 4);
|
||||
// *0-*f Pitch, Volume, Enable
|
||||
switch (voice_reg)
|
||||
{
|
||||
case 0x0: // 0x*0 Voice 0 Pitch LSB
|
||||
case 0x2: // 0x*2 Voice 1 Pitch LSB
|
||||
case 0x4: // 0x*4 Voice 2 Pitch LSB
|
||||
case 0x6: // 0x*6 Voice 3 Pitch LSB
|
||||
case 0x8: // 0x*8 Voice 4 Pitch LSB
|
||||
if (m_test.resetpos) // Reset address
|
||||
m_voice[voice_freq].addr = 0;
|
||||
m_voice[voice_freq].pitch = (m_voice[voice_freq].pitch & ~0x0ff) | data;
|
||||
m_voice[voice_freq].counter = m_voice[voice_freq].pitch;
|
||||
break;
|
||||
case 0x1: // 0x*1 Voice 0 Pitch MSB
|
||||
case 0x3: // 0x*3 Voice 1 Pitch MSB
|
||||
case 0x5: // 0x*5 Voice 2 Pitch MSB
|
||||
case 0x7: // 0x*7 Voice 3 Pitch MSB
|
||||
case 0x9: // 0x*9 Voice 4 Pitch MSB
|
||||
if (m_test.resetpos) // Reset address
|
||||
m_voice[voice_freq].addr = 0;
|
||||
m_voice[voice_freq].pitch = (m_voice[voice_freq].pitch & ~0xf00) | (u16(bitfield(data, 0, 4)) << 8);
|
||||
m_voice[voice_freq].counter = m_voice[voice_freq].pitch;
|
||||
break;
|
||||
case 0xa: // 0x*a Voice 0 Volume
|
||||
case 0xb: // 0x*b Voice 1 Volume
|
||||
case 0xc: // 0x*c Voice 2 Volume
|
||||
case 0xd: // 0x*d Voice 3 Volume
|
||||
case 0xe: // 0x*e Voice 4 Volume
|
||||
m_voice[voice_reg - 0xa].volume = bitfield(data, 0, 4);
|
||||
break;
|
||||
case 0xf: // 0x*f Enable/Disable flag
|
||||
m_voice[0].enable = bitfield(data, 0);
|
||||
m_voice[1].enable = bitfield(data, 1);
|
||||
m_voice[2].enable = bitfield(data, 2);
|
||||
m_voice[3].enable = bitfield(data, 3);
|
||||
m_voice[4].enable = bitfield(data, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void k051649_scc_core::scc_w(bool is_sccplus, u8 address, u8 data)
|
||||
{
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3/4 Waveform
|
||||
wave_w(false, address, data);
|
||||
break;
|
||||
case 0b100: // 0x80-0x9f Pitch, Volume, Enable
|
||||
freq_vol_enable_w(address, data);
|
||||
break;
|
||||
case 0b111: // 0xe0-0xff Test register
|
||||
m_test.freq_4bit = bitfield(data, 0);
|
||||
m_test.freq_8bit = bitfield(data, 1);
|
||||
m_test.resetpos = bitfield(data, 5);
|
||||
m_test.rotate = bitfield(data, 6);
|
||||
m_test.rotate4 = bitfield(data, 7);
|
||||
break;
|
||||
}
|
||||
m_reg[address] = data;
|
||||
}
|
||||
|
||||
void k052539_scc_core::scc_w(bool is_sccplus, u8 address, u8 data)
|
||||
{
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
if (is_sccplus)
|
||||
{
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
||||
case 0b100: // 0x80-0x9f Voice 4 Waveform
|
||||
wave_w(true, address, data);
|
||||
break;
|
||||
case 0b101: // 0xa0-0xbf Pitch, Volume, Enable
|
||||
freq_vol_enable_w(address, data);
|
||||
break;
|
||||
case 0b110: // 0xc0-0xdf Test register
|
||||
m_test.freq_4bit = bitfield(data, 0);
|
||||
m_test.freq_8bit = bitfield(data, 1);
|
||||
m_test.resetpos = bitfield(data, 5);
|
||||
m_test.rotate = bitfield(data, 6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3/4 Waveform
|
||||
wave_w(false, address, data);
|
||||
break;
|
||||
case 0b100: // 0x80-0x9f Pitch, Volume, Enable
|
||||
freq_vol_enable_w(address, data);
|
||||
break;
|
||||
case 0b110: // 0xc0-0xdf Test register
|
||||
m_test.freq_4bit = bitfield(data, 0);
|
||||
m_test.freq_8bit = bitfield(data, 1);
|
||||
m_test.resetpos = bitfield(data, 5);
|
||||
m_test.rotate = bitfield(data, 6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_reg[address] = data;
|
||||
}
|
||||
|
||||
u8 k051649_scc_core::scc_r(bool is_sccplus, u8 address)
|
||||
{
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
const u8 wave = bitfield(address, 0, 5);
|
||||
u8 ret = 0xff;
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
||||
case 0b101: // 0xa0-0xbf Voice 4 Waveform
|
||||
ret = wave_r(false, (std::min<u8>(4, voice) << 5) | wave);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
u8 k052539_scc_core::scc_r(bool is_sccplus, u8 address)
|
||||
{
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
const u8 wave = bitfield(address, 0, 5);
|
||||
u8 ret = 0xff;
|
||||
if (is_sccplus)
|
||||
{
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
||||
case 0b100: // 0x80-0x9f Voice 4 Waveform
|
||||
ret = wave_r(true, address);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
||||
case 0b101: // 0xa0-0xbf Voice 4 Waveform
|
||||
ret = wave_r(false, (std::min<u8>(4, voice) << 5) | wave);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Contributor(s): Natt Akuma, James Alan Nguyen, Laurens Holst
|
||||
Modifiers and Contributors for Furnace: Natt Akuma, tildearrow, Grauw
|
||||
Konami SCC emulation core
|
||||
|
||||
See scc.cpp for more info.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifndef _VGSOUND_EMU_SCC_HPP
|
||||
#define _VGSOUND_EMU_SCC_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace scc
|
||||
{
|
||||
typedef unsigned char u8;
|
||||
typedef signed char s8;
|
||||
typedef unsigned short u16;
|
||||
typedef signed short s16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed int s32;
|
||||
|
||||
// get bitfield, bitfield(input, position, len)
|
||||
template<typename T> T bitfield(T in, u8 pos, u8 len = 1)
|
||||
{
|
||||
return (in >> pos) & (len ? (T(1 << len) - 1) : 1);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace scc;
|
||||
// shared for SCCs
|
||||
class scc_core
|
||||
{
|
||||
public:
|
||||
// constructor
|
||||
scc_core()
|
||||
: m_voice{*this,*this,*this,*this,*this}
|
||||
{};
|
||||
virtual ~scc_core(){};
|
||||
|
||||
// accessors
|
||||
virtual u8 scc_r(bool is_sccplus, u8 address) = 0;
|
||||
virtual void scc_w(bool is_sccplus, u8 address, u8 data) = 0;
|
||||
|
||||
// internal state
|
||||
virtual void reset();
|
||||
void tick();
|
||||
|
||||
// getters
|
||||
s32 out() { return m_out; } // output to DA0...DA10 pin
|
||||
s32 chan_out(u8 ch) { return m_voice[ch].out; }
|
||||
u8 reg(u8 address) { return m_reg[address]; }
|
||||
|
||||
protected:
|
||||
// voice structs
|
||||
struct voice_t
|
||||
{
|
||||
// constructor
|
||||
voice_t(scc_core &host) : m_host(host) {};
|
||||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
|
||||
// registers
|
||||
scc_core &m_host;
|
||||
s8 wave[32] = {0}; // internal waveform
|
||||
bool enable = false; // output enable flag
|
||||
u16 pitch = 0; // pitch
|
||||
u8 volume = 0; // volume
|
||||
u8 addr = 0; // waveform pointer
|
||||
u16 counter = 0; // frequency counter
|
||||
s32 out = 0; // current output
|
||||
};
|
||||
voice_t m_voice[5]; // 5 voices
|
||||
|
||||
// accessor
|
||||
u8 wave_r(bool is_sccplus, u8 address);
|
||||
void wave_w(bool is_sccplus, u8 address, u8 data);
|
||||
void freq_vol_enable_w(u8 address, u8 data);
|
||||
|
||||
struct test_t
|
||||
{
|
||||
// constructor
|
||||
test_t()
|
||||
: freq_4bit(0)
|
||||
, freq_8bit(0)
|
||||
, resetpos(0)
|
||||
, rotate(0)
|
||||
, rotate4(0)
|
||||
{ };
|
||||
|
||||
void reset()
|
||||
{
|
||||
freq_4bit = 0;
|
||||
freq_8bit = 0;
|
||||
resetpos = 0;
|
||||
rotate = 0;
|
||||
rotate4 = 0;
|
||||
}
|
||||
|
||||
u8 freq_4bit : 1; // 4 bit frequency
|
||||
u8 freq_8bit : 1; // 8 bit frequency
|
||||
u8 resetpos : 1; // reset counter after pitch writes
|
||||
u8 rotate : 1; // rotate and write protect waveform for all channels
|
||||
u8 rotate4 : 1; // same as above but for channel 4 only
|
||||
};
|
||||
|
||||
test_t m_test; // test register
|
||||
s32 m_out = 0; // output to DA0...10
|
||||
|
||||
u8 m_reg[256] = {0}; // register pool
|
||||
};
|
||||
|
||||
// SCC core
|
||||
class k051649_scc_core : public scc_core
|
||||
{
|
||||
public:
|
||||
// accessors
|
||||
virtual u8 scc_r(bool is_sccplus, u8 address) override;
|
||||
virtual void scc_w(bool is_sccplus, u8 address, u8 data) override;
|
||||
};
|
||||
|
||||
class k052539_scc_core : public k051649_scc_core
|
||||
{
|
||||
public:
|
||||
// accessors
|
||||
virtual u8 scc_r(bool is_sccplus, u8 address) override;
|
||||
virtual void scc_w(bool is_sccplus, u8 address, u8 data) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,324 +0,0 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Modifiers and Contributors for Furnace: cam900, tildearrow
|
||||
Konami VRC VI sound emulation core
|
||||
|
||||
It's one of NES mapper with built-in sound chip, and also one of 2 Konami VRCs with this feature. (rest one has OPLL derivatives.)
|
||||
|
||||
It's also DACless like other sound chip and mapper-with-sound manufactured by konami,
|
||||
the Chips 6 bit digital sound output is needs converted to analog sound output when you it want to make some sounds, or send to sound mixer.
|
||||
|
||||
Its are used for Akumajou Densetsu (Japan release of Castlevania III), Madara, Esper Dream 2.
|
||||
|
||||
The chip is installed in 351951 PCB and 351949A PCB.
|
||||
|
||||
351951 PCB is used exclusivly for Akumajou Densetsu, Small board has VRC VI, PRG and CHR ROM.
|
||||
- It's configuration also calls VRC6a, iNES mapper 024.
|
||||
|
||||
351949A PCB is for Last 2 titles with VRC VI, Bigger board has VRC VI, PRG and CHR ROM, and Battery Backed 8K x 8 bit SRAM.
|
||||
- Additionally, It's PRG A0 and A1 bit to VRC VI input is swapped, compare to above.
|
||||
- It's configuration also calls VRC6b, iNES mapper 026.
|
||||
|
||||
The chip itself has 053328, 053329, 053330 Revision, but Its difference between revision is unknown.
|
||||
|
||||
Like other mappers for NES, It has internal timer - Its timer can be sync with scanline like other Konami mapper in this era.
|
||||
|
||||
Register layout (Sound and Timer only; 351951 PCB case, 351949A swaps xxx1 and xxx2):
|
||||
|
||||
Address Bits Description
|
||||
7654 3210
|
||||
|
||||
9000-9002 Pulse 1
|
||||
|
||||
9000 x--- ---- Pulse 1 Duty ignore
|
||||
-xxx ---- Pulse 1 Duty cycle
|
||||
---- xxxx Pulse 1 Volume
|
||||
9001 xxxx xxxx Pulse 1 Pitch bit 0-7
|
||||
9002 x--- ---- Pulse 1 Enable
|
||||
---- xxxx Pulse 1 Pitch bit 8-11
|
||||
|
||||
9003 Sound control
|
||||
|
||||
9003 ---- -x-- 4 bit Frequency mode
|
||||
---- -0x- 8 bit Frequency mode
|
||||
---- ---x Halt
|
||||
|
||||
a000-a002 Pulse 2
|
||||
|
||||
a000 x--- ---- Pulse 2 Duty ignore
|
||||
-xxx ---- Pulse 2 Duty cycle
|
||||
---- xxxx Pulse 2 Volume
|
||||
a001 xxxx xxxx Pulse 2 Pitch bit 0-7
|
||||
a002 x--- ---- Pulse 2 Enable
|
||||
---- xxxx Pulse 2 Pitch bit 8-11
|
||||
|
||||
b000-b002 Sawtooth
|
||||
|
||||
b000 --xx xxxx Sawtooth Accumulate Rate
|
||||
b001 xxxx xxxx Sawtooth Pitch bit 0-7
|
||||
b002 x--- ---- Sawtooth Enable
|
||||
---- xxxx Sawtooth Pitch bit 8-11
|
||||
|
||||
f000-f002 IRQ Timer
|
||||
|
||||
f000 xxxx xxxx IRQ Timer latch
|
||||
f001 ---- -0-- Sync with scanline
|
||||
---- --x- Enable timer
|
||||
---- ---x Enable timer after IRQ Acknowledge
|
||||
f002 ---- ---- IRQ Acknowledge
|
||||
|
||||
Frequency calculations:
|
||||
|
||||
if 4 bit Frequency Mode then
|
||||
Frequency: Input clock / (bit 8 to 11 of Pitch + 1)
|
||||
end else if 8 bit Frequency Mode then
|
||||
Frequency: Input clock / (bit 4 to 11 of Pitch + 1)
|
||||
end else then
|
||||
Frequency: Input clock / (Pitch + 1)
|
||||
end
|
||||
*/
|
||||
|
||||
#include "vrcvi.hpp"
|
||||
#include <string.h>
|
||||
|
||||
void vrcvi_core::tick()
|
||||
{
|
||||
m_out = 0;
|
||||
if (!m_control.m_halt) // Halt flag
|
||||
{
|
||||
// tick per each clock
|
||||
int elemIndex=0;
|
||||
for (auto & elem : m_pulse)
|
||||
{
|
||||
if (elem.tick()) {
|
||||
m_out += elem.m_control.m_volume; // add 4 bit pulse output
|
||||
m_ch_out[elemIndex]=elem.m_control.m_volume;
|
||||
} else {
|
||||
m_ch_out[elemIndex]=0;
|
||||
}
|
||||
elemIndex++;
|
||||
}
|
||||
if (m_sawtooth.tick()) {
|
||||
m_out += bitfield(m_sawtooth.m_accum, 3, 5); // add 5 bit sawtooth output
|
||||
m_ch_out[2]=bitfield(m_sawtooth.m_accum, 3, 5);
|
||||
} else {
|
||||
m_ch_out[2]=0;
|
||||
}
|
||||
}
|
||||
if (m_timer.tick())
|
||||
m_timer.counter_tick();
|
||||
}
|
||||
|
||||
void vrcvi_core::reset()
|
||||
{
|
||||
for (auto & elem : m_pulse)
|
||||
elem.reset();
|
||||
|
||||
m_sawtooth.reset();
|
||||
m_timer.reset();
|
||||
m_control.reset();
|
||||
m_out = 0;
|
||||
memset(m_ch_out,0,sizeof(m_ch_out));
|
||||
}
|
||||
|
||||
bool vrcvi_core::alu_t::tick()
|
||||
{
|
||||
if (m_divider.m_enable)
|
||||
{
|
||||
const u16 temp = m_counter;
|
||||
// post decrement
|
||||
if (bitfield(m_host.m_control.m_shift, 1))
|
||||
{
|
||||
m_counter = (m_counter & 0x0ff) | (bitfield(bitfield(m_counter, 8, 4) - 1, 0, 4) << 8);
|
||||
m_counter = (m_counter & 0xf00) | (bitfield(bitfield(m_counter, 0, 8) - 1, 0, 8) << 0);
|
||||
}
|
||||
else if (bitfield(m_host.m_control.m_shift, 0))
|
||||
{
|
||||
m_counter = (m_counter & 0x00f) | (bitfield(bitfield(m_counter, 4, 8) - 1, 0, 8) << 4);
|
||||
m_counter = (m_counter & 0xff0) | (bitfield(bitfield(m_counter, 0, 4) - 1, 0, 4) << 0);
|
||||
}
|
||||
else
|
||||
m_counter = bitfield(bitfield(m_counter, 0, 12) - 1, 0, 12);
|
||||
|
||||
// carry handling
|
||||
bool carry = bitfield(m_host.m_control.m_shift, 1) ? (bitfield(temp, 8, 4) == 0) :
|
||||
(bitfield(m_host.m_control.m_shift, 0) ? (bitfield(temp, 4, 8) == 0) :
|
||||
(bitfield(temp, 0, 12) == 0));
|
||||
if (carry)
|
||||
m_counter = m_divider.m_divider;
|
||||
|
||||
return carry;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool vrcvi_core::pulse_t::tick()
|
||||
{
|
||||
if (!m_divider.m_enable)
|
||||
return false;
|
||||
|
||||
if (vrcvi_core::alu_t::tick())
|
||||
m_cycle = bitfield(m_cycle + 1, 0, 4);
|
||||
|
||||
return m_control.m_mode ? true : ((m_cycle > m_control.m_duty) ? true : false);
|
||||
}
|
||||
|
||||
bool vrcvi_core::sawtooth_t::tick()
|
||||
{
|
||||
if (!m_divider.m_enable)
|
||||
return false;
|
||||
|
||||
if (vrcvi_core::alu_t::tick())
|
||||
{
|
||||
if (bitfield(m_cycle++, 0)) // Even step only
|
||||
m_accum += m_rate;
|
||||
if (m_cycle >= 14) // Reset accumulator at every 14 cycles
|
||||
{
|
||||
m_accum = 0;
|
||||
m_cycle = 0;
|
||||
}
|
||||
}
|
||||
return (m_accum == 0) ? false : true;
|
||||
}
|
||||
|
||||
void vrcvi_core::alu_t::reset()
|
||||
{
|
||||
m_divider.reset();
|
||||
m_counter = 0;
|
||||
m_cycle = 0;
|
||||
}
|
||||
|
||||
void vrcvi_core::pulse_t::reset()
|
||||
{
|
||||
vrcvi_core::alu_t::reset();
|
||||
m_control.reset();
|
||||
}
|
||||
|
||||
void vrcvi_core::sawtooth_t::reset()
|
||||
{
|
||||
vrcvi_core::alu_t::reset();
|
||||
m_rate = 0;
|
||||
m_accum = 0;
|
||||
}
|
||||
|
||||
bool vrcvi_core::timer_t::tick()
|
||||
{
|
||||
if (m_timer_control.m_enable)
|
||||
{
|
||||
if (!m_timer_control.m_sync) // scanline sync mode
|
||||
{
|
||||
m_prescaler -= 3;
|
||||
if (m_prescaler <= 0)
|
||||
{
|
||||
m_prescaler += 341;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (m_timer_control.m_enable && m_timer_control.m_sync) ? true : false;
|
||||
}
|
||||
|
||||
void vrcvi_core::timer_t::counter_tick()
|
||||
{
|
||||
if (bitfield(++m_counter, 0, 8) == 0)
|
||||
{
|
||||
m_counter = m_counter_latch;
|
||||
irq_set();
|
||||
}
|
||||
}
|
||||
|
||||
void vrcvi_core::timer_t::reset()
|
||||
{
|
||||
m_timer_control.reset();
|
||||
m_prescaler = 341;
|
||||
m_counter = m_counter_latch = 0;
|
||||
irq_clear();
|
||||
}
|
||||
|
||||
// Accessors
|
||||
|
||||
void vrcvi_core::alu_t::divider_t::write(bool msb, u8 data)
|
||||
{
|
||||
if (msb)
|
||||
{
|
||||
m_divider = (m_divider & ~0xf00) | (bitfield<u32>(data, 0, 4) << 8);
|
||||
m_enable = bitfield(data, 7);
|
||||
}
|
||||
else
|
||||
m_divider = (m_divider & ~0x0ff) | data;
|
||||
}
|
||||
|
||||
|
||||
void vrcvi_core::pulse_w(u8 voice, u8 address, u8 data)
|
||||
{
|
||||
pulse_t &v = m_pulse[voice];
|
||||
switch (address)
|
||||
{
|
||||
case 0x00: // Control - 0x9000 (Pulse 1), 0xa000 (Pulse 2)
|
||||
v.m_control.m_mode = bitfield(data, 7);
|
||||
v.m_control.m_duty = bitfield(data, 4, 3);
|
||||
v.m_control.m_volume = bitfield(data, 0, 4);
|
||||
break;
|
||||
case 0x01: // Pitch LSB - 0x9001/0x9002 (Pulse 1), 0xa001/0xa002 (Pulse 2)
|
||||
v.m_divider.write(false, data);
|
||||
break;
|
||||
case 0x02: // Pitch MSB, Enable/Disable - 0x9002/0x9001 (Pulse 1), 0xa002/0xa001 (Pulse 2)
|
||||
v.m_divider.write(true, data);
|
||||
if (!v.m_divider.m_enable) // Reset duty cycle
|
||||
v.m_cycle = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void vrcvi_core::saw_w(u8 address, u8 data)
|
||||
{
|
||||
switch (address)
|
||||
{
|
||||
case 0x00: // Sawtooth Accumulate - 0xb000
|
||||
m_sawtooth.m_rate = bitfield(data, 0, 6);
|
||||
break;
|
||||
case 0x01: // Pitch LSB - 0xb001/0xb002 (Sawtooth)
|
||||
m_sawtooth.m_divider.write(false, data);
|
||||
break;
|
||||
case 0x02: // Pitch MSB, Enable/Disable - 0xb002/0xb001 (Sawtooth)
|
||||
m_sawtooth.m_divider.write(true, data);
|
||||
if (!m_sawtooth.m_divider.m_enable) // Reset accumulator
|
||||
m_sawtooth.m_accum = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void vrcvi_core::timer_w(u8 address, u8 data)
|
||||
{
|
||||
switch (address)
|
||||
{
|
||||
case 0x00: // Timer latch - 0xf000
|
||||
m_timer.m_counter_latch = data;
|
||||
break;
|
||||
case 0x01: // Timer control - 0xf001/0xf002
|
||||
m_timer.m_timer_control.m_sync = bitfield(data, 2);
|
||||
m_timer.m_timer_control.m_enable = bitfield(data, 1);
|
||||
m_timer.m_timer_control.m_enable_ack = bitfield(data, 0);
|
||||
if (m_timer.m_timer_control.m_enable)
|
||||
{
|
||||
m_timer.m_counter = m_timer.m_counter_latch;
|
||||
m_timer.m_prescaler = 341;
|
||||
}
|
||||
m_timer.irq_clear();
|
||||
break;
|
||||
case 0x02: // IRQ Acknowledge - 0xf002/0xf001
|
||||
m_timer.irq_clear();
|
||||
m_timer.m_timer_control.m_enable = m_timer.m_timer_control.m_enable_ack;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void vrcvi_core::control_w(u8 data)
|
||||
{
|
||||
// Global control - 0x9003
|
||||
m_control.m_halt = bitfield(data, 0);
|
||||
m_control.m_shift = bitfield(data, 1, 2);
|
||||
}
|
||||
|
|
@ -1,242 +0,0 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Modifiers and Contributors for Furnace: cam900, tildearrow
|
||||
Konami VRC VI sound emulation core
|
||||
|
||||
See vrcvi.cpp to more infos.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifndef _VGSOUND_EMU_VRCVI_HPP
|
||||
#define _VGSOUND_EMU_VRCVI_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace vrcvi
|
||||
{
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed char s8;
|
||||
typedef signed short s16;
|
||||
|
||||
// get bitfield, bitfield(input, position, len)
|
||||
template<typename T> T bitfield(T in, u8 pos, u8 len = 1)
|
||||
{
|
||||
return (in >> pos) & (len ? (T(1 << len) - 1) : 1);
|
||||
}
|
||||
};
|
||||
|
||||
class vrcvi_intf
|
||||
{
|
||||
public:
|
||||
virtual void irq_w(bool irq) { }
|
||||
};
|
||||
|
||||
using namespace vrcvi;
|
||||
class vrcvi_core
|
||||
{
|
||||
public:
|
||||
friend class vrcvi_intf;
|
||||
// constructor
|
||||
vrcvi_core(vrcvi_intf &intf)
|
||||
: m_pulse{*this,*this}
|
||||
, m_sawtooth(*this)
|
||||
, m_timer(*this)
|
||||
, m_intf(intf)
|
||||
{
|
||||
}
|
||||
// accessors, getters, setters
|
||||
void pulse_w(u8 voice, u8 address, u8 data);
|
||||
void saw_w(u8 address, u8 data);
|
||||
void timer_w(u8 address, u8 data);
|
||||
void control_w(u8 data);
|
||||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
|
||||
// 6 bit output
|
||||
s8 out() { return m_out; }
|
||||
// channel output
|
||||
s16 chan_out(u8 ch) { return m_ch_out[ch]; }
|
||||
private:
|
||||
// Common ALU for sound channels
|
||||
struct alu_t
|
||||
{
|
||||
alu_t(vrcvi_core &host)
|
||||
: m_host(host)
|
||||
{ };
|
||||
|
||||
|
||||
virtual void reset();
|
||||
virtual bool tick();
|
||||
|
||||
struct divider_t
|
||||
{
|
||||
divider_t()
|
||||
: m_divider(0)
|
||||
, m_enable(0)
|
||||
{ };
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_divider = 0;
|
||||
m_enable = 0;
|
||||
}
|
||||
|
||||
void write(bool msb, u8 data);
|
||||
|
||||
u16 m_divider : 12; // divider (pitch)
|
||||
u16 m_enable : 1; // channel enable flag
|
||||
};
|
||||
|
||||
vrcvi_core &m_host;
|
||||
divider_t m_divider;
|
||||
u16 m_counter = 0; // clock counter
|
||||
u8 m_cycle = 0; // clock cycle
|
||||
};
|
||||
|
||||
// 2 Pulse channels
|
||||
struct pulse_t : alu_t
|
||||
{
|
||||
pulse_t(vrcvi_core &host)
|
||||
: alu_t(host)
|
||||
{ };
|
||||
|
||||
virtual void reset() override;
|
||||
virtual bool tick() override;
|
||||
|
||||
// Control bits
|
||||
struct pulse_control_t
|
||||
{
|
||||
pulse_control_t()
|
||||
: m_mode(0)
|
||||
, m_duty(0)
|
||||
, m_volume(0)
|
||||
{ };
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_mode = 0;
|
||||
m_duty = 0;
|
||||
m_volume = 0;
|
||||
}
|
||||
|
||||
u8 m_mode : 1; // duty toggle flag
|
||||
u8 m_duty : 3; // 3 bit duty cycle
|
||||
u8 m_volume : 4; // 4 bit volume
|
||||
};
|
||||
|
||||
pulse_control_t m_control;
|
||||
};
|
||||
|
||||
// 1 Sawtooth channel
|
||||
struct sawtooth_t : alu_t
|
||||
{
|
||||
sawtooth_t(vrcvi_core &host)
|
||||
: alu_t(host)
|
||||
{ };
|
||||
|
||||
virtual void reset() override;
|
||||
virtual bool tick() override;
|
||||
|
||||
u8 m_rate = 0; // sawtooth accumulate rate
|
||||
u8 m_accum = 0; // sawtooth accumulator, high 5 bit is accumulated to output
|
||||
};
|
||||
|
||||
// Internal timer
|
||||
struct timer_t
|
||||
{
|
||||
timer_t(vrcvi_core &host)
|
||||
: m_host(host)
|
||||
{ };
|
||||
|
||||
void reset();
|
||||
bool tick();
|
||||
void counter_tick();
|
||||
|
||||
// IRQ update
|
||||
void update() { m_host.m_intf.irq_w(m_timer_control.m_irq_trigger); }
|
||||
void irq_set()
|
||||
{
|
||||
if (!m_timer_control.m_irq_trigger)
|
||||
{
|
||||
m_timer_control.m_irq_trigger = 1;
|
||||
update();
|
||||
}
|
||||
}
|
||||
void irq_clear()
|
||||
{
|
||||
if (m_timer_control.m_irq_trigger)
|
||||
{
|
||||
m_timer_control.m_irq_trigger = 0;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
// Control bits
|
||||
struct timer_control_t
|
||||
{
|
||||
timer_control_t()
|
||||
: m_irq_trigger(0)
|
||||
, m_enable_ack(0)
|
||||
, m_enable(0)
|
||||
, m_sync(0)
|
||||
{ };
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_irq_trigger = 0;
|
||||
m_enable_ack = 0;
|
||||
m_enable = 0;
|
||||
m_sync = 0;
|
||||
}
|
||||
|
||||
u8 m_irq_trigger : 1;
|
||||
u8 m_enable_ack : 1;
|
||||
u8 m_enable : 1;
|
||||
u8 m_sync : 1;
|
||||
};
|
||||
|
||||
vrcvi_core &m_host; // host core
|
||||
timer_control_t m_timer_control; // timer control bits
|
||||
s16 m_prescaler = 341; // prescaler
|
||||
u8 m_counter = 0; // clock counter
|
||||
u8 m_counter_latch = 0; // clock counter latch
|
||||
};
|
||||
|
||||
struct global_control_t
|
||||
{
|
||||
global_control_t()
|
||||
: m_halt(0)
|
||||
, m_shift(0)
|
||||
{ };
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_halt = 0;
|
||||
m_shift = 0;
|
||||
}
|
||||
|
||||
u8 m_halt : 1; // halt sound
|
||||
u8 m_shift : 2; // 4/8 bit right shift
|
||||
};
|
||||
|
||||
pulse_t m_pulse[2]; // 2 pulse channels
|
||||
sawtooth_t m_sawtooth; // sawtooth channel
|
||||
timer_t m_timer; // internal timer
|
||||
global_control_t m_control; // control
|
||||
|
||||
vrcvi_intf &m_intf;
|
||||
|
||||
s8 m_out = 0; // 6 bit output
|
||||
s8 m_ch_out[3] = {0}; // per-channel output
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,225 +0,0 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Modifiers and Contributors for Furnace: cam900, tildearrow
|
||||
Seta/Allumer X1-010 Emulation core
|
||||
|
||||
the chip has 16 voices, all voices can be switchable to Wavetable or PCM sample playback mode.
|
||||
It has also 2 output channels, but no known hardware using this feature for stereo sound.
|
||||
|
||||
Wavetable needs to paired with envelope, it's always enabled and similar as AY PSG's one
|
||||
but its shape is stored at RAM.
|
||||
|
||||
PCM volume is stored by each register.
|
||||
|
||||
Both volume is 4bit per output.
|
||||
|
||||
Everything except PCM sample is stored at paired 8 bit RAM.
|
||||
|
||||
RAM layout (common case: Address bit 12 is swapped when RAM is shared with CPU)
|
||||
|
||||
-----------------------------
|
||||
0000...007f Voice Registers
|
||||
|
||||
0000...0007 Voice 0 Register
|
||||
|
||||
Address Bits Description
|
||||
7654 3210
|
||||
0 x--- ---- Frequency divider*
|
||||
---- -x-- Envelope one-shot mode
|
||||
---- --x- Sound format
|
||||
---- --0- PCM
|
||||
---- --1- Wavetable
|
||||
---- ---x Keyon/off
|
||||
PCM case:
|
||||
1 xxxx xxxx Volume (Each nibble is for each output)
|
||||
|
||||
2 xxxx xxxx Frequency*
|
||||
|
||||
4 xxxx xxxx Start address / 4096
|
||||
|
||||
5 xxxx xxxx 0x100 - (End address / 4096)
|
||||
Wavetable case:
|
||||
1 ---x xxxx Wavetable data select
|
||||
|
||||
2 xxxx xxxx Frequency LSB*
|
||||
3 xxxx xxxx "" MSB
|
||||
|
||||
4 xxxx xxxx Envelope period (.10 fixed point, Low 8 bit)
|
||||
|
||||
5 ---x xxxx Envelope shape select (!= 0 : Reserved for Voice registers)
|
||||
|
||||
0008...000f Voice 1 Register
|
||||
...
|
||||
0078...007f Voice 15 Register
|
||||
-----------------------------
|
||||
0080...0fff Envelope shape data (Same as volume; Each nibble is for each output)
|
||||
|
||||
0080...00ff Envelope shape data 1
|
||||
0100...017f Envelope shape data 2
|
||||
...
|
||||
0f80...0fff Envelope shape data 31
|
||||
-----------------------------
|
||||
1000...1fff Wavetable data
|
||||
|
||||
1000...107f Wavetable data 0
|
||||
1080...10ff Wavetable data 1
|
||||
...
|
||||
1f80...1fff Wavetable data 31
|
||||
-----------------------------
|
||||
|
||||
* Frequency is 4.4 fixed point for PCM,
|
||||
6.10 for Wavetable.
|
||||
Frequency divider is higher precision or just right shift?
|
||||
needs verification.
|
||||
*/
|
||||
|
||||
#include "x1_010.hpp"
|
||||
|
||||
void x1_010_core::tick()
|
||||
{
|
||||
// reset output
|
||||
m_out[0] = m_out[1] = 0;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
voice_t &v = m_voice[i];
|
||||
v.tick();
|
||||
m_out[0] += v.data * v.vol_out[0];
|
||||
m_out[1] += v.data * v.vol_out[1];
|
||||
}
|
||||
}
|
||||
|
||||
void x1_010_core::voice_t::tick()
|
||||
{
|
||||
data = vol_out[0] = vol_out[1] = 0;
|
||||
if (flag.keyon)
|
||||
{
|
||||
if (flag.wavetable) // Wavetable
|
||||
{
|
||||
// envelope, each nibble is for each output
|
||||
u8 vol = m_host.m_envelope[(bitfield(end_envshape, 0, 5) << 7) | bitfield(env_acc, 10, 7)];
|
||||
vol_out[0] = bitfield(vol, 4, 4);
|
||||
vol_out[1] = bitfield(vol, 0, 4);
|
||||
env_acc += start_envfreq;
|
||||
if (flag.env_oneshot && bitfield(env_acc, 17))
|
||||
flag.keyon = false;
|
||||
else
|
||||
env_acc = bitfield(env_acc, 0, 17);
|
||||
// get wavetable data
|
||||
data = m_host.m_wave[(bitfield(vol_wave, 0, 5) << 7) | bitfield(acc, 10, 7)];
|
||||
acc = bitfield(acc + (freq >> flag.div), 0, 17);
|
||||
}
|
||||
else // PCM sample
|
||||
{
|
||||
// volume register, each nibble is for each output
|
||||
vol_out[0] = bitfield(vol_wave, 4, 4);
|
||||
vol_out[1] = bitfield(vol_wave, 0, 4);
|
||||
// get PCM sample
|
||||
data = m_host.m_intf.read_byte(bitfield(acc, 4, 20));
|
||||
acc += bitfield(freq, 0, 8) >> flag.div;
|
||||
if ((acc >> 16) > (0xff ^ end_envshape))
|
||||
flag.keyon = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u8 x1_010_core::ram_r(u16 offset)
|
||||
{
|
||||
if (offset & 0x1000) // wavetable data
|
||||
return m_wave[offset & 0xfff];
|
||||
else if (offset & 0xf80) // envelope shape data
|
||||
return m_envelope[offset & 0xfff];
|
||||
else // channel register
|
||||
return m_voice[bitfield(offset, 3, 4)].reg_r(offset & 0x7);
|
||||
}
|
||||
|
||||
void x1_010_core::ram_w(u16 offset, u8 data)
|
||||
{
|
||||
if (offset & 0x1000) // wavetable data
|
||||
m_wave[offset & 0xfff] = data;
|
||||
else if (offset & 0xf80) // envelope shape data
|
||||
m_envelope[offset & 0xfff] = data;
|
||||
else // channel register
|
||||
m_voice[bitfield(offset, 3, 4)].reg_w(offset & 0x7, data);
|
||||
}
|
||||
|
||||
u8 x1_010_core::voice_t::reg_r(u8 offset)
|
||||
{
|
||||
switch (offset & 0x7)
|
||||
{
|
||||
case 0x00: return (flag.div << 7)
|
||||
| (flag.env_oneshot << 2)
|
||||
| (flag.wavetable << 1)
|
||||
| (flag.keyon << 0);
|
||||
case 0x01: return vol_wave;
|
||||
case 0x02: return bitfield(freq, 0, 8);
|
||||
case 0x03: return bitfield(freq, 8, 8);
|
||||
case 0x04: return start_envfreq;
|
||||
case 0x05: return end_envshape;
|
||||
default: break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void x1_010_core::voice_t::reg_w(u8 offset, u8 data)
|
||||
{
|
||||
switch (offset & 0x7)
|
||||
{
|
||||
case 0x00:
|
||||
{
|
||||
const bool prev_keyon = flag.keyon;
|
||||
flag.div = bitfield(data, 7);
|
||||
flag.env_oneshot = bitfield(data, 2);
|
||||
flag.wavetable = bitfield(data, 1);
|
||||
flag.keyon = bitfield(data, 0);
|
||||
if (!prev_keyon && flag.keyon) // Key on
|
||||
{
|
||||
acc = flag.wavetable ? 0 : (u32(start_envfreq) << 16);
|
||||
env_acc = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x01:
|
||||
vol_wave = data;
|
||||
break;
|
||||
case 0x02:
|
||||
freq = (freq & 0xff00) | data;
|
||||
break;
|
||||
case 0x03:
|
||||
freq = (freq & 0x00ff) | (u16(data) << 8);
|
||||
break;
|
||||
case 0x04:
|
||||
start_envfreq = data;
|
||||
break;
|
||||
case 0x05:
|
||||
end_envshape = data;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void x1_010_core::voice_t::reset()
|
||||
{
|
||||
flag.reset();
|
||||
vol_wave = 0;
|
||||
freq = 0;
|
||||
start_envfreq = 0;
|
||||
end_envshape = 0;
|
||||
acc = 0;
|
||||
env_acc = 0;
|
||||
data = 0;
|
||||
vol_out[0] = vol_out[1] = 0;
|
||||
}
|
||||
|
||||
void x1_010_core::reset()
|
||||
{
|
||||
for (auto & elem : m_voice)
|
||||
elem.reset();
|
||||
|
||||
std::fill_n(&m_envelope[0], 0x1000, 0);
|
||||
std::fill_n(&m_wave[0], 0x1000, 0);
|
||||
m_out[0] = m_out[1] = 0;
|
||||
}
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://gitlab.com/cam900/vgsound_emu/-/blob/V1/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Modifiers and Contributors for Furnace: cam900, tildearrow
|
||||
Seta/Allumer X1-010 Emulation core
|
||||
|
||||
See x1_010.cpp for more info.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifndef _VGSOUND_EMU_X1_010_HPP
|
||||
#define _VGSOUND_EMU_X1_010_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace x1_010
|
||||
{
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed char s8;
|
||||
typedef signed int s32;
|
||||
|
||||
template<typename T> T bitfield(T in, u8 pos, u8 len = 1)
|
||||
{
|
||||
return (in >> pos) & (len ? (T(1 << len) - 1) : 1);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace x1_010;
|
||||
class x1_010_mem_intf
|
||||
{
|
||||
public:
|
||||
virtual u8 read_byte(u32 address) { return 0; }
|
||||
};
|
||||
|
||||
using namespace x1_010;
|
||||
class x1_010_core
|
||||
{
|
||||
friend class x1_010_mem_intf;
|
||||
public:
|
||||
// constructor
|
||||
x1_010_core(x1_010_mem_intf &intf)
|
||||
: m_voice{*this,*this,*this,*this,
|
||||
*this,*this,*this,*this,
|
||||
*this,*this,*this,*this,
|
||||
*this,*this,*this,*this}
|
||||
, m_intf(intf)
|
||||
{
|
||||
m_envelope = std::make_unique<u8[]>(0x1000);
|
||||
m_wave = std::make_unique<u8[]>(0x1000);
|
||||
|
||||
std::fill_n(&m_envelope[0], 0x1000, 0);
|
||||
std::fill_n(&m_wave[0], 0x1000, 0);
|
||||
}
|
||||
|
||||
// register accessor
|
||||
u8 ram_r(u16 offset);
|
||||
void ram_w(u16 offset, u8 data);
|
||||
|
||||
// getters
|
||||
s32 output(u8 channel) { return m_out[channel & 1]; }
|
||||
s32 chan_out(u8 channel) { return (m_voice[channel].data * (m_voice[channel].vol_out[0]+m_voice[channel].vol_out[1]))<<2; }
|
||||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
|
||||
private:
|
||||
// 16 voices in chip
|
||||
struct voice_t
|
||||
{
|
||||
// constructor
|
||||
voice_t(x1_010_core &host) : m_host(host) {}
|
||||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
|
||||
// register accessor
|
||||
u8 reg_r(u8 offset);
|
||||
void reg_w(u8 offset, u8 data);
|
||||
|
||||
// registers
|
||||
x1_010_core &m_host;
|
||||
struct flag_t
|
||||
{
|
||||
u8 div : 1;
|
||||
u8 env_oneshot : 1;
|
||||
u8 wavetable : 1;
|
||||
u8 keyon : 1;
|
||||
void reset()
|
||||
{
|
||||
div = 0;
|
||||
env_oneshot = 0;
|
||||
wavetable = 0;
|
||||
keyon = 0;
|
||||
}
|
||||
flag_t()
|
||||
: div(0)
|
||||
, env_oneshot(0)
|
||||
, wavetable(0)
|
||||
, keyon(0)
|
||||
{ }
|
||||
};
|
||||
flag_t flag;
|
||||
u8 vol_wave = 0;
|
||||
u16 freq = 0;
|
||||
u8 start_envfreq = 0;
|
||||
u8 end_envshape = 0;
|
||||
|
||||
// internal registers
|
||||
u32 acc = 0;
|
||||
u32 env_acc = 0;
|
||||
s8 data = 0;
|
||||
u8 vol_out[2] = {0};
|
||||
};
|
||||
voice_t m_voice[16];
|
||||
|
||||
// RAM
|
||||
std::unique_ptr<u8[]> m_envelope = nullptr;
|
||||
std::unique_ptr<u8[]> m_wave = nullptr;
|
||||
|
||||
// output data
|
||||
s32 m_out[2] = {0};
|
||||
|
||||
x1_010_mem_intf &m_intf;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -112,7 +112,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -179,7 +179,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -232,7 +232,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -324,7 +324,7 @@ void DivPlatformTX81Z::muteChannel(int ch, bool mute) {
|
|||
if (isMuted[ch]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[ch].state.alg][i]) {
|
||||
if (KVS(ch,i)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[ch].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -353,7 +353,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
||||
if (KVS(c.chan,i)) {
|
||||
if (!chan[c.chan].active || chan[c.chan].insChanged) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
}
|
||||
|
|
@ -419,7 +419,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
||||
if (KVS(c.chan,i)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -519,7 +519,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[c.chan].state.alg][c.value]) {
|
||||
if (KVS(c.chan,c.value)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -811,7 +811,7 @@ void DivPlatformTX81Z::forceIns() {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
|
|||
|
|
@ -86,9 +86,10 @@ void DivPlatformVRC6::acquire(short* bufL, short* bufR, size_t start, size_t len
|
|||
// Oscilloscope buffer part
|
||||
if (++writeOscBuf>=32) {
|
||||
writeOscBuf=0;
|
||||
for (int i=0; i<3; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=vrc6.chan_out(i)<<10;
|
||||
for (int i=0; i<2; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=vrc6.pulse_out(i)<<10;
|
||||
}
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=vrc6.sawtooth_out()<<10;
|
||||
}
|
||||
|
||||
// Command part
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@
|
|||
#include <queue>
|
||||
#include "../dispatch.h"
|
||||
#include "../macroInt.h"
|
||||
#include "sound/vrcvi/vrcvi.hpp"
|
||||
#include "vgsound_emu/src/vrcvi/vrcvi.hpp"
|
||||
|
||||
|
||||
class DivPlatformVRC6: public DivDispatch {
|
||||
class DivPlatformVRC6: public DivDispatch, public vrcvi_intf {
|
||||
struct Channel {
|
||||
int freq, baseFreq, pitch, pitch2, note;
|
||||
int dacPeriod, dacRate, dacOut;
|
||||
|
|
@ -75,7 +75,6 @@ class DivPlatformVRC6: public DivDispatch {
|
|||
std::queue<QueuedWrite> writes;
|
||||
unsigned char sampleBank;
|
||||
unsigned char writeOscBuf;
|
||||
vrcvi_intf intf;
|
||||
vrcvi_core vrc6;
|
||||
unsigned char regPool[13];
|
||||
|
||||
|
|
@ -101,7 +100,7 @@ class DivPlatformVRC6: public DivDispatch {
|
|||
const char** getRegisterSheet();
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
DivPlatformVRC6() : vrc6(intf) {};
|
||||
DivPlatformVRC6() : vrc6(*this) {};
|
||||
~DivPlatformVRC6();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@
|
|||
#include <math.h>
|
||||
|
||||
//#define rWrite(a,v) pendingWrites[a]=v;
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) { x1_010->ram_w(a,v); if (dumpWrites) { addWrite(a,v); } }
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) { x1_010.ram_w(a,v); if (dumpWrites) { addWrite(a,v); } }
|
||||
|
||||
#define chRead(c,a) x1_010->ram_r((c<<3)|(a&7))
|
||||
#define chRead(c,a) x1_010.ram_r((c<<3)|(a&7))
|
||||
#define chWrite(c,a,v) rWrite((c<<3)|(a&7),v)
|
||||
#define waveWrite(c,a,v) rWrite(0x1000|(chan[c].waveBank<<11)|(c<<7)|(a&0x7f),(v-128)&0xff)
|
||||
#define envFill(c,a) rWrite(0x800|(c<<7)|(a&0x7f),(chan[c].lvol<<4)|chan[c].rvol)
|
||||
|
|
@ -207,10 +207,10 @@ const char** DivPlatformX1_010::getRegisterSheet() {
|
|||
|
||||
void DivPlatformX1_010::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
x1_010->tick();
|
||||
x1_010.tick();
|
||||
|
||||
signed int tempL=x1_010->output(0);
|
||||
signed int tempR=x1_010->output(1);
|
||||
signed int tempL=x1_010.output(0);
|
||||
signed int tempR=x1_010.output(1);
|
||||
|
||||
if (tempL<-32768) tempL=-32768;
|
||||
if (tempL>32767) tempL=32767;
|
||||
|
|
@ -222,11 +222,18 @@ void DivPlatformX1_010::acquire(short* bufL, short* bufR, size_t start, size_t l
|
|||
bufR[h]=stereo?tempR:bufL[h];
|
||||
|
||||
for (int i=0; i<16; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=x1_010->chan_out(i);
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(x1_010.voice_out(i,0)+x1_010.voice_out(i,1))>>1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u8 DivPlatformX1_010::read_byte(u32 address) {
|
||||
if ((sampleMem!=NULL) && (address<getSampleMemCapacity())) {
|
||||
return sampleMem[address];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
double DivPlatformX1_010::NoteX1_010(int ch, int note) {
|
||||
if (chan[ch].pcm) { // PCM note
|
||||
double off=8192.0;
|
||||
|
|
@ -811,7 +818,7 @@ DivDispatchOscBuffer* DivPlatformX1_010::getOscBuffer(int ch) {
|
|||
|
||||
unsigned char* DivPlatformX1_010::getRegisterPool() {
|
||||
for (int i=0; i<0x2000; i++) {
|
||||
regPool[i]=x1_010->ram_r(i);
|
||||
regPool[i]=x1_010.ram_r(i);
|
||||
}
|
||||
return regPool;
|
||||
}
|
||||
|
|
@ -829,7 +836,7 @@ void DivPlatformX1_010::reset() {
|
|||
chan[i].ws.setEngine(parent);
|
||||
chan[i].ws.init(NULL,128,255,false);
|
||||
}
|
||||
x1_010->reset();
|
||||
x1_010.reset();
|
||||
sampleBank=0;
|
||||
// set per-channel initial panning
|
||||
for (int i=0; i<16; i++) {
|
||||
|
|
@ -942,9 +949,7 @@ int DivPlatformX1_010::init(DivEngine* p, int channels, int sugRate, unsigned in
|
|||
setFlags(flags);
|
||||
sampleMem=new unsigned char[getSampleMemCapacity()];
|
||||
sampleMemLen=0;
|
||||
intf.memory=sampleMem;
|
||||
x1_010=new x1_010_core(intf);
|
||||
x1_010->reset();
|
||||
x1_010.reset();
|
||||
reset();
|
||||
return 16;
|
||||
}
|
||||
|
|
@ -953,7 +958,6 @@ void DivPlatformX1_010::quit() {
|
|||
for (int i=0; i<16; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
delete x1_010;
|
||||
delete[] sampleMem;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,20 +24,9 @@
|
|||
#include "../engine.h"
|
||||
#include "../macroInt.h"
|
||||
#include "../waveSynth.h"
|
||||
#include "sound/x1_010/x1_010.hpp"
|
||||
#include "vgsound_emu/src/x1_010/x1_010.hpp"
|
||||
|
||||
class DivX1_010Interface: public x1_010_mem_intf {
|
||||
public:
|
||||
unsigned char* memory;
|
||||
int sampleBank;
|
||||
virtual u8 read_byte(u32 address) override {
|
||||
if (memory==NULL) return 0;
|
||||
return memory[address & 0xfffff];
|
||||
}
|
||||
DivX1_010Interface(): memory(NULL), sampleBank(0) {}
|
||||
};
|
||||
|
||||
class DivPlatformX1_010: public DivDispatch {
|
||||
class DivPlatformX1_010: public DivDispatch, public vgsound_emu_mem_intf {
|
||||
struct Channel {
|
||||
struct Envelope {
|
||||
struct EnvFlag {
|
||||
|
|
@ -118,14 +107,14 @@ class DivPlatformX1_010: public DivDispatch {
|
|||
unsigned char* sampleMem;
|
||||
size_t sampleMemLen;
|
||||
unsigned char sampleBank;
|
||||
DivX1_010Interface intf;
|
||||
x1_010_core* x1_010;
|
||||
x1_010_core x1_010;
|
||||
unsigned char regPool[0x2000];
|
||||
double NoteX1_010(int ch, int note);
|
||||
void updateWave(int ch);
|
||||
void updateEnvelope(int ch);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
public:
|
||||
u8 read_byte(u32 address);
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
|
|
@ -151,6 +140,10 @@ class DivPlatformX1_010: public DivDispatch {
|
|||
const char** getRegisterSheet();
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
DivPlatformX1_010():
|
||||
DivDispatch(),
|
||||
vgsound_emu_mem_intf(),
|
||||
x1_010(*this) {}
|
||||
~DivPlatformX1_010();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -261,7 +261,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -310,7 +310,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -427,7 +427,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
||||
if (KVS(c.chan,i)) {
|
||||
if (!chan[c.chan].active || chan[c.chan].insChanged) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
}
|
||||
|
|
@ -487,7 +487,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
||||
if (KVS(c.chan,i)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -572,7 +572,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[c.chan].state.alg][c.value]) {
|
||||
if (KVS(c.chan,c.value)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -764,7 +764,7 @@ void DivPlatformYM2203::muteChannel(int ch, bool mute) {
|
|||
if (isMuted[ch]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[ch].state.alg][j]) {
|
||||
if (KVS(ch,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[ch].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -781,7 +781,7 @@ void DivPlatformYM2203::forceIns() {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ void DivPlatformYM2203Ext::forceIns() {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ void DivPlatformYM2608::tick(bool sysTick) {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -421,7 +421,7 @@ void DivPlatformYM2608::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -475,7 +475,7 @@ void DivPlatformYM2608::tick(bool sysTick) {
|
|||
}
|
||||
if (m.tl.had) {
|
||||
op.tl=127-m.tl.val;
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -698,7 +698,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
|||
for (int i=0; i<4; i++) {
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
||||
if (KVS(c.chan,i)) {
|
||||
if (!chan[c.chan].active || chan[c.chan].insChanged) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
}
|
||||
|
|
@ -779,7 +779,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
|||
for (int i=0; i<4; i++) {
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
||||
if (KVS(c.chan,i)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -890,7 +890,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
|||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.tl=c.value2;
|
||||
if (isOutput[chan[c.chan].state.alg][c.value]) {
|
||||
if (KVS(c.chan,c.value)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -1093,7 +1093,7 @@ void DivPlatformYM2608::forceIns() {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -1114,6 +1114,11 @@ void DivPlatformYM2608::forceIns() {
|
|||
}
|
||||
for (int i=9; i<16; i++) {
|
||||
chan[i].insChanged=true;
|
||||
if (i>14) { // ADPCM-B
|
||||
immWrite(0x10b,chan[i].outVol);
|
||||
} else {
|
||||
immWrite(0x18+(i-9),isMuted[i]?0:((chan[i].pan<<6)|chan[i].vol));
|
||||
}
|
||||
}
|
||||
|
||||
ay->forceIns();
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ void DivPlatformYM2608Ext::forceIns() {
|
|||
if (i==2) { // extended channel
|
||||
if (isOpMuted[j]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (isOutput[chan[i].state.alg][j]) {
|
||||
} else if (KVS(i,j)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
|
|
@ -451,7 +451,7 @@ void DivPlatformYM2608Ext::forceIns() {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -472,8 +472,13 @@ void DivPlatformYM2608Ext::forceIns() {
|
|||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
for (int i=6; i<16; i++) {
|
||||
for (int i=9; i<16; i++) {
|
||||
chan[i].insChanged=true;
|
||||
if (i>14) { // ADPCM-B
|
||||
immWrite(0x10b,chan[i].outVol);
|
||||
} else {
|
||||
immWrite(0x18+(i-9),isMuted[i]?0:((chan[i].pan<<6)|chan[i].vol));
|
||||
}
|
||||
}
|
||||
ay->forceIns();
|
||||
ay->flushWrites();
|
||||
|
|
|
|||
|
|
@ -417,7 +417,7 @@ void DivPlatformYM2610::tick(bool sysTick) {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -462,7 +462,7 @@ void DivPlatformYM2610::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -516,7 +516,7 @@ void DivPlatformYM2610::tick(bool sysTick) {
|
|||
}
|
||||
if (m.tl.had) {
|
||||
op.tl=127-m.tl.val;
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -742,7 +742,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
for (int i=0; i<4; i++) {
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
||||
if (KVS(c.chan,i)) {
|
||||
if (!chan[c.chan].active || chan[c.chan].insChanged) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
}
|
||||
|
|
@ -823,7 +823,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
for (int i=0; i<4; i++) {
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
||||
if (KVS(c.chan,i)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -934,7 +934,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.tl=c.value2;
|
||||
if (isOutput[chan[c.chan].state.alg][c.value]) {
|
||||
if (KVS(c.chan,c.value)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -1137,7 +1137,7 @@ void DivPlatformYM2610::forceIns() {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
|
|||
|
|
@ -400,7 +400,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -445,7 +445,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -499,7 +499,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
|
|||
}
|
||||
if (m.tl.had) {
|
||||
op.tl=127-m.tl.val;
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -724,7 +724,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
for (int i=0; i<4; i++) {
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
||||
if (KVS(c.chan,i)) {
|
||||
if (!chan[c.chan].active || chan[c.chan].insChanged) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
}
|
||||
|
|
@ -805,7 +805,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
for (int i=0; i<4; i++) {
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
||||
if (KVS(c.chan,i)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -916,7 +916,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
op.tl=c.value2;
|
||||
if (isOutput[chan[c.chan].state.alg][c.value]) {
|
||||
if (KVS(c.chan,c.value)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
@ -1119,7 +1119,7 @@ void DivPlatformYM2610B::forceIns() {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ void DivPlatformYM2610BExt::forceIns() {
|
|||
if (i==2 && extMode) { // extended channel
|
||||
if (isOpMuted[j]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (isOutput[chan[i].state.alg][j]) {
|
||||
} else if (KVS(i,j)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
|
|
@ -451,7 +451,7 @@ void DivPlatformYM2610BExt::forceIns() {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ void DivPlatformYM2610Ext::forceIns() {
|
|||
if (i==1 && extMode) { // extended channel
|
||||
if (isOpMuted[j]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (isOutput[chan[i].state.alg][j]) {
|
||||
} else if (KVS(i,j)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
|
|
@ -451,7 +451,7 @@ void DivPlatformYM2610Ext::forceIns() {
|
|||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
|
|
|||
|
|
@ -379,7 +379,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
break;
|
||||
case 0xed: // delay
|
||||
if (effectVal!=0) {
|
||||
bool comparison=(song.delayBehavior==1)?(effectVal<=nextSpeed):(effectVal<nextSpeed);
|
||||
bool comparison=(song.delayBehavior==1)?(effectVal<=nextSpeed):(effectVal<(nextSpeed*(curSubSong->timeBase+1)));
|
||||
if (song.delayBehavior==2) comparison=true;
|
||||
if (comparison) {
|
||||
chan[i].rowDelay=effectVal+1;
|
||||
|
|
|
|||
|
|
@ -511,6 +511,7 @@ struct DivSong {
|
|||
bool e1e2StopOnSameNote;
|
||||
bool brokenPortaArp;
|
||||
bool snNoLowPeriods;
|
||||
bool autoSystem;
|
||||
|
||||
std::vector<DivInstrument*> ins;
|
||||
std::vector<DivWavetable*> wave;
|
||||
|
|
@ -614,7 +615,8 @@ struct DivSong {
|
|||
brokenOutVol(false),
|
||||
e1e2StopOnSameNote(false),
|
||||
brokenPortaArp(false),
|
||||
snNoLowPeriods(false) {
|
||||
snNoLowPeriods(false),
|
||||
autoSystem(true) {
|
||||
for (int i=0; i<32; i++) {
|
||||
system[i]=DIV_SYSTEM_NULL;
|
||||
systemVol[i]=64;
|
||||
|
|
|
|||
|
|
@ -23,16 +23,17 @@
|
|||
|
||||
const char* aboutLine[]={
|
||||
"tildearrow",
|
||||
"is proud to present",
|
||||
"is not so happy to present",
|
||||
"",
|
||||
("Furnace " DIV_VERSION),
|
||||
"",
|
||||
"the biggest multi-system chiptune tracker!",
|
||||
"featuring DefleMask song compatibility.",
|
||||
"",
|
||||
"zero disassembly.",
|
||||
"just clean-room design,",
|
||||
"time and dedication.",
|
||||
"what a mess of a versioning scheme we have...",
|
||||
"I mean it! these pre-releases are like normal releases",
|
||||
"by now but only because I promised you to have SNES in",
|
||||
"0.6pre2 I am doing this whole mess...",
|
||||
"",
|
||||
"> CREDITS <",
|
||||
"",
|
||||
|
|
|
|||
|
|
@ -63,12 +63,45 @@ float FurnaceGUI::computeGradPos(int type, int chan) {
|
|||
return chanOscBright[chan];
|
||||
break;
|
||||
case GUI_OSCREF_NOTE_TRIGGER:
|
||||
return keyHit[chan]*5.0f;
|
||||
return keyHit1[chan];
|
||||
break;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void FurnaceGUI::calcChanOsc() {
|
||||
std::vector<DivDispatchOscBuffer*> oscBufs;
|
||||
std::vector<ChanOscStatus*> oscFFTs;
|
||||
std::vector<int> oscChans;
|
||||
|
||||
int chans=e->getTotalChannelCount();
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
DivDispatchOscBuffer* buf=e->getOscBuffer(i);
|
||||
if (buf!=NULL && e->curSubSong->chanShow[i]) {
|
||||
// 30ms should be enough
|
||||
int displaySize=(float)(buf->rate)*0.03f;
|
||||
if (e->isRunning()) {
|
||||
float minLevel=1.0f;
|
||||
float maxLevel=-1.0f;
|
||||
unsigned short needlePos=buf->needle;
|
||||
needlePos-=displaySize;
|
||||
for (unsigned short i=0; i<512; i++) {
|
||||
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f;
|
||||
if (minLevel>y) minLevel=y;
|
||||
if (maxLevel<y) maxLevel=y;
|
||||
}
|
||||
float estimate=pow(maxLevel-minLevel,0.5f);
|
||||
if (estimate>1.0f) estimate=1.0f;
|
||||
chanOscVol[i]=MAX(chanOscVol[i]*0.87f,estimate);
|
||||
}
|
||||
} else {
|
||||
chanOscVol[i]=MAX(chanOscVol[i]*0.87f,0.0f);
|
||||
}
|
||||
if (chanOscVol[i]<0.00001f) chanOscVol[i]=0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void FurnaceGUI::drawChanOsc() {
|
||||
if (nextWindow==GUI_WINDOW_CHAN_OSC) {
|
||||
chanOscOpen=true;
|
||||
|
|
@ -361,7 +394,6 @@ void FurnaceGUI::drawChanOsc() {
|
|||
if (maxLevel<y) maxLevel=y;
|
||||
}
|
||||
dcOff=(minLevel+maxLevel)*0.5f;
|
||||
chanOscVol[ch]=MAX(chanOscVol[ch]*0.87f,maxLevel-minLevel);
|
||||
for (unsigned short i=0; i<512; i++) {
|
||||
float x=(float)i/512.0f;
|
||||
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f;
|
||||
|
|
|
|||
|
|
@ -645,6 +645,7 @@ void FurnaceGUI::doAction(int what) {
|
|||
} else {
|
||||
wantScrollList=true;
|
||||
MARK_MODIFIED;
|
||||
RESET_WAVE_MACRO_ZOOM;
|
||||
}
|
||||
break;
|
||||
case GUI_ACTION_WAVE_LIST_DUPLICATE:
|
||||
|
|
@ -657,6 +658,7 @@ void FurnaceGUI::doAction(int what) {
|
|||
(*e->song.wave[curWave])=(*e->song.wave[prevWave]);
|
||||
wantScrollList=true;
|
||||
MARK_MODIFIED;
|
||||
RESET_WAVE_MACRO_ZOOM;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -1326,6 +1328,7 @@ void FurnaceGUI::doAction(int what) {
|
|||
}
|
||||
nextWindow=GUI_WINDOW_WAVE_EDIT;
|
||||
MARK_MODIFIED;
|
||||
RESET_WAVE_MACRO_ZOOM;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -405,7 +405,7 @@ void FurnaceGUI::doCopy(bool cut) {
|
|||
}
|
||||
}
|
||||
|
||||
void FurnaceGUI::doPaste(PasteMode mode) {
|
||||
void FurnaceGUI::doPaste(PasteMode mode, int arg) {
|
||||
finishSelection();
|
||||
prepareUndo(GUI_UNDO_PATTERN_PASTE);
|
||||
char* clipText=SDL_GetClipboardText();
|
||||
|
|
@ -481,14 +481,16 @@ void FurnaceGUI::doPaste(PasteMode mode) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if ((mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG) && strcmp(note,"...")==0) {
|
||||
if ((mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG ||
|
||||
mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) && strcmp(note,"...")==0) {
|
||||
// do nothing.
|
||||
} else {
|
||||
if (mode!=GUI_PASTE_MODE_MIX_BG || (pat->data[j][0]==0 && pat->data[j][1]==0)) {
|
||||
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || (pat->data[j][0]==0 && pat->data[j][1]==0)) {
|
||||
if (!decodeNote(note,pat->data[j][0],pat->data[j][1])) {
|
||||
invalidData=true;
|
||||
break;
|
||||
}
|
||||
if (mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) pat->data[j][2]=arg;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -505,7 +507,7 @@ void FurnaceGUI::doPaste(PasteMode mode) {
|
|||
note[2]=0;
|
||||
|
||||
if (iFine==1) {
|
||||
if (!opMaskPaste.ins) {
|
||||
if (!opMaskPaste.ins || mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) {
|
||||
iFine++;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -527,7 +529,8 @@ void FurnaceGUI::doPaste(PasteMode mode) {
|
|||
}
|
||||
|
||||
if (strcmp(note,"..")==0) {
|
||||
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG)) {
|
||||
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG ||
|
||||
mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG)) {
|
||||
pat->data[j][iFine+1]=-1;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -536,7 +539,7 @@ void FurnaceGUI::doPaste(PasteMode mode) {
|
|||
invalidData=true;
|
||||
break;
|
||||
}
|
||||
if (mode!=GUI_PASTE_MODE_MIX_BG || pat->data[j][iFine+1]==-1) {
|
||||
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || pat->data[j][iFine+1]==-1) {
|
||||
if (iFine<(3+e->curPat[iCoarse].effectCols*2)) pat->data[j][iFine+1]=val;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
227
src/gui/gui.cpp
227
src/gui/gui.cpp
|
|
@ -584,13 +584,117 @@ void FurnaceGUI::updateWindowTitle() {
|
|||
if (sdlWin!=NULL) SDL_SetWindowTitle(sdlWin,title.c_str());
|
||||
}
|
||||
|
||||
void FurnaceGUI::autoDetectSystem() {
|
||||
std::map<DivSystem,int> sysCountMap;
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
try {
|
||||
sysCountMap.at(e->song.system[i])++;
|
||||
} catch (std::exception& ex) {
|
||||
sysCountMap[e->song.system[i]]=1;
|
||||
}
|
||||
}
|
||||
|
||||
logV("sysCountMap:");
|
||||
for (std::pair<DivSystem,int> k: sysCountMap) {
|
||||
logV("%s: %d",e->getSystemName(k.first),k.second);
|
||||
}
|
||||
|
||||
bool isMatch=false;
|
||||
std::map<DivSystem,int> defCountMap;
|
||||
for (FurnaceGUISysCategory& i: sysCategories) {
|
||||
for (FurnaceGUISysDef& j: i.systems) {
|
||||
defCountMap.clear();
|
||||
for (size_t k=0; k<j.definition.size(); k+=4) {
|
||||
if (j.definition[k]==0) break;
|
||||
try {
|
||||
defCountMap.at((DivSystem)j.definition[k])++;
|
||||
} catch (std::exception& ex) {
|
||||
defCountMap[(DivSystem)j.definition[k]]=1;
|
||||
}
|
||||
}
|
||||
if (defCountMap.size()!=sysCountMap.size()) continue;
|
||||
isMatch=true;
|
||||
logV("trying on defCountMap: %s",j.name);
|
||||
for (std::pair<DivSystem,int> k: defCountMap) {
|
||||
logV("- %s: %d",e->getSystemName(k.first),k.second);
|
||||
}
|
||||
for (std::pair<DivSystem,int> k: defCountMap) {
|
||||
try {
|
||||
if (sysCountMap.at(k.first)!=k.second) {
|
||||
isMatch=false;
|
||||
break;
|
||||
}
|
||||
} catch (std::exception& ex) {
|
||||
isMatch=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isMatch) {
|
||||
logV("match found!");
|
||||
e->song.systemName=j.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isMatch) break;
|
||||
}
|
||||
|
||||
if (!isMatch) {
|
||||
bool isFirst=true;
|
||||
e->song.systemName="";
|
||||
for (std::pair<DivSystem,int> k: sysCountMap) {
|
||||
if (!isFirst) e->song.systemName+=" + ";
|
||||
if (k.second>1) {
|
||||
e->song.systemName+=fmt::sprintf("%d×",k.second);
|
||||
}
|
||||
if (k.first==DIV_SYSTEM_N163) {
|
||||
e->song.systemName+=settings.c163Name;
|
||||
} else {
|
||||
e->song.systemName+=e->getSystemName(k.first);
|
||||
}
|
||||
isFirst=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImVec4 FurnaceGUI::channelColor(int ch) {
|
||||
switch (settings.channelColors) {
|
||||
case 0:
|
||||
return uiColors[GUI_COLOR_CHANNEL_BG];
|
||||
break;
|
||||
case 1:
|
||||
return uiColors[GUI_COLOR_CHANNEL_FM+e->getChannelType(ch)];
|
||||
break;
|
||||
case 2:
|
||||
return uiColors[GUI_COLOR_INSTR_STD+e->getPreferInsType(ch)];
|
||||
break;
|
||||
}
|
||||
// invalid
|
||||
return uiColors[GUI_COLOR_TEXT];
|
||||
}
|
||||
|
||||
ImVec4 FurnaceGUI::channelTextColor(int ch) {
|
||||
switch (settings.channelTextColors) {
|
||||
case 0:
|
||||
return uiColors[GUI_COLOR_CHANNEL_FG];
|
||||
break;
|
||||
case 1:
|
||||
return uiColors[GUI_COLOR_CHANNEL_FM+e->getChannelType(ch)];
|
||||
break;
|
||||
case 2:
|
||||
return uiColors[GUI_COLOR_INSTR_STD+e->getPreferInsType(ch)];
|
||||
break;
|
||||
}
|
||||
// invalid
|
||||
return uiColors[GUI_COLOR_TEXT];
|
||||
}
|
||||
|
||||
const char* defaultLayout="[Window][DockSpaceViewport_11111111]\n\
|
||||
Pos=0,24\n\
|
||||
Size=1280,731\n\
|
||||
Size=1280,776\n\
|
||||
Collapsed=0\n\
|
||||
\n\
|
||||
[Window][Debug##Default]\n\
|
||||
Pos=54,0\n\
|
||||
Pos=54,19\n\
|
||||
Size=400,400\n\
|
||||
Collapsed=0\n\
|
||||
\n\
|
||||
|
|
@ -601,9 +705,9 @@ Collapsed=0\n\
|
|||
\n\
|
||||
[Window][Song Information]\n\
|
||||
Pos=978,24\n\
|
||||
Size=302,217\n\
|
||||
Size=302,179\n\
|
||||
Collapsed=0\n\
|
||||
DockId=0x00000004,0\n\
|
||||
DockId=0x0000000F,0\n\
|
||||
\n\
|
||||
[Window][Orders]\n\
|
||||
Pos=0,24\n\
|
||||
|
|
@ -615,7 +719,7 @@ DockId=0x00000007,0\n\
|
|||
Pos=653,24\n\
|
||||
Size=323,217\n\
|
||||
Collapsed=0\n\
|
||||
DockId=0x00000006,2\n\
|
||||
DockId=0x00000006,0\n\
|
||||
\n\
|
||||
[Window][Wavetables]\n\
|
||||
Pos=653,24\n\
|
||||
|
|
@ -627,13 +731,13 @@ DockId=0x00000006,1\n\
|
|||
Pos=653,24\n\
|
||||
Size=323,217\n\
|
||||
Collapsed=0\n\
|
||||
DockId=0x00000006,0\n\
|
||||
DockId=0x00000006,2\n\
|
||||
\n\
|
||||
[Window][Pattern]\n\
|
||||
Pos=0,243\n\
|
||||
Size=1246,512\n\
|
||||
Size=1246,557\n\
|
||||
Collapsed=0\n\
|
||||
DockId=0x0000000B,0\n\
|
||||
DockId=0x00000013,0\n\
|
||||
\n\
|
||||
[Window][Instrument Editor]\n\
|
||||
Pos=372,102\n\
|
||||
|
|
@ -642,7 +746,7 @@ Collapsed=0\n\
|
|||
\n\
|
||||
[Window][Warning]\n\
|
||||
Pos=481,338\n\
|
||||
Size=346,71\n\
|
||||
Size=264,86\n\
|
||||
Collapsed=0\n\
|
||||
\n\
|
||||
[Window][Sample Editor]\n\
|
||||
|
|
@ -675,8 +779,8 @@ Size=514,71\n\
|
|||
Collapsed=0\n\
|
||||
\n\
|
||||
[Window][Mixer]\n\
|
||||
Pos=63,55\n\
|
||||
Size=450,215\n\
|
||||
Pos=429,198\n\
|
||||
Size=453,355\n\
|
||||
Collapsed=0\n\
|
||||
\n\
|
||||
[Window][Oscilloscope]\n\
|
||||
|
|
@ -687,7 +791,7 @@ DockId=0x0000000E,0\n\
|
|||
\n\
|
||||
[Window][Volume Meter]\n\
|
||||
Pos=1248,243\n\
|
||||
Size=32,512\n\
|
||||
Size=32,557\n\
|
||||
Collapsed=0\n\
|
||||
DockId=0x0000000C,0\n\
|
||||
\n\
|
||||
|
|
@ -762,9 +866,10 @@ Size=368,449\n\
|
|||
Collapsed=0\n\
|
||||
\n\
|
||||
[Window][Register View]\n\
|
||||
Pos=847,180\n\
|
||||
Size=417,393\n\
|
||||
Pos=829,243\n\
|
||||
Size=417,557\n\
|
||||
Collapsed=0\n\
|
||||
DockId=0x00000014,0\n\
|
||||
\n\
|
||||
[Window][New Song]\n\
|
||||
Pos=267,110\n\
|
||||
|
|
@ -783,8 +888,40 @@ Size=304,40\n\
|
|||
Collapsed=0\n\
|
||||
DockId=0x0000000A,0\n\
|
||||
\n\
|
||||
[Window][Subsongs]\n\
|
||||
Pos=978,205\n\
|
||||
Size=302,36\n\
|
||||
Collapsed=0\n\
|
||||
DockId=0x00000010,0\n\
|
||||
\n\
|
||||
[Window][Oscilloscope (per-channel)]\n\
|
||||
Pos=1095,243\n\
|
||||
Size=151,557\n\
|
||||
Collapsed=0\n\
|
||||
DockId=0x00000012,0\n\
|
||||
\n\
|
||||
[Window][Piano]\n\
|
||||
Pos=177,669\n\
|
||||
Size=922,118\n\
|
||||
Collapsed=0\n\
|
||||
\n\
|
||||
[Window][Log Viewer]\n\
|
||||
Pos=60,60\n\
|
||||
Size=541,637\n\
|
||||
Collapsed=0\n\
|
||||
\n\
|
||||
[Window][Pattern Manager]\n\
|
||||
Pos=60,60\n\
|
||||
Size=1099,366\n\
|
||||
Collapsed=0\n\
|
||||
\n\
|
||||
[Window][Chip Manager]\n\
|
||||
Pos=60,60\n\
|
||||
Size=490,407\n\
|
||||
Collapsed=0\n\
|
||||
\n\
|
||||
[Docking][Data]\n\
|
||||
DockSpace ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,24 Size=1280,731 Split=Y Selected=0x6C01C512\n\
|
||||
DockSpace ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,24 Size=1280,776 Split=Y Selected=0x6C01C512\n\
|
||||
DockNode ID=0x00000001 Parent=0x8B93E3BD SizeRef=1280,217 Split=X Selected=0xF3094A52\n\
|
||||
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=976,231 Split=X Selected=0x65CC51DC\n\
|
||||
DockNode ID=0x00000007 Parent=0x00000003 SizeRef=345,231 HiddenTabBar=1 Selected=0x8F5BFC9A\n\
|
||||
|
|
@ -795,10 +932,17 @@ DockSpace ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,24 Size=1280,731 Spl
|
|||
DockNode ID=0x0000000E Parent=0x00000009 SizeRef=292,105 HiddenTabBar=1 Selected=0x6D682373\n\
|
||||
DockNode ID=0x0000000A Parent=0x00000005 SizeRef=292,40 HiddenTabBar=1 Selected=0x0DE44CFF\n\
|
||||
DockNode ID=0x00000006 Parent=0x00000008 SizeRef=323,406 Selected=0xD2AD486B\n\
|
||||
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=302,231 Selected=0x60B9D088\n\
|
||||
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=302,231 Split=Y Selected=0x60B9D088\n\
|
||||
DockNode ID=0x0000000F Parent=0x00000004 SizeRef=302,179 Selected=0x60B9D088\n\
|
||||
DockNode ID=0x00000010 Parent=0x00000004 SizeRef=302,36 HiddenTabBar=1 Selected=0x723A6369\n\
|
||||
DockNode ID=0x00000002 Parent=0x8B93E3BD SizeRef=1280,512 Split=X Selected=0x6C01C512\n\
|
||||
DockNode ID=0x0000000B Parent=0x00000002 SizeRef=1246,503 CentralNode=1 HiddenTabBar=1 Selected=0xB9ADD0D5\n\
|
||||
DockNode ID=0x0000000C Parent=0x00000002 SizeRef=32,503 HiddenTabBar=1 Selected=0x644DA2C1\n\n";
|
||||
DockNode ID=0x0000000B Parent=0x00000002 SizeRef=1246,503 Split=X Selected=0xB9ADD0D5\n\
|
||||
DockNode ID=0x00000011 Parent=0x0000000B SizeRef=1093,557 Split=X Selected=0xB9ADD0D5\n\
|
||||
DockNode ID=0x00000013 Parent=0x00000011 SizeRef=827,557 CentralNode=1 HiddenTabBar=1 Selected=0xB9ADD0D5\n\
|
||||
DockNode ID=0x00000014 Parent=0x00000011 SizeRef=417,557 Selected=0x425428FB\n\
|
||||
DockNode ID=0x00000012 Parent=0x0000000B SizeRef=151,557 HiddenTabBar=1 Selected=0x4C07BC58\n\
|
||||
DockNode ID=0x0000000C Parent=0x00000002 SizeRef=32,503 HiddenTabBar=1 Selected=0x644DA2C1\n";
|
||||
|
||||
|
||||
void FurnaceGUI::prepareLayout() {
|
||||
FILE* check;
|
||||
|
|
@ -2086,6 +2230,30 @@ void FurnaceGUI::editOptions(bool topMenu) {
|
|||
if (ImGui::BeginMenu("paste special...")) {
|
||||
if (ImGui::MenuItem("paste mix",BIND_FOR(GUI_ACTION_PAT_PASTE_MIX))) doPaste(GUI_PASTE_MODE_MIX_FG);
|
||||
if (ImGui::MenuItem("paste mix (background)",BIND_FOR(GUI_ACTION_PAT_PASTE_MIX_BG))) doPaste(GUI_PASTE_MODE_MIX_BG);
|
||||
if (ImGui::BeginMenu("paste with ins (foreground)")) {
|
||||
if (e->song.ins.empty()) {
|
||||
ImGui::Text("no instruments available");
|
||||
}
|
||||
for (size_t i=0; i<e->song.ins.size(); i++) {
|
||||
snprintf(id,4095,"%.2X: %s",(int)i,e->song.ins[i]->name.c_str());
|
||||
if (ImGui::MenuItem(id)) {
|
||||
doPaste(GUI_PASTE_MODE_INS_FG,i);
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("paste with ins (background)")) {
|
||||
if (e->song.ins.empty()) {
|
||||
ImGui::Text("no instruments available");
|
||||
}
|
||||
for (size_t i=0; i<e->song.ins.size(); i++) {
|
||||
snprintf(id,4095,"%.2X: %s",(int)i,e->song.ins[i]->name.c_str());
|
||||
if (ImGui::MenuItem(id)) {
|
||||
doPaste(GUI_PASTE_MODE_INS_BG,i);
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::MenuItem("paste flood",BIND_FOR(GUI_ACTION_PAT_PASTE_FLOOD))) doPaste(GUI_PASTE_MODE_FLOOD);
|
||||
if (ImGui::MenuItem("paste overflow",BIND_FOR(GUI_ACTION_PAT_PASTE_OVERFLOW))) doPaste(GUI_PASTE_MODE_OVERFLOW);
|
||||
ImGui::EndMenu();
|
||||
|
|
@ -3260,6 +3428,9 @@ bool FurnaceGUI::loop() {
|
|||
showError("cannot add chip! ("+e->getLastError()+")");
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
if (e->song.autoSystem) {
|
||||
autoDetectSystem();
|
||||
}
|
||||
updateWindowTitle();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
|
|
@ -3280,6 +3451,9 @@ bool FurnaceGUI::loop() {
|
|||
DivSystem picked=systemPicker();
|
||||
if (picked!=DIV_SYSTEM_NULL) {
|
||||
e->changeSystem(i,picked,preserveChanPos);
|
||||
if (e->song.autoSystem) {
|
||||
autoDetectSystem();
|
||||
}
|
||||
updateWindowTitle();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
|
@ -3295,6 +3469,10 @@ bool FurnaceGUI::loop() {
|
|||
if (!e->removeSystem(i,preserveChanPos)) {
|
||||
showError("cannot remove chip! ("+e->getLastError()+")");
|
||||
}
|
||||
if (e->song.autoSystem) {
|
||||
autoDetectSystem();
|
||||
updateWindowTitle();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
|
|
@ -3469,6 +3647,8 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
calcChanOsc();
|
||||
|
||||
if (mobileUI) {
|
||||
globalWinFlags=ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoBringToFrontOnFocus;
|
||||
//globalWinFlags=ImGuiWindowFlags_NoTitleBar;
|
||||
|
|
@ -3920,6 +4100,7 @@ bool FurnaceGUI::loop() {
|
|||
showError("cannot load wavetable! ("+e->getLastError()+")");
|
||||
} else {
|
||||
MARK_MODIFIED;
|
||||
RESET_WAVE_MACRO_ZOOM;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -4436,6 +4617,10 @@ bool FurnaceGUI::loop() {
|
|||
case GUI_WARN_SYSTEM_DEL:
|
||||
if (ImGui::Button("Yes")) {
|
||||
e->removeSystem(sysToDelete,preserveChanPos);
|
||||
if (e->song.autoSystem) {
|
||||
autoDetectSystem();
|
||||
updateWindowTitle();
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
|
@ -5094,6 +5279,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
macroPointSize(16),
|
||||
waveEditStyle(0),
|
||||
mobileMenuPos(0.0f),
|
||||
autoButtonSize(0.0f),
|
||||
curSysSection(NULL),
|
||||
pendingRawSampleDepth(8),
|
||||
pendingRawSampleChannels(1),
|
||||
|
|
@ -5276,6 +5462,8 @@ FurnaceGUI::FurnaceGUI():
|
|||
macroOffY(0),
|
||||
macroScaleX(100.0f),
|
||||
macroScaleY(100.0f),
|
||||
macroRandMin(0),
|
||||
macroRandMax(0),
|
||||
macroLoopDragStart(0,0),
|
||||
macroLoopDragAreaSize(0,0),
|
||||
macroLoopDragTarget(NULL),
|
||||
|
|
@ -5479,6 +5667,9 @@ FurnaceGUI::FurnaceGUI():
|
|||
waveGenFMCon2[0]=true;
|
||||
waveGenFMCon3[0]=true;
|
||||
|
||||
memset(keyHit,0,sizeof(float)*DIV_MAX_CHANS);
|
||||
memset(keyHit1,0,sizeof(float)*DIV_MAX_CHANS);
|
||||
|
||||
memset(pianoKeyHit,0,sizeof(float)*180);
|
||||
memset(pianoKeyPressed,0,sizeof(bool)*180);
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,12 @@
|
|||
#define MARK_MODIFIED modified=true;
|
||||
#define WAKE_UP drawHalt=16;
|
||||
|
||||
#define RESET_WAVE_MACRO_ZOOM \
|
||||
for (DivInstrument* _wi: e->song.ins) { \
|
||||
_wi->std.waveMacro.vZoom=-1; \
|
||||
_wi->std.waveMacro.vScroll=-1; \
|
||||
}
|
||||
|
||||
#define BIND_FOR(x) getKeyName(actionKeys[x],true).c_str()
|
||||
|
||||
// TODO:
|
||||
|
|
@ -156,6 +162,8 @@ enum FurnaceGUIColors {
|
|||
GUI_COLOR_INSTR_OPL_DRUMS,
|
||||
GUI_COLOR_INSTR_UNKNOWN,
|
||||
|
||||
GUI_COLOR_CHANNEL_BG,
|
||||
GUI_COLOR_CHANNEL_FG,
|
||||
GUI_COLOR_CHANNEL_FM,
|
||||
GUI_COLOR_CHANNEL_PULSE,
|
||||
GUI_COLOR_CHANNEL_NOISE,
|
||||
|
|
@ -590,7 +598,9 @@ enum PasteMode {
|
|||
GUI_PASTE_MODE_MIX_FG,
|
||||
GUI_PASTE_MODE_MIX_BG,
|
||||
GUI_PASTE_MODE_FLOOD,
|
||||
GUI_PASTE_MODE_OVERFLOW
|
||||
GUI_PASTE_MODE_OVERFLOW,
|
||||
GUI_PASTE_MODE_INS_FG,
|
||||
GUI_PASTE_MODE_INS_BG
|
||||
};
|
||||
|
||||
#define FURKMOD_CTRL (1U<<31)
|
||||
|
|
@ -1013,7 +1023,7 @@ class FurnaceGUI {
|
|||
int zsmExportTickRate;
|
||||
int macroPointSize;
|
||||
int waveEditStyle;
|
||||
float mobileMenuPos;
|
||||
float mobileMenuPos, autoButtonSize;
|
||||
const int* curSysSection;
|
||||
|
||||
String pendingRawSample;
|
||||
|
|
@ -1173,6 +1183,8 @@ class FurnaceGUI {
|
|||
int channelStyle;
|
||||
int channelVolStyle;
|
||||
int channelFeedbackStyle;
|
||||
int channelFont;
|
||||
int channelTextCenter;
|
||||
int maxRecentFile;
|
||||
unsigned int maxUndoSteps;
|
||||
String mainFontPath;
|
||||
|
|
@ -1291,9 +1303,11 @@ class FurnaceGUI {
|
|||
saveUnusedPatterns(0),
|
||||
channelColors(1),
|
||||
channelTextColors(0),
|
||||
channelStyle(0),
|
||||
channelStyle(1),
|
||||
channelVolStyle(0),
|
||||
channelFeedbackStyle(1),
|
||||
channelFont(1),
|
||||
channelTextCenter(1),
|
||||
maxRecentFile(10),
|
||||
maxUndoSteps(100),
|
||||
mainFontPath(""),
|
||||
|
|
@ -1449,6 +1463,7 @@ class FurnaceGUI {
|
|||
FurnaceGUIMacroDesc lastMacroDesc;
|
||||
int macroOffX, macroOffY;
|
||||
float macroScaleX, macroScaleY;
|
||||
int macroRandMin, macroRandMax;
|
||||
|
||||
ImVec2 macroLoopDragStart;
|
||||
ImVec2 macroLoopDragAreaSize;
|
||||
|
|
@ -1550,6 +1565,7 @@ class FurnaceGUI {
|
|||
|
||||
// visualizer
|
||||
float keyHit[DIV_MAX_CHANS];
|
||||
float keyHit1[DIV_MAX_CHANS];
|
||||
int lastIns[DIV_MAX_CHANS];
|
||||
|
||||
// log window
|
||||
|
|
@ -1590,6 +1606,7 @@ class FurnaceGUI {
|
|||
void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, unsigned char egt, unsigned char algOrGlobalSus, float maxTl, float maxArDr, const ImVec2& size, unsigned short instType);
|
||||
void drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size);
|
||||
void drawSysConf(int chan, DivSystem type, unsigned int& flags, bool modifyOnChange);
|
||||
void kvsConfig(DivInstrument* ins);
|
||||
|
||||
// these ones offer ctrl-wheel fine value changes.
|
||||
bool CWSliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format=NULL, ImGuiSliderFlags flags=0);
|
||||
|
|
@ -1599,9 +1616,13 @@ class FurnaceGUI {
|
|||
bool CWVSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format="%d", ImGuiSliderFlags flags=0);
|
||||
|
||||
void updateWindowTitle();
|
||||
void autoDetectSystem();
|
||||
void prepareLayout();
|
||||
ImVec4 channelColor(int ch);
|
||||
ImVec4 channelTextColor(int ch);
|
||||
|
||||
void readOsc();
|
||||
void calcChanOsc();
|
||||
|
||||
void pushAccentColors(const ImVec4& one, const ImVec4& two, const ImVec4& border, const ImVec4& borderShadow);
|
||||
void popAccentColors();
|
||||
|
|
@ -1693,7 +1714,7 @@ class FurnaceGUI {
|
|||
void doInsert();
|
||||
void doTranspose(int amount, OperationMask& mask);
|
||||
void doCopy(bool cut);
|
||||
void doPaste(PasteMode mode=GUI_PASTE_MODE_NORMAL);
|
||||
void doPaste(PasteMode mode=GUI_PASTE_MODE_NORMAL, int arg=0);
|
||||
void doChangeIns(int ins);
|
||||
void doInterpolate();
|
||||
void doFade(int p0, int p1, bool mode);
|
||||
|
|
|
|||
|
|
@ -775,6 +775,8 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
|
|||
D(GUI_COLOR_INSTR_OPL_DRUMS,"",ImVec4(0.3f,1.0f,0.9f,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)),
|
||||
D(GUI_COLOR_CHANNEL_FG,"",ImVec4(1.0f,1.0f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_CHANNEL_FM,"",ImVec4(0.2f,0.8f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_CHANNEL_PULSE,"",ImVec4(0.4f,1.0f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_CHANNEL_NOISE,"",ImVec4(0.8f,0.8f,0.8f,1.0f)),
|
||||
|
|
|
|||
|
|
@ -1211,6 +1211,46 @@ inline bool enBit30(const int val) {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
void FurnaceGUI::kvsConfig(DivInstrument* ins) {
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("(click to configure KVS)");
|
||||
}
|
||||
int opCount=4;
|
||||
if (ins->type==DIV_INS_OPLL) opCount=2;
|
||||
if (ins->type==DIV_INS_OPL) opCount=(ins->fm.ops==4)?4:2;
|
||||
if (ImGui::BeginPopupContextItem("IKVSOpt",ImGuiPopupFlags_MouseButtonLeft)) {
|
||||
ImGui::Text("operator level changes with volume?");
|
||||
if (ImGui::BeginTable("KVSTable",4,ImGuiTableFlags_BordersInner)) {
|
||||
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch);
|
||||
for (int i=0; i<4; i++) {
|
||||
int o=(opCount==4)?orderedOps[i]:i;
|
||||
if (!(i&1)) ImGui::TableNextRow();
|
||||
const char* label="AUTO##OPKVS";
|
||||
if (ins->fm.op[o].kvs==0) {
|
||||
label="NO##OPKVS";
|
||||
} else if (ins->fm.op[o].kvs==1) {
|
||||
label="YES##OPKVS";
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d",i+1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushID(o);
|
||||
if (ImGui::Button(label,ImVec2(ImGui::GetContentRegionAvail().x,0.0f))) {
|
||||
if (++ins->fm.op[o].kvs>2) ins->fm.op[o].kvs=0;
|
||||
PARAMETER;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
|
||||
float asFloat[256];
|
||||
int asInt[256];
|
||||
|
|
@ -1579,15 +1619,17 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
|
|||
if (dragItem!=NULL) { \
|
||||
if (dragItem->IsDataType("FUR_OP")) { \
|
||||
if (opToMove!=i && opToMove>=0) { \
|
||||
int destOp=(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]:i; \
|
||||
int sourceOp=(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[opToMove]:opToMove; \
|
||||
if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) { \
|
||||
e->lockEngine([this,ins,i]() { \
|
||||
ins->fm.op[orderedOps[i]]=ins->fm.op[orderedOps[opToMove]]; \
|
||||
e->lockEngine([ins,destOp,sourceOp]() { \
|
||||
ins->fm.op[destOp]=ins->fm.op[sourceOp]; \
|
||||
}); \
|
||||
} else { \
|
||||
e->lockEngine([this,ins,i]() { \
|
||||
DivInstrumentFM::Operator origOp=ins->fm.op[orderedOps[opToMove]]; \
|
||||
ins->fm.op[orderedOps[opToMove]]=ins->fm.op[orderedOps[i]]; \
|
||||
ins->fm.op[orderedOps[i]]=origOp; \
|
||||
e->lockEngine([ins,destOp,sourceOp]() { \
|
||||
DivInstrumentFM::Operator origOp=ins->fm.op[sourceOp]; \
|
||||
ins->fm.op[sourceOp]=ins->fm.op[destOp]; \
|
||||
ins->fm.op[destOp]=origOp; \
|
||||
}); \
|
||||
} \
|
||||
PARAMETER; \
|
||||
|
|
@ -1767,6 +1809,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
P(CWSliderScalar(FM_NAME(FM_AMS),ImGuiDataType_U8,&ins->fm.ams,&_ZERO,&_THREE)); rightClickable
|
||||
ImGui::TableNextColumn();
|
||||
drawAlgorithm(ins->fm.alg,FM_ALGS_4OP,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale));
|
||||
kvsConfig(ins);
|
||||
break;
|
||||
case DIV_INS_OPZ:
|
||||
ImGui::TableNextColumn();
|
||||
|
|
@ -1779,6 +1822,8 @@ void FurnaceGUI::drawInsEdit() {
|
|||
P(CWSliderScalar(FM_NAME(FM_AMS2),ImGuiDataType_U8,&ins->fm.ams2,&_ZERO,&_THREE)); rightClickable
|
||||
ImGui::TableNextColumn();
|
||||
drawAlgorithm(ins->fm.alg,FM_ALGS_4OP,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale));
|
||||
kvsConfig(ins);
|
||||
|
||||
if (ImGui::Button("Request from TX81Z")) {
|
||||
doAction(GUI_ACTION_TX81Z_REQUEST);
|
||||
}
|
||||
|
|
@ -1811,6 +1856,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
ImGui::TableNextColumn();
|
||||
drawAlgorithm(ins->fm.alg&algMax,fourOp?FM_ALGS_4OP_OPL:FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale));
|
||||
kvsConfig(ins);
|
||||
break;
|
||||
}
|
||||
case DIV_INS_OPLL: {
|
||||
|
|
@ -1835,6 +1881,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::EndDisabled();
|
||||
ImGui::TableNextColumn();
|
||||
drawAlgorithm(0,FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,24.0*dpiScale));
|
||||
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
|
||||
bool isPresent[4];
|
||||
|
|
@ -2926,6 +2973,14 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
}
|
||||
|
||||
if (ins->type==DIV_INS_OPZ) {
|
||||
ImGui::SameLine();
|
||||
bool fixedOn=op.egt;
|
||||
if (ImGui::Checkbox("Fixed",&fixedOn)) { PARAMETER
|
||||
op.egt=fixedOn;
|
||||
}
|
||||
}
|
||||
|
||||
//52.0 controls vert scaling; default 96
|
||||
drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale),ins->type);
|
||||
//P(CWSliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_ZERO,&_THIRTY_ONE)); rightClickable
|
||||
|
|
@ -3010,23 +3065,85 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::Text("%s",FM_NAME(FM_KSL));
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
P(CWSliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); rightClickable
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",FM_NAME(FM_MULT));
|
||||
|
||||
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ) {
|
||||
int detune=(op.dt&7)-(settings.unsignedDetune?0:3);
|
||||
if (ins->type==DIV_INS_OPZ) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (CWSliderInt("##DT",&detune,-3,4)) { PARAMETER
|
||||
op.dt=detune+(settings.unsignedDetune?0:3);
|
||||
} rightClickable
|
||||
P(CWSliderScalar(FM_NAME(FM_EGSHIFT),ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); rightClickable
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",FM_NAME(FM_DT));
|
||||
ImGui::Text("%s",FM_NAME(FM_EGSHIFT));
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
P(CWSliderScalar(FM_NAME(FM_REV),ImGuiDataType_U8,&op.dam,&_ZERO,&_SEVEN)); rightClickable
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",FM_NAME(FM_REV));
|
||||
}
|
||||
|
||||
if (ins->type==DIV_INS_OPZ) {
|
||||
if (op.egt) {
|
||||
int block=op.dt;
|
||||
int freqNum=(op.mult<<4)|(op.dvb&15);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (CWSliderInt(FM_NAME(FM_MULT),&block,0,7)) { PARAMETER
|
||||
if (block<0) block=0;
|
||||
if (block>7) block=7;
|
||||
op.dt=block;
|
||||
} rightClickable
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Block");
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (CWSliderInt(FM_NAME(FM_FINE),&freqNum,0,255)) { PARAMETER
|
||||
if (freqNum<0) freqNum=0;
|
||||
if (freqNum>255) freqNum=255;
|
||||
op.mult=freqNum>>4;
|
||||
op.dvb=freqNum&15;
|
||||
} rightClickable
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("FreqNum");
|
||||
} else {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
P(CWSliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); rightClickable
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",FM_NAME(FM_MULT));
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
P(CWSliderScalar(FM_NAME(FM_FINE),ImGuiDataType_U8,&op.dvb,&_ZERO,&_FIFTEEN)); rightClickable
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",FM_NAME(FM_FINE));
|
||||
}
|
||||
} else {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
P(CWSliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); rightClickable
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",FM_NAME(FM_MULT));
|
||||
}
|
||||
|
||||
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ) {
|
||||
if (!(ins->type==DIV_INS_OPZ && op.egt)) {
|
||||
int detune=(op.dt&7)-(settings.unsignedDetune?0:3);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (CWSliderInt("##DT",&detune,-3,4)) { PARAMETER
|
||||
op.dt=detune+(settings.unsignedDetune?0:3);
|
||||
} rightClickable
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",FM_NAME(FM_DT));
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
|
@ -4225,7 +4342,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
|
||||
const char* waveLabel="Waveform";
|
||||
int waveMax=(ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_VERA)?3:255;
|
||||
int waveMax=(ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_VERA)?3:(MAX(1,e->song.waveLen-1));
|
||||
bool bitMode=false;
|
||||
if (ins->type==DIV_INS_C64 || ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_SAA1099) {
|
||||
bitMode=true;
|
||||
|
|
@ -4554,6 +4671,28 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("randomize...")) {
|
||||
if (macroRandMin<lastMacroDesc.min) macroRandMin=lastMacroDesc.min;
|
||||
if (macroRandMin>lastMacroDesc.max) macroRandMin=lastMacroDesc.max;
|
||||
if (macroRandMax<lastMacroDesc.min) macroRandMax=lastMacroDesc.min;
|
||||
if (macroRandMax>lastMacroDesc.max) macroRandMax=lastMacroDesc.max;
|
||||
ImGui::InputInt("Min",¯oRandMin,1,10);
|
||||
ImGui::InputInt("Max",¯oRandMax,1,10);
|
||||
if (ImGui::Button("randomize")) {
|
||||
for (int i=0; i<lastMacroDesc.macro->len; i++) {
|
||||
int val=0;
|
||||
if (macroRandMax<=macroRandMin) {
|
||||
val=macroRandMin;
|
||||
} else {
|
||||
val=macroRandMin+(rand()%(macroRandMax-macroRandMin+1));
|
||||
}
|
||||
lastMacroDesc.macro->val[i]=val;
|
||||
}
|
||||
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
// for suck's fake Clang extension!
|
||||
#include <imgui.h>
|
||||
#define _USE_MATH_DEFINES
|
||||
#include "gui.h"
|
||||
#include "../ta-log.h"
|
||||
|
|
@ -24,6 +26,7 @@
|
|||
#include "IconsFontAwesome4.h"
|
||||
#include "misc/cpp/imgui_stdlib.h"
|
||||
#include "guiConst.h"
|
||||
#include "../utfutils.h"
|
||||
#include <fmt/printf.h>
|
||||
|
||||
inline float randRange(float min, float max) {
|
||||
|
|
@ -400,7 +403,7 @@ void FurnaceGUI::drawPattern() {
|
|||
ImGui::PushStyleColor(ImGuiCol_Header,uiColors[GUI_COLOR_PATTERN_SELECTION]);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered,uiColors[GUI_COLOR_PATTERN_SELECTION_HOVER]);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive,uiColors[GUI_COLOR_PATTERN_SELECTION_ACTIVE]);
|
||||
if (ImGui::BeginTable("PatternView",displayChans+2,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollX|ImGuiTableFlags_ScrollY|ImGuiTableFlags_NoPadInnerX)) {
|
||||
if (ImGui::BeginTable("PatternView",displayChans+2,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollX|ImGuiTableFlags_ScrollY|ImGuiTableFlags_NoPadInnerX|ImGuiTableFlags_NoBordersInFrozenArea)) {
|
||||
ImGui::TableSetupColumn("pos",ImGuiTableColumnFlags_WidthFixed);
|
||||
char chanID[2048];
|
||||
float lineHeight=(ImGui::GetTextLineHeight()+2*dpiScale);
|
||||
|
|
@ -447,41 +450,46 @@ void FurnaceGUI::drawPattern() {
|
|||
if (!e->curSubSong->chanShow[i]) continue;
|
||||
ImGui::TableNextColumn();
|
||||
bool displayTooltip=false;
|
||||
if (e->curSubSong->chanCollapse[i]) {
|
||||
const char* chName=e->getChannelShortName(i);
|
||||
if (strlen(chName)>3) {
|
||||
snprintf(chanID,2048,"...##_CH%d",i);
|
||||
} else {
|
||||
snprintf(chanID,2048,"%s##_CH%d",chName,i);
|
||||
}
|
||||
displayTooltip=true;
|
||||
} else {
|
||||
const char* chName=e->getChannelName(i);
|
||||
size_t chNameLimit=6+4*e->curPat[i].effectCols;
|
||||
if (strlen(chName)>chNameLimit) {
|
||||
String shortChName=chName;
|
||||
shortChName.resize(chNameLimit-3);
|
||||
shortChName+="...";
|
||||
snprintf(chanID,2048," %s##_CH%d",shortChName.c_str(),i);
|
||||
displayTooltip=true;
|
||||
} else {
|
||||
snprintf(chanID,2048," %s##_CH%d",chName,i);
|
||||
}
|
||||
}
|
||||
|
||||
bool muted=e->isChannelMuted(i);
|
||||
ImVec4 chanHead=muted?uiColors[GUI_COLOR_CHANNEL_MUTED]:uiColors[GUI_COLOR_CHANNEL_FM+e->getChannelType(i)];
|
||||
ImVec4 chanHead=muted?uiColors[GUI_COLOR_CHANNEL_MUTED]:channelColor(i);
|
||||
ImVec4 chanHeadActive=chanHead;
|
||||
ImVec4 chanHeadHover=chanHead;
|
||||
ImVec4 chanHeadBase=chanHead;
|
||||
|
||||
if (e->keyHit[i]) {
|
||||
keyHit[i]=0.2;
|
||||
if (!muted) {
|
||||
int note=e->getChanState(i)->note+60;
|
||||
if (note>=0 && note<180) {
|
||||
pianoKeyHit[note]=1.0;
|
||||
keyHit1[i]=1.0f;
|
||||
if (settings.channelFeedbackStyle==1) {
|
||||
keyHit[i]=0.2;
|
||||
if (!muted) {
|
||||
int note=e->getChanState(i)->note+60;
|
||||
if (note>=0 && note<180) {
|
||||
pianoKeyHit[note]=1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
e->keyHit[i]=false;
|
||||
}
|
||||
if (settings.channelFeedbackStyle==2 && e->isRunning()) {
|
||||
float amount=((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i));
|
||||
if (!e->getChanState(i)->keyOn) amount=0.0f;
|
||||
keyHit[i]=amount*0.2f;
|
||||
if (!muted) {
|
||||
int note=e->getChanState(i)->note+60;
|
||||
if (note>=0 && note<180) {
|
||||
pianoKeyHit[note]=amount;
|
||||
}
|
||||
}
|
||||
} else if (settings.channelFeedbackStyle==3 && e->isRunning()) {
|
||||
bool active=e->getChanState(i)->keyOn;
|
||||
keyHit[i]=active?0.2f:0.0f;
|
||||
if (!muted) {
|
||||
int note=e->getChanState(i)->note+60;
|
||||
if (note>=0 && note<180) {
|
||||
pianoKeyHit[note]=active?1.0f:0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (settings.guiColorsBase) {
|
||||
chanHead.x*=1.0-keyHit[i]; chanHead.y*=1.0-keyHit[i]; chanHead.z*=1.0-keyHit[i];
|
||||
chanHeadActive.x*=0.5; chanHeadActive.y*=0.5; chanHeadActive.z*=0.5;
|
||||
|
|
@ -491,17 +499,210 @@ void FurnaceGUI::drawPattern() {
|
|||
chanHeadActive.x*=0.8; chanHeadActive.y*=0.8; chanHeadActive.z*=0.8;
|
||||
chanHeadHover.x*=0.4+keyHit[i]; chanHeadHover.y*=0.4+keyHit[i]; chanHeadHover.z*=0.4+keyHit[i];
|
||||
}
|
||||
keyHit[i]-=0.02*60.0*ImGui::GetIO().DeltaTime;
|
||||
keyHit[i]-=((settings.channelStyle==0)?0.02:0.01)*60.0*ImGui::GetIO().DeltaTime;
|
||||
if (keyHit[i]<0) keyHit[i]=0;
|
||||
ImGui::PushStyleColor(ImGuiCol_Header,chanHead);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive,chanHeadActive);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered,chanHeadHover);
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,ImGui::GetColorU32(chanHead));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(channelTextColor(i)));
|
||||
if (settings.channelStyle==0) ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,ImGui::GetColorU32(chanHead));
|
||||
if (muted) ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_CHANNEL_MUTED]);
|
||||
ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale));
|
||||
if (settings.channelFont==0) ImGui::PushFont(mainFont);
|
||||
|
||||
// TODO: appearance
|
||||
ImGuiWindow* window=ImGui::GetCurrentWindow();
|
||||
ImVec2 size=ImVec2(
|
||||
1.0f,
|
||||
lineHeight+1.0f*dpiScale
|
||||
);
|
||||
ImDrawList* dl=ImGui::GetWindowDrawList();
|
||||
|
||||
if (settings.channelStyle!=0) {
|
||||
size.y+=6.0f*dpiScale;
|
||||
}
|
||||
|
||||
if (settings.channelStyle==2) {
|
||||
size.y+=6.0f*dpiScale;
|
||||
}
|
||||
|
||||
ImVec2 minArea=window->DC.CursorPos;
|
||||
ImVec2 maxArea=ImVec2(
|
||||
minArea.x+window->WorkRect.Max.x-window->WorkRect.Min.x,
|
||||
minArea.y+size.y
|
||||
);
|
||||
ImRect rect=ImRect(minArea,maxArea);
|
||||
float padding=ImGui::CalcTextSize("A").x;
|
||||
|
||||
ImVec2 minLabelArea=minArea;
|
||||
ImVec2 maxLabelArea=maxArea;
|
||||
|
||||
if (e->curSubSong->chanCollapse[i]) {
|
||||
const char* chName=e->getChannelShortName(i);
|
||||
if (strlen(chName)>3) {
|
||||
snprintf(chanID,2048,"...");
|
||||
} else {
|
||||
snprintf(chanID,2048,"%s",chName);
|
||||
}
|
||||
displayTooltip=true;
|
||||
} else {
|
||||
minLabelArea.x+=padding;
|
||||
maxLabelArea.x-=padding;
|
||||
if (settings.channelStyle==3) { // split button
|
||||
maxLabelArea.x-=ImGui::GetFrameHeightWithSpacing();
|
||||
}
|
||||
const char* chName=e->getChannelName(i);
|
||||
float chNameLimit=maxLabelArea.x-minLabelArea.x;
|
||||
if (ImGui::CalcTextSize(chName).x>chNameLimit) {
|
||||
String shortChName;
|
||||
float totalAdvanced=0.0f;
|
||||
float ellipsisSize=ImGui::CalcTextSize("...").x;
|
||||
for (const char* j=chName; *j;) {
|
||||
signed char l;
|
||||
int ch=decodeUTF8((const unsigned char*)j,l);
|
||||
|
||||
totalAdvanced+=ImGui::GetFont()->GetCharAdvance(ch);
|
||||
if (totalAdvanced>(chNameLimit-ellipsisSize)) break;
|
||||
|
||||
for (int k=0; k<l; k++) {
|
||||
shortChName+=j[k];
|
||||
}
|
||||
|
||||
j+=l;
|
||||
}
|
||||
shortChName+="...";
|
||||
snprintf(chanID,2048,"%s",shortChName.c_str());
|
||||
displayTooltip=true;
|
||||
} else {
|
||||
snprintf(chanID,2048,"%s",chName);
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.channelTextCenter) {
|
||||
minLabelArea.x+=0.5f*(maxLabelArea.x-minLabelArea.x-ImGui::CalcTextSize(chanID).x);
|
||||
}
|
||||
|
||||
if (extraChannelButtons==0 || settings.channelVolStyle!=0) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0.0f,0.0f));
|
||||
|
||||
ImGui::PushID(2048+i);
|
||||
switch (settings.channelStyle) {
|
||||
case 0: // classic
|
||||
ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
|
||||
if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) {
|
||||
bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID));
|
||||
ImU32 col=hovered?ImGui::GetColorU32(ImGuiCol_HeaderHovered):ImGui::GetColorU32(ImGuiCol_Header);
|
||||
dl->AddRectFilled(rect.Min,rect.Max,col);
|
||||
dl->AddText(ImVec2(minLabelArea.x,rect.Min.y),ImGui::GetColorU32(channelTextColor(i)),chanID);
|
||||
}
|
||||
break;
|
||||
case 1: { // line
|
||||
ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
|
||||
if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) {
|
||||
bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID));
|
||||
ImU32 fadeCol0=ImGui::GetColorU32(ImVec4(
|
||||
chanHeadBase.x,
|
||||
chanHeadBase.y,
|
||||
chanHeadBase.z,
|
||||
hovered?0.25f:0.0f
|
||||
));
|
||||
ImU32 fadeCol=ImGui::GetColorU32(ImVec4(
|
||||
chanHeadBase.x,
|
||||
chanHeadBase.y,
|
||||
chanHeadBase.z,
|
||||
hovered?0.5f:MIN(1.0f,chanHeadBase.w*keyHit[i]*4.0f)
|
||||
));
|
||||
dl->AddRectFilledMultiColor(rect.Min,rect.Max,fadeCol0,fadeCol0,fadeCol,fadeCol);
|
||||
dl->AddLine(ImVec2(rect.Min.x,rect.Max.y),ImVec2(rect.Max.x,rect.Max.y),ImGui::GetColorU32(chanHeadBase),2.0f*dpiScale);
|
||||
dl->AddText(ImVec2(minLabelArea.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: { // round
|
||||
ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
|
||||
if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) {
|
||||
bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID));
|
||||
ImU32 fadeCol0=ImGui::GetColorU32(ImVec4(
|
||||
chanHeadBase.x,
|
||||
chanHeadBase.y,
|
||||
chanHeadBase.z,
|
||||
hovered?0.5f:MIN(1.0f,0.3f+chanHeadBase.w*keyHit[i]*1.5f)
|
||||
));
|
||||
ImU32 fadeCol=ImGui::GetColorU32(ImVec4(
|
||||
chanHeadBase.x,
|
||||
chanHeadBase.y,
|
||||
chanHeadBase.z,
|
||||
hovered?0.3f:MIN(1.0f,0.2f+chanHeadBase.w*keyHit[i]*1.2f)
|
||||
));
|
||||
ImVec2 rMin=rect.Min;
|
||||
ImVec2 rMax=rect.Max;
|
||||
rMin.x+=3.0f*dpiScale;
|
||||
rMin.y+=6.0f*dpiScale;
|
||||
rMax.x-=3.0f*dpiScale;
|
||||
rMax.y-=6.0f*dpiScale;
|
||||
dl->AddRectFilledMultiColor(rMin,rMax,fadeCol0,fadeCol0,fadeCol,fadeCol,4.0f*dpiScale);
|
||||
dl->AddText(ImVec2(minLabelArea.x,rect.Min.y+6.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: // split button
|
||||
ImGui::Dummy(ImVec2(1.0f,2.0f*dpiScale));
|
||||
ImGui::SetCursorPosX(minLabelArea.x);
|
||||
ImGui::TextUnformatted(chanID);
|
||||
ImGui::SameLine();
|
||||
ImGui::PushFont(mainFont);
|
||||
ImGui::SmallButton(muted?ICON_FA_VOLUME_OFF:ICON_FA_VOLUME_UP);
|
||||
ImGui::PopFont();
|
||||
break;
|
||||
case 4: { // square border
|
||||
ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
|
||||
if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) {
|
||||
bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID));
|
||||
ImU32 fadeCol=ImGui::GetColorU32(ImVec4(
|
||||
chanHeadBase.x,
|
||||
chanHeadBase.y,
|
||||
chanHeadBase.z,
|
||||
hovered?1.0f:MIN(1.0f,0.2f+chanHeadBase.w*keyHit[i]*4.0f)
|
||||
));
|
||||
ImVec2 rMin=rect.Min;
|
||||
ImVec2 rMax=rect.Max;
|
||||
rMin.x+=2.0f*dpiScale;
|
||||
rMin.y+=3.0f*dpiScale;
|
||||
rMax.x-=3.0f*dpiScale;
|
||||
rMax.y-=3.0f*dpiScale;
|
||||
dl->AddRect(rMin,rMax,fadeCol,0.0f,2.0*dpiScale);
|
||||
dl->AddText(ImVec2(minLabelArea.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 5: { // round border
|
||||
ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
|
||||
if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) {
|
||||
bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID));
|
||||
ImU32 fadeCol=ImGui::GetColorU32(ImVec4(
|
||||
chanHeadBase.x,
|
||||
chanHeadBase.y,
|
||||
chanHeadBase.z,
|
||||
hovered?1.0f:MIN(1.0f,0.2f+chanHeadBase.w*keyHit[i]*4.0f)
|
||||
));
|
||||
ImVec2 rMin=rect.Min;
|
||||
ImVec2 rMax=rect.Max;
|
||||
rMin.x+=2.0f*dpiScale;
|
||||
rMin.y+=3.0f*dpiScale;
|
||||
rMax.x-=3.0f*dpiScale;
|
||||
rMax.y-=3.0f*dpiScale;
|
||||
dl->AddRect(rMin,rMax,fadeCol,4.0f*dpiScale,ImDrawFlags_RoundCornersAll,2.0*dpiScale);
|
||||
dl->AddText(ImVec2(minLabelArea.x,rect.Min.y+3.0*dpiScale),ImGui::GetColorU32(channelTextColor(i)),chanID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
if (extraChannelButtons==0 || settings.channelVolStyle!=0) ImGui::PopStyleVar();
|
||||
|
||||
if (displayTooltip && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s",e->getChannelName(i));
|
||||
}
|
||||
if (settings.channelFont==0) ImGui::PopFont();
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||
if (settings.soloAction!=1 && soloTimeout>0 && soloChan==i) {
|
||||
e->toggleSolo(i);
|
||||
|
|
@ -513,11 +714,72 @@ void FurnaceGUI::drawPattern() {
|
|||
}
|
||||
}
|
||||
if (muted) ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopStyleColor(4);
|
||||
if (settings.soloAction!=2) if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
|
||||
inhibitMenu=true;
|
||||
e->toggleSolo(i);
|
||||
}
|
||||
|
||||
if (settings.channelStyle==3) {
|
||||
ImGui::Dummy(ImVec2(1.0f,2.0f*dpiScale));
|
||||
}
|
||||
|
||||
// volume bar
|
||||
if (settings.channelVolStyle!=0) {
|
||||
ImVec2 sizeV=ImVec2(
|
||||
1.0f,
|
||||
6.0*dpiScale
|
||||
);
|
||||
ImVec2 minAreaV=window->DC.CursorPos;
|
||||
ImVec2 maxAreaV=ImVec2(
|
||||
minAreaV.x+window->WorkRect.Max.x-window->WorkRect.Min.x,
|
||||
minAreaV.y+sizeV.y
|
||||
);
|
||||
ImRect rectV=ImRect(minAreaV,maxAreaV);
|
||||
ImGui::ItemSize(sizeV,ImGui::GetStyle().FramePadding.y);
|
||||
if (ImGui::ItemAdd(rectV,ImGui::GetID(chanID))) {
|
||||
float xLeft=0.0f;
|
||||
float xRight=1.0f;
|
||||
|
||||
if (e->keyHit[i]) {
|
||||
keyHit1[i]=1.0f;
|
||||
e->keyHit[i]=false;
|
||||
}
|
||||
|
||||
if (e->isRunning()) {
|
||||
DivChannelState* cs=e->getChanState(i);
|
||||
float stereoPan=(float)(e->convertPanSplitToLinearLR(cs->panL,cs->panR,256)-128)/128.0;
|
||||
switch (settings.channelVolStyle) {
|
||||
case 1: // simple
|
||||
xRight=((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i))*0.9+(keyHit1[i]*0.1f);
|
||||
break;
|
||||
case 2: { // stereo
|
||||
float amount=((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i))*0.4+(keyHit1[i]*0.1f);
|
||||
xRight=0.5+amount*(1.0+MIN(0.0,stereoPan));
|
||||
xLeft=0.5-amount*(1.0-MAX(0.0,stereoPan));
|
||||
break;
|
||||
}
|
||||
case 3: // real
|
||||
xRight=chanOscVol[i];
|
||||
break;
|
||||
case 4: // real (stereo)
|
||||
xRight=0.5+chanOscVol[i]*0.5*(1.0+MIN(0.0,stereoPan));
|
||||
xLeft=0.5-chanOscVol[i]*0.5*(1.0-MAX(0.0,stereoPan));
|
||||
break;
|
||||
}
|
||||
|
||||
dl->AddRectFilled(
|
||||
ImLerp(rectV.Min,rectV.Max,ImVec2(xLeft,0.0f)),
|
||||
ImLerp(rectV.Min,rectV.Max,ImVec2(xRight,1.0f)),
|
||||
ImGui::GetColorU32(chanHeadBase)
|
||||
);
|
||||
}
|
||||
keyHit1[i]-=0.2f;
|
||||
if (keyHit1[i]<0.0f) keyHit1[i]=0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// extra buttons
|
||||
if (extraChannelButtons==2) {
|
||||
DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][ord],true);
|
||||
ImGui::PushFont(mainFont);
|
||||
|
|
|
|||
|
|
@ -960,11 +960,27 @@ void FurnaceGUI::initSystemPresets() {
|
|||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"NEC PC-98 (with PC-9801-86)", { // -73 also has OPNA
|
||||
DIV_SYSTEM_PC98, 64, 0, 1,
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16), // 2x 16-bit Burr Brown DAC
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16),
|
||||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"NEC PC-98 (with PC-9801-86; extended channel 3)", { // -73 also has OPNA
|
||||
DIV_SYSTEM_PC98_EXT, 64, 0, 1,
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16),
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16),
|
||||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"NEC PC-98 (with PC-9801-73)", {
|
||||
DIV_SYSTEM_PC98, 64, 0, 1,
|
||||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"NEC PC-98 (with PC-9801-73; extended channel 3)", {
|
||||
DIV_SYSTEM_PC98_EXT, 64, 0, 1,
|
||||
0
|
||||
}
|
||||
|
|
@ -972,6 +988,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible)", {
|
||||
DIV_SYSTEM_OPN, 64, 0, 2, // 4MHz
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16)|(1<<20),
|
||||
DIV_SYSTEM_OPL3, 64, 0, 0,
|
||||
0
|
||||
}
|
||||
|
|
@ -979,6 +996,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible; extended channel 3)", {
|
||||
DIV_SYSTEM_OPN_EXT, 64, 0, 2, // 4MHz
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16)|(1<<20),
|
||||
DIV_SYSTEM_OPL3, 64, 0, 0,
|
||||
0
|
||||
}
|
||||
|
|
@ -986,6 +1004,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible in drums mode)", {
|
||||
DIV_SYSTEM_OPN, 64, 0, 2, // 4MHz
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16)|(1<<20),
|
||||
DIV_SYSTEM_OPL3_DRUMS, 64, 0, 2,
|
||||
0
|
||||
}
|
||||
|
|
@ -993,6 +1012,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible in drums mode; extended channel 3)", {
|
||||
DIV_SYSTEM_OPN_EXT, 64, 0, 2, // 4MHz
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16)|(1<<20),
|
||||
DIV_SYSTEM_OPL3_DRUMS, 64, 0, 2,
|
||||
0
|
||||
}
|
||||
|
|
@ -1092,6 +1112,20 @@ void FurnaceGUI::initSystemPresets() {
|
|||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"Atari ST", {
|
||||
DIV_SYSTEM_AY8910, 64, 0, 19,
|
||||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"Atari STE", {
|
||||
DIV_SYSTEM_AY8910, 64, 0, 19,
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 50667|(7<<16),
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 50667|(7<<16),
|
||||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"SAM Coupé", {
|
||||
DIV_SYSTEM_SAA1099, 64, 0, 0,
|
||||
|
|
@ -1146,17 +1180,33 @@ void FurnaceGUI::initSystemPresets() {
|
|||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"PC + AdLib/Sound Blaster", {
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"PC + AdLib", {
|
||||
DIV_SYSTEM_OPL2, 64, 0, 0,
|
||||
DIV_SYSTEM_PCSPKR, 64, 0, 0,
|
||||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"PC + AdLib (drums mode)", {
|
||||
DIV_SYSTEM_OPL2, 64, 0, 0,
|
||||
DIV_SYSTEM_PCSPKR, 64, 0, 0,
|
||||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"PC + Sound Blaster", {
|
||||
DIV_SYSTEM_OPL2, 64, 0, 0,
|
||||
DIV_SYSTEM_PCSPKR, 64, 0, 0,
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 22049|(7<<16),
|
||||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"PC + AdLib/Sound Blaster (drums mode)", {
|
||||
DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0,
|
||||
DIV_SYSTEM_PCSPKR, 64, 0, 0,
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 22049|(7<<16),
|
||||
0
|
||||
}
|
||||
));
|
||||
|
|
@ -1165,6 +1215,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
DIV_SYSTEM_OPL2, 64, 0, 0,
|
||||
DIV_SYSTEM_SAA1099, 64, 0, 1,
|
||||
DIV_SYSTEM_SAA1099, 64, 0, 1,
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 22049|(7<<16),
|
||||
DIV_SYSTEM_PCSPKR, 64, 0, 0,
|
||||
0
|
||||
}
|
||||
|
|
@ -1174,6 +1225,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0,
|
||||
DIV_SYSTEM_SAA1099, 64, 0, 1,
|
||||
DIV_SYSTEM_SAA1099, 64, 0, 1,
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 22049|(7<<16),
|
||||
DIV_SYSTEM_PCSPKR, 64, 0, 0,
|
||||
0
|
||||
}
|
||||
|
|
@ -1182,6 +1234,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
"PC + Sound Blaster Pro", {
|
||||
DIV_SYSTEM_OPL2, 64, -127, 0,
|
||||
DIV_SYSTEM_OPL2, 64, 127, 0,
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 22049|(7<<16)|(1<<20), //alternatively 44.1 khz mono
|
||||
DIV_SYSTEM_PCSPKR, 64, 0, 0,
|
||||
0
|
||||
}
|
||||
|
|
@ -1190,6 +1243,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
"PC + Sound Blaster Pro (drums mode)", {
|
||||
DIV_SYSTEM_OPL2_DRUMS, 64, -127, 0,
|
||||
DIV_SYSTEM_OPL2_DRUMS, 64, 127, 0,
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 22049|(7<<16)|(1<<20), //alternatively 44.1 khz mono
|
||||
DIV_SYSTEM_PCSPKR, 64, 0, 0,
|
||||
0
|
||||
}
|
||||
|
|
@ -1197,6 +1251,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"PC + Sound Blaster Pro 2", {
|
||||
DIV_SYSTEM_OPL3, 64, 0, 0,
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16)|(1<<20),
|
||||
DIV_SYSTEM_PCSPKR, 64, 0, 0,
|
||||
0
|
||||
}
|
||||
|
|
@ -1204,6 +1259,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"PC + Sound Blaster Pro 2 (drums mode)", {
|
||||
DIV_SYSTEM_OPL3_DRUMS, 64, 0, 0,
|
||||
DIV_SYSTEM_PCM_DAC, 64, 0, 44099|(15<<16)|(1<<20),
|
||||
DIV_SYSTEM_PCSPKR, 64, 0, 0,
|
||||
0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1284,14 +1284,14 @@ void FurnaceGUI::drawSettings() {
|
|||
}
|
||||
|
||||
ImGui::Text("Channel name colors:");
|
||||
if (ImGui::RadioButton("Single##CTC0",settings.channelColors==0)) {
|
||||
settings.channelColors=0;
|
||||
if (ImGui::RadioButton("Single##CTC0",settings.channelTextColors==0)) {
|
||||
settings.channelTextColors=0;
|
||||
}
|
||||
if (ImGui::RadioButton("Channel type##CTC1",settings.channelColors==1)) {
|
||||
settings.channelColors=1;
|
||||
if (ImGui::RadioButton("Channel type##CTC1",settings.channelTextColors==1)) {
|
||||
settings.channelTextColors=1;
|
||||
}
|
||||
if (ImGui::RadioButton("Instrument type##CTC2",settings.channelColors==2)) {
|
||||
settings.channelColors=2;
|
||||
if (ImGui::RadioButton("Instrument type##CTC2",settings.channelTextColors==2)) {
|
||||
settings.channelTextColors=2;
|
||||
}
|
||||
|
||||
ImGui::Text("Channel style:");
|
||||
|
|
@ -1327,6 +1327,9 @@ void FurnaceGUI::drawSettings() {
|
|||
if (ImGui::RadioButton("Real##CHV3",settings.channelVolStyle==3)) {
|
||||
settings.channelVolStyle=3;
|
||||
}
|
||||
if (ImGui::RadioButton("Real (stereo)##CHV4",settings.channelVolStyle==4)) {
|
||||
settings.channelVolStyle=4;
|
||||
}
|
||||
|
||||
ImGui::Text("Channel feedback style:");
|
||||
|
||||
|
|
@ -1343,6 +1346,20 @@ void FurnaceGUI::drawSettings() {
|
|||
settings.channelFeedbackStyle=3;
|
||||
}
|
||||
|
||||
ImGui::Text("Channel font:");
|
||||
|
||||
if (ImGui::RadioButton("Regular##CHFont0",settings.channelFont==0)) {
|
||||
settings.channelFont=0;
|
||||
}
|
||||
if (ImGui::RadioButton("Monospace##CHFont1",settings.channelFont==1)) {
|
||||
settings.channelFont=1;
|
||||
}
|
||||
|
||||
bool channelTextCenterB=settings.channelTextCenter;
|
||||
if (ImGui::Checkbox("Center channel name",&channelTextCenterB)) {
|
||||
settings.channelTextCenter=channelTextCenterB;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
bool insEditColorizeB=settings.insEditColorize;
|
||||
|
|
@ -1660,6 +1677,8 @@ void FurnaceGUI::drawSettings() {
|
|||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode("Channel")) {
|
||||
UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_BG,"Single color (background)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_FG,"Single color (text)");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_FM,"FM");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_PULSE,"Pulse");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_NOISE,"Noise");
|
||||
|
|
@ -2278,9 +2297,11 @@ void FurnaceGUI::syncSettings() {
|
|||
settings.saveUnusedPatterns=e->getConfInt("saveUnusedPatterns",0);
|
||||
settings.channelColors=e->getConfInt("channelColors",1);
|
||||
settings.channelTextColors=e->getConfInt("channelTextColors",0);
|
||||
settings.channelStyle=e->getConfInt("channelStyle",0);
|
||||
settings.channelStyle=e->getConfInt("channelStyle",1);
|
||||
settings.channelVolStyle=e->getConfInt("channelVolStyle",0);
|
||||
settings.channelFeedbackStyle=e->getConfInt("channelFeedbackStyle",1);
|
||||
settings.channelFont=e->getConfInt("channelFont",1);
|
||||
settings.channelTextCenter=e->getConfInt("channelTextCenter",1);
|
||||
settings.maxRecentFile=e->getConfInt("maxRecentFile",10);
|
||||
|
||||
clampSetting(settings.mainFontSize,2,96);
|
||||
|
|
@ -2378,8 +2399,10 @@ void FurnaceGUI::syncSettings() {
|
|||
clampSetting(settings.channelColors,0,2);
|
||||
clampSetting(settings.channelTextColors,0,2);
|
||||
clampSetting(settings.channelStyle,0,5);
|
||||
clampSetting(settings.channelVolStyle,0,3);
|
||||
clampSetting(settings.channelVolStyle,0,4);
|
||||
clampSetting(settings.channelFeedbackStyle,0,3);
|
||||
clampSetting(settings.channelFont,0,1);
|
||||
clampSetting(settings.channelTextCenter,0,1);
|
||||
clampSetting(settings.maxRecentFile,0,30);
|
||||
|
||||
settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys",""));
|
||||
|
|
@ -2535,6 +2558,8 @@ void FurnaceGUI::commitSettings() {
|
|||
e->setConf("channelStyle",settings.channelStyle);
|
||||
e->setConf("channelVolStyle",settings.channelVolStyle);
|
||||
e->setConf("channelFeedbackStyle",settings.channelFeedbackStyle);
|
||||
e->setConf("channelFont",settings.channelFont);
|
||||
e->setConf("channelTextCenter",settings.channelTextCenter);
|
||||
e->setConf("maxRecentFile",settings.maxRecentFile);
|
||||
|
||||
// colors
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ void FurnaceGUI::drawSongInfo() {
|
|||
ImGui::TableNextColumn();
|
||||
float avail=ImGui::GetContentRegionAvail().x;
|
||||
ImGui::SetNextItemWidth(avail);
|
||||
if (ImGui::InputText("##Name",&e->song.name)) { MARK_MODIFIED
|
||||
if (ImGui::InputText("##Name",&e->song.name,ImGuiInputTextFlags_UndoRedo)) { MARK_MODIFIED
|
||||
updateWindowTitle();
|
||||
}
|
||||
if (e->song.insLen==2) {
|
||||
|
|
@ -61,7 +61,7 @@ void FurnaceGUI::drawSongInfo() {
|
|||
ImGui::Text("Author");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(avail);
|
||||
if (ImGui::InputText("##Author",&e->song.author)) {
|
||||
if (ImGui::InputText("##Author",&e->song.author,ImGuiInputTextFlags_UndoRedo)) {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
|
||||
|
|
@ -70,18 +70,30 @@ void FurnaceGUI::drawSongInfo() {
|
|||
ImGui::Text("Album");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(avail);
|
||||
if (ImGui::InputText("##Category",&e->song.category)) {
|
||||
if (ImGui::InputText("##Category",&e->song.category,ImGuiInputTextFlags_UndoRedo)) {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("System");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(avail);
|
||||
if (ImGui::InputText("##SystemName",&e->song.systemName)) {
|
||||
ImGui::SetNextItemWidth(MAX(16.0f*dpiScale,avail-autoButtonSize-ImGui::GetStyle().ItemSpacing.x));
|
||||
if (ImGui::InputText("##SystemName",&e->song.systemName,ImGuiInputTextFlags_UndoRedo)) {
|
||||
MARK_MODIFIED;
|
||||
updateWindowTitle();
|
||||
e->song.autoSystem=false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
pushToggleColors(e->song.autoSystem);
|
||||
if (ImGui::Button("Auto")) {
|
||||
e->song.autoSystem=!e->song.autoSystem;
|
||||
if (e->song.autoSystem) {
|
||||
autoDetectSystem();
|
||||
updateWindowTitle();
|
||||
}
|
||||
}
|
||||
popToggleColors();
|
||||
autoButtonSize=ImGui::GetItemRectSize().x;
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ void FurnaceGUI::drawNotes() {
|
|||
}
|
||||
if (!notesOpen) return;
|
||||
if (ImGui::Begin("Song Comments",¬esOpen,globalWinFlags)) {
|
||||
ImGui::InputTextMultiline("##SongNotes",&e->song.notes,ImGui::GetContentRegionAvail());
|
||||
ImGui::InputTextMultiline("##SongNotes",&e->song.notes,ImGui::GetContentRegionAvail(),ImGuiInputTextFlags_UndoRedo);
|
||||
}
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_NOTES;
|
||||
ImGui::End();
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ void FurnaceGUI::drawSubSongs() {
|
|||
ImGui::Text("Name");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::InputText("##SubSongName",&e->curSubSong->name)) {
|
||||
if (ImGui::InputText("##SubSongName",&e->curSubSong->name,ImGuiInputTextFlags_UndoRedo)) {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
|
|||
if (ImGui::RadioButton("PAL (3.55MHz)",(flags&15)==1)) {
|
||||
copyOfFlags=(flags&(~15))|1;
|
||||
}
|
||||
if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&15)==2)) {
|
||||
if (ImGui::RadioButton("Arcade (4MHz)",(flags&15)==2)) {
|
||||
copyOfFlags=(flags&(~15))|2;
|
||||
}
|
||||
if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&15)==3)) {
|
||||
|
|
@ -358,6 +358,14 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
|
|||
if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) {
|
||||
copyOfFlags=(flags&(~0x40))|(stereo?0x40:0);
|
||||
}
|
||||
if (stereo) {
|
||||
int sep=256-((flags>>8)&255);
|
||||
if (CWSliderInt("Separation",&sep,1,256)) {
|
||||
if (sep<1) sep=1;
|
||||
if (sep>256) sep=256;
|
||||
copyOfFlags=(flags&(~0xff00))|((256-sep)<<8);
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
bool clockSel=flags&0x80;
|
||||
ImGui::BeginDisabled((type==DIV_SYSTEM_AY8910) && ((flags&0x30)!=16));
|
||||
|
|
@ -765,6 +773,9 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
|
|||
if (copyOfFlags!=flags) {
|
||||
if (chan>=0) {
|
||||
e->setSysFlags(chan,copyOfFlags,restart);
|
||||
if (e->song.autoSystem) {
|
||||
autoDetectSystem();
|
||||
}
|
||||
updateWindowTitle();
|
||||
} else {
|
||||
flags=copyOfFlags;
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ bool FurnaceGUI::parseSysEx(unsigned char* data, size_t len) {
|
|||
op.rs=reader.readC();
|
||||
reader.readC(); // EBS - ignore
|
||||
op.am=reader.readC();
|
||||
// TODO: don't ignore after I add KVS to Furnace
|
||||
reader.readC(); // KVS - ignore
|
||||
op.tl=3+((99-reader.readC())*124)/99;
|
||||
unsigned char freq=reader.readC();
|
||||
|
|
|
|||
|
|
@ -82,6 +82,9 @@ void FurnaceGUI::drawSysManager() {
|
|||
DivSystem picked=systemPicker();
|
||||
if (picked!=DIV_SYSTEM_NULL) {
|
||||
e->changeSystem(i,picked,preserveChanPos);
|
||||
if (e->song.autoSystem) {
|
||||
autoDetectSystem();
|
||||
}
|
||||
updateWindowTitle();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
|
@ -110,6 +113,9 @@ void FurnaceGUI::drawSysManager() {
|
|||
if (!e->addSystem(picked)) {
|
||||
showError("cannot add chip! ("+e->getLastError()+")");
|
||||
}
|
||||
if (e->song.autoSystem) {
|
||||
autoDetectSystem();
|
||||
}
|
||||
updateWindowTitle();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const char* waveGenBaseShapes[4]={
|
|||
"Pulse"
|
||||
};
|
||||
|
||||
const float multFactors[16]={
|
||||
const float multFactors[17]={
|
||||
M_PI,
|
||||
2*M_PI,
|
||||
4*M_PI,
|
||||
|
|
@ -49,6 +49,7 @@ const float multFactors[16]={
|
|||
26*M_PI,
|
||||
28*M_PI,
|
||||
30*M_PI,
|
||||
32*M_PI,
|
||||
};
|
||||
|
||||
void FurnaceGUI::doGenerateWave() {
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ TAParamResult pVersion(String) {
|
|||
printf("- reSID by Dag Lem (GPLv2)\n");
|
||||
printf("- reSIDfp by Dag Lem, Antti Lankila and Leandro Nini (GPLv2)\n");
|
||||
printf("- Stella by Stella Team (GPLv2)\n");
|
||||
printf("- vgsound_emu (first version) by cam900 (BSD 3-clause)\n");
|
||||
printf("- vgsound_emu (second version, modified version) by cam900 (zlib license)\n");
|
||||
return TA_PARAM_QUIT;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue