YM2612: add reverse sample playback

This commit is contained in:
tildearrow 2022-05-27 02:47:44 -05:00
parent 9d6d84a87f
commit 0b7d27dc29
7 changed files with 34 additions and 10 deletions

View file

@ -59,6 +59,7 @@ enum DivDispatchCmds {
DIV_CMD_SAMPLE_FREQ, // (frequency) DIV_CMD_SAMPLE_FREQ, // (frequency)
DIV_CMD_SAMPLE_BANK, // (bank) DIV_CMD_SAMPLE_BANK, // (bank)
DIV_CMD_SAMPLE_POS, // (pos) DIV_CMD_SAMPLE_POS, // (pos)
DIV_CMD_SAMPLE_DIR, // (direction)
DIV_CMD_FM_HARD_RESET, // (enabled) DIV_CMD_FM_HARD_RESET, // (enabled)
DIV_CMD_FM_LFO, // (speed) DIV_CMD_FM_LFO, // (speed)
@ -222,6 +223,8 @@ struct DivRegWrite {
* - data is the sample rate * - data is the sample rate
* - 0xffffxx02: stop sample playback * - 0xffffxx02: stop sample playback
* - xx is the instance ID * - xx is the instance ID
* - 0xffffxx03: set sample playback direction
* - x is the instance ID
* - 0xffffffff: reset * - 0xffffffff: reset
*/ */
unsigned int addr; unsigned int addr;

View file

@ -375,7 +375,7 @@ class DivEngine {
void processRow(int i, bool afterDelay); void processRow(int i, bool afterDelay);
void nextOrder(); void nextOrder();
void nextRow(); void nextRow();
void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool isSecond); void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond);
// returns true if end of song. // returns true if end of song.
bool nextTick(bool noAccum=false, bool inhibitLowLat=false); bool nextTick(bool noAccum=false, bool inhibitLowLat=false);
bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal); bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal);

View file

@ -125,6 +125,9 @@ const char* DivPlatformGenesis::getEffectName(unsigned char effect) {
case 0x5f: case 0x5f:
return "5Fxx: Set decay 2 of operator 4 (0 to 1F)"; return "5Fxx: Set decay 2 of operator 4 (0 to 1F)";
break; break;
case 0xdf:
return "DFxx: Set sample playback direction (0: normal; 1: reverse)";
break;
} }
return NULL; return NULL;
} }
@ -148,12 +151,12 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s
if (s->samples>0) { if (s->samples>0) {
if (!isMuted[5]) { if (!isMuted[5]) {
if (dacReady && writes.size()<16) { if (dacReady && writes.size()<16) {
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80); urgentWrite(0x2a,(unsigned char)s->data8[dacDirection?(s->samples-dacPos-1):dacPos]+0x80);
dacReady=false; dacReady=false;
} }
} }
if (++dacPos>=s->samples) { if (++dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) { if (s->loopStart>=0 && s->loopStart<(int)s->samples && !dacDirection) {
dacPos=s->loopStart; dacPos=s->loopStart;
} else { } else {
dacSample=-1; dacSample=-1;
@ -228,12 +231,12 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
if (s->samples>0) { if (s->samples>0) {
if (!isMuted[5]) { if (!isMuted[5]) {
if (dacReady && writes.size()<16) { if (dacReady && writes.size()<16) {
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80); urgentWrite(0x2a,(unsigned char)s->data8[dacDirection?(s->samples-dacPos-1):dacPos]+0x80);
dacReady=false; dacReady=false;
} }
} }
if (++dacPos>=s->samples) { if (++dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) { if (s->loopStart>=0 && s->loopStart<(int)s->samples && !dacDirection) {
dacPos=s->loopStart; dacPos=s->loopStart;
} else { } else {
dacSample=-1; dacSample=-1;
@ -770,6 +773,11 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
sampleBank=parent->song.sample.size()/12; sampleBank=parent->song.sample.size()/12;
} }
break; break;
case DIV_CMD_SAMPLE_DIR: {
dacDirection=c.value;
if (dumpWrites) addWrite(0xffff0003,dacDirection);
break;
}
case DIV_CMD_LEGATO: { case DIV_CMD_LEGATO: {
if (c.chan==5 && chan[c.chan].furnaceDac && dacMode) { if (c.chan==5 && chan[c.chan].furnaceDac && dacMode) {
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
@ -1062,11 +1070,14 @@ void DivPlatformGenesis::reset() {
dacDelay=0; dacDelay=0;
dacReady=true; dacReady=true;
dacSample=-1; dacSample=-1;
dacDirection=false;
sampleBank=0; sampleBank=0;
lfoValue=8; lfoValue=8;
extMode=false; extMode=false;
if (dumpWrites) addWrite(0xffff0003,dacDirection);
// LFO // LFO
immWrite(0x22,lfoValue); immWrite(0x22,lfoValue);

View file

@ -93,6 +93,7 @@ class DivPlatformGenesis: public DivDispatch {
int dacSample; int dacSample;
int dacDelay; int dacDelay;
bool dacReady; bool dacReady;
bool dacDirection;
unsigned char sampleBank; unsigned char sampleBank;
unsigned char lfoValue; unsigned char lfoValue;

View file

@ -63,6 +63,7 @@ const char* cmdName[]={
"SAMPLE_FREQ", "SAMPLE_FREQ",
"SAMPLE_BANK", "SAMPLE_BANK",
"SAMPLE_POS", "SAMPLE_POS",
"SAMPLE_DIR",
"FM_HARD_RESET", "FM_HARD_RESET",
"FM_LFO", "FM_LFO",
@ -556,6 +557,9 @@ void DivEngine::processRow(int i, bool afterDelay) {
clockDrift=0; clockDrift=0;
subticks=0; subticks=0;
break; break;
case 0xdf: // set sample direction
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_DIR,i,effectVal));
break;
case 0xe0: // arp speed case 0xe0: // arp speed
if (effectVal>0) { if (effectVal>0) {
curSubSong->arpLen=effectVal; curSubSong->arpLen=effectVal;

View file

@ -24,7 +24,7 @@
constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0; constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool isSecond) { void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond) {
unsigned char baseAddr1=isSecond?0xa0:0x50; unsigned char baseAddr1=isSecond?0xa0:0x50;
unsigned char baseAddr2=isSecond?0x80:0; unsigned char baseAddr2=isSecond?0x80:0;
unsigned short baseAddr2S=isSecond?0x8000:0; unsigned short baseAddr2S=isSecond?0x8000:0;
@ -502,8 +502,8 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(0x95); w->writeC(0x95);
w->writeC(streamID); w->writeC(streamID);
w->writeS(write.val); // sample number w->writeS(write.val); // sample number
w->writeC((sample->loopStart==0)); // flags w->writeC((sample->loopStart==0)|(sampleDir[streamID]?0x10:0)); // flags
if (sample->loopStart>0) { if (sample->loopStart>0 && !sampleDir[streamID]) {
loopTimer[streamID]=sample->length8; loopTimer[streamID]=sample->length8;
loopSample[streamID]=write.val; loopSample[streamID]=write.val;
} }
@ -520,6 +520,9 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(streamID); w->writeC(streamID);
loopSample[streamID]=-1; loopSample[streamID]=-1;
break; break;
case 3: // set sample direction
sampleDir[streamID]=write.val;
break;
} }
return; return;
} }
@ -884,11 +887,13 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
double loopTimer[DIV_MAX_CHANS]; double loopTimer[DIV_MAX_CHANS];
double loopFreq[DIV_MAX_CHANS]; double loopFreq[DIV_MAX_CHANS];
int loopSample[DIV_MAX_CHANS]; int loopSample[DIV_MAX_CHANS];
bool sampleDir[DIV_MAX_CHANS];
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
loopTimer[i]=0; loopTimer[i]=0;
loopFreq[i]=0; loopFreq[i]=0;
loopSample[i]=-1; loopSample[i]=-1;
sampleDir[i]=false;
} }
bool writeDACSamples=false; bool writeDACSamples=false;
@ -1762,7 +1767,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
for (int i=0; i<song.systemLen; i++) { for (int i=0; i<song.systemLen; i++) {
std::vector<DivRegWrite>& writes=disCont[i].dispatch->getRegisterWrites(); std::vector<DivRegWrite>& writes=disCont[i].dispatch->getRegisterWrites();
for (DivRegWrite& j: writes) { for (DivRegWrite& j: writes) {
performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,isSecond[i]); performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i]);
writeCount++; writeCount++;
} }
writes.clear(); writes.clear();

View file

@ -394,7 +394,7 @@ const FurnaceGUIColors fxColors[256]={
GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID,
GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_MISC, // DF
// E0-FF extended effects // E0-FF extended effects
GUI_COLOR_PATTERN_EFFECT_MISC, // E0 GUI_COLOR_PATTERN_EFFECT_MISC, // E0