add experimental low-latency mode

do not use (yet)!
This commit is contained in:
tildearrow 2022-04-15 05:37:23 -05:00
parent d47a59f5f0
commit 96715ed88c
38 changed files with 201 additions and 120 deletions

View file

@ -2232,6 +2232,7 @@ bool DivEngine::initAudioBackend() {
lowQuality=getConfInt("audioQuality",0); lowQuality=getConfInt("audioQuality",0);
forceMono=getConfInt("forceMono",0); forceMono=getConfInt("forceMono",0);
lowLatency=getConfInt("lowLatency",0);
metroVol=(float)(getConfInt("metroVol",100))/100.0f; metroVol=(float)(getConfInt("metroVol",100))/100.0f;
if (metroVol<0.0f) metroVol=0.0f; if (metroVol<0.0f) metroVol=0.0f;
if (metroVol>2.0f) metroVol=2.0f; if (metroVol>2.0f) metroVol=2.0f;

View file

@ -202,8 +202,9 @@ class DivEngine {
bool firstTick; bool firstTick;
bool skipping; bool skipping;
bool midiIsDirect; bool midiIsDirect;
bool lowLatency;
int softLockCount; int softLockCount;
int ticks, curRow, curOrder, remainingLoops, nextSpeed; int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed;
double divider; double divider;
int cycles; int cycles;
double clockDrift; double clockDrift;
@ -303,6 +304,7 @@ class DivEngine {
float* oscBuf[2]; float* oscBuf[2];
float oscSize; float oscSize;
int oscReadPos, oscWritePos; int oscReadPos, oscWritePos;
int tickMult;
void runExportThread(); void runExportThread();
void nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size); void nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size);
@ -745,7 +747,9 @@ class DivEngine {
firstTick(false), firstTick(false),
skipping(false), skipping(false),
midiIsDirect(false), midiIsDirect(false),
lowLatency(false),
softLockCount(0), softLockCount(0),
subticks(0),
ticks(0), ticks(0),
curRow(0), curRow(0),
curOrder(0), curOrder(0),
@ -789,6 +793,7 @@ class DivEngine {
oscSize(1), oscSize(1),
oscReadPos(0), oscReadPos(0),
oscWritePos(0), oscWritePos(0),
tickMult(1),
adpcmAMem(NULL), adpcmAMem(NULL),
adpcmAMemLen(0), adpcmAMemLen(0),
adpcmBMem(NULL), adpcmBMem(NULL),

View file

@ -19,6 +19,7 @@
#include "macroInt.h" #include "macroInt.h"
#include "instrument.h" #include "instrument.h"
#include "engine.h"
void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released) { void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released) {
if (finished) { if (finished) {
@ -51,9 +52,16 @@ void DivMacroInt::next() {
if (ins==NULL) return; if (ins==NULL) return;
// run macros // run macros
// TODO: potentially get rid of list to avoid allocations // TODO: potentially get rid of list to avoid allocations
for (size_t i=0; i<macroListLen; i++) { if (--subTick<=0) {
if (macroList[i]!=NULL && macroSource[i]!=NULL) { if (e==NULL) {
macroList[i]->doMacro(*macroSource[i],released); subTick=1;
} else {
subTick=e->tickMult;
}
for (size_t i=0; i<macroListLen; i++) {
if (macroList[i]!=NULL && macroSource[i]!=NULL) {
macroList[i]->doMacro(*macroSource[i],released);
}
} }
} }
} }
@ -62,6 +70,10 @@ void DivMacroInt::release() {
released=true; released=true;
} }
void DivMacroInt::setEngine(DivEngine* eng) {
e=eng;
}
#define ADD_MACRO(m,s) \ #define ADD_MACRO(m,s) \
macroList[macroListLen]=&m; \ macroList[macroListLen]=&m; \
macroSource[macroListLen++]=&s; macroSource[macroListLen++]=&s;

View file

@ -22,6 +22,8 @@
#include "instrument.h" #include "instrument.h"
class DivEngine;
struct DivMacroStruct { struct DivMacroStruct {
int pos; int pos;
int val; int val;
@ -49,10 +51,12 @@ struct DivMacroStruct {
}; };
class DivMacroInt { class DivMacroInt {
DivEngine* e;
DivInstrument* ins; DivInstrument* ins;
DivMacroStruct* macroList[128]; DivMacroStruct* macroList[128];
DivInstrumentMacro* macroSource[128]; DivInstrumentMacro* macroSource[128];
size_t macroListLen; size_t macroListLen;
int subTick;
bool released; bool released;
public: public:
// common macro // common macro
@ -102,6 +106,12 @@ class DivMacroInt {
*/ */
void next(); void next();
/**
* set the engine.
* @param the engine
*/
void setEngine(DivEngine* eng);
/** /**
* initialize the macro interpreter. * initialize the macro interpreter.
* @param which an instrument, or NULL. * @param which an instrument, or NULL.
@ -115,8 +125,10 @@ class DivMacroInt {
void notifyInsDeletion(DivInstrument* which); void notifyInsDeletion(DivInstrument* which);
DivMacroInt(): DivMacroInt():
e(NULL),
ins(NULL), ins(NULL),
macroListLen(0), macroListLen(0),
subTick(0),
released(false), released(false),
vol(), vol(),
arp(), arp(),

View file

@ -414,6 +414,7 @@ void* DivPlatformAmiga::getChanState(int ch) {
void DivPlatformAmiga::reset() { void DivPlatformAmiga::reset() {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
chan[i]=DivPlatformAmiga::Channel(); chan[i]=DivPlatformAmiga::Channel();
chan[i].std.setEngine(parent);
chan[i].ws.setEngine(parent); chan[i].ws.setEngine(parent);
chan[i].ws.init(NULL,32,255); chan[i].ws.init(NULL,32,255);
filter[0][i]=0; filter[0][i]=0;

View file

@ -735,6 +735,7 @@ void DivPlatformArcade::reset() {
} }
for (int i=0; i<8; i++) { for (int i=0; i<8; i++) {
chan[i]=DivPlatformArcade::Channel(); chan[i]=DivPlatformArcade::Channel();
chan[i].std.setEngine(parent);
chan[i].vol=0x7f; chan[i].vol=0x7f;
chan[i].outVol=0x7f; chan[i].outVol=0x7f;
} }

View file

@ -513,6 +513,7 @@ void DivPlatformAY8910::reset() {
memset(regPool,0,16); memset(regPool,0,16);
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
chan[i]=DivPlatformAY8910::Channel(); chan[i]=DivPlatformAY8910::Channel();
chan[i].std.setEngine(parent);
chan[i].vol=0x0f; chan[i].vol=0x0f;
} }
if (dumpWrites) { if (dumpWrites) {

View file

@ -528,6 +528,7 @@ void DivPlatformAY8930::reset() {
memset(regPool,0,32); memset(regPool,0,32);
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
chan[i]=DivPlatformAY8930::Channel(); chan[i]=DivPlatformAY8930::Channel();
chan[i].std.setEngine(parent);
chan[i].vol=31; chan[i].vol=31;
ayEnvPeriod[i]=0; ayEnvPeriod[i]=0;
ayEnvMode[i]=0; ayEnvMode[i]=0;

View file

@ -274,6 +274,7 @@ void DivPlatformBubSysWSG::reset() {
memset(regPool,0,4*2); memset(regPool,0,4*2);
for (int i=0; i<2; i++) { for (int i=0; i<2; i++) {
chan[i]=DivPlatformBubSysWSG::Channel(); chan[i]=DivPlatformBubSysWSG::Channel();
chan[i].std.setEngine(parent);
chan[i].ws.setEngine(parent); chan[i].ws.setEngine(parent);
chan[i].ws.init(NULL,32,15,false); chan[i].ws.init(NULL,32,15,false);
} }

View file

@ -482,6 +482,7 @@ bool DivPlatformC64::getDCOffRequired() {
void DivPlatformC64::reset() { void DivPlatformC64::reset() {
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
chan[i]=DivPlatformC64::Channel(); chan[i]=DivPlatformC64::Channel();
chan[i].std.setEngine(parent);
} }
sid.reset(); sid.reset();

View file

@ -408,6 +408,7 @@ int DivPlatformFDS::getRegisterPoolSize() {
void DivPlatformFDS::reset() { void DivPlatformFDS::reset() {
for (int i=0; i<1; i++) { for (int i=0; i<1; i++) {
chan[i]=DivPlatformFDS::Channel(); chan[i]=DivPlatformFDS::Channel();
chan[i].std.setEngine(parent);
} }
ws.setEngine(parent); ws.setEngine(parent);
ws.init(NULL,64,63,false); ws.init(NULL,64,63,false);

View file

@ -424,6 +424,7 @@ int DivPlatformGB::getRegisterPoolSize() {
void DivPlatformGB::reset() { void DivPlatformGB::reset() {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
chan[i]=DivPlatformGB::Channel(); chan[i]=DivPlatformGB::Channel();
chan[i].std.setEngine(parent);
} }
ws.setEngine(parent); ws.setEngine(parent);
ws.init(NULL,32,15,false); ws.init(NULL,32,15,false);

View file

@ -827,6 +827,7 @@ void DivPlatformGenesis::reset() {
} }
for (int i=0; i<10; i++) { for (int i=0; i<10; i++) {
chan[i]=DivPlatformGenesis::Channel(); chan[i]=DivPlatformGenesis::Channel();
chan[i].std.setEngine(parent);
chan[i].vol=0x7f; chan[i].vol=0x7f;
chan[i].outVol=0x7f; chan[i].outVol=0x7f;
} }

View file

@ -329,11 +329,11 @@ int DivPlatformLynx::getRegisterPoolSize()
} }
void DivPlatformLynx::reset() { void DivPlatformLynx::reset() {
mikey=std::make_unique<Lynx::Mikey>(rate);
mikey = std::make_unique<Lynx::Mikey>( rate );
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
chan[i]= DivPlatformLynx::Channel(); chan[i]=DivPlatformLynx::Channel();
chan[i].std.setEngine(parent);
} }
if (dumpWrites) { if (dumpWrites) {
addWrite(0xffffffff,0); addWrite(0xffffffff,0);

View file

@ -348,6 +348,7 @@ float DivPlatformMMC5::getPostAmp() {
void DivPlatformMMC5::reset() { void DivPlatformMMC5::reset() {
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
chan[i]=DivPlatformMMC5::Channel(); chan[i]=DivPlatformMMC5::Channel();
chan[i].std.setEngine(parent);
} }
if (dumpWrites) { if (dumpWrites) {
addWrite(0xffffffff,0); addWrite(0xffffffff,0);

View file

@ -625,6 +625,7 @@ void DivPlatformN163::reset() {
while (!writes.empty()) writes.pop(); while (!writes.empty()) writes.pop();
for (int i=0; i<8; i++) { for (int i=0; i<8; i++) {
chan[i]=DivPlatformN163::Channel(); chan[i]=DivPlatformN163::Channel();
chan[i].std.setEngine(parent);
chan[i].ws.setEngine(parent); chan[i].ws.setEngine(parent);
chan[i].ws.init(NULL,32,15,false); chan[i].ws.init(NULL,32,15,false);
} }

View file

@ -474,6 +474,7 @@ float DivPlatformNES::getPostAmp() {
void DivPlatformNES::reset() { void DivPlatformNES::reset() {
for (int i=0; i<5; i++) { for (int i=0; i<5; i++) {
chan[i]=DivPlatformNES::Channel(); chan[i]=DivPlatformNES::Channel();
chan[i].std.setEngine(parent);
} }
if (dumpWrites) { if (dumpWrites) {
addWrite(0xffffffff,0); addWrite(0xffffffff,0);

View file

@ -934,6 +934,7 @@ void DivPlatformOPL::reset() {
for (int i=0; i<totalChans; i++) { for (int i=0; i<totalChans; i++) {
chan[i]=DivPlatformOPL::Channel(); chan[i]=DivPlatformOPL::Channel();
chan[i].std.setEngine(parent);
chan[i].vol=0x3f; chan[i].vol=0x3f;
chan[i].outVol=0x3f; chan[i].outVol=0x3f;
} }

View file

@ -756,6 +756,7 @@ void DivPlatformOPLL::reset() {
} }
for (int i=0; i<11; i++) { for (int i=0; i<11; i++) {
chan[i]=DivPlatformOPLL::Channel(); chan[i]=DivPlatformOPLL::Channel();
chan[i].std.setEngine(parent);
chan[i].vol=15; chan[i].vol=15;
chan[i].outVol=15; chan[i].outVol=15;
} }

View file

@ -462,6 +462,7 @@ void DivPlatformPCE::reset() {
memset(regPool,0,128); memset(regPool,0,128);
for (int i=0; i<6; i++) { for (int i=0; i<6; i++) {
chan[i]=DivPlatformPCE::Channel(); chan[i]=DivPlatformPCE::Channel();
chan[i].std.setEngine(parent);
chan[i].ws.setEngine(parent); chan[i].ws.setEngine(parent);
chan[i].ws.init(NULL,32,31,false); chan[i].ws.init(NULL,32,31,false);
} }

View file

@ -327,6 +327,7 @@ int DivPlatformPCSpeaker::getRegisterPoolSize() {
void DivPlatformPCSpeaker::reset() { void DivPlatformPCSpeaker::reset() {
for (int i=0; i<1; i++) { for (int i=0; i<1; i++) {
chan[i]=DivPlatformPCSpeaker::Channel(); chan[i]=DivPlatformPCSpeaker::Channel();
chan[i].std.setEngine(parent);
} }
if (dumpWrites) { if (dumpWrites) {
addWrite(0xffffffff,0); addWrite(0xffffffff,0);

View file

@ -252,6 +252,7 @@ int DivPlatformPET::getRegisterPoolSize() {
void DivPlatformPET::reset() { void DivPlatformPET::reset() {
memset(regPool,0,16); memset(regPool,0,16);
chan=Channel(); chan=Channel();
chan.std.setEngine(parent);
} }
bool DivPlatformPET::isStereo() { bool DivPlatformPET::isStereo() {

View file

@ -523,6 +523,7 @@ void* DivPlatformQSound::getChanState(int ch) {
void DivPlatformQSound::reset() { void DivPlatformQSound::reset() {
for (int i=0; i<16; i++) { for (int i=0; i<16; i++) {
chan[i]=DivPlatformQSound::Channel(); chan[i]=DivPlatformQSound::Channel();
chan[i].std.setEngine(parent);
} }
qsound_reset(&chip); qsound_reset(&chip);
while(!chip.ready_flag) { while(!chip.ready_flag) {

View file

@ -392,6 +392,7 @@ void DivPlatformSAA1099::reset() {
} }
for (int i=0; i<6; i++) { for (int i=0; i<6; i++) {
chan[i]=DivPlatformSAA1099::Channel(); chan[i]=DivPlatformSAA1099::Channel();
chan[i].std.setEngine(parent);
chan[i].vol=0x0f; chan[i].vol=0x0f;
} }
if (dumpWrites) { if (dumpWrites) {

View file

@ -350,6 +350,7 @@ void DivPlatformSegaPCM::reset() {
memset(regPool,0,256); memset(regPool,0,256);
for (int i=0; i<16; i++) { for (int i=0; i<16; i++) {
chan[i]=DivPlatformSegaPCM::Channel(); chan[i]=DivPlatformSegaPCM::Channel();
chan[i].std.setEngine(parent);
chan[i].vol=0x7f; chan[i].vol=0x7f;
chan[i].outVol=0x7f; chan[i].outVol=0x7f;
} }

View file

@ -278,6 +278,7 @@ void* DivPlatformSMS::getChanState(int ch) {
void DivPlatformSMS::reset() { void DivPlatformSMS::reset() {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
chan[i]=DivPlatformSMS::Channel(); chan[i]=DivPlatformSMS::Channel();
chan[i].std.setEngine(parent);
} }
if (dumpWrites) { if (dumpWrites) {
addWrite(0xffffffff,0); addWrite(0xffffffff,0);

View file

@ -453,6 +453,7 @@ void DivPlatformSwan::reset() {
chan[i]=Channel(); chan[i]=Channel();
chan[i].vol=15; chan[i].vol=15;
chan[i].pan=0xff; chan[i].pan=0xff;
chan[i].std.setEngine(parent);
chan[i].ws.setEngine(parent); chan[i].ws.setEngine(parent);
chan[i].ws.init(NULL,32,15,false); chan[i].ws.init(NULL,32,15,false);
rWrite(0x08+i,0xff); rWrite(0x08+i,0xff);

View file

@ -294,6 +294,7 @@ void DivPlatformTIA::reset() {
memset(regPool,0,16); memset(regPool,0,16);
for (int i=0; i<2; i++) { for (int i=0; i<2; i++) {
chan[i]=DivPlatformTIA::Channel(); chan[i]=DivPlatformTIA::Channel();
chan[i].std.setEngine(parent);
chan[i].vol=0x0f; chan[i].vol=0x0f;
} }
} }

View file

@ -729,6 +729,7 @@ void DivPlatformTX81Z::reset() {
} }
for (int i=0; i<8; i++) { for (int i=0; i<8; i++) {
chan[i]=DivPlatformTX81Z::Channel(); chan[i]=DivPlatformTX81Z::Channel();
chan[i].std.setEngine(parent);
chan[i].vol=0x7f; chan[i].vol=0x7f;
chan[i].outVol=0x7f; chan[i].outVol=0x7f;
} }

View file

@ -125,6 +125,7 @@ void DivPlatformVERA::acquire(short* bufL, short* bufR, size_t start, size_t len
void DivPlatformVERA::reset() { void DivPlatformVERA::reset() {
for (int i=0; i<17; i++) { for (int i=0; i<17; i++) {
chan[i]=Channel(); chan[i]=Channel();
chan[i].std.setEngine(parent);
} }
psg_reset(psg); psg_reset(psg);
pcm_reset(pcm); pcm_reset(pcm);

View file

@ -278,6 +278,7 @@ void DivPlatformVIC20::reset() {
memset(regPool,0,16); memset(regPool,0,16);
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
chan[i]=Channel(); chan[i]=Channel();
chan[i].std.setEngine(parent);
} }
vic_sound_machine_init(vic,rate,chipClock); vic_sound_machine_init(vic,rate,chipClock);
hasWaveWrite=false; hasWaveWrite=false;

View file

@ -429,6 +429,7 @@ int DivPlatformVRC6::getRegisterPoolSize() {
void DivPlatformVRC6::reset() { void DivPlatformVRC6::reset() {
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
chan[i]=DivPlatformVRC6::Channel(); chan[i]=DivPlatformVRC6::Channel();
chan[i].std.setEngine(parent);
} }
// a poll may be necessary to decide the default // a poll may be necessary to decide the default
chan[2].vol=30; chan[2].vol=30;

View file

@ -818,6 +818,7 @@ void DivPlatformX1_010::reset() {
for (int i=0; i<16; i++) { for (int i=0; i<16; i++) {
chan[i]=DivPlatformX1_010::Channel(); chan[i]=DivPlatformX1_010::Channel();
chan[i].reset(); chan[i].reset();
chan[i].std.setEngine(parent);
chan[i].ws.setEngine(parent); chan[i].ws.setEngine(parent);
chan[i].ws.init(NULL,128,255,false); chan[i].ws.init(NULL,128,255,false);
} }

View file

@ -1068,6 +1068,7 @@ void DivPlatformYM2610::reset() {
fm->reset(); fm->reset();
for (int i=0; i<14; i++) { for (int i=0; i<14; i++) {
chan[i]=DivPlatformYM2610::Channel(); chan[i]=DivPlatformYM2610::Channel();
chan[i].std.setEngine(parent);
} }
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
chan[i].vol=0x7f; chan[i].vol=0x7f;

View file

@ -1131,6 +1131,7 @@ void DivPlatformYM2610B::reset() {
fm->reset(); fm->reset();
for (int i=0; i<16; i++) { for (int i=0; i<16; i++) {
chan[i]=DivPlatformYM2610B::Channel(); chan[i]=DivPlatformYM2610B::Channel();
chan[i].std.setEngine(parent);
} }
for (int i=0; i<6; i++) { for (int i=0; i<6; i++) {
chan[i].vol=0x7f; chan[i].vol=0x7f;

View file

@ -1473,7 +1473,12 @@ bool DivEngine::nextTick(bool noAccum) {
bool ret=false; bool ret=false;
if (divider<10) divider=10; if (divider<10) divider=10;
cycles=got.rate*pow(2,MASTER_CLOCK_PREC)/divider; if (lowLatency) {
tickMult=1000/divider;
if (tickMult<1) tickMult=1;
}
cycles=got.rate*pow(2,MASTER_CLOCK_PREC)/(divider*tickMult);
clockDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)divider); clockDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)divider);
if (clockDrift>=divider) { if (clockDrift>=divider) {
clockDrift-=divider; clockDrift-=divider;
@ -1499,130 +1504,133 @@ bool DivEngine::nextTick(bool noAccum) {
} }
if (!freelance) { if (!freelance) {
if (stepPlay!=1) if (--ticks<=0) { if (--subticks<=0) {
ret=endOfSong; subticks=tickMult;
if (endOfSong) { if (stepPlay!=1) if (--ticks<=0) {
if (song.loopModality!=2) { ret=endOfSong;
playSub(true); if (endOfSong) {
if (song.loopModality!=2) {
playSub(true);
}
} }
endOfSong=false;
if (stepPlay==2) stepPlay=1;
nextRow();
} }
endOfSong=false; // process stuff
if (stepPlay==2) stepPlay=1; for (int i=0; i<chans; i++) {
nextRow(); if (chan[i].rowDelay>0) {
} if (--chan[i].rowDelay==0) {
// process stuff processRow(i,true);
for (int i=0; i<chans; i++) { }
if (chan[i].rowDelay>0) {
if (--chan[i].rowDelay==0) {
processRow(i,true);
} }
} if (chan[i].retrigSpeed) {
if (chan[i].retrigSpeed) { if (--chan[i].retrigTick<0) {
if (--chan[i].retrigTick<0) { chan[i].retrigTick=chan[i].retrigSpeed-1;
chan[i].retrigTick=chan[i].retrigSpeed-1; dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL));
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL)); keyHit[i]=true;
keyHit[i]=true; }
} }
} if (!song.noSlidesOnFirstTick || !firstTick) {
if (!song.noSlidesOnFirstTick || !firstTick) { if (chan[i].volSpeed!=0) {
if (chan[i].volSpeed!=0) { chan[i].volume=(chan[i].volume&0xff)|(dispatchCmd(DivCommand(DIV_CMD_GET_VOLUME,i))<<8);
chan[i].volume=(chan[i].volume&0xff)|(dispatchCmd(DivCommand(DIV_CMD_GET_VOLUME,i))<<8); chan[i].volume+=chan[i].volSpeed;
chan[i].volume+=chan[i].volSpeed; if (chan[i].volume>chan[i].volMax) {
if (chan[i].volume>chan[i].volMax) { chan[i].volume=chan[i].volMax;
chan[i].volume=chan[i].volMax; chan[i].volSpeed=0;
chan[i].volSpeed=0; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); } else if (chan[i].volume<0) {
} else if (chan[i].volume<0) { chan[i].volSpeed=0;
chan[i].volSpeed=0; if (song.legacyVolumeSlides) {
if (song.legacyVolumeSlides) { chan[i].volume=chan[i].volMax+1;
chan[i].volume=chan[i].volMax+1; } else {
chan[i].volume=0;
}
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
} else { } else {
chan[i].volume=0; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
} }
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
} else {
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
} }
} }
} if (chan[i].vibratoDepth>0) {
if (chan[i].vibratoDepth>0) { chan[i].vibratoPos+=chan[i].vibratoRate;
chan[i].vibratoPos+=chan[i].vibratoRate; if (chan[i].vibratoPos>=64) chan[i].vibratoPos-=64;
if (chan[i].vibratoPos>=64) chan[i].vibratoPos-=64; switch (chan[i].vibratoDir) {
switch (chan[i].vibratoDir) { case 1: // up
case 1: // up dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(MAX(0,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15)));
dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(MAX(0,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); break;
break; case 2: // down
case 2: // down dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(MIN(0,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15)));
dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(MIN(0,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); break;
break; default: // both
default: // both dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15)));
dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); break;
break;
}
}
if (!song.noSlidesOnFirstTick || !firstTick) {
if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) {
if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed,chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) {
chan[i].portaSpeed=0;
chan[i].oldNote=chan[i].note;
chan[i].note=chan[i].portaNote;
chan[i].inPorta=false;
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
} }
} }
} if (!song.noSlidesOnFirstTick || !firstTick) {
if (chan[i].cut>0) { if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) {
if (--chan[i].cut<1) { if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed,chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) {
chan[i].oldNote=chan[i].note; chan[i].portaSpeed=0;
//chan[i].note=-1; chan[i].oldNote=chan[i].note;
if (chan[i].inPorta && song.noteOffResetsSlides) { chan[i].note=chan[i].portaNote;
chan[i].keyOff=true; chan[i].inPorta=false;
chan[i].keyOn=false;
if (chan[i].stopOnOff) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
chan[i].stopOnOff=false;
}
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
chan[i+1].portaNote=-1;
chan[i+1].portaSpeed=-1;
}*/
}
dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
chan[i].scheduledSlideReset=true;
}
dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,i));
}
}
if (chan[i].resetArp) {
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
chan[i].resetArp=false;
}
if (song.rowResetsArpPos && firstTick) {
chan[i].arpStage=-1;
}
if (chan[i].arp!=0 && !chan[i].arpYield && chan[i].portaSpeed<1) {
if (--chan[i].arpTicks<1) {
chan[i].arpTicks=song.arpLen;
chan[i].arpStage++;
if (chan[i].arpStage>2) chan[i].arpStage=0;
switch (chan[i].arpStage) {
case 0:
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
break; }
case 1:
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note+(chan[i].arp>>4)));
break;
case 2:
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note+(chan[i].arp&15)));
break;
} }
} }
} else { if (chan[i].cut>0) {
chan[i].arpYield=false; if (--chan[i].cut<1) {
chan[i].oldNote=chan[i].note;
//chan[i].note=-1;
if (chan[i].inPorta && song.noteOffResetsSlides) {
chan[i].keyOff=true;
chan[i].keyOn=false;
if (chan[i].stopOnOff) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
chan[i].stopOnOff=false;
}
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
chan[i+1].portaNote=-1;
chan[i+1].portaSpeed=-1;
}*/
}
dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
chan[i].scheduledSlideReset=true;
}
dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,i));
}
}
if (chan[i].resetArp) {
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
chan[i].resetArp=false;
}
if (song.rowResetsArpPos && firstTick) {
chan[i].arpStage=-1;
}
if (chan[i].arp!=0 && !chan[i].arpYield && chan[i].portaSpeed<1) {
if (--chan[i].arpTicks<1) {
chan[i].arpTicks=song.arpLen;
chan[i].arpStage++;
if (chan[i].arpStage>2) chan[i].arpStage=0;
switch (chan[i].arpStage) {
case 0:
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
break;
case 1:
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note+(chan[i].arp>>4)));
break;
case 2:
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note+(chan[i].arp&15)));
break;
}
}
} else {
chan[i].arpYield=false;
}
} }
} }
} }
@ -1636,7 +1644,7 @@ bool DivEngine::nextTick(bool noAccum) {
if (stepPlay!=1) { if (stepPlay!=1) {
if (!noAccum) { if (!noAccum) {
totalTicksR++; totalTicksR++;
totalTicks+=1000000/divider; totalTicks+=1000000/(divider*tickMult);
} }
if (totalTicks>=1000000) { if (totalTicks>=1000000) {
totalTicks-=1000000; totalTicks-=1000000;

View file

@ -818,6 +818,7 @@ class FurnaceGUI {
int scrollChangesOrder; int scrollChangesOrder;
int oplStandardWaveNames; int oplStandardWaveNames;
int cursorMoveNoScroll; int cursorMoveNoScroll;
int lowLatency;
unsigned int maxUndoSteps; unsigned int maxUndoSteps;
String mainFontPath; String mainFontPath;
String patFontPath; String patFontPath;
@ -889,6 +890,7 @@ class FurnaceGUI {
scrollChangesOrder(0), scrollChangesOrder(0),
oplStandardWaveNames(0), oplStandardWaveNames(0),
cursorMoveNoScroll(0), cursorMoveNoScroll(0),
lowLatency(0),
maxUndoSteps(100), maxUndoSteps(100),
mainFontPath(""), mainFontPath(""),
patFontPath(""), patFontPath(""),

View file

@ -405,6 +405,14 @@ void FurnaceGUI::drawSettings() {
e->setMetronomeVol(((float)settings.metroVol)/100.0f); e->setMetronomeVol(((float)settings.metroVol)/100.0f);
} }
bool lowLatencyB=settings.lowLatency;
if (ImGui::Checkbox("Low-latency mode (experimental!)",&lowLatencyB)) {
settings.lowLatency=lowLatencyB;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("reduces latency by running the engine faster than the tick rate.\nuseful for live playback/jam mode.\n\nwarning: experimental! may produce glitches.\nonly enable if your buffer size is small (10ms or less).");
}
bool forceMonoB=settings.forceMono; bool forceMonoB=settings.forceMono;
if (ImGui::Checkbox("Force mono audio",&forceMonoB)) { if (ImGui::Checkbox("Force mono audio",&forceMonoB)) {
settings.forceMono=forceMonoB; settings.forceMono=forceMonoB;
@ -1600,6 +1608,7 @@ void FurnaceGUI::syncSettings() {
settings.scrollChangesOrder=e->getConfInt("scrollChangesOrder",0); settings.scrollChangesOrder=e->getConfInt("scrollChangesOrder",0);
settings.oplStandardWaveNames=e->getConfInt("oplStandardWaveNames",0); settings.oplStandardWaveNames=e->getConfInt("oplStandardWaveNames",0);
settings.cursorMoveNoScroll=e->getConfInt("cursorMoveNoScroll",0); settings.cursorMoveNoScroll=e->getConfInt("cursorMoveNoScroll",0);
settings.lowLatency=e->getConfInt("lowLatency",0);
clampSetting(settings.mainFontSize,2,96); clampSetting(settings.mainFontSize,2,96);
clampSetting(settings.patFontSize,2,96); clampSetting(settings.patFontSize,2,96);
@ -1660,6 +1669,7 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.scrollChangesOrder,0,1); clampSetting(settings.scrollChangesOrder,0,1);
clampSetting(settings.oplStandardWaveNames,0,1); clampSetting(settings.oplStandardWaveNames,0,1);
clampSetting(settings.cursorMoveNoScroll,0,1); clampSetting(settings.cursorMoveNoScroll,0,1);
clampSetting(settings.lowLatency,0,1);
// keybinds // keybinds
for (int i=0; i<GUI_ACTION_MAX; i++) { for (int i=0; i<GUI_ACTION_MAX; i++) {
@ -1747,6 +1757,7 @@ void FurnaceGUI::commitSettings() {
e->setConf("scrollChangesOrder",settings.scrollChangesOrder); e->setConf("scrollChangesOrder",settings.scrollChangesOrder);
e->setConf("oplStandardWaveNames",settings.oplStandardWaveNames); e->setConf("oplStandardWaveNames",settings.oplStandardWaveNames);
e->setConf("cursorMoveNoScroll",settings.cursorMoveNoScroll); e->setConf("cursorMoveNoScroll",settings.cursorMoveNoScroll);
e->setConf("lowLatency",settings.lowLatency);
// colors // colors
for (int i=0; i<GUI_COLOR_MAX; i++) { for (int i=0; i<GUI_COLOR_MAX; i++) {