diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index bf55d9d6..3f24d5a2 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -974,6 +974,7 @@ void DivEngine::renderSamplesP() { void DivEngine::renderSamples() { sPreview.sample=-1; sPreview.pos=0; + sPreview.dir=false; // step 1: render samples for (int i=0; inotifyPlaybackStop(); } @@ -1914,9 +1918,11 @@ void DivEngine::previewSample(int sample, int note, int pStart, int pEnd) { BUSY_BEGIN; sPreview.pBegin=pStart; sPreview.pEnd=pEnd; + sPreview.dir=false; if (sample<0 || sample>=(int)song.sample.size()) { sPreview.sample=-1; sPreview.pos=0; + sPreview.dir=false; BUSY_END; return; } @@ -1932,6 +1938,7 @@ void DivEngine::previewSample(int sample, int note, int pStart, int pEnd) { sPreview.pos=(sPreview.pBegin>=0)?sPreview.pBegin:0; sPreview.sample=sample; sPreview.wave=-1; + sPreview.dir=false; BUSY_END; } @@ -1939,6 +1946,7 @@ void DivEngine::stopSamplePreview() { BUSY_BEGIN; sPreview.sample=-1; sPreview.pos=0; + sPreview.dir=false; BUSY_END; } @@ -1947,6 +1955,7 @@ void DivEngine::previewWave(int wave, int note) { if (wave<0 || wave>=(int)song.wave.size()) { sPreview.wave=-1; sPreview.pos=0; + sPreview.dir=false; BUSY_END; return; } @@ -1962,6 +1971,7 @@ void DivEngine::previewWave(int wave, int note) { sPreview.pos=0; sPreview.sample=-1; sPreview.wave=wave; + sPreview.dir=false; BUSY_END; } @@ -1969,6 +1979,7 @@ void DivEngine::stopWavePreview() { BUSY_BEGIN; sPreview.wave=-1; sPreview.pos=0; + sPreview.dir=false; BUSY_END; } @@ -2382,6 +2393,7 @@ int DivEngine::addSample() { song.sampleLen=sampleCount+1; sPreview.sample=-1; sPreview.pos=0; + sPreview.dir=false; saveLock.unlock(); renderSamples(); BUSY_END; @@ -2601,13 +2613,16 @@ int DivEngine::addSampleFromFile(const char* path) { inst.detune = inst.detune - 100; short pitch = ((0x3c-inst.basenote)*100) + inst.detune; sample->centerRate=si.samplerate*pow(2.0,pitch/(12.0 * 100.0)); - if(inst.loop_count && inst.loops[0].mode == SF_LOOP_FORWARD) + if(inst.loop_count && inst.loops[0].mode >= SF_LOOP_FORWARD) { + sample->loopMode=(DivSampleLoopMode)(inst.loops[0].mode-SF_LOOP_FORWARD); sample->loopStart=inst.loops[0].start; sample->loopEnd=inst.loops[0].end; if(inst.loops[0].end < (unsigned int)sampleCount) sampleCount=inst.loops[0].end; } + else + sample->loop=false; } if (sample->centerRate<4000) sample->centerRate=4000; @@ -2627,6 +2642,7 @@ void DivEngine::delSample(int index) { BUSY_BEGIN; sPreview.sample=-1; sPreview.pos=0; + sPreview.dir=false; saveLock.lock(); if (index>=0 && index<(int)song.sample.size()) { delete song.sample[index]; @@ -2843,6 +2859,7 @@ bool DivEngine::moveSampleUp(int which) { BUSY_BEGIN; sPreview.sample=-1; sPreview.pos=0; + sPreview.dir=false; DivSample* prev=song.sample[which]; saveLock.lock(); song.sample[which]=song.sample[which-1]; @@ -2882,6 +2899,7 @@ bool DivEngine::moveSampleDown(int which) { BUSY_BEGIN; sPreview.sample=-1; sPreview.pos=0; + sPreview.dir=false; DivSample* prev=song.sample[which]; saveLock.lock(); song.sample[which]=song.sample[which+1]; diff --git a/src/engine/engine.h b/src/engine/engine.h index 001ea194..6d49d710 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -353,14 +353,16 @@ class DivEngine { struct SamplePreview { int sample; int wave; - unsigned int pos; + int pos; int pBegin, pEnd; + bool dir; SamplePreview(): sample(-1), wave(-1), pos(0), pBegin(-1), - pEnd(-1) {} + pEnd(-1), + dir(false) {} } sPreview; short vibTable[64]; diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 8dcd55d2..76cff9df 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -1669,6 +1669,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { sample->loopStart=reader.readI(); sample->loopEnd=reader.readI(); + sample->loop=(sample->loopStart>=0)&&(sample->loopEnd>=0); for (int i=0; i<4; i++) { reader.readI(); @@ -1694,6 +1695,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version>=19) { sample->loopStart=reader.readI(); + sample->loop=(sample->loopStart>=0)&&(sample->loopEnd>=0); } else { reader.readI(); } @@ -1948,6 +1950,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { if (loopLen>=2) { sample->loopStart=loopStart; sample->loopEnd=loopEnd; + sample->loop=(sample->loopStart>=0)&&(sample->loopEnd>=0); } sample->init(slen); ds.sample.push_back(sample); @@ -2485,6 +2488,7 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) { } s->loopStart=sample[i].loopStart*2; s->loopEnd=(sample[i].loopStart+sample[i].loopLen)*2; + s->loop=(s->loopStart>=0)&&(s->loopEnd>=0); reader.read(s->data8,sample[i].len); ds.sample.push_back(s); } @@ -3586,8 +3590,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeC(0); // reserved w->writeC(0); w->writeC(0); - w->writeI(sample->loopStart); - w->writeI(sample->loopEnd); + w->writeI(sample->loop?sample->loopStart:-1); + w->writeI(sample->loop?sample->loopEnd:-1); for (int i=0; i<4; i++) { w->writeI(0xffffffff); diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 1b007530..eec6f66c 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -110,13 +110,13 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le } } else { DivSample* s=parent->getSample(chan[i].sample); - if (s->samples>0) { - if (chan[i].audPossamples) { + if (s->getEndPosition()>0) { + if (chan[i].audPos<(unsigned int)s->getEndPosition()) { writeAudDat(s->data8[chan[i].audPos++]); } - if (s->isLoopable() && chan[i].audPos>=MIN(131071,s->getEndPosition())) { - chan[i].audPos=s->loopStart; - } else if (chan[i].audPos>=MIN(131071,s->samples)) { + if (s->isLoopable() && chan[i].audPos>=MIN(131071,(unsigned int)s->getLoopEndPosition())) { + chan[i].audPos=s->getLoopStartPosition(); + } else if (chan[i].audPos>=MIN(131071,(unsigned int)s->getEndPosition())) { chan[i].sample=-1; } } else { diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index b6dbb8d1..d3c1e6c3 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -154,9 +154,9 @@ void DivPlatformGenesis::processDAC() { if (s->samples>0) { while (chan[i].dacPeriod>=(chipClock/576)) { ++chan[i].dacPos; - if (!chan[i].dacDirection && (s->isLoopable() && chan[i].dacPos>=s->getEndPosition())) { - chan[i].dacPos=s->loopStart; - } else if (chan[i].dacPos>=s->samples) { + if (!chan[i].dacDirection && (s->isLoopable() && chan[i].dacPos>=(unsigned int)s->getLoopEndPosition())) { + chan[i].dacPos=s->getLoopStartPosition(); + } else if (chan[i].dacPos>=(unsigned int)s->getEndPosition()) { chan[i].dacSample=-1; chan[i].dacPeriod=0; break; @@ -200,9 +200,9 @@ void DivPlatformGenesis::processDAC() { } } chan[5].dacPos++; - if (!chan[5].dacDirection && (s->isLoopable() && chan[5].dacPos>=s->getEndPosition())) { - chan[5].dacPos=s->loopStart; - } else if (chan[5].dacPos>=s->samples) { + if (!chan[5].dacDirection && (s->isLoopable() && chan[5].dacPos>=(unsigned int)s->getLoopEndPosition())) { + chan[5].dacPos=s->getLoopStartPosition(); + } else if (chan[5].dacPos>=(unsigned int)s->getEndPosition()) { chan[5].dacSample=-1; if (parent->song.brokenDACMode) { rWrite(0x2b,0); diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 7c349e5a..836ce315 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -158,9 +158,9 @@ void DivPlatformLynx::acquire(short* bufL, short* bufR, size_t start, size_t len WRITE_OUTPUT(i,(s->data8[chan[i].samplePos++]*chan[i].outVol)>>7); } - if (s->isLoopable() && chan[i].samplePos>=(int)s->getEndPosition()) { - chan[i].samplePos=s->loopStart; - } else if (chan[i].samplePos>=(int)s->samples) { + if (s->isLoopable() && chan[i].samplePos>=s->getLoopEndPosition()) { + chan[i].samplePos=s->getLoopStartPosition(); + } else if (chan[i].samplePos>=s->getEndPosition()) { chan[i].sample=-1; } } diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 2154e855..40e2f5c1 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -58,14 +58,14 @@ void DivPlatformMMC5::acquire(short* bufL, short* bufR, size_t start, size_t len dacPeriod+=dacRate; if (dacPeriod>=rate) { DivSample* s=parent->getSample(dacSample); - if (s->samples>0) { + if (s->getEndPosition()>0) { if (!isMuted[2]) { rWrite(0x5011,((unsigned char)s->data8[dacPos]+0x80)); } dacPos++; - if (s->isLoopable() && dacPos>=s->getEndPosition()) { - dacPos=s->loopStart; - } else if (dacPos>=s->samples) { + if (s->isLoopable() && dacPos>=(unsigned int)s->getLoopEndPosition()) { + dacPos=s->getLoopStartPosition(); + } else if (dacPos>=(unsigned int)s->getEndPosition()) { dacSample=-1; } dacPeriod-=rate; diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index bd1be94a..d835e385 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -97,7 +97,7 @@ void DivPlatformNES::doWrite(unsigned short addr, unsigned char data) { dacPeriod+=dacRate; \ if (dacPeriod>=rate) { \ DivSample* s=parent->getSample(dacSample); \ - if (s->samples>0) { \ + if (s->getEndPosition()>0) { \ if (!isMuted[4]) { \ unsigned char next=((unsigned char)s->data8[dacPos]+0x80)>>1; \ if (dacAntiClickOn && dacAntiClickisLoopable() && dacPos>=s->getEndPosition()) { \ - dacPos=s->loopStart; \ - } else if (dacPos>=s->samples) { \ + if (s->isLoopable() && dacPos>=(unsigned int)s->getLoopEndPosition()) { \ + dacPos=s->getLoopStartPosition(); \ + } else if (dacPos>=(unsigned int)s->getEndPosition()) { \ dacSample=-1; \ } \ dacPeriod-=rate; \ diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 5d9a0de8..d71e1914 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -82,7 +82,7 @@ void DivPlatformPCE::acquire(short* bufL, short* bufR, size_t start, size_t len) chan[i].dacPeriod+=chan[i].dacRate; if (chan[i].dacPeriod>rate) { DivSample* s=parent->getSample(chan[i].dacSample); - if (s->samples<=0) { + if (s->getEndPosition()<=0) { chan[i].dacSample=-1; continue; } @@ -90,9 +90,9 @@ void DivPlatformPCE::acquire(short* bufL, short* bufR, size_t start, size_t len) chWrite(i,0x04,0xdf); chWrite(i,0x06,(((unsigned char)s->data8[chan[i].dacPos]+0x80)>>3)); chan[i].dacPos++; - if (s->isLoopable() && chan[i].dacPos>=s->getEndPosition()) { - chan[i].dacPos=s->loopStart; - } else if (chan[i].dacPos>=s->samples) { + if (s->isLoopable() && chan[i].dacPos>=(unsigned int)s->getLoopEndPosition()) { + chan[i].dacPos=s->getLoopStartPosition(); + } else if (chan[i].dacPos>=(unsigned int)s->getEndPosition()) { chan[i].dacSample=-1; } chan[i].dacPeriod-=rate; diff --git a/src/engine/platform/pcmdac.cpp b/src/engine/platform/pcmdac.cpp index 213cb85a..67a533b1 100644 --- a/src/engine/platform/pcmdac.cpp +++ b/src/engine/platform/pcmdac.cpp @@ -49,13 +49,13 @@ void DivPlatformPCMDAC::acquire(short* bufL, short* bufR, size_t start, size_t l output=(chan.ws.output[chan.audPos]^0x80)<<8; } else { DivSample* s=parent->getSample(chan.sample); - if (s->samples>0) { - if (s->isLoopable() && chan.audPos>=s->getEndPosition()) { - chan.audPos=s->loopStart; - } else if (chan.audPos>=s->samples) { + if (s->getEndPosition()>0) { + if (s->isLoopable() && chan.audPos>=(unsigned int)s->getLoopEndPosition()) { + chan.audPos=s->getLoopStartPosition(); + } else if (chan.audPos>=(unsigned int)s->getEndPosition()) { chan.sample=-1; } - if (chan.audPossamples) { + if (chan.audPos<(unsigned int)s->getEndPosition()) { output=s->data16[chan.audPos]; } } else { diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index 6bc88dbf..04696f2c 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -301,16 +301,17 @@ void DivPlatformQSound::tick(bool sysTick) { qsound_bank = 0x8000 | (s->offQSound >> 16); qsound_addr = s->offQSound & 0xffff; - int length = s->getEndPosition(); + int loopStart=s->getLoopStartPosition(); + int length = s->getLoopEndPosition(); if (length > 65536 - 16) { length = 65536 - 16; } - if (s->loopStart == -1 || s->loopStart >= length) { + if (loopStart == -1 || loopStart >= length) { qsound_end = s->offQSound + length + 15; qsound_loop = 15; } else { qsound_end = s->offQSound + length; - qsound_loop = length - s->loopStart; + qsound_loop = length - loopStart; } } if (chan[i].std.arp.had) { diff --git a/src/engine/platform/rf5c68.cpp b/src/engine/platform/rf5c68.cpp index e4a39d44..5b829c34 100644 --- a/src/engine/platform/rf5c68.cpp +++ b/src/engine/platform/rf5c68.cpp @@ -143,7 +143,7 @@ void DivPlatformRF5C68::tick(bool sysTick) { start=start+MIN(chan[i].audPos,s->length8); } if (s->isLoopable()) { - loop=start+s->loopStart; + loop=start+s->getLoopStartPosition(); } start=MIN(start,getSampleMemCapacity()-31); loop=MIN(loop,getSampleMemCapacity()-31); @@ -393,7 +393,7 @@ void DivPlatformRF5C68::renderSamples() { size_t memPos=0; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; - int length=s->getEndPosition(DIV_SAMPLE_DEPTH_8BIT); + int length=s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT); int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-31,length); if (actualLength>0) { s->offRF5C68=memPos; diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index d66fcce0..6a63f823 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -45,7 +45,7 @@ void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t for (int i=0; i<16; i++) { if (chan[i].pcm.sample>=0 && chan[i].pcm.samplesong.sampleLen) { DivSample* s=parent->getSample(chan[i].pcm.sample); - if (s->samples<=0) { + if (s->getEndPosition()<=0) { chan[i].pcm.sample=-1; oscBuf[i]->data[oscBuf[i]->needle++]=0; continue; @@ -56,9 +56,9 @@ void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t pcmR+=(s->data8[chan[i].pcm.pos>>8]*chan[i].chVolR); } chan[i].pcm.pos+=chan[i].pcm.freq; - if (s->isLoopable() && chan[i].pcm.pos>=(s->getEndPosition()<<8)) { - chan[i].pcm.pos=s->loopStart<<8; - } else if (chan[i].pcm.pos>=(s->samples<<8)) { + if (s->isLoopable() && chan[i].pcm.pos>=((unsigned int)s->getLoopEndPosition()<<8)) { + chan[i].pcm.pos=s->getLoopStartPosition()<<8; + } else if (chan[i].pcm.pos>=((unsigned int)s->getEndPosition()<<8)) { chan[i].pcm.sample=-1; } } else { @@ -200,16 +200,17 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { chan[c.chan].macroInit(ins); if (dumpWrites) { // Sega PCM writes DivSample* s=parent->getSample(chan[c.chan].pcm.sample); - int actualLength=(int)(s->getEndPosition(DIV_SAMPLE_DEPTH_8BIT)); + int loopStart=s->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT); + int actualLength=(s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)); if (actualLength>0xfeff) actualLength=0xfeff; addWrite(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3)); addWrite(0x10084+(c.chan<<3),(s->offSegaPCM)&0xff); addWrite(0x10085+(c.chan<<3),(s->offSegaPCM>>8)&0xff); addWrite(0x10006+(c.chan<<3),MIN(255,((s->offSegaPCM&0xffff)+actualLength-1)>>8)); - if (s->loopStart<0 || s->loopStart>=actualLength) { + if (loopStart<0 || loopStart>=actualLength) { addWrite(0x10086+(c.chan<<3),2+((s->offSegaPCM>>16)<<3)); } else { - int loopPos=(s->offSegaPCM&0xffff)+s->loopStart+s->loopOffP; + int loopPos=(s->offSegaPCM&0xffff)+loopStart+s->loopOffP; addWrite(0x10004+(c.chan<<3),loopPos&0xff); addWrite(0x10005+(c.chan<<3),(loopPos>>8)&0xff); addWrite(0x10086+(c.chan<<3),((s->offSegaPCM>>16)<<3)); @@ -233,16 +234,17 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { chan[c.chan].furnacePCM=false; if (dumpWrites) { // Sega PCM writes DivSample* s=parent->getSample(chan[c.chan].pcm.sample); - int actualLength=(int)(s->getEndPosition(DIV_SAMPLE_DEPTH_8BIT)); + int loopStart=s->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT); + int actualLength=(s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)); if (actualLength>65536) actualLength=65536; addWrite(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3)); addWrite(0x10084+(c.chan<<3),(s->offSegaPCM)&0xff); addWrite(0x10085+(c.chan<<3),(s->offSegaPCM>>8)&0xff); addWrite(0x10006+(c.chan<<3),MIN(255,((s->offSegaPCM&0xffff)+actualLength-1)>>8)); - if (s->loopStart<0 || s->loopStart>=actualLength) { + if (loopStart<0 || loopStart>=actualLength) { addWrite(0x10086+(c.chan<<3),2+((s->offSegaPCM>>16)<<3)); } else { - int loopPos=(s->offSegaPCM&0xffff)+s->loopStart+s->loopOffP; + int loopPos=(s->offSegaPCM&0xffff)+loopStart+s->loopOffP; addWrite(0x10004+(c.chan<<3),loopPos&0xff); addWrite(0x10005+(c.chan<<3),(loopPos>>8)&0xff); addWrite(0x10086+(c.chan<<3),((s->offSegaPCM>>16)<<3)); diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 6c6b0b32..a212c65f 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -220,7 +220,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) { DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note)); if (sample!=NULL) { - unsigned int sampleEnd=sample->offSU+(sample->getEndPosition()); + unsigned int sampleEnd=sample->offSU+(sample->getLoopEndPosition()); unsigned int off=sample->offSU+chan[i].hasOffset; chan[i].hasOffset=0; if (sampleEnd>=getSampleMemCapacity(0)) sampleEnd=getSampleMemCapacity(0)-1; @@ -229,7 +229,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) { chWrite(i,0x0c,sampleEnd&0xff); chWrite(i,0x0d,sampleEnd>>8); if (sample->isLoopable()) { - unsigned int sampleLoop=sample->offSU+sample->loopStart; + unsigned int sampleLoop=sample->offSU+sample->getLoopStartPosition(); if (sampleLoop>=getSampleMemCapacity(0)) sampleLoop=getSampleMemCapacity(0)-1; chWrite(i,0x0e,sampleLoop&0xff); chWrite(i,0x0f,sampleLoop>>8); @@ -603,7 +603,7 @@ void DivPlatformSoundUnit::renderSamples() { for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; if (s->data8==NULL) continue; - int paddedLen=s->samples; + int paddedLen=s->getEndPosition(); if (memPos>=getSampleMemCapacity(0)) { logW("out of PCM memory for sample %d!",i); break; diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index b6da2327..5110d7e1 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -78,14 +78,14 @@ void DivPlatformSwan::acquire(short* bufL, short* bufR, size_t start, size_t len dacPeriod+=dacRate; while (dacPeriod>rate) { DivSample* s=parent->getSample(dacSample); - if (s->samples<=0) { + if (s->getEndPosition()<=0) { dacSample=-1; continue; } rWrite(0x09,(unsigned char)s->data8[dacPos++]+0x80); - if (s->isLoopable() && dacPos>=s->getEndPosition()) { - dacPos=s->loopStart; - } else if (dacPos>=s->samples) { + if (s->isLoopable() && dacPos>=(unsigned int)s->getLoopEndPosition()) { + dacPos=s->getLoopStartPosition(); + } else if (dacPos>=(unsigned int)s->getEndPosition()) { dacSample=-1; } dacPeriod-=rate; diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 6376cc19..301091bf 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -70,7 +70,7 @@ void DivPlatformVERA::acquire(short* bufL, short* bufR, size_t start, size_t len size_t pos=start; DivSample* s=parent->getSample(chan[16].pcm.sample); while (len>0) { - if (s->samples>0) { + if (s->getEndPosition()>0) { while (pcm_is_fifo_almost_empty(pcm)) { short tmp_l=0; short tmp_r=0; @@ -96,9 +96,9 @@ void DivPlatformVERA::acquire(short* bufL, short* bufR, size_t start, size_t len rWritePCMData(tmp_r&0xff); } chan[16].pcm.pos++; - if (s->isLoopable() && chan[16].pcm.pos>=s->getEndPosition()) { - chan[16].pcm.pos=s->loopStart; - } else if (chan[16].pcm.pos>=s->samples) { + if (s->isLoopable() && chan[16].pcm.pos>=(unsigned int)s->getLoopEndPosition()) { + chan[16].pcm.pos=s->getLoopStartPosition(); + } else if (chan[16].pcm.pos>=(unsigned int)s->getEndPosition()) { chan[16].pcm.sample=-1; break; } diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 8a34d925..fa81afb9 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -66,7 +66,7 @@ void DivPlatformVRC6::acquire(short* bufL, short* bufR, size_t start, size_t len chan[i].dacPeriod+=chan[i].dacRate; if (chan[i].dacPeriod>rate) { DivSample* s=parent->getSample(chan[i].dacSample); - if (s->samples<=0) { + if (s->getEndPosition()<=0) { chan[i].dacSample=-1; chWrite(i,0,0); continue; @@ -77,9 +77,9 @@ void DivPlatformVRC6::acquire(short* bufL, short* bufR, size_t start, size_t len chWrite(i,0,0x80|chan[i].dacOut); } chan[i].dacPos++; - if (s->isLoopable() && chan[i].dacPos>=s->getEndPosition()) { - chan[i].dacPos=s->loopStart; - } else if (chan[i].dacPos>=s->samples) { + if (s->isLoopable() && chan[i].dacPos>=(unsigned int)s->getLoopEndPosition()) { + chan[i].dacPos=s->getLoopStartPosition(); + } else if (chan[i].dacPos>=(unsigned int)s->getEndPosition()) { chan[i].dacSample=-1; chWrite(i,0,0); } diff --git a/src/engine/platform/zxbeeper.cpp b/src/engine/platform/zxbeeper.cpp index 01702dc5..dbe21de6 100644 --- a/src/engine/platform/zxbeeper.cpp +++ b/src/engine/platform/zxbeeper.cpp @@ -46,9 +46,9 @@ void DivPlatformZXBeeper::acquire(short* bufL, short* bufR, size_t start, size_t if (curSample>=0 && curSamplesong.sampleLen) { if (--curSamplePeriod<0) { DivSample* s=parent->getSample(curSample); - if (s->samples>0) { + if (s->getEndPosition()>0) { sampleOut=(s->data8[curSamplePos++]>0); - if (curSamplePos>=s->samples) curSample=-1; + if (curSamplePos>=(unsigned int)s->getEndPosition()) curSample=-1; // 256 bits if (curSamplePos>2047) curSample=-1; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 3ba4f141..d3fa252d 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -760,6 +760,7 @@ void DivEngine::processRow(int i, bool afterDelay) { sPreview.sample=-1; sPreview.wave=-1; sPreview.pos=0; + sPreview.dir=false; break; } } @@ -1268,26 +1269,109 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi DivSample* s=song.sample[sPreview.sample]; for (size_t i=0; i=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++]; + samp_temp=s->data16[sPreview.pos]; + if (sPreview.dir) { + sPreview.pos--; + } + else { + sPreview.pos++; + } } blip_add_delta(samp_bb,i,samp_temp-samp_prevSample); samp_prevSample=samp_temp; - if (sPreview.pos>=s->getEndPosition() || (sPreview.pEnd>=0 && (int)sPreview.pos>=sPreview.pEnd)) { - if (s->isLoopable() && (int)sPreview.pos>=s->loopStart) { - sPreview.pos=s->loopStart; + if (sPreview.dir) { // backward + if (sPreview.posgetLoopStartPosition() || (sPreview.pBegin>=0 && sPreview.posisLoopable() && sPreview.posgetLoopEndPosition()) { + switch (s->loopMode) { + case DivSampleLoopMode::DIV_SAMPLE_LOOP_FORWARD: + sPreview.pos=s->getLoopStartPosition(); + sPreview.dir=false; + break; + case DivSampleLoopMode::DIV_SAMPLE_LOOP_BACKWARD: + sPreview.pos=s->getLoopEndPosition()-1; + sPreview.dir=true; + break; + case DivSampleLoopMode::DIV_SAMPLE_LOOP_PINGPONG: + sPreview.pos=s->getLoopStartPosition(); + sPreview.dir=false; + break; + default: + break; + } + } + } + } else { // forward + if (sPreview.pos>=s->getLoopEndPosition() || (sPreview.pEnd>=0 && sPreview.pos>=sPreview.pEnd)) { + if (s->isLoopable() && sPreview.pos>=s->getLoopStartPosition()) { + switch (s->loopMode) { + case DivSampleLoopMode::DIV_SAMPLE_LOOP_FORWARD: + sPreview.pos=s->getLoopStartPosition(); + sPreview.dir=false; + break; + case DivSampleLoopMode::DIV_SAMPLE_LOOP_BACKWARD: + sPreview.pos=s->getLoopEndPosition()-1; + sPreview.dir=true; + break; + case DivSampleLoopMode::DIV_SAMPLE_LOOP_PINGPONG: + sPreview.pos=s->getLoopEndPosition()-1; + sPreview.dir=true; + break; + default: + break; + } + } } } } - - if (sPreview.pos>=s->getEndPosition() || (sPreview.pEnd>=0 && (int)sPreview.pos>=sPreview.pEnd)) { - if (s->isLoopable() && (int)sPreview.pos>=s->loopStart) { - sPreview.pos=s->loopStart; - } else if (sPreview.pos>=s->samples) { - sPreview.sample=-1; + if (sPreview.dir) { // backward + if (sPreview.pos<=s->getLoopStartPosition() || (sPreview.pBegin>=0 && sPreview.pos<=sPreview.pBegin)) { + if (s->isLoopable() && sPreview.pos>=s->getLoopStartPosition()) { + switch (s->loopMode) { + case DivSampleLoopMode::DIV_SAMPLE_LOOP_FORWARD: + sPreview.pos=s->getLoopStartPosition(); + sPreview.dir=false; + break; + case DivSampleLoopMode::DIV_SAMPLE_LOOP_BACKWARD: + sPreview.pos=s->getLoopEndPosition()-1; + sPreview.dir=true; + break; + case DivSampleLoopMode::DIV_SAMPLE_LOOP_PINGPONG: + sPreview.pos=s->getLoopStartPosition(); + sPreview.dir=false; + break; + default: + break; + } + } else if (sPreview.pos<0) { + sPreview.sample=-1; + } + } + } else { // forward + if (sPreview.pos>=s->getLoopEndPosition() || (sPreview.pEnd>=0 && sPreview.pos>=sPreview.pEnd)) { + if (s->isLoopable() && sPreview.pos>=s->getLoopStartPosition()) { + switch (s->loopMode) { + case DivSampleLoopMode::DIV_SAMPLE_LOOP_FORWARD: + sPreview.pos=s->getLoopStartPosition(); + sPreview.dir=false; + break; + case DivSampleLoopMode::DIV_SAMPLE_LOOP_BACKWARD: + sPreview.pos=s->getLoopEndPosition()-1; + sPreview.dir=true; + break; + case DivSampleLoopMode::DIV_SAMPLE_LOOP_PINGPONG: + sPreview.pos=s->getLoopEndPosition()-1; + sPreview.dir=true; + break; + default: + break; + } + } else if (sPreview.pos>=s->getEndPosition()) { + sPreview.sample=-1; + } } } } else if (sPreview.wave>=0 && sPreview.wave<(int)song.wave.size()) { @@ -1298,7 +1382,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); diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index 60eb35bb..5697176c 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -39,57 +39,143 @@ DivSampleHistory::~DivSampleHistory() { } bool DivSample::isLoopable() { - return (loopStart>=0 && loopStartloopStart && loopEnd<=(int)samples); + return loop && ((loopStart>=0 && loopStartloopStart && loopEnd<=(int)samples)); } -unsigned int DivSample::getEndPosition(DivSampleDepth depth) { - int end=loopEnd; - unsigned int len=samples; +int DivSample::getSampleOffset(int offset, int length, DivSampleDepth depth) { + if ((length==0) || (offset==length)) { + int off=offset; + switch (depth) { + case DIV_SAMPLE_DEPTH_1BIT: + off=(offset+7)/8; + break; + case DIV_SAMPLE_DEPTH_1BIT_DPCM: + off=(offset+7)/8; + break; + case DIV_SAMPLE_DEPTH_YMZ_ADPCM: + off=(offset+1)/2; + break; + case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: + off=(offset+1)/2; + break; + case DIV_SAMPLE_DEPTH_ADPCM_A: + off=(offset+1)/2; + break; + case DIV_SAMPLE_DEPTH_ADPCM_B: + off=(offset+1)/2; + break; + case DIV_SAMPLE_DEPTH_8BIT: + off=offset; + break; + case DIV_SAMPLE_DEPTH_BRR: + off=9*((offset+15)/16); + break; + case DIV_SAMPLE_DEPTH_VOX: + off=(offset+1)/2; + break; + case DIV_SAMPLE_DEPTH_16BIT: + off=offset*2; + break; + default: + break; + } + return off; + } else { + int off=offset; + int len=length; + switch (depth) { + case DIV_SAMPLE_DEPTH_1BIT: + off=(offset+7)/8; + len=(length+7)/8; + break; + case DIV_SAMPLE_DEPTH_1BIT_DPCM: + off=(offset+7)/8; + len=(length+7)/8; + break; + case DIV_SAMPLE_DEPTH_YMZ_ADPCM: + off=(offset+1)/2; + len=(length+1)/2; + break; + case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: + off=(offset+1)/2; + len=(length+1)/2; + break; + case DIV_SAMPLE_DEPTH_ADPCM_A: + off=(offset+1)/2; + len=(length+1)/2; + break; + case DIV_SAMPLE_DEPTH_ADPCM_B: + off=(offset+1)/2; + len=(length+1)/2; + break; + case DIV_SAMPLE_DEPTH_8BIT: + off=offset; + len=length; + break; + case DIV_SAMPLE_DEPTH_BRR: + off=9*((offset+15)/16); + len=9*((length+15)/16); + break; + case DIV_SAMPLE_DEPTH_VOX: + off=(offset+1)/2; + len=(length+1)/2; + break; + case DIV_SAMPLE_DEPTH_16BIT: + off=offset*2; + len=length*2; + break; + default: + break; + } + return isLoopable()?off:len; + } +} + +int DivSample::getLoopStartPosition(DivSampleDepth depth) { + return getSampleOffset(loopStart,0,depth); +} + +int DivSample::getLoopEndPosition(DivSampleDepth depth) { + return getSampleOffset(loopEnd,samples,depth); +} + +int DivSample::getEndPosition(DivSampleDepth depth) { + int off=samples; switch (depth) { case DIV_SAMPLE_DEPTH_1BIT: - end=(loopEnd+7)/8; - len=length1; + off=length1; break; case DIV_SAMPLE_DEPTH_1BIT_DPCM: - end=(loopEnd+7)/8; - len=lengthDPCM; + off=lengthDPCM; break; case DIV_SAMPLE_DEPTH_YMZ_ADPCM: - end=(loopEnd+1)/2; - len=lengthZ; + off=lengthZ; break; case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: - end=(loopEnd+1)/2; - len=lengthQSoundA; + off=lengthQSoundA; break; case DIV_SAMPLE_DEPTH_ADPCM_A: - end=(loopEnd+1)/2; - len=lengthA; + off=lengthA; break; case DIV_SAMPLE_DEPTH_ADPCM_B: - end=(loopEnd+1)/2; - len=lengthB; + off=lengthB; break; case DIV_SAMPLE_DEPTH_8BIT: - end=loopEnd; - len=length8; + off=length8; break; case DIV_SAMPLE_DEPTH_BRR: - end=9*((loopEnd+15)/16); - len=lengthBRR; + off=lengthBRR; break; case DIV_SAMPLE_DEPTH_VOX: - end=(loopEnd+1)/2; - len=lengthVOX; + off=lengthVOX; break; case DIV_SAMPLE_DEPTH_16BIT: - end=loopEnd*2; - len=length16; + off=length16; break; default: break; } - return isLoopable()?end:len; + return off; } void DivSample::setSampleCount(unsigned int count) { @@ -138,7 +224,7 @@ bool DivSample::save(const char* path) { if(isLoopable()) { inst.loop_count = 1; - inst.loops[0].mode = SF_LOOP_FORWARD; + inst.loops[0].mode = (int)loopMode+SF_LOOP_FORWARD; inst.loops[0].start = loopStart; inst.loops[0].end = loopEnd; } @@ -895,9 +981,9 @@ DivSampleHistory* DivSample::prepareUndo(bool data, bool doNotPush) { duplicate=new unsigned char[getCurBufLen()]; memcpy(duplicate,getCurBuf(),getCurBufLen()); } - h=new DivSampleHistory(duplicate,getCurBufLen(),samples,depth,rate,centerRate,loopStart,loopEnd); + h=new DivSampleHistory(duplicate,getCurBufLen(),samples,depth,rate,centerRate,loopStart,loopEnd,loop,loopMode); } else { - h=new DivSampleHistory(depth,rate,centerRate,loopStart,loopEnd); + h=new DivSampleHistory(depth,rate,centerRate,loopStart,loopEnd,loop,loopMode); } if (!doNotPush) { while (!redoHist.empty()) { @@ -928,7 +1014,9 @@ DivSampleHistory* DivSample::prepareUndo(bool data, bool doNotPush) { rate=h->rate; \ centerRate=h->centerRate; \ loopStart=h->loopStart; \ - loopEnd=h->loopEnd; + loopEnd=h->loopEnd; \ + loop=h->loop; \ + loopMode=h->loopMode; int DivSample::undo() { diff --git a/src/engine/sample.h b/src/engine/sample.h index 103bcaa2..05c97ac8 100644 --- a/src/engine/sample.h +++ b/src/engine/sample.h @@ -25,6 +25,13 @@ #include "../ta-utils.h" #include +enum DivSampleLoopMode: unsigned char { + DIV_SAMPLE_LOOP_FORWARD=0, + DIV_SAMPLE_LOOP_BACKWARD, + DIV_SAMPLE_LOOP_PINGPONG, + DIV_SAMPLE_LOOP_MAX // boundary for loop mode +}; + enum DivSampleDepth: unsigned char { DIV_SAMPLE_DEPTH_1BIT=0, DIV_SAMPLE_DEPTH_1BIT_DPCM=1, @@ -53,8 +60,10 @@ struct DivSampleHistory { unsigned int length, samples; DivSampleDepth depth; int rate, centerRate, loopStart, loopEnd; + bool loop; + DivSampleLoopMode loopMode; bool hasSample; - DivSampleHistory(void* d, unsigned int l, unsigned int s, DivSampleDepth de, int r, int cr, int ls, int le): + DivSampleHistory(void* d, unsigned int l, unsigned int s, DivSampleDepth de, int r, int cr, int ls, int le, bool lp, DivSampleLoopMode lm): data((unsigned char*)d), length(l), samples(s), @@ -63,8 +72,10 @@ struct DivSampleHistory { centerRate(cr), loopStart(ls), loopEnd(le), + loop(lp), + loopMode(lm), hasSample(true) {} - DivSampleHistory(DivSampleDepth de, int r, int cr, int ls, int le): + DivSampleHistory(DivSampleDepth de, int r, int cr, int ls, int le, bool lp, DivSampleLoopMode lm): data(NULL), length(0), samples(0), @@ -73,6 +84,8 @@ struct DivSampleHistory { centerRate(cr), loopStart(ls), loopEnd(le), + loop(lp), + loopMode(lm), hasSample(false) {} ~DivSampleHistory(); }; @@ -92,6 +105,13 @@ struct DivSample { // - 10: VOX ADPCM // - 16: 16-bit PCM DivSampleDepth depth; + bool loop; + // valid values are: + // - 0: No loop + // - 1: Forward loop + // - 2: Backward loop + // - 3: Pingpong loop + DivSampleLoopMode loopMode; // these are the new data structures. signed char* data8; // 8 @@ -120,11 +140,29 @@ struct DivSample { */ bool isLoopable(); + /** + * get sample start position + * @return the samples start position. + */ + int getLoopStartPosition(DivSampleDepth depth=DIV_SAMPLE_DEPTH_MAX); + + /** + * get sample loop end position + * @return the samples loop end position. + */ + int getLoopEndPosition(DivSampleDepth depth=DIV_SAMPLE_DEPTH_MAX); + /** * get sample end position * @return the samples end position. */ - unsigned int getEndPosition(DivSampleDepth depth=DIV_SAMPLE_DEPTH_MAX); + int getEndPosition(DivSampleDepth depth=DIV_SAMPLE_DEPTH_MAX); + + /** + * get sample offset + * @return the sample offset. + */ + int getSampleOffset(int offset, int length, DivSampleDepth depth=DIV_SAMPLE_DEPTH_MAX); /** * @warning DO NOT USE - internal functions @@ -253,6 +291,8 @@ struct DivSample { loopEnd(-1), loopOffP(0), depth(DIV_SAMPLE_DEPTH_16BIT), + loop(false), + loopMode(DIV_SAMPLE_LOOP_FORWARD), data8(NULL), data16(NULL), data1(NULL), diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 15de0156..51948f27 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -511,7 +511,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write w->writeC(0x95); w->writeC(streamID); w->writeS(write.val); // sample number - w->writeC((sample->loopStart==0)|(sampleDir[streamID]?0x10:0)); // flags + w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0)|(sampleDir[streamID]?0x10:0)); // flags if (sample->isLoopable() && !sampleDir[streamID]) { loopTimer[streamID]=sample->length8; loopSample[streamID]=write.val; @@ -1549,7 +1549,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p size_t memPos=0; for (int i=0; igetEndPosition(DIV_SAMPLE_DEPTH_8BIT)+0xff)&(~0xff); + unsigned int alignedSize=(sample->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)+0xff)&(~0xff); if (alignedSize>65536) alignedSize=65536; if ((memPos&0xff0000)!=((memPos+alignedSize)&0xff0000)) { memPos=(memPos+0xffff)&0xff0000; @@ -1559,9 +1559,9 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p sample->offSegaPCM=memPos; unsigned int readPos=0; for (unsigned int j=0; j=sample->getEndPosition(DIV_SAMPLE_DEPTH_8BIT)) { + if (readPos>=sample->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)) { if (sample->isLoopable()) { - readPos=sample->loopStart; + readPos=sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT); pcmMem[memPos++]=((unsigned char)sample->data8[readPos]+0x80); } else { pcmMem[memPos++]=0x80; @@ -1572,7 +1572,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p readPos++; if (memPos>=16777216) break; } - sample->loopOffP=readPos-sample->loopStart; + sample->loopOffP=readPos-sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT); if (memPos>=16777216) break; } @@ -1897,12 +1897,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p if (loopSample[nextToTouch]loopStart<(int)sample->getEndPosition(DIV_SAMPLE_DEPTH_8BIT)) { + if (sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)) { w->writeC(0x93); w->writeC(nextToTouch); - w->writeI(sample->off8+sample->loopStart); + w->writeI(sample->off8+sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)); w->writeC(0x81); - w->writeI(sample->getEndPosition(DIV_SAMPLE_DEPTH_8BIT)-sample->loopStart); + w->writeI(sample->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)-sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)); } } loopSample[nextToTouch]=-1; diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 6ad2dddd..58dfac39 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -157,10 +157,11 @@ void FurnaceGUI::drawDebug() { ImGui::Text("loopStart: %d",sample->loopStart); ImGui::Text("loopEnd: %d", sample->loopEnd); ImGui::Text("loopOffP: %d",sample->loopOffP); - if (sampleDepths[sample->depth]!=NULL) { - ImGui::Text("depth: %d (%s)",(unsigned char)sample->depth,sampleDepths[sample->depth]); + ImGui::Text(sample->loop?"loop: Enabled":"loop: Disabled"); + if (sampleLoopModes[sample->loopMode]!=NULL) { + ImGui::Text("loopMode: %d (%s)",(unsigned char)sample->loopMode,sampleLoopModes[sample->loopMode]); } else { - ImGui::Text("depth: %d ()",(unsigned char)sample->depth); + ImGui::Text("loopMode: %d ()",(unsigned char)sample->loopMode); } ImGui::Text("length8: %d",sample->length8); diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index ca640d62..48780dcc 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -710,6 +710,8 @@ void FurnaceGUI::doAction(int what) { sample->name=prevSample->name; sample->loopStart=prevSample->loopStart; sample->loopEnd=prevSample->loopEnd; + sample->loop=prevSample->loop; + sample->loopMode=prevSample->loopMode; sample->depth=prevSample->depth; if (sample->init(prevSample->samples)) { if (prevSample->getCurBuf()!=NULL) { @@ -1264,6 +1266,7 @@ void FurnaceGUI::doAction(int what) { sample->loopStart=start; sample->loopEnd=end; + sample->loop=true; updateSampleTex=true; e->renderSamples(); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 59c917ae..da86ce86 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -116,6 +116,12 @@ const char* insTypes[DIV_INS_MAX+1]={ NULL }; +const char* sampleLoopModes[DIV_SAMPLE_LOOP_MAX]={ + "Forward", + "Backward", + "Ping pong" +}; + const char* sampleDepths[DIV_SAMPLE_DEPTH_MAX]={ "1-bit PCM", "1-bit DPCM", diff --git a/src/gui/guiConst.h b/src/gui/guiConst.h index a6df68f6..d6824efb 100644 --- a/src/gui/guiConst.h +++ b/src/gui/guiConst.h @@ -40,6 +40,7 @@ extern const char* noteNames[180]; extern const char* noteNamesG[180]; extern const char* pitchLabel[11]; extern const char* insTypes[]; +extern const char* sampleLoopModes[]; extern const char* sampleDepths[]; extern const char* resampleStrats[]; extern const int availableSystems[]; diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index e9b5cfb3..ff307210 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -46,6 +46,12 @@ void FurnaceGUI::drawSampleEdit() { sampleType=sampleDepths[sample->depth]; } } + String loopType="Invalid"; + if (sample->loopModeloopMode]!=NULL) { + loopType=sampleLoopModes[sample->loopMode]; + } + } if (!settings.sampleLayout) { ImGui::Text("Name"); ImGui::SameLine(); @@ -96,9 +102,11 @@ void FurnaceGUI::drawSampleEdit() { bool doLoop=(sample->isLoopable()); if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED if (doLoop) { + sample->loop=true; sample->loopStart=0; sample->loopEnd=sample->samples; } else { + sample->loop=false; sample->loopStart=-1; sample->loopEnd=sample->samples; } @@ -107,6 +115,23 @@ void FurnaceGUI::drawSampleEdit() { if (doLoop) { ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::Text("Loop Mode"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::BeginCombo("##SampleLoopMode",loopType.c_str())) { + for (int i=0; iprepareUndo(true); + sample->loopMode=(DivSampleLoopMode)i; + e->renderSamplesP(); + updateSampleTex=true; + MARK_MODIFIED; + } + } + ImGui::EndCombo(); + } + ImGui::TableNextColumn(); ImGui::Text("Loop Start"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -631,15 +656,35 @@ void FurnaceGUI::drawSampleEdit() { bool doLoop=(sample->isLoopable()); if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED if (doLoop) { + sample->loop=true; sample->loopStart=0; sample->loopEnd=sample->samples; } else { + sample->loop=false; sample->loopStart=-1; sample->loopEnd=sample->samples; } updateSampleTex=true; } if (doLoop) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Loop Mode"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::BeginCombo("##SampleLoopMode",loopType.c_str())) { + for (int i=0; iprepareUndo(true); + sample->loopMode=(DivSampleLoopMode)i; + e->renderSamplesP(); + updateSampleTex=true; + MARK_MODIFIED; + } + } + ImGui::EndCombo(); + } ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Loop Start");