Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt

This commit is contained in:
cam900 2022-09-25 18:32:06 +09:00
commit 0a49d4bfd0
449 changed files with 36799 additions and 10388 deletions

View file

@ -31,7 +31,9 @@ void DivEngine::nextOrder() {
curRow=0;
if (repeatPattern) return;
if (++curOrder>=curSubSong->ordersLen) {
logV("end of orders reached");
endOfSong=true;
memset(walked,0,8192);
curOrder=0;
}
}
@ -57,6 +59,16 @@ const char* cmdName[]={
"PRE_PORTA",
"PRE_NOTE",
"HINT_VIBRATO",
"HINT_VIBRATO_RANGE",
"HINT_VIBRATO_SHAPE",
"HINT_PITCH",
"HINT_ARPEGGIO",
"HINT_VOLUME",
"HINT_VOL_SLIDE",
"HINT_PORTA",
"HINT_LEGATO",
"SAMPLE_MODE",
"SAMPLE_FREQ",
"SAMPLE_BANK",
@ -157,6 +169,7 @@ const char* cmdName[]={
"X1_010_ENVELOPE_PERIOD",
"X1_010_ENVELOPE_SLIDE",
"X1_010_AUTO_ENVELOPE",
"X1_010_SAMPLE_BANK_SLOT",
"WS_SWEEP_TIME",
"WS_SWEEP_AMOUNT",
@ -193,6 +206,8 @@ const char* cmdName[]={
"DIV_CMD_SU_SYNC_PERIOD_LOW",
"DIV_CMD_SU_SYNC_PERIOD_HIGH",
"ADPCMA_GLOBAL_VOLUME",
"ALWAYS_SET_VOLUME"
};
@ -215,7 +230,27 @@ const char* formatNote(unsigned char note, unsigned char octave) {
int DivEngine::dispatchCmd(DivCommand c) {
if (view==DIV_STATUS_COMMANDS) {
printf("%8d | %d: %s(%d, %d)\n",totalTicksR,c.chan,cmdName[c.cmd],c.value,c.value2);
if (!skipping) {
switch (c.cmd) {
// strip away hinted/useless commands
case DIV_ALWAYS_SET_VOLUME:
break;
case DIV_CMD_GET_VOLUME:
break;
case DIV_CMD_VOLUME:
break;
case DIV_CMD_NOTE_PORTA:
break;
case DIV_CMD_LEGATO:
break;
case DIV_CMD_PITCH:
break;
case DIV_CMD_PRE_NOTE:
break;
default:
printf("%8d | %d: %s(%d, %d)\n",totalTicksR,c.chan,cmdName[c.cmd],c.value,c.value2);
}
}
}
totalCmds++;
if (cmdStreamEnabled && cmdStream.size()<2000) {
@ -276,13 +311,39 @@ int DivEngine::dispatchCmd(DivCommand c) {
}
bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effectVal) {
if (sysDefs[sysOfChan[ch]]==NULL) return false;
return sysDefs[sysOfChan[ch]]->effectFunc(ch,effect,effectVal);
DivSysDef* sysDef=sysDefs[sysOfChan[ch]];
if (sysDef==NULL) return false;
auto iter=sysDef->effectHandlers.find(effect);
if (iter==sysDef->effectHandlers.end()) return false;
EffectHandler handler=iter->second;
int val=0;
int val2=0;
try {
val=handler.val?handler.val(effect,effectVal):effectVal;
val2=handler.val2?handler.val2(effect,effectVal):0;
} catch (DivDoNotHandleEffect& e) {
return false;
}
// wouldn't this cause problems if it were to return 0?
return dispatchCmd(DivCommand(handler.dispatchCmd,ch,val,val2));
}
bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char effectVal) {
if (sysDefs[sysOfChan[ch]]==NULL) return false;
return sysDefs[sysOfChan[ch]]->postEffectFunc(ch,effect,effectVal);
DivSysDef* sysDef=sysDefs[sysOfChan[ch]];
if (sysDef==NULL) return false;
auto iter=sysDef->postEffectHandlers.find(effect);
if (iter==sysDef->postEffectHandlers.end()) return false;
EffectHandler handler=iter->second;
int val=0;
int val2=0;
try {
val=handler.val?handler.val(effect,effectVal):effectVal;
val2=handler.val2?handler.val2(effect,effectVal):0;
} catch (DivDoNotHandleEffect& e) {
return true;
}
// wouldn't this cause problems if it were to return 0?
return dispatchCmd(DivCommand(handler.dispatchCmd,ch,val,val2));
}
void DivEngine::processRow(int i, bool afterDelay) {
@ -306,20 +367,38 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (effectVal>0) speed2=effectVal;
break;
case 0x0b: // change order
if (changeOrd==-1) {
if (changeOrd==-1 || song.jumpTreatment==0) {
changeOrd=effectVal;
changePos=0;
if (song.jumpTreatment==1 || song.jumpTreatment==2) {
changePos=0;
}
}
break;
case 0x0d: // next order
if (changeOrd<0 && (curOrder<(curSubSong->ordersLen-1) || !song.ignoreJumpAtEnd)) {
changeOrd=-2;
changePos=effectVal;
if (song.jumpTreatment==2) {
if ((curOrder<(curSubSong->ordersLen-1) || !song.ignoreJumpAtEnd)) {
changeOrd=-2;
changePos=effectVal;
}
} else if (song.jumpTreatment==1) {
if (changeOrd<0 && (curOrder<(curSubSong->ordersLen-1) || !song.ignoreJumpAtEnd)) {
changeOrd=-2;
changePos=effectVal;
}
} else {
if (curOrder<(curSubSong->ordersLen-1) || !song.ignoreJumpAtEnd) {
if (changeOrd<0) {
changeOrd=-2;
}
changePos=effectVal;
}
}
break;
case 0xed: // delay
if (effectVal!=0) {
if (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;
chan[i].delayOrder=whatOrder;
chan[i].delayRow=whatRow;
@ -330,6 +409,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
}
returnAfterPre=true;
} else {
logV("higher than nextSpeed! %d>%d",effectVal,nextSpeed);
chan[i].delayLocked=false;
}
}
@ -337,6 +417,8 @@ void DivEngine::processRow(int i, bool afterDelay) {
}
}
if (returnAfterPre) return;
} else {
logV("honoring delay at position %d",whatRow);
}
if (chan[i].delayLocked) return;
@ -344,10 +426,16 @@ void DivEngine::processRow(int i, bool afterDelay) {
// instrument
bool insChanged=false;
if (pat->data[whatRow][2]!=-1) {
dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,i,pat->data[whatRow][2]));
if (chan[i].lastIns!=pat->data[whatRow][2]) {
dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,i,pat->data[whatRow][2]));
chan[i].lastIns=pat->data[whatRow][2];
insChanged=true;
if (song.legacyVolumeSlides && chan[i].volume==chan[i].volMax+1) {
logV("forcing volume");
chan[i].volume=chan[i].volMax;
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8));
}
}
}
// note
@ -359,11 +447,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (chan[i].stopOnOff) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
chan[i].stopOnOff=false;
}
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
chan[i+1].portaNote=-1;
chan[i+1].portaSpeed=-1;
@ -380,11 +470,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (chan[i].stopOnOff) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
chan[i].stopOnOff=false;
}
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
chan[i+1].portaNote=-1;
chan[i+1].portaSpeed=-1;
@ -401,6 +493,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (!chan[i].keyOn) {
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsArp(dispatchChanOfChan[i])) {
chan[i].arp=0;
dispatchCmd(DivCommand(DIV_CMD_HINT_ARPEGGIO,i,chan[i].arp));
}
}
chan[i].doNote=true;
@ -417,6 +510,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
}
chan[i].volume=pat->data[whatRow][3]<<8;
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8));
}
}
@ -461,11 +555,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (effectVal==0) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
chan[i].inPorta=false;
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
} else {
chan[i].portaNote=song.limitSlides?0x60:255;
chan[i].portaSpeed=effectVal;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
chan[i].portaStop=true;
chan[i].nowYouCanStop=false;
chan[i].stopOnOff=false;
@ -481,11 +577,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (effectVal==0) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
chan[i].inPorta=false;
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
} else {
chan[i].portaNote=song.limitSlides?disCont[dispatchOfChan[i]].dispatch->getPortaFloor(dispatchChanOfChan[i]):-60;
chan[i].portaSpeed=effectVal;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
chan[i].portaStop=true;
chan[i].nowYouCanStop=false;
chan[i].stopOnOff=false;
@ -499,6 +597,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (effectVal==0) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
chan[i].inPorta=false;
dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
} else {
@ -512,6 +611,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].inPorta=true;
chan[i].wasShorthandPorta=false;
}
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
chan[i].portaStop=true;
if (chan[i].keyOn) chan[i].doNote=false;
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
@ -523,6 +623,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
case 0x04: // vibrato
chan[i].vibratoDepth=effectVal&15;
chan[i].vibratoRate=effectVal>>4;
dispatchCmd(DivCommand(DIV_CMD_HINT_VIBRATO,i,chan[i].vibratoDepth,chan[i].vibratoRate));
dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15)));
break;
case 0x07: // tremolo
@ -546,12 +647,14 @@ void DivEngine::processRow(int i, bool afterDelay) {
} else {
chan[i].volSpeed=0;
}
dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed));
break;
case 0x00: // arpeggio
chan[i].arp=effectVal;
if (chan[i].arp==0 && song.arp0Reset) {
chan[i].resetArp=true;
}
dispatchCmd(DivCommand(DIV_CMD_HINT_ARPEGGIO,i,chan[i].arp));
break;
case 0x0c: // retrigger
if (effectVal!=0) {
@ -567,14 +670,11 @@ void DivEngine::processRow(int i, bool afterDelay) {
break;
case 0xc0: case 0xc1: case 0xc2: case 0xc3: // set Hz
divider=(double)(((effect&0x3)<<8)|effectVal);
if (divider<10) divider=10;
if (divider<1) divider=1;
cycles=got.rate*pow(2,MASTER_CLOCK_PREC)/divider;
clockDrift=0;
subticks=0;
break;
case 0xdf: // set sample direction
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_DIR,i,effectVal));
break;
case 0xe0: // arp speed
if (effectVal>0) {
curSubSong->arpLen=effectVal;
@ -583,6 +683,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
case 0xe1: // portamento up
chan[i].portaNote=chan[i].note+(effectVal&15);
chan[i].portaSpeed=(effectVal>>4)*4;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
chan[i].portaStop=true;
chan[i].nowYouCanStop=false;
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
@ -601,6 +702,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
case 0xe2: // portamento down
chan[i].portaNote=chan[i].note-(effectVal&15);
chan[i].portaSpeed=(effectVal>>4)*4;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
chan[i].portaStop=true;
chan[i].nowYouCanStop=false;
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
@ -618,9 +720,11 @@ void DivEngine::processRow(int i, bool afterDelay) {
break;
case 0xe3: // vibrato direction
chan[i].vibratoDir=effectVal;
dispatchCmd(DivCommand(DIV_CMD_HINT_VIBRATO_SHAPE,i,chan[i].vibratoDir));
break;
case 0xe4: // vibrato fine
chan[i].vibratoFine=effectVal;
dispatchCmd(DivCommand(DIV_CMD_HINT_VIBRATO_RANGE,i,chan[i].vibratoFine));
break;
case 0xe5: // pitch
chan[i].pitch=effectVal-0x80;
@ -631,6 +735,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
}
//chan[i].pitch+=globalPitch;
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_HINT_PITCH,i,chan[i].pitch));
break;
case 0xea: // legato mode
chan[i].legato=effectVal;
@ -639,7 +744,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_BANK,i,effectVal));
break;
case 0xec: // delayed note cut
if (effectVal>0 && effectVal<nextSpeed) {
if (effectVal>0 && (song.delayBehavior==2 || effectVal<nextSpeed)) {
chan[i].cut=effectVal+1;
}
break;
@ -653,7 +758,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
break;
case 0xf0: // set Hz by tempo
divider=(double)effectVal*2.0/5.0;
if (divider<10) divider=10;
if (divider<1) divider=1;
cycles=got.rate*pow(2,MASTER_CLOCK_PREC)/divider;
clockDrift=0;
subticks=0;
@ -680,17 +785,21 @@ void DivEngine::processRow(int i, bool afterDelay) {
break;
case 0xf3: // fine volume ramp up
chan[i].volSpeed=effectVal;
dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed));
break;
case 0xf4: // fine volume ramp down
chan[i].volSpeed=-effectVal;
dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed));
break;
case 0xf8: // single volume ramp up
chan[i].volume=MIN(chan[i].volume+effectVal*256,chan[i].volMax);
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8));
break;
case 0xf9: // single volume ramp down
chan[i].volume=MAX(chan[i].volume-effectVal*256,0);
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8));
break;
case 0xfa: // fast volume ramp
if (effectVal!=0) {
@ -702,6 +811,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
} else {
chan[i].volSpeed=0;
}
dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed));
break;
case 0xff: // stop song
@ -733,15 +843,18 @@ void DivEngine::processRow(int i, bool afterDelay) {
dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15)));
if (chan[i].legato) {
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
dispatchCmd(DivCommand(DIV_CMD_HINT_LEGATO,i,chan[i].note));
} else {
if (chan[i].inPorta && chan[i].keyOn && !chan[i].shorthandPorta) {
if (song.e1e2StopOnSameNote && chan[i].wasShorthandPorta) {
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
chan[i].wasShorthandPorta=false;
chan[i].inPorta=false;
} else {
chan[i].portaNote=chan[i].note;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
}
} else if (!chan[i].noteOnInhibit) {
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note,chan[i].volume>>8));
@ -752,12 +865,14 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (!chan[i].keyOn && chan[i].scheduledSlideReset) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
chan[i].scheduledSlideReset=false;
chan[i].inPorta=false;
}
if (!chan[i].keyOn && chan[i].volume>chan[i].volMax) {
chan[i].volume=chan[i].volMax;
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8));
}
chan[i].keyOn=true;
chan[i].keyOff=false;
@ -781,7 +896,7 @@ void DivEngine::nextRow() {
static char pb1[4096];
static char pb2[4096];
static char pb3[4096];
if (view==DIV_STATUS_PATTERN) {
if (view==DIV_STATUS_PATTERN && !skipping) {
strcpy(pb1,"");
strcpy(pb3,"");
for (int i=0; i<chans; i++) {
@ -826,22 +941,29 @@ void DivEngine::nextRow() {
prevRow=curRow;
for (int i=0; i<chans; i++) {
chan[i].rowDelay=0;
if (song.delayBehavior!=2) {
chan[i].rowDelay=0;
}
processRow(i,false);
}
walked[((curOrder<<5)+(curRow>>3))&8191]|=1<<(curRow&7);
if (changeOrd!=-1) {
if (repeatPattern) {
curRow=0;
changeOrd=-1;
} else {
curRow=changePos;
changePos=0;
if (changeOrd==-2) changeOrd=curOrder+1;
if (changeOrd<=curOrder) endOfSong=true;
// old loop detection routine
//if (changeOrd<=curOrder) endOfSong=true;
curOrder=changeOrd;
if (curOrder>=curSubSong->ordersLen) {
curOrder=0;
endOfSong=true;
memset(walked,0,8192);
}
changeOrd=-1;
}
@ -851,6 +973,13 @@ void DivEngine::nextRow() {
if (haltOn==DIV_HALT_PATTERN) halted=true;
}
// new loop detection routine
if (!endOfSong && walked[((curOrder<<5)+(curRow>>3))&8191]&(1<<(curRow&7))) {
logV("loop reached");
endOfSong=true;
memset(walked,0,8192);
}
if (song.brokenSpeedSel) {
if ((curSubSong->patLen&1) && curOrder&1) {
ticks=((curRow&1)?speed2:speed1)*(curSubSong->timeBase+1);
@ -876,7 +1005,9 @@ void DivEngine::nextRow() {
if (!(pat->data[curRow][0]==0 && pat->data[curRow][1]==0)) {
if (pat->data[curRow][0]!=100 && pat->data[curRow][0]!=101 && pat->data[curRow][0]!=102) {
if (!chan[i].legato) {
dispatchCmd(DivCommand(DIV_CMD_PRE_NOTE,i,ticks));
if (disCont[dispatchOfChan[i]].dispatch!=NULL) {
if (disCont[dispatchOfChan[i]].dispatch->getWantPreNote()) dispatchCmd(DivCommand(DIV_CMD_PRE_NOTE,i,ticks));
}
if (song.oneTickCut) {
bool doPrepareCut=true;
@ -906,7 +1037,7 @@ void DivEngine::nextRow() {
bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
bool ret=false;
if (divider<10) divider=10;
if (divider<1) divider=1;
if (lowLatency && !skipping && !inhibitLowLat) {
tickMult=1000/divider;
@ -924,13 +1055,30 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
// MIDI clock
if (output) if (!skipping && output->midiOut!=NULL) {
output->midiOut->send(TAMidiMessage(TA_MIDI_CLOCK,0,0));
//output->midiOut->send(TAMidiMessage(TA_MIDI_CLOCK,0,0));
}
if (!pendingNotes.empty()) {
bool isOn[DIV_MAX_CHANS];
memset(isOn,0,DIV_MAX_CHANS*sizeof(bool));
for (int i=pendingNotes.size()-1; i>=0; i--) {
if (pendingNotes[i].channel<0 || pendingNotes[i].channel>=chans) continue;
if (pendingNotes[i].on) {
isOn[pendingNotes[i].channel]=true;
} else {
if (isOn[pendingNotes[i].channel]) {
logV("erasing off -> on sequence in %d",pendingNotes[i].channel);
pendingNotes.erase(pendingNotes.begin()+i);
}
}
}
}
while (!pendingNotes.empty()) {
DivNoteEvent& note=pendingNotes.front();
if (note.channel<0 || note.channel>=chans) {
pendingNotes.pop();
pendingNotes.pop_front();
continue;
}
if (note.on) {
@ -950,7 +1098,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,note.channel));
}
}
pendingNotes.pop();
pendingNotes.pop_front();
}
if (!freelance) {
@ -995,15 +1143,19 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
if (chan[i].volume>chan[i].volMax) {
chan[i].volume=chan[i].volMax;
chan[i].volSpeed=0;
dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8));
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,0));
} else if (chan[i].volume<0) {
chan[i].volSpeed=0;
dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,0));
if (song.legacyVolumeSlides) {
chan[i].volume=chan[i].volMax+1;
} else {
chan[i].volume=0;
}
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8));
} else {
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
}
@ -1032,10 +1184,12 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) {
if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(song.linearPitch==2?song.pitchSlideSpeed:1),chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) {
chan[i].portaSpeed=0;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
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));
dispatchCmd(DivCommand(DIV_CMD_HINT_LEGATO,i,chan[i].note));
}
}
}
@ -1049,11 +1203,13 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
if (chan[i].stopOnOff) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
chan[i].stopOnOff=false;
}
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
chan[i+1].portaNote=-1;
chan[i+1].portaSpeed=-1;
@ -1067,6 +1223,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
}
if (chan[i].resetArp) {
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
dispatchCmd(DivCommand(DIV_CMD_HINT_LEGATO,i,chan[i].note));
chan[i].resetArp=false;
}
if (song.rowResetsArpPos && firstTick) {
@ -1115,7 +1272,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
}
}
if (consoleMode && subticks<=1) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,curSubSong->ordersLen,curRow,curSubSong->patLen,cmdsPerSecond);
if (consoleMode && subticks<=1 && !skipping) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,curSubSong->ordersLen,curRow,curSubSong->patLen,cmdsPerSecond);
}
if (haltOn==DIV_HALT_TICK) halted=true;
@ -1153,7 +1310,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
case TA_MIDI_NOTE_OFF: {
if (chan<0 || chan>=chans) break;
if (midiIsDirect) {
pendingNotes.push(DivNoteEvent(chan,-1,-1,-1,false));
pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false));
} else {
autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]);
}
@ -1168,13 +1325,13 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
if (chan<0 || chan>=chans) break;
if (msg.data[1]==0) {
if (midiIsDirect) {
pendingNotes.push(DivNoteEvent(chan,-1,-1,-1,false));
pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false));
} else {
autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]);
}
} else {
if (midiIsDirect) {
pendingNotes.push(DivNoteEvent(chan,ins,msg.data[0]-12,msg.data[1],true));
pendingNotes.push_back(DivNoteEvent(chan,ins,msg.data[0]-12,msg.data[1],true));
} else {
autoNoteOn(msg.type&15,ins,msg.data[0]-12,msg.data[1]);
}
@ -1210,7 +1367,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
const int loopStart=pBeginVaild?sPreview.pBegin:s->loopStart;
const int loopEnd=pEndVaild?sPreview.pEnd:(int)s->loopEnd;
for (size_t i=0; i<prevtotal; i++) {
if (sPreview.pos>=s->samples || (sPreview.pEnd>=0 && (int)sPreview.pos>=sPreview.pEnd)) {
if (sPreview.pos>=(int)s->samples || (sPreview.pEnd>=0 && sPreview.pos>=sPreview.pEnd)) {
samp_temp=0;
} else {
samp_temp=s->data16[sPreview.pos];
@ -1223,98 +1380,95 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
blip_add_delta(samp_bb,i,samp_temp-samp_prevSample);
samp_prevSample=samp_temp;
if (sPreview.dir) {
if (s->isLoopable() && ((int)sPreview.pos)<loopStart) {
switch (s->loopMode) {
case DIV_SAMPLE_LOOPMODE_FORWARD:
sPreview.dir=false;
sPreview.pos=loopStart+1;
break;
case DIV_SAMPLE_LOOPMODE_BACKWARD:
sPreview.dir=true;
sPreview.pos=loopEnd-1;
break;
case DIV_SAMPLE_LOOPMODE_PINGPONG:
sPreview.dir=false;
sPreview.pos=loopStart+1;
break;
case DIV_SAMPLE_LOOPMODE_ONESHOT:
default:
break;
if (sPreview.dir) { // backward
if (sPreview.pos<s->loopStart || (sPreview.pBegin>=0 && sPreview.pos<sPreview.pBegin)) {
if (s->isLoopable() && sPreview.pos<s->loopEnd) {
switch (s->loopMode) {
case DivSampleLoopMode::DIV_SAMPLE_LOOP_FORWARD:
sPreview.pos=s->loopStart;
sPreview.dir=false;
break;
case DivSampleLoopMode::DIV_SAMPLE_LOOP_BACKWARD:
sPreview.pos=s->loopEnd-1;
sPreview.dir=true;
break;
case DivSampleLoopMode::DIV_SAMPLE_LOOP_PINGPONG:
sPreview.pos=s->loopStart;
sPreview.dir=false;
break;
default:
break;
}
}
}
} else {
if (s->isLoopable() && ((int)sPreview.pos)>=loopEnd) {
switch (s->loopMode) {
case DIV_SAMPLE_LOOPMODE_FORWARD:
sPreview.dir=false;
sPreview.pos=loopStart;
break;
case DIV_SAMPLE_LOOPMODE_BACKWARD:
sPreview.dir=true;
sPreview.pos=loopEnd-1;
break;
case DIV_SAMPLE_LOOPMODE_PINGPONG:
sPreview.dir=true;
sPreview.pos=loopEnd-1;
break;
case DIV_SAMPLE_LOOPMODE_ONESHOT:
default:
break;
} else { // forward
if (sPreview.pos>=s->loopEnd || (sPreview.pEnd>=0 && sPreview.pos>=sPreview.pEnd)) {
if (s->isLoopable() && sPreview.pos>=s->loopStart) {
switch (s->loopMode) {
case DivSampleLoopMode::DIV_SAMPLE_LOOP_FORWARD:
sPreview.pos=s->loopStart;
sPreview.dir=false;
break;
case DivSampleLoopMode::DIV_SAMPLE_LOOP_BACKWARD:
sPreview.pos=s->loopEnd-1;
sPreview.dir=true;
break;
case DivSampleLoopMode::DIV_SAMPLE_LOOP_PINGPONG:
sPreview.pos=s->loopEnd-1;
sPreview.dir=true;
break;
default:
break;
}
}
}
}
}
if (sPreview.dir) {
if ((s->isLoopable() && sPreview.pos<s->loopEnd) && ((int)sPreview.pos)<loopStart) {
switch (s->loopMode) {
case DIV_SAMPLE_LOOPMODE_FORWARD:
sPreview.dir=false;
sPreview.pos=loopStart+1;
break;
case DIV_SAMPLE_LOOPMODE_BACKWARD:
sPreview.dir=true;
sPreview.pos=loopEnd-1;
break;
case DIV_SAMPLE_LOOPMODE_PINGPONG:
sPreview.dir=false;
sPreview.pos=loopStart+1;
break;
case DIV_SAMPLE_LOOPMODE_ONESHOT:
default:
if (((int)sPreview.pos)<0) {
sPreview.sample=-1;
}
break;
if (sPreview.dir) { // backward
if (sPreview.pos<=s->loopStart || (sPreview.pBegin>=0 && sPreview.pos<=sPreview.pBegin)) {
if (s->isLoopable() && sPreview.pos>=s->loopStart) {
switch (s->loopMode) {
case DivSampleLoopMode::DIV_SAMPLE_LOOP_FORWARD:
sPreview.pos=s->loopStart;
sPreview.dir=false;
break;
case DivSampleLoopMode::DIV_SAMPLE_LOOP_BACKWARD:
sPreview.pos=s->loopEnd-1;
sPreview.dir=true;
break;
case DivSampleLoopMode::DIV_SAMPLE_LOOP_PINGPONG:
sPreview.pos=s->loopStart;
sPreview.dir=false;
break;
default:
break;
}
} else if (sPreview.pos<0) {
sPreview.sample=-1;
}
} else if (((int)sPreview.pos)<0) {
sPreview.sample=-1;
}
} else {
if ((s->isLoopable() && (int)sPreview.pos>=s->loopStart) && ((int)sPreview.pos)>=loopEnd) {
switch (s->loopMode) {
case DIV_SAMPLE_LOOPMODE_FORWARD:
sPreview.dir=false;
sPreview.pos=loopStart;
break;
case DIV_SAMPLE_LOOPMODE_BACKWARD:
sPreview.dir=true;
sPreview.pos=loopEnd-1;
break;
case DIV_SAMPLE_LOOPMODE_PINGPONG:
sPreview.dir=true;
sPreview.pos=loopEnd-1;
break;
case DIV_SAMPLE_LOOPMODE_ONESHOT:
default:
if (sPreview.pos>=s->samples) {
sPreview.sample=-1;
}
break;
} else { // forward
if (sPreview.pos>=s->loopEnd || (sPreview.pEnd>=0 && sPreview.pos>=sPreview.pEnd)) {
if (s->isLoopable() && sPreview.pos>=s->loopStart) {
switch (s->loopMode) {
case DivSampleLoopMode::DIV_SAMPLE_LOOP_FORWARD:
sPreview.pos=s->loopStart;
sPreview.dir=false;
break;
case DivSampleLoopMode::DIV_SAMPLE_LOOP_BACKWARD:
sPreview.pos=s->loopEnd-1;
sPreview.dir=true;
break;
case DivSampleLoopMode::DIV_SAMPLE_LOOP_PINGPONG:
sPreview.pos=s->loopEnd-1;
sPreview.dir=true;
break;
default:
break;
}
} else if (sPreview.pos>=(int)s->samples) {
sPreview.sample=-1;
}
} else if (sPreview.pos>=s->samples) {
sPreview.sample=-1;
}
}
} else if (sPreview.wave>=0 && sPreview.wave<(int)song.wave.size()) {
@ -1325,7 +1479,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
} else {
samp_temp=((MIN(wave->data[sPreview.pos],wave->max)<<14)/wave->max)-8192;
}
if (++sPreview.pos>=(unsigned int)wave->len) {
if (++sPreview.pos>=wave->len) {
sPreview.pos=0;
}
blip_add_delta(samp_bb,i,samp_temp-samp_prevSample);
@ -1355,25 +1509,22 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
}
// logic starts here
size_t runtotal[32];
size_t runLeft[32];
size_t runPos[32];
size_t lastAvail[32];
for (int i=0; i<song.systemLen; i++) {
lastAvail[i]=blip_samples_avail(disCont[i].bb[0]);
if (lastAvail[i]>0) {
disCont[i].flush(lastAvail[i]);
disCont[i].lastAvail=blip_samples_avail(disCont[i].bb[0]);
if (disCont[i].lastAvail>0) {
disCont[i].flush(disCont[i].lastAvail);
}
runtotal[i]=blip_clocks_needed(disCont[i].bb[0],size-lastAvail[i]);
if (runtotal[i]>disCont[i].bbInLen) {
disCont[i].runtotal=blip_clocks_needed(disCont[i].bb[0],size-disCont[i].lastAvail);
if (disCont[i].runtotal>disCont[i].bbInLen) {
logV("growing dispatch %d bbIn to %d",i,disCont[i].runtotal+256);
delete[] disCont[i].bbIn[0];
delete[] disCont[i].bbIn[1];
disCont[i].bbIn[0]=new short[runtotal[i]+256];
disCont[i].bbIn[1]=new short[runtotal[i]+256];
disCont[i].bbInLen=runtotal[i]+256;
disCont[i].bbIn[0]=new short[disCont[i].runtotal+256];
disCont[i].bbIn[1]=new short[disCont[i].runtotal+256];
disCont[i].bbInLen=disCont[i].runtotal+256;
}
runLeft[i]=runtotal[i];
runPos[i]=0;
disCont[i].runLeft=disCont[i].runtotal;
disCont[i].runPos=0;
}
if (metroTickLen<size) {
@ -1425,10 +1576,10 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
// 3. tick the clock and fill buffers as needed
if (cycles<runLeftG) {
for (int i=0; i<song.systemLen; i++) {
int total=(cycles*runtotal[i])/(size<<MASTER_CLOCK_PREC);
disCont[i].acquire(runPos[i],total);
runLeft[i]-=total;
runPos[i]+=total;
int total=(cycles*disCont[i].runtotal)/(size<<MASTER_CLOCK_PREC);
disCont[i].acquire(disCont[i].runPos,total);
disCont[i].runLeft-=total;
disCont[i].runPos+=total;
}
runLeftG-=cycles;
cycles=0;
@ -1436,8 +1587,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
cycles-=runLeftG;
runLeftG=0;
for (int i=0; i<song.systemLen; i++) {
disCont[i].acquire(runPos[i],runLeft[i]);
runLeft[i]=0;
disCont[i].acquire(disCont[i].runPos,disCont[i].runLeft);
disCont[i].runLeft=0;
}
}
}
@ -1458,7 +1609,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
totalProcessed=size-(runLeftG>>MASTER_CLOCK_PREC);
for (int i=0; i<song.systemLen; i++) {
disCont[i].fillBuf(runtotal[i],lastAvail[i],size-lastAvail[i]);
disCont[i].fillBuf(disCont[i].runtotal,disCont[i].lastAvail,size-disCont[i].lastAvail);
}
for (int i=0; i<song.systemLen; i++) {
@ -1512,6 +1663,14 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
out[1][i]=out[0][i];
}
}
if (clampSamples) {
for (size_t i=0; i<size; i++) {
if (out[0][i]<-1.0) out[0][i]=-1.0;
if (out[0][i]>1.0) out[0][i]=1.0;
if (out[1][i]<-1.0) out[1][i]=-1.0;
if (out[1][i]>1.0) out[1][i]=1.0;
}
}
isBusy.unlock();
std::chrono::steady_clock::time_point ts_processEnd=std::chrono::steady_clock::now();