Prepare for backward and bi-directional loop

This commit is contained in:
cam900 2022-08-11 22:21:54 +09:00
parent da8f7dabd5
commit d44f5f0b2b
27 changed files with 418 additions and 123 deletions

View File

@ -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; i<song.sampleLen; i++) {
@ -1686,6 +1687,7 @@ void DivEngine::play() {
sPreview.sample=-1;
sPreview.wave=-1;
sPreview.pos=0;
sPreview.dir=false;
if (stepPlay==0) {
freelance=false;
playSub(false);
@ -1708,6 +1710,7 @@ void DivEngine::playToRow(int row) {
sPreview.sample=-1;
sPreview.wave=-1;
sPreview.pos=0;
sPreview.dir=false;
freelance=false;
playSub(false,row);
for (int i=0; i<DIV_MAX_CHANS; i++) {
@ -1745,6 +1748,7 @@ void DivEngine::stop() {
sPreview.sample=-1;
sPreview.wave=-1;
sPreview.pos=0;
sPreview.dir=false;
for (int i=0; i<song.systemLen; i++) {
disCont[i].dispatch->notifyPlaybackStop();
}
@ -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];

View File

@ -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];

View File

@ -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);

View File

@ -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].audPos<s->samples) {
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 {

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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 && dacAntiClick<next) { \
@ -109,9 +109,9 @@ void DivPlatformNES::doWrite(unsigned short addr, unsigned char data) {
} \
} \
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; \

View File

@ -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;

View File

@ -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.audPos<s->samples) {
if (chan.audPos<(unsigned int)s->getEndPosition()) {
output=s->data16[chan.audPos];
}
} else {

View File

@ -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) {

View File

@ -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; i<parent->song.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;

View File

@ -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.sample<parent->song.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));

View File

@ -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; i<parent->song.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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}

View File

@ -46,9 +46,9 @@ void DivPlatformZXBeeper::acquire(short* bufL, short* bufR, size_t start, size_t
if (curSample>=0 && curSample<parent->song.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;

View File

@ -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<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++];
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.pos<s->getLoopStartPosition() || (sPreview.pBegin>=0 && sPreview.pos<sPreview.pBegin)) {
if (s->isLoopable() && sPreview.pos<s->getLoopEndPosition()) {
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);

View File

@ -39,57 +39,143 @@ DivSampleHistory::~DivSampleHistory() {
}
bool DivSample::isLoopable() {
return (loopStart>=0 && loopStart<loopEnd) && (loopEnd>loopStart && loopEnd<=(int)samples);
return loop && ((loopStart>=0 && loopStart<loopEnd) && (loopEnd>loopStart && 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() {

View File

@ -25,6 +25,13 @@
#include "../ta-utils.h"
#include <deque>
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),

View File

@ -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; i<song.sampleLen; i++) {
DivSample* sample=song.sample[i];
unsigned int alignedSize=(sample->getEndPosition(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<alignedSize; j++) {
if (readPos>=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]<song.sampleLen) {
DivSample* sample=song.sample[loopSample[nextToTouch]];
// insert loop
if (sample->loopStart<(int)sample->getEndPosition(DIV_SAMPLE_DEPTH_8BIT)) {
if (sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)<sample->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;

View File

@ -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 (<NULL!>)",(unsigned char)sample->depth);
ImGui::Text("loopMode: %d (<NULL!>)",(unsigned char)sample->loopMode);
}
ImGui::Text("length8: %d",sample->length8);

View File

@ -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();

View File

@ -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",

View File

@ -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[];

View File

@ -46,6 +46,12 @@ void FurnaceGUI::drawSampleEdit() {
sampleType=sampleDepths[sample->depth];
}
}
String loopType="Invalid";
if (sample->loopMode<DIV_SAMPLE_LOOP_MAX) {
if (sampleLoopModes[sample->loopMode]!=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; i<DIV_SAMPLE_LOOP_MAX; i++) {
if (sampleLoopModes[i]==NULL) continue;
if (ImGui::Selectable(sampleLoopModes[i])) {
sample->prepareUndo(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; i<DIV_SAMPLE_LOOP_MAX; i++) {
if (sampleLoopModes[i]==NULL) continue;
if (ImGui::Selectable(sampleLoopModes[i])) {
sample->prepareUndo(true);
sample->loopMode=(DivSampleLoopMode)i;
e->renderSamplesP();
updateSampleTex=true;
MARK_MODIFIED;
}
}
ImGui::EndCombo();
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Loop Start");