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() { void DivEngine::renderSamples() {
sPreview.sample=-1; sPreview.sample=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false;
// step 1: render samples // step 1: render samples
for (int i=0; i<song.sampleLen; i++) { for (int i=0; i<song.sampleLen; i++) {
@ -1686,6 +1687,7 @@ void DivEngine::play() {
sPreview.sample=-1; sPreview.sample=-1;
sPreview.wave=-1; sPreview.wave=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false;
if (stepPlay==0) { if (stepPlay==0) {
freelance=false; freelance=false;
playSub(false); playSub(false);
@ -1708,6 +1710,7 @@ void DivEngine::playToRow(int row) {
sPreview.sample=-1; sPreview.sample=-1;
sPreview.wave=-1; sPreview.wave=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false;
freelance=false; freelance=false;
playSub(false,row); playSub(false,row);
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
@ -1745,6 +1748,7 @@ void DivEngine::stop() {
sPreview.sample=-1; sPreview.sample=-1;
sPreview.wave=-1; sPreview.wave=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false;
for (int i=0; i<song.systemLen; i++) { for (int i=0; i<song.systemLen; i++) {
disCont[i].dispatch->notifyPlaybackStop(); disCont[i].dispatch->notifyPlaybackStop();
} }
@ -1914,9 +1918,11 @@ void DivEngine::previewSample(int sample, int note, int pStart, int pEnd) {
BUSY_BEGIN; BUSY_BEGIN;
sPreview.pBegin=pStart; sPreview.pBegin=pStart;
sPreview.pEnd=pEnd; sPreview.pEnd=pEnd;
sPreview.dir=false;
if (sample<0 || sample>=(int)song.sample.size()) { if (sample<0 || sample>=(int)song.sample.size()) {
sPreview.sample=-1; sPreview.sample=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false;
BUSY_END; BUSY_END;
return; 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.pos=(sPreview.pBegin>=0)?sPreview.pBegin:0;
sPreview.sample=sample; sPreview.sample=sample;
sPreview.wave=-1; sPreview.wave=-1;
sPreview.dir=false;
BUSY_END; BUSY_END;
} }
@ -1939,6 +1946,7 @@ void DivEngine::stopSamplePreview() {
BUSY_BEGIN; BUSY_BEGIN;
sPreview.sample=-1; sPreview.sample=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false;
BUSY_END; BUSY_END;
} }
@ -1947,6 +1955,7 @@ void DivEngine::previewWave(int wave, int note) {
if (wave<0 || wave>=(int)song.wave.size()) { if (wave<0 || wave>=(int)song.wave.size()) {
sPreview.wave=-1; sPreview.wave=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false;
BUSY_END; BUSY_END;
return; return;
} }
@ -1962,6 +1971,7 @@ void DivEngine::previewWave(int wave, int note) {
sPreview.pos=0; sPreview.pos=0;
sPreview.sample=-1; sPreview.sample=-1;
sPreview.wave=wave; sPreview.wave=wave;
sPreview.dir=false;
BUSY_END; BUSY_END;
} }
@ -1969,6 +1979,7 @@ void DivEngine::stopWavePreview() {
BUSY_BEGIN; BUSY_BEGIN;
sPreview.wave=-1; sPreview.wave=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false;
BUSY_END; BUSY_END;
} }
@ -2382,6 +2393,7 @@ int DivEngine::addSample() {
song.sampleLen=sampleCount+1; song.sampleLen=sampleCount+1;
sPreview.sample=-1; sPreview.sample=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false;
saveLock.unlock(); saveLock.unlock();
renderSamples(); renderSamples();
BUSY_END; BUSY_END;
@ -2601,13 +2613,16 @@ int DivEngine::addSampleFromFile(const char* path) {
inst.detune = inst.detune - 100; inst.detune = inst.detune - 100;
short pitch = ((0x3c-inst.basenote)*100) + inst.detune; short pitch = ((0x3c-inst.basenote)*100) + inst.detune;
sample->centerRate=si.samplerate*pow(2.0,pitch/(12.0 * 100.0)); 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->loopStart=inst.loops[0].start;
sample->loopEnd=inst.loops[0].end; sample->loopEnd=inst.loops[0].end;
if(inst.loops[0].end < (unsigned int)sampleCount) if(inst.loops[0].end < (unsigned int)sampleCount)
sampleCount=inst.loops[0].end; sampleCount=inst.loops[0].end;
} }
else
sample->loop=false;
} }
if (sample->centerRate<4000) sample->centerRate=4000; if (sample->centerRate<4000) sample->centerRate=4000;
@ -2627,6 +2642,7 @@ void DivEngine::delSample(int index) {
BUSY_BEGIN; BUSY_BEGIN;
sPreview.sample=-1; sPreview.sample=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false;
saveLock.lock(); saveLock.lock();
if (index>=0 && index<(int)song.sample.size()) { if (index>=0 && index<(int)song.sample.size()) {
delete song.sample[index]; delete song.sample[index];
@ -2843,6 +2859,7 @@ bool DivEngine::moveSampleUp(int which) {
BUSY_BEGIN; BUSY_BEGIN;
sPreview.sample=-1; sPreview.sample=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false;
DivSample* prev=song.sample[which]; DivSample* prev=song.sample[which];
saveLock.lock(); saveLock.lock();
song.sample[which]=song.sample[which-1]; song.sample[which]=song.sample[which-1];
@ -2882,6 +2899,7 @@ bool DivEngine::moveSampleDown(int which) {
BUSY_BEGIN; BUSY_BEGIN;
sPreview.sample=-1; sPreview.sample=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false;
DivSample* prev=song.sample[which]; DivSample* prev=song.sample[which];
saveLock.lock(); saveLock.lock();
song.sample[which]=song.sample[which+1]; song.sample[which]=song.sample[which+1];

View file

@ -353,14 +353,16 @@ class DivEngine {
struct SamplePreview { struct SamplePreview {
int sample; int sample;
int wave; int wave;
unsigned int pos; int pos;
int pBegin, pEnd; int pBegin, pEnd;
bool dir;
SamplePreview(): SamplePreview():
sample(-1), sample(-1),
wave(-1), wave(-1),
pos(0), pos(0),
pBegin(-1), pBegin(-1),
pEnd(-1) {} pEnd(-1),
dir(false) {}
} sPreview; } sPreview;
short vibTable[64]; short vibTable[64];

View file

@ -1669,6 +1669,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
sample->loopStart=reader.readI(); sample->loopStart=reader.readI();
sample->loopEnd=reader.readI(); sample->loopEnd=reader.readI();
sample->loop=(sample->loopStart>=0)&&(sample->loopEnd>=0);
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
reader.readI(); reader.readI();
@ -1694,6 +1695,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version>=19) { if (ds.version>=19) {
sample->loopStart=reader.readI(); sample->loopStart=reader.readI();
sample->loop=(sample->loopStart>=0)&&(sample->loopEnd>=0);
} else { } else {
reader.readI(); reader.readI();
} }
@ -1948,6 +1950,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
if (loopLen>=2) { if (loopLen>=2) {
sample->loopStart=loopStart; sample->loopStart=loopStart;
sample->loopEnd=loopEnd; sample->loopEnd=loopEnd;
sample->loop=(sample->loopStart>=0)&&(sample->loopEnd>=0);
} }
sample->init(slen); sample->init(slen);
ds.sample.push_back(sample); 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->loopStart=sample[i].loopStart*2;
s->loopEnd=(sample[i].loopStart+sample[i].loopLen)*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); reader.read(s->data8,sample[i].len);
ds.sample.push_back(s); ds.sample.push_back(s);
} }
@ -3586,8 +3590,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeC(0); // reserved w->writeC(0); // reserved
w->writeC(0); w->writeC(0);
w->writeC(0); w->writeC(0);
w->writeI(sample->loopStart); w->writeI(sample->loop?sample->loopStart:-1);
w->writeI(sample->loopEnd); w->writeI(sample->loop?sample->loopEnd:-1);
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
w->writeI(0xffffffff); w->writeI(0xffffffff);

View file

@ -110,13 +110,13 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le
} }
} else { } else {
DivSample* s=parent->getSample(chan[i].sample); DivSample* s=parent->getSample(chan[i].sample);
if (s->samples>0) { if (s->getEndPosition()>0) {
if (chan[i].audPos<s->samples) { if (chan[i].audPos<(unsigned int)s->getEndPosition()) {
writeAudDat(s->data8[chan[i].audPos++]); writeAudDat(s->data8[chan[i].audPos++]);
} }
if (s->isLoopable() && chan[i].audPos>=MIN(131071,s->getEndPosition())) { if (s->isLoopable() && chan[i].audPos>=MIN(131071,(unsigned int)s->getLoopEndPosition())) {
chan[i].audPos=s->loopStart; chan[i].audPos=s->getLoopStartPosition();
} else if (chan[i].audPos>=MIN(131071,s->samples)) { } else if (chan[i].audPos>=MIN(131071,(unsigned int)s->getEndPosition())) {
chan[i].sample=-1; chan[i].sample=-1;
} }
} else { } else {

View file

@ -154,9 +154,9 @@ void DivPlatformGenesis::processDAC() {
if (s->samples>0) { if (s->samples>0) {
while (chan[i].dacPeriod>=(chipClock/576)) { while (chan[i].dacPeriod>=(chipClock/576)) {
++chan[i].dacPos; ++chan[i].dacPos;
if (!chan[i].dacDirection && (s->isLoopable() && chan[i].dacPos>=s->getEndPosition())) { if (!chan[i].dacDirection && (s->isLoopable() && chan[i].dacPos>=(unsigned int)s->getLoopEndPosition())) {
chan[i].dacPos=s->loopStart; chan[i].dacPos=s->getLoopStartPosition();
} else if (chan[i].dacPos>=s->samples) { } else if (chan[i].dacPos>=(unsigned int)s->getEndPosition()) {
chan[i].dacSample=-1; chan[i].dacSample=-1;
chan[i].dacPeriod=0; chan[i].dacPeriod=0;
break; break;
@ -200,9 +200,9 @@ void DivPlatformGenesis::processDAC() {
} }
} }
chan[5].dacPos++; chan[5].dacPos++;
if (!chan[5].dacDirection && (s->isLoopable() && chan[5].dacPos>=s->getEndPosition())) { if (!chan[5].dacDirection && (s->isLoopable() && chan[5].dacPos>=(unsigned int)s->getLoopEndPosition())) {
chan[5].dacPos=s->loopStart; chan[5].dacPos=s->getLoopStartPosition();
} else if (chan[5].dacPos>=s->samples) { } else if (chan[5].dacPos>=(unsigned int)s->getEndPosition()) {
chan[5].dacSample=-1; chan[5].dacSample=-1;
if (parent->song.brokenDACMode) { if (parent->song.brokenDACMode) {
rWrite(0x2b,0); 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); WRITE_OUTPUT(i,(s->data8[chan[i].samplePos++]*chan[i].outVol)>>7);
} }
if (s->isLoopable() && chan[i].samplePos>=(int)s->getEndPosition()) { if (s->isLoopable() && chan[i].samplePos>=s->getLoopEndPosition()) {
chan[i].samplePos=s->loopStart; chan[i].samplePos=s->getLoopStartPosition();
} else if (chan[i].samplePos>=(int)s->samples) { } else if (chan[i].samplePos>=s->getEndPosition()) {
chan[i].sample=-1; 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; dacPeriod+=dacRate;
if (dacPeriod>=rate) { if (dacPeriod>=rate) {
DivSample* s=parent->getSample(dacSample); DivSample* s=parent->getSample(dacSample);
if (s->samples>0) { if (s->getEndPosition()>0) {
if (!isMuted[2]) { if (!isMuted[2]) {
rWrite(0x5011,((unsigned char)s->data8[dacPos]+0x80)); rWrite(0x5011,((unsigned char)s->data8[dacPos]+0x80));
} }
dacPos++; dacPos++;
if (s->isLoopable() && dacPos>=s->getEndPosition()) { if (s->isLoopable() && dacPos>=(unsigned int)s->getLoopEndPosition()) {
dacPos=s->loopStart; dacPos=s->getLoopStartPosition();
} else if (dacPos>=s->samples) { } else if (dacPos>=(unsigned int)s->getEndPosition()) {
dacSample=-1; dacSample=-1;
} }
dacPeriod-=rate; dacPeriod-=rate;

View file

@ -97,7 +97,7 @@ void DivPlatformNES::doWrite(unsigned short addr, unsigned char data) {
dacPeriod+=dacRate; \ dacPeriod+=dacRate; \
if (dacPeriod>=rate) { \ if (dacPeriod>=rate) { \
DivSample* s=parent->getSample(dacSample); \ DivSample* s=parent->getSample(dacSample); \
if (s->samples>0) { \ if (s->getEndPosition()>0) { \
if (!isMuted[4]) { \ if (!isMuted[4]) { \
unsigned char next=((unsigned char)s->data8[dacPos]+0x80)>>1; \ unsigned char next=((unsigned char)s->data8[dacPos]+0x80)>>1; \
if (dacAntiClickOn && dacAntiClick<next) { \ if (dacAntiClickOn && dacAntiClick<next) { \
@ -109,9 +109,9 @@ void DivPlatformNES::doWrite(unsigned short addr, unsigned char data) {
} \ } \
} \ } \
dacPos++; \ dacPos++; \
if (s->isLoopable() && dacPos>=s->getEndPosition()) { \ if (s->isLoopable() && dacPos>=(unsigned int)s->getLoopEndPosition()) { \
dacPos=s->loopStart; \ dacPos=s->getLoopStartPosition(); \
} else if (dacPos>=s->samples) { \ } else if (dacPos>=(unsigned int)s->getEndPosition()) { \
dacSample=-1; \ dacSample=-1; \
} \ } \
dacPeriod-=rate; \ 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; chan[i].dacPeriod+=chan[i].dacRate;
if (chan[i].dacPeriod>rate) { if (chan[i].dacPeriod>rate) {
DivSample* s=parent->getSample(chan[i].dacSample); DivSample* s=parent->getSample(chan[i].dacSample);
if (s->samples<=0) { if (s->getEndPosition()<=0) {
chan[i].dacSample=-1; chan[i].dacSample=-1;
continue; continue;
} }
@ -90,9 +90,9 @@ void DivPlatformPCE::acquire(short* bufL, short* bufR, size_t start, size_t len)
chWrite(i,0x04,0xdf); chWrite(i,0x04,0xdf);
chWrite(i,0x06,(((unsigned char)s->data8[chan[i].dacPos]+0x80)>>3)); chWrite(i,0x06,(((unsigned char)s->data8[chan[i].dacPos]+0x80)>>3));
chan[i].dacPos++; chan[i].dacPos++;
if (s->isLoopable() && chan[i].dacPos>=s->getEndPosition()) { if (s->isLoopable() && chan[i].dacPos>=(unsigned int)s->getLoopEndPosition()) {
chan[i].dacPos=s->loopStart; chan[i].dacPos=s->getLoopStartPosition();
} else if (chan[i].dacPos>=s->samples) { } else if (chan[i].dacPos>=(unsigned int)s->getEndPosition()) {
chan[i].dacSample=-1; chan[i].dacSample=-1;
} }
chan[i].dacPeriod-=rate; 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; output=(chan.ws.output[chan.audPos]^0x80)<<8;
} else { } else {
DivSample* s=parent->getSample(chan.sample); DivSample* s=parent->getSample(chan.sample);
if (s->samples>0) { if (s->getEndPosition()>0) {
if (s->isLoopable() && chan.audPos>=s->getEndPosition()) { if (s->isLoopable() && chan.audPos>=(unsigned int)s->getLoopEndPosition()) {
chan.audPos=s->loopStart; chan.audPos=s->getLoopStartPosition();
} else if (chan.audPos>=s->samples) { } else if (chan.audPos>=(unsigned int)s->getEndPosition()) {
chan.sample=-1; chan.sample=-1;
} }
if (chan.audPos<s->samples) { if (chan.audPos<(unsigned int)s->getEndPosition()) {
output=s->data16[chan.audPos]; output=s->data16[chan.audPos];
} }
} else { } else {

View file

@ -301,16 +301,17 @@ void DivPlatformQSound::tick(bool sysTick) {
qsound_bank = 0x8000 | (s->offQSound >> 16); qsound_bank = 0x8000 | (s->offQSound >> 16);
qsound_addr = s->offQSound & 0xffff; qsound_addr = s->offQSound & 0xffff;
int length = s->getEndPosition(); int loopStart=s->getLoopStartPosition();
int length = s->getLoopEndPosition();
if (length > 65536 - 16) { if (length > 65536 - 16) {
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_end = s->offQSound + length + 15;
qsound_loop = 15; qsound_loop = 15;
} else { } else {
qsound_end = s->offQSound + length; qsound_end = s->offQSound + length;
qsound_loop = length - s->loopStart; qsound_loop = length - loopStart;
} }
} }
if (chan[i].std.arp.had) { 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); start=start+MIN(chan[i].audPos,s->length8);
} }
if (s->isLoopable()) { if (s->isLoopable()) {
loop=start+s->loopStart; loop=start+s->getLoopStartPosition();
} }
start=MIN(start,getSampleMemCapacity()-31); start=MIN(start,getSampleMemCapacity()-31);
loop=MIN(loop,getSampleMemCapacity()-31); loop=MIN(loop,getSampleMemCapacity()-31);
@ -393,7 +393,7 @@ void DivPlatformRF5C68::renderSamples() {
size_t memPos=0; size_t memPos=0;
for (int i=0; i<parent->song.sampleLen; i++) { for (int i=0; i<parent->song.sampleLen; i++) {
DivSample* s=parent->song.sample[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); int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-31,length);
if (actualLength>0) { if (actualLength>0) {
s->offRF5C68=memPos; 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++) { for (int i=0; i<16; i++) {
if (chan[i].pcm.sample>=0 && chan[i].pcm.sample<parent->song.sampleLen) { if (chan[i].pcm.sample>=0 && chan[i].pcm.sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[i].pcm.sample); DivSample* s=parent->getSample(chan[i].pcm.sample);
if (s->samples<=0) { if (s->getEndPosition()<=0) {
chan[i].pcm.sample=-1; chan[i].pcm.sample=-1;
oscBuf[i]->data[oscBuf[i]->needle++]=0; oscBuf[i]->data[oscBuf[i]->needle++]=0;
continue; 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); pcmR+=(s->data8[chan[i].pcm.pos>>8]*chan[i].chVolR);
} }
chan[i].pcm.pos+=chan[i].pcm.freq; chan[i].pcm.pos+=chan[i].pcm.freq;
if (s->isLoopable() && chan[i].pcm.pos>=(s->getEndPosition()<<8)) { if (s->isLoopable() && chan[i].pcm.pos>=((unsigned int)s->getLoopEndPosition()<<8)) {
chan[i].pcm.pos=s->loopStart<<8; chan[i].pcm.pos=s->getLoopStartPosition()<<8;
} else if (chan[i].pcm.pos>=(s->samples<<8)) { } else if (chan[i].pcm.pos>=((unsigned int)s->getEndPosition()<<8)) {
chan[i].pcm.sample=-1; chan[i].pcm.sample=-1;
} }
} else { } else {
@ -200,16 +200,17 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
chan[c.chan].macroInit(ins); chan[c.chan].macroInit(ins);
if (dumpWrites) { // Sega PCM writes if (dumpWrites) { // Sega PCM writes
DivSample* s=parent->getSample(chan[c.chan].pcm.sample); 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; if (actualLength>0xfeff) actualLength=0xfeff;
addWrite(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3)); addWrite(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3));
addWrite(0x10084+(c.chan<<3),(s->offSegaPCM)&0xff); addWrite(0x10084+(c.chan<<3),(s->offSegaPCM)&0xff);
addWrite(0x10085+(c.chan<<3),(s->offSegaPCM>>8)&0xff); addWrite(0x10085+(c.chan<<3),(s->offSegaPCM>>8)&0xff);
addWrite(0x10006+(c.chan<<3),MIN(255,((s->offSegaPCM&0xffff)+actualLength-1)>>8)); 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)); addWrite(0x10086+(c.chan<<3),2+((s->offSegaPCM>>16)<<3));
} else { } 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(0x10004+(c.chan<<3),loopPos&0xff);
addWrite(0x10005+(c.chan<<3),(loopPos>>8)&0xff); addWrite(0x10005+(c.chan<<3),(loopPos>>8)&0xff);
addWrite(0x10086+(c.chan<<3),((s->offSegaPCM>>16)<<3)); addWrite(0x10086+(c.chan<<3),((s->offSegaPCM>>16)<<3));
@ -233,16 +234,17 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
chan[c.chan].furnacePCM=false; chan[c.chan].furnacePCM=false;
if (dumpWrites) { // Sega PCM writes if (dumpWrites) { // Sega PCM writes
DivSample* s=parent->getSample(chan[c.chan].pcm.sample); 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; if (actualLength>65536) actualLength=65536;
addWrite(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3)); addWrite(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3));
addWrite(0x10084+(c.chan<<3),(s->offSegaPCM)&0xff); addWrite(0x10084+(c.chan<<3),(s->offSegaPCM)&0xff);
addWrite(0x10085+(c.chan<<3),(s->offSegaPCM>>8)&0xff); addWrite(0x10085+(c.chan<<3),(s->offSegaPCM>>8)&0xff);
addWrite(0x10006+(c.chan<<3),MIN(255,((s->offSegaPCM&0xffff)+actualLength-1)>>8)); 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)); addWrite(0x10086+(c.chan<<3),2+((s->offSegaPCM>>16)<<3));
} else { } 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(0x10004+(c.chan<<3),loopPos&0xff);
addWrite(0x10005+(c.chan<<3),(loopPos>>8)&0xff); addWrite(0x10005+(c.chan<<3),(loopPos>>8)&0xff);
addWrite(0x10086+(c.chan<<3),((s->offSegaPCM>>16)<<3)); 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); DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note)); DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note));
if (sample!=NULL) { 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; unsigned int off=sample->offSU+chan[i].hasOffset;
chan[i].hasOffset=0; chan[i].hasOffset=0;
if (sampleEnd>=getSampleMemCapacity(0)) sampleEnd=getSampleMemCapacity(0)-1; if (sampleEnd>=getSampleMemCapacity(0)) sampleEnd=getSampleMemCapacity(0)-1;
@ -229,7 +229,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
chWrite(i,0x0c,sampleEnd&0xff); chWrite(i,0x0c,sampleEnd&0xff);
chWrite(i,0x0d,sampleEnd>>8); chWrite(i,0x0d,sampleEnd>>8);
if (sample->isLoopable()) { 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; if (sampleLoop>=getSampleMemCapacity(0)) sampleLoop=getSampleMemCapacity(0)-1;
chWrite(i,0x0e,sampleLoop&0xff); chWrite(i,0x0e,sampleLoop&0xff);
chWrite(i,0x0f,sampleLoop>>8); chWrite(i,0x0f,sampleLoop>>8);
@ -603,7 +603,7 @@ void DivPlatformSoundUnit::renderSamples() {
for (int i=0; i<parent->song.sampleLen; i++) { for (int i=0; i<parent->song.sampleLen; i++) {
DivSample* s=parent->song.sample[i]; DivSample* s=parent->song.sample[i];
if (s->data8==NULL) continue; if (s->data8==NULL) continue;
int paddedLen=s->samples; int paddedLen=s->getEndPosition();
if (memPos>=getSampleMemCapacity(0)) { if (memPos>=getSampleMemCapacity(0)) {
logW("out of PCM memory for sample %d!",i); logW("out of PCM memory for sample %d!",i);
break; break;

View file

@ -78,14 +78,14 @@ void DivPlatformSwan::acquire(short* bufL, short* bufR, size_t start, size_t len
dacPeriod+=dacRate; dacPeriod+=dacRate;
while (dacPeriod>rate) { while (dacPeriod>rate) {
DivSample* s=parent->getSample(dacSample); DivSample* s=parent->getSample(dacSample);
if (s->samples<=0) { if (s->getEndPosition()<=0) {
dacSample=-1; dacSample=-1;
continue; continue;
} }
rWrite(0x09,(unsigned char)s->data8[dacPos++]+0x80); rWrite(0x09,(unsigned char)s->data8[dacPos++]+0x80);
if (s->isLoopable() && dacPos>=s->getEndPosition()) { if (s->isLoopable() && dacPos>=(unsigned int)s->getLoopEndPosition()) {
dacPos=s->loopStart; dacPos=s->getLoopStartPosition();
} else if (dacPos>=s->samples) { } else if (dacPos>=(unsigned int)s->getEndPosition()) {
dacSample=-1; dacSample=-1;
} }
dacPeriod-=rate; 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; size_t pos=start;
DivSample* s=parent->getSample(chan[16].pcm.sample); DivSample* s=parent->getSample(chan[16].pcm.sample);
while (len>0) { while (len>0) {
if (s->samples>0) { if (s->getEndPosition()>0) {
while (pcm_is_fifo_almost_empty(pcm)) { while (pcm_is_fifo_almost_empty(pcm)) {
short tmp_l=0; short tmp_l=0;
short tmp_r=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); rWritePCMData(tmp_r&0xff);
} }
chan[16].pcm.pos++; chan[16].pcm.pos++;
if (s->isLoopable() && chan[16].pcm.pos>=s->getEndPosition()) { if (s->isLoopable() && chan[16].pcm.pos>=(unsigned int)s->getLoopEndPosition()) {
chan[16].pcm.pos=s->loopStart; chan[16].pcm.pos=s->getLoopStartPosition();
} else if (chan[16].pcm.pos>=s->samples) { } else if (chan[16].pcm.pos>=(unsigned int)s->getEndPosition()) {
chan[16].pcm.sample=-1; chan[16].pcm.sample=-1;
break; 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; chan[i].dacPeriod+=chan[i].dacRate;
if (chan[i].dacPeriod>rate) { if (chan[i].dacPeriod>rate) {
DivSample* s=parent->getSample(chan[i].dacSample); DivSample* s=parent->getSample(chan[i].dacSample);
if (s->samples<=0) { if (s->getEndPosition()<=0) {
chan[i].dacSample=-1; chan[i].dacSample=-1;
chWrite(i,0,0); chWrite(i,0,0);
continue; 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); chWrite(i,0,0x80|chan[i].dacOut);
} }
chan[i].dacPos++; chan[i].dacPos++;
if (s->isLoopable() && chan[i].dacPos>=s->getEndPosition()) { if (s->isLoopable() && chan[i].dacPos>=(unsigned int)s->getLoopEndPosition()) {
chan[i].dacPos=s->loopStart; chan[i].dacPos=s->getLoopStartPosition();
} else if (chan[i].dacPos>=s->samples) { } else if (chan[i].dacPos>=(unsigned int)s->getEndPosition()) {
chan[i].dacSample=-1; chan[i].dacSample=-1;
chWrite(i,0,0); 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 (curSample>=0 && curSample<parent->song.sampleLen) {
if (--curSamplePeriod<0) { if (--curSamplePeriod<0) {
DivSample* s=parent->getSample(curSample); DivSample* s=parent->getSample(curSample);
if (s->samples>0) { if (s->getEndPosition()>0) {
sampleOut=(s->data8[curSamplePos++]>0); sampleOut=(s->data8[curSamplePos++]>0);
if (curSamplePos>=s->samples) curSample=-1; if (curSamplePos>=(unsigned int)s->getEndPosition()) curSample=-1;
// 256 bits // 256 bits
if (curSamplePos>2047) curSample=-1; if (curSamplePos>2047) curSample=-1;

View file

@ -760,6 +760,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
sPreview.sample=-1; sPreview.sample=-1;
sPreview.wave=-1; sPreview.wave=-1;
sPreview.pos=0; sPreview.pos=0;
sPreview.dir=false;
break; break;
} }
} }
@ -1268,26 +1269,109 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
DivSample* s=song.sample[sPreview.sample]; DivSample* s=song.sample[sPreview.sample];
for (size_t i=0; i<prevtotal; i++) { 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; samp_temp=0;
} else { } 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); blip_add_delta(samp_bb,i,samp_temp-samp_prevSample);
samp_prevSample=samp_temp; samp_prevSample=samp_temp;
if (sPreview.pos>=s->getEndPosition() || (sPreview.pEnd>=0 && (int)sPreview.pos>=sPreview.pEnd)) { if (sPreview.dir) { // backward
if (s->isLoopable() && (int)sPreview.pos>=s->loopStart) { if (sPreview.pos<s->getLoopStartPosition() || (sPreview.pBegin>=0 && sPreview.pos<sPreview.pBegin)) {
sPreview.pos=s->loopStart; 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.dir) { // backward
if (sPreview.pos>=s->getEndPosition() || (sPreview.pEnd>=0 && (int)sPreview.pos>=sPreview.pEnd)) { if (sPreview.pos<=s->getLoopStartPosition() || (sPreview.pBegin>=0 && sPreview.pos<=sPreview.pBegin)) {
if (s->isLoopable() && (int)sPreview.pos>=s->loopStart) { if (s->isLoopable() && sPreview.pos>=s->getLoopStartPosition()) {
sPreview.pos=s->loopStart; switch (s->loopMode) {
} else if (sPreview.pos>=s->samples) { case DivSampleLoopMode::DIV_SAMPLE_LOOP_FORWARD:
sPreview.sample=-1; 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()) { } 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 { } else {
samp_temp=((MIN(wave->data[sPreview.pos],wave->max)<<14)/wave->max)-8192; 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; sPreview.pos=0;
} }
blip_add_delta(samp_bb,i,samp_temp-samp_prevSample); blip_add_delta(samp_bb,i,samp_temp-samp_prevSample);

View file

@ -39,57 +39,143 @@ DivSampleHistory::~DivSampleHistory() {
} }
bool DivSample::isLoopable() { 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 DivSample::getSampleOffset(int offset, int length, DivSampleDepth depth) {
int end=loopEnd; if ((length==0) || (offset==length)) {
unsigned int len=samples; 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) { switch (depth) {
case DIV_SAMPLE_DEPTH_1BIT: case DIV_SAMPLE_DEPTH_1BIT:
end=(loopEnd+7)/8; off=length1;
len=length1;
break; break;
case DIV_SAMPLE_DEPTH_1BIT_DPCM: case DIV_SAMPLE_DEPTH_1BIT_DPCM:
end=(loopEnd+7)/8; off=lengthDPCM;
len=lengthDPCM;
break; break;
case DIV_SAMPLE_DEPTH_YMZ_ADPCM: case DIV_SAMPLE_DEPTH_YMZ_ADPCM:
end=(loopEnd+1)/2; off=lengthZ;
len=lengthZ;
break; break;
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: case DIV_SAMPLE_DEPTH_QSOUND_ADPCM:
end=(loopEnd+1)/2; off=lengthQSoundA;
len=lengthQSoundA;
break; break;
case DIV_SAMPLE_DEPTH_ADPCM_A: case DIV_SAMPLE_DEPTH_ADPCM_A:
end=(loopEnd+1)/2; off=lengthA;
len=lengthA;
break; break;
case DIV_SAMPLE_DEPTH_ADPCM_B: case DIV_SAMPLE_DEPTH_ADPCM_B:
end=(loopEnd+1)/2; off=lengthB;
len=lengthB;
break; break;
case DIV_SAMPLE_DEPTH_8BIT: case DIV_SAMPLE_DEPTH_8BIT:
end=loopEnd; off=length8;
len=length8;
break; break;
case DIV_SAMPLE_DEPTH_BRR: case DIV_SAMPLE_DEPTH_BRR:
end=9*((loopEnd+15)/16); off=lengthBRR;
len=lengthBRR;
break; break;
case DIV_SAMPLE_DEPTH_VOX: case DIV_SAMPLE_DEPTH_VOX:
end=(loopEnd+1)/2; off=lengthVOX;
len=lengthVOX;
break; break;
case DIV_SAMPLE_DEPTH_16BIT: case DIV_SAMPLE_DEPTH_16BIT:
end=loopEnd*2; off=length16;
len=length16;
break; break;
default: default:
break; break;
} }
return isLoopable()?end:len; return off;
} }
void DivSample::setSampleCount(unsigned int count) { void DivSample::setSampleCount(unsigned int count) {
@ -138,7 +224,7 @@ bool DivSample::save(const char* path) {
if(isLoopable()) if(isLoopable())
{ {
inst.loop_count = 1; 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].start = loopStart;
inst.loops[0].end = loopEnd; inst.loops[0].end = loopEnd;
} }
@ -895,9 +981,9 @@ DivSampleHistory* DivSample::prepareUndo(bool data, bool doNotPush) {
duplicate=new unsigned char[getCurBufLen()]; duplicate=new unsigned char[getCurBufLen()];
memcpy(duplicate,getCurBuf(),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 { } else {
h=new DivSampleHistory(depth,rate,centerRate,loopStart,loopEnd); h=new DivSampleHistory(depth,rate,centerRate,loopStart,loopEnd,loop,loopMode);
} }
if (!doNotPush) { if (!doNotPush) {
while (!redoHist.empty()) { while (!redoHist.empty()) {
@ -928,7 +1014,9 @@ DivSampleHistory* DivSample::prepareUndo(bool data, bool doNotPush) {
rate=h->rate; \ rate=h->rate; \
centerRate=h->centerRate; \ centerRate=h->centerRate; \
loopStart=h->loopStart; \ loopStart=h->loopStart; \
loopEnd=h->loopEnd; loopEnd=h->loopEnd; \
loop=h->loop; \
loopMode=h->loopMode;
int DivSample::undo() { int DivSample::undo() {

View file

@ -25,6 +25,13 @@
#include "../ta-utils.h" #include "../ta-utils.h"
#include <deque> #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 { enum DivSampleDepth: unsigned char {
DIV_SAMPLE_DEPTH_1BIT=0, DIV_SAMPLE_DEPTH_1BIT=0,
DIV_SAMPLE_DEPTH_1BIT_DPCM=1, DIV_SAMPLE_DEPTH_1BIT_DPCM=1,
@ -53,8 +60,10 @@ struct DivSampleHistory {
unsigned int length, samples; unsigned int length, samples;
DivSampleDepth depth; DivSampleDepth depth;
int rate, centerRate, loopStart, loopEnd; int rate, centerRate, loopStart, loopEnd;
bool loop;
DivSampleLoopMode loopMode;
bool hasSample; 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), data((unsigned char*)d),
length(l), length(l),
samples(s), samples(s),
@ -63,8 +72,10 @@ struct DivSampleHistory {
centerRate(cr), centerRate(cr),
loopStart(ls), loopStart(ls),
loopEnd(le), loopEnd(le),
loop(lp),
loopMode(lm),
hasSample(true) {} 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), data(NULL),
length(0), length(0),
samples(0), samples(0),
@ -73,6 +84,8 @@ struct DivSampleHistory {
centerRate(cr), centerRate(cr),
loopStart(ls), loopStart(ls),
loopEnd(le), loopEnd(le),
loop(lp),
loopMode(lm),
hasSample(false) {} hasSample(false) {}
~DivSampleHistory(); ~DivSampleHistory();
}; };
@ -92,6 +105,13 @@ struct DivSample {
// - 10: VOX ADPCM // - 10: VOX ADPCM
// - 16: 16-bit PCM // - 16: 16-bit PCM
DivSampleDepth depth; 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. // these are the new data structures.
signed char* data8; // 8 signed char* data8; // 8
@ -120,11 +140,29 @@ struct DivSample {
*/ */
bool isLoopable(); 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 * get sample end position
* @return the samples 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 * @warning DO NOT USE - internal functions
@ -253,6 +291,8 @@ struct DivSample {
loopEnd(-1), loopEnd(-1),
loopOffP(0), loopOffP(0),
depth(DIV_SAMPLE_DEPTH_16BIT), depth(DIV_SAMPLE_DEPTH_16BIT),
loop(false),
loopMode(DIV_SAMPLE_LOOP_FORWARD),
data8(NULL), data8(NULL),
data16(NULL), data16(NULL),
data1(NULL), data1(NULL),

View file

@ -511,7 +511,7 @@ 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)|(sampleDir[streamID]?0x10:0)); // flags w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0)|(sampleDir[streamID]?0x10:0)); // flags
if (sample->isLoopable() && !sampleDir[streamID]) { if (sample->isLoopable() && !sampleDir[streamID]) {
loopTimer[streamID]=sample->length8; loopTimer[streamID]=sample->length8;
loopSample[streamID]=write.val; loopSample[streamID]=write.val;
@ -1549,7 +1549,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
size_t memPos=0; size_t memPos=0;
for (int i=0; i<song.sampleLen; i++) { for (int i=0; i<song.sampleLen; i++) {
DivSample* sample=song.sample[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 (alignedSize>65536) alignedSize=65536;
if ((memPos&0xff0000)!=((memPos+alignedSize)&0xff0000)) { if ((memPos&0xff0000)!=((memPos+alignedSize)&0xff0000)) {
memPos=(memPos+0xffff)&0xff0000; memPos=(memPos+0xffff)&0xff0000;
@ -1559,9 +1559,9 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
sample->offSegaPCM=memPos; sample->offSegaPCM=memPos;
unsigned int readPos=0; unsigned int readPos=0;
for (unsigned int j=0; j<alignedSize; j++) { 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()) { if (sample->isLoopable()) {
readPos=sample->loopStart; readPos=sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT);
pcmMem[memPos++]=((unsigned char)sample->data8[readPos]+0x80); pcmMem[memPos++]=((unsigned char)sample->data8[readPos]+0x80);
} else { } else {
pcmMem[memPos++]=0x80; pcmMem[memPos++]=0x80;
@ -1572,7 +1572,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
readPos++; readPos++;
if (memPos>=16777216) break; if (memPos>=16777216) break;
} }
sample->loopOffP=readPos-sample->loopStart; sample->loopOffP=readPos-sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT);
if (memPos>=16777216) break; if (memPos>=16777216) break;
} }
@ -1897,12 +1897,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
if (loopSample[nextToTouch]<song.sampleLen) { if (loopSample[nextToTouch]<song.sampleLen) {
DivSample* sample=song.sample[loopSample[nextToTouch]]; DivSample* sample=song.sample[loopSample[nextToTouch]];
// insert loop // 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(0x93);
w->writeC(nextToTouch); w->writeC(nextToTouch);
w->writeI(sample->off8+sample->loopStart); w->writeI(sample->off8+sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT));
w->writeC(0x81); 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; loopSample[nextToTouch]=-1;

View file

@ -157,10 +157,11 @@ void FurnaceGUI::drawDebug() {
ImGui::Text("loopStart: %d",sample->loopStart); ImGui::Text("loopStart: %d",sample->loopStart);
ImGui::Text("loopEnd: %d", sample->loopEnd); ImGui::Text("loopEnd: %d", sample->loopEnd);
ImGui::Text("loopOffP: %d",sample->loopOffP); ImGui::Text("loopOffP: %d",sample->loopOffP);
if (sampleDepths[sample->depth]!=NULL) { ImGui::Text(sample->loop?"loop: Enabled":"loop: Disabled");
ImGui::Text("depth: %d (%s)",(unsigned char)sample->depth,sampleDepths[sample->depth]); if (sampleLoopModes[sample->loopMode]!=NULL) {
ImGui::Text("loopMode: %d (%s)",(unsigned char)sample->loopMode,sampleLoopModes[sample->loopMode]);
} else { } 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); ImGui::Text("length8: %d",sample->length8);

View file

@ -710,6 +710,8 @@ void FurnaceGUI::doAction(int what) {
sample->name=prevSample->name; sample->name=prevSample->name;
sample->loopStart=prevSample->loopStart; sample->loopStart=prevSample->loopStart;
sample->loopEnd=prevSample->loopEnd; sample->loopEnd=prevSample->loopEnd;
sample->loop=prevSample->loop;
sample->loopMode=prevSample->loopMode;
sample->depth=prevSample->depth; sample->depth=prevSample->depth;
if (sample->init(prevSample->samples)) { if (sample->init(prevSample->samples)) {
if (prevSample->getCurBuf()!=NULL) { if (prevSample->getCurBuf()!=NULL) {
@ -1264,6 +1266,7 @@ void FurnaceGUI::doAction(int what) {
sample->loopStart=start; sample->loopStart=start;
sample->loopEnd=end; sample->loopEnd=end;
sample->loop=true;
updateSampleTex=true; updateSampleTex=true;
e->renderSamples(); e->renderSamples();

View file

@ -116,6 +116,12 @@ const char* insTypes[DIV_INS_MAX+1]={
NULL NULL
}; };
const char* sampleLoopModes[DIV_SAMPLE_LOOP_MAX]={
"Forward",
"Backward",
"Ping pong"
};
const char* sampleDepths[DIV_SAMPLE_DEPTH_MAX]={ const char* sampleDepths[DIV_SAMPLE_DEPTH_MAX]={
"1-bit PCM", "1-bit PCM",
"1-bit DPCM", "1-bit DPCM",

View file

@ -40,6 +40,7 @@ extern const char* noteNames[180];
extern const char* noteNamesG[180]; extern const char* noteNamesG[180];
extern const char* pitchLabel[11]; extern const char* pitchLabel[11];
extern const char* insTypes[]; extern const char* insTypes[];
extern const char* sampleLoopModes[];
extern const char* sampleDepths[]; extern const char* sampleDepths[];
extern const char* resampleStrats[]; extern const char* resampleStrats[];
extern const int availableSystems[]; extern const int availableSystems[];

View file

@ -46,6 +46,12 @@ void FurnaceGUI::drawSampleEdit() {
sampleType=sampleDepths[sample->depth]; 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) { if (!settings.sampleLayout) {
ImGui::Text("Name"); ImGui::Text("Name");
ImGui::SameLine(); ImGui::SameLine();
@ -96,9 +102,11 @@ void FurnaceGUI::drawSampleEdit() {
bool doLoop=(sample->isLoopable()); bool doLoop=(sample->isLoopable());
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
if (doLoop) { if (doLoop) {
sample->loop=true;
sample->loopStart=0; sample->loopStart=0;
sample->loopEnd=sample->samples; sample->loopEnd=sample->samples;
} else { } else {
sample->loop=false;
sample->loopStart=-1; sample->loopStart=-1;
sample->loopEnd=sample->samples; sample->loopEnd=sample->samples;
} }
@ -107,6 +115,23 @@ void FurnaceGUI::drawSampleEdit() {
if (doLoop) { if (doLoop) {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); 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::Text("Loop Start");
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
@ -631,15 +656,35 @@ void FurnaceGUI::drawSampleEdit() {
bool doLoop=(sample->isLoopable()); bool doLoop=(sample->isLoopable());
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
if (doLoop) { if (doLoop) {
sample->loop=true;
sample->loopStart=0; sample->loopStart=0;
sample->loopEnd=sample->samples; sample->loopEnd=sample->samples;
} else { } else {
sample->loop=false;
sample->loopStart=-1; sample->loopStart=-1;
sample->loopEnd=sample->samples; sample->loopEnd=sample->samples;
} }
updateSampleTex=true; updateSampleTex=true;
} }
if (doLoop) { 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::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("Loop Start"); ImGui::Text("Loop Start");