Implement sample loop end position, enum-ise sample depth (#557)

TODO: new sample format
This commit is contained in:
cam900 2022-07-22 13:36:42 +09:00 committed by GitHub
parent a137eefd20
commit 5127d5ef18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 461 additions and 306 deletions

View File

@ -2226,7 +2226,7 @@ int DivEngine::addSampleFromFile(const char* path) {
sample->rate=33144;
sample->centerRate=33144;
sample->depth=1;
sample->depth=DIV_SAMPLE_DEPTH_1BIT_DPCM;
sample->init(len*8);
if (fread(sample->dataDPCM,1,len,f)==0) {
@ -2301,9 +2301,9 @@ int DivEngine::addSampleFromFile(const char* path) {
int index=0;
if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8) {
sample->depth=8;
sample->depth=DIV_SAMPLE_DEPTH_8BIT;
} else {
sample->depth=16;
sample->depth=DIV_SAMPLE_DEPTH_16BIT;
}
sample->init(si.frames);
if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8) {
@ -2358,6 +2358,7 @@ int DivEngine::addSampleFromFile(const char* path) {
if(inst.loop_count && 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;
}

View File

@ -799,17 +799,17 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
sample->rate=ymuSampleRate*400;
}
if (ds.version>0x15) {
sample->depth=reader.readC();
if (sample->depth!=8 && sample->depth!=16) {
sample->depth=(DivSampleDepth)reader.readC();
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) {
logW("%d: sample depth is wrong! (%d)",i,sample->depth);
sample->depth=16;
sample->depth=DIV_SAMPLE_DEPTH_16BIT;
}
} else {
if (ds.version>0x08) {
sample->depth=16;
sample->depth=DIV_SAMPLE_DEPTH_16BIT;
} else {
// it appears samples were stored as ADPCM back then
sample->depth=3;
sample->depth=DIV_SAMPLE_DEPTH_YMZ_ADPCM;
}
}
if (length>0) {
@ -838,7 +838,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
if (k>=sample->samples) {
break;
}
if (sample->depth==8) {
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
float next=(float)(data[(unsigned int)j]-0x80)*mult;
sample->data8[k++]=fmin(fmax(next,-128),127);
} else {
@ -1631,7 +1631,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
logD("reading sample %d at %x...",i,samplePtr[i]);
sample->name=reader.readString();
sample->samples=reader.readI();
sample->samples=sample->loopEnd=reader.readI();
sample->rate=reader.readI();
if (ds.version<58) {
vol=reader.readS();
@ -1639,7 +1639,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} else {
reader.readI();
}
sample->depth=reader.readC();
sample->depth=(DivSampleDepth)reader.readC();
// reserved
reader.readC();
@ -1657,6 +1657,13 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
reader.readI();
}
/*
if (ds.version>=100) {
sample->loopEnd=reader.readI();
} else {
reader.readI();
}
*/
if (ds.version>=58) { // modern sample
sample->init(sample->samples);
reader.read(sample->getCurBuf(),sample->getCurBufLen());
@ -1670,9 +1677,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
// render data
if (sample->depth!=8 && sample->depth!=16) {
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) {
logW("%d: sample depth is wrong! (%d)",i,sample->depth);
sample->depth=16;
sample->depth=DIV_SAMPLE_DEPTH_16BIT;
}
sample->samples=(double)sample->samples/samplePitches[pitch];
sample->init(sample->samples);
@ -1683,7 +1690,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (k>=sample->samples) {
break;
}
if (sample->depth==8) {
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
float next=(float)(data[(unsigned int)j]-0x80)*mult;
sample->data8[k++]=fmin(fmax(next,-128),127);
} else {
@ -1877,7 +1884,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
logD("reading samples... (%d)",insCount);
for (int i=0; i<insCount; i++) {
DivSample* sample=new DivSample;
sample->depth=8;
sample->depth=DIV_SAMPLE_DEPTH_8BIT;
sample->name=reader.readString(22);
logD("%d: %s",i+1,sample->name);
int slen=((unsigned short)reader.readS_BE())*2;
@ -1897,8 +1904,8 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
loopLen=0;
}
if (loopLen>=2) {
if (loopEnd<slen) slen=loopEnd;
sample->loopStart=loopStart;
sample->loopEnd=loopEnd;
}
sample->init(slen);
ds.sample.push_back(sample);
@ -3043,6 +3050,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeC(0);
w->writeS(sample->centerRate);
w->writeI(sample->loopStart);
//w->writeI(sample->loopEnd);
w->write(sample->getCurBuf(),sample->getCurBufLen());

View File

@ -114,12 +114,10 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le
if (chan[i].audPos<s->samples) {
writeAudDat(s->data8[chan[i].audPos++]);
}
if (chan[i].audPos>=s->samples || chan[i].audPos>=131071) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
chan[i].audPos=s->loopStart;
} else {
chan[i].sample=-1;
}
if (s->isLoopable() && chan[i].audPos>=MIN(131071,s->getEndPosition())) {
chan[i].audPos=s->loopStart;
} else if (chan[i].audPos>=MIN(131071,s->samples)) {
chan[i].sample=-1;
}
} else {
chan[i].sample=-1;

View File

@ -153,14 +153,13 @@ void DivPlatformGenesis::processDAC() {
if (chan[i].dacPeriod>=(chipClock/576)) {
if (s->samples>0) {
while (chan[i].dacPeriod>=(chipClock/576)) {
if (++chan[i].dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples && !chan[i].dacDirection) {
chan[i].dacPos=s->loopStart;
} else {
chan[i].dacSample=-1;
chan[i].dacPeriod=0;
break;
}
++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) {
chan[i].dacSample=-1;
chan[i].dacPeriod=0;
break;
}
chan[i].dacPeriod-=(chipClock/576);
}
@ -200,14 +199,13 @@ void DivPlatformGenesis::processDAC() {
chan[5].dacReady=false;
}
}
if (++chan[5].dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples && !chan[5].dacDirection) {
chan[5].dacPos=s->loopStart;
} else {
chan[5].dacSample=-1;
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
}
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) {
chan[5].dacSample=-1;
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
}
}
while (chan[5].dacPeriod>=rate) chan[5].dacPeriod-=rate;

View File

@ -158,12 +158,10 @@ 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 (chan[i].samplePos>=(int)s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
chan[i].samplePos=s->loopStart;
} else {
chan[i].sample=-1;
}
if (s->isLoopable() && chan[i].samplePos>=(int)s->getEndPosition()) {
chan[i].samplePos=s->loopStart;
} else if (chan[i].samplePos>=(int)s->samples) {
chan[i].sample=-1;
}
}
}

View File

@ -62,12 +62,11 @@ void DivPlatformMMC5::acquire(short* bufL, short* bufR, size_t start, size_t len
if (!isMuted[2]) {
rWrite(0x5011,((unsigned char)s->data8[dacPos]+0x80));
}
if (++dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
dacPos=s->loopStart;
} else {
dacSample=-1;
}
dacPos++;
if (s->isLoopable() && dacPos>=s->getEndPosition()) {
dacPos=s->loopStart;
} else if (dacPos>=s->samples) {
dacSample=-1;
}
dacPeriod-=rate;
} else {

View File

@ -108,12 +108,11 @@ void DivPlatformNES::doWrite(unsigned short addr, unsigned char data) {
rWrite(0x4011,next); \
} \
} \
if (++dacPos>=s->samples) { \
if (s->loopStart>=0 && s->loopStart<(int)s->samples) { \
dacPos=s->loopStart; \
} else { \
dacSample=-1; \
} \
dacPos++; \
if (s->isLoopable() && dacPos>=s->getEndPosition()) { \
dacPos=s->loopStart; \
} else if (dacPos>=s->samples) { \
dacSample=-1; \
} \
dacPeriod-=rate; \
} else { \

View File

@ -771,7 +771,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
int end=s->offB+s->lengthB-1;
immWrite(11,(end>>2)&0xff);
immWrite(12,(end>>10)&0xff);
immWrite(7,(s->loopStart>=0)?0xb0:0xa0); // start/repeat
immWrite(7,(s->isLoopable())?0xb0:0xa0); // start/repeat
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value;
chan[c.chan].baseFreq=NOTE_ADPCMB(chan[c.chan].note);
@ -807,7 +807,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
int end=s->offB+s->lengthB-1;
immWrite(11,(end>>2)&0xff);
immWrite(12,(end>>10)&0xff);
immWrite(7,(s->loopStart>=0)?0xb0:0xa0); // start/repeat
immWrite(7,(s->isLoopable())?0xb0:0xa0); // start/repeat
int freq=(65536.0*(double)s->rate)/(double)chipRateBase;
immWrite(16,freq&0xff);
immWrite(17,(freq>>8)&0xff);

View File

@ -90,12 +90,10 @@ 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 (chan[i].dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
chan[i].dacPos=s->loopStart;
} else {
chan[i].dacSample=-1;
}
if (s->isLoopable() && chan[i].dacPos>=s->getEndPosition()) {
chan[i].dacPos=s->loopStart;
} else if (chan[i].dacPos>=s->samples) {
chan[i].dacSample=-1;
}
chan[i].dacPeriod-=rate;
}

View File

@ -301,7 +301,7 @@ void DivPlatformQSound::tick(bool sysTick) {
qsound_bank = 0x8000 | (s->offQSound >> 16);
qsound_addr = s->offQSound & 0xffff;
int length = s->samples;
int length = s->getEndPosition();
if (length > 65536 - 16) {
length = 65536 - 16;
}

View File

@ -142,7 +142,7 @@ void DivPlatformRF5C68::tick(bool sysTick) {
if (chan[i].audPos>0) {
start=start+MIN(chan[i].audPos,s->length8);
}
if (s->loopStart>=0) {
if (s->isLoopable()) {
loop=start+s->loopStart;
}
start=MIN(start,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->length8;
int length=s->getEndPosition(DIV_SAMPLE_DEPTH_8BIT);
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-31,length);
if (actualLength>0) {
s->offRF5C68=memPos;

View File

@ -56,12 +56,10 @@ 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 (chan[i].pcm.pos>=(s->samples<<8)) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
chan[i].pcm.pos=s->loopStart<<8;
} else {
chan[i].pcm.sample=-1;
}
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)) {
chan[i].pcm.sample=-1;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=0;
@ -202,7 +200,7 @@ 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->length8;
int actualLength=(int)(s->getEndPosition(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);
@ -235,7 +233,7 @@ 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->length8;
int actualLength=(int)(s->getEndPosition(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);

View File

@ -213,7 +213,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->samples;
unsigned int sampleEnd=sample->offSU+(sample->getEndPosition());
unsigned int off=sample->offSU+chan[i].hasOffset;
chan[i].hasOffset=0;
if (sampleEnd>=getSampleMemCapacity(0)) sampleEnd=getSampleMemCapacity(0)-1;
@ -221,7 +221,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
chWrite(i,0x0b,off>>8);
chWrite(i,0x0c,sampleEnd&0xff);
chWrite(i,0x0d,sampleEnd>>8);
if (sample->loopStart>=0 && sample->loopStart<(int)sample->samples) {
if (sample->isLoopable()) {
unsigned int sampleLoop=sample->offSU+sample->loopStart;
if (sampleLoop>=getSampleMemCapacity(0)) sampleLoop=getSampleMemCapacity(0)-1;
chWrite(i,0x0e,sampleLoop&0xff);

View File

@ -83,12 +83,10 @@ void DivPlatformSwan::acquire(short* bufL, short* bufR, size_t start, size_t len
continue;
}
rWrite(0x09,(unsigned char)s->data8[dacPos++]+0x80);
if (dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
dacPos=s->loopStart;
} else {
dacSample=-1;
}
if (s->isLoopable() && dacPos>=s->getEndPosition()) {
dacPos=s->loopStart;
} else if (dacPos>=s->samples) {
dacSample=-1;
}
dacPeriod-=rate;
}

View File

@ -96,13 +96,11 @@ void DivPlatformVERA::acquire(short* bufL, short* bufR, size_t start, size_t len
rWritePCMData(tmp_r&0xff);
}
chan[16].pcm.pos++;
if (chan[16].pcm.pos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
chan[16].pcm.pos=s->loopStart;
} else {
chan[16].pcm.sample=-1;
break;
}
if (s->isLoopable() && chan[16].pcm.pos>=s->getEndPosition()) {
chan[16].pcm.pos=s->loopStart;
} else if (chan[16].pcm.pos>=s->samples) {
chan[16].pcm.sample=-1;
break;
}
}
} else {
@ -267,12 +265,12 @@ int DivPlatformVERA::dispatch(DivCommand c) {
chan[16].pcm.pos=0;
DivSample* s=parent->getSample(chan[16].pcm.sample);
unsigned char ctrl=0x90|chan[16].vol; // always stereo
if (s->depth==16) {
if (s->depth==DIV_SAMPLE_DEPTH_16BIT) {
chan[16].pcm.depth16=true;
ctrl|=0x20;
} else {
chan[16].pcm.depth16=false;
if (s->depth!=8) chan[16].pcm.sample=-1;
if (s->depth!=DIV_SAMPLE_DEPTH_8BIT) chan[16].pcm.sample=-1;
}
rWritePCMCtrl(ctrl);
}

View File

@ -77,13 +77,11 @@ void DivPlatformVRC6::acquire(short* bufL, short* bufR, size_t start, size_t len
chWrite(i,0,0x80|chan[i].dacOut);
}
chan[i].dacPos++;
if (chan[i].dacPos>=s->samples) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
chan[i].dacPos=s->loopStart;
} else {
chan[i].dacSample=-1;
chWrite(i,0,0);
}
if (s->isLoopable() && chan[i].dacPos>=s->getEndPosition()) {
chan[i].dacPos=s->loopStart;
} else if (chan[i].dacPos>=s->samples) {
chan[i].dacSample=-1;
chWrite(i,0,0);
}
chan[i].dacPeriod-=rate;
}

View File

@ -761,7 +761,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
immWrite(0x104,(end>>5)&0xff);
immWrite(0x105,(end>>13)&0xff);
immWrite(0x101,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|2);
immWrite(0x100,(s->loopStart>=0)?0xb0:0xa0); // start/repeat
immWrite(0x100,(s->isLoopable())?0xb0:0xa0); // start/repeat
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value;
chan[c.chan].baseFreq=NOTE_ADPCMB(chan[c.chan].note);
@ -796,7 +796,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
immWrite(0x104,(end>>5)&0xff);
immWrite(0x105,(end>>13)&0xff);
immWrite(0x101,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|2);
immWrite(0x100,(s->loopStart>=0)?0xb0:0xa0); // start/repeat
immWrite(0x100,(s->isLoopable())?0xb0:0xa0); // start/repeat
int freq=(65536.0*(double)s->rate)/((double)chipClock/144.0);
immWrite(0x109,freq&0xff);
immWrite(0x10a,(freq>>8)&0xff);

View File

@ -793,7 +793,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
immWrite(0x14,(end>>8)&0xff);
immWrite(0x15,end>>16);
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6));
immWrite(0x10,(s->loopStart>=0)?0x90:0x80); // start/repeat
immWrite(0x10,(s->isLoopable())?0x90:0x80); // start/repeat
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value;
chan[c.chan].baseFreq=NOTE_ADPCMB(chan[c.chan].note);
@ -828,7 +828,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
immWrite(0x14,(end>>8)&0xff);
immWrite(0x15,end>>16);
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6));
immWrite(0x10,(s->loopStart>=0)?0x90:0x80); // start/repeat
immWrite(0x10,(s->isLoopable())?0x90:0x80); // start/repeat
int freq=(65536.0*(double)s->rate)/((double)chipClock/144.0);
immWrite(0x19,freq&0xff);
immWrite(0x1a,(freq>>8)&0xff);

View File

@ -775,7 +775,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
immWrite(0x14,(end>>8)&0xff);
immWrite(0x15,end>>16);
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6));
immWrite(0x10,(s->loopStart>=0)?0x90:0x80); // start/repeat
immWrite(0x10,(s->isLoopable())?0x90:0x80); // start/repeat
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value;
chan[c.chan].baseFreq=NOTE_ADPCMB(chan[c.chan].note);
@ -810,7 +810,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
immWrite(0x14,(end>>8)&0xff);
immWrite(0x15,end>>16);
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6));
immWrite(0x10,(s->loopStart>=0)?0x90:0x80); // start/repeat
immWrite(0x10,(s->isLoopable())?0x90:0x80); // start/repeat
int freq=(65536.0*(double)s->rate)/((double)chipClock/144.0);
immWrite(0x19,freq&0xff);
immWrite(0x1a,(freq>>8)&0xff);

View File

@ -136,9 +136,9 @@ void DivPlatformYMZ280B::tick(bool sysTick) {
DivSample* s=parent->getSample(chan[i].sample);
unsigned char ctrl;
switch (s->depth) {
case 3: ctrl=0x20; break;
case 8: ctrl=0x40; break;
case 16: ctrl=0x60; break;
case DIV_SAMPLE_DEPTH_YMZ_ADPCM: ctrl=0x20; break;
case DIV_SAMPLE_DEPTH_8BIT: ctrl=0x40; break;
case DIV_SAMPLE_DEPTH_16BIT: ctrl=0x60; break;
default: ctrl=0;
}
double off=(s->centerRate>=1)?((double)s->centerRate/8363.0):1.0;
@ -146,40 +146,44 @@ void DivPlatformYMZ280B::tick(bool sysTick) {
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>511) chan[i].freq=511;
// ADPCM has half the range
if (s->depth==3 && chan[i].freq>255) chan[i].freq=255;
ctrl|=(chan[i].active?0x80:0)|((s->loopStart>=0)?0x10:0)|(chan[i].freq>>8);
if (s->depth==DIV_SAMPLE_DEPTH_YMZ_ADPCM && chan[i].freq>255) chan[i].freq=255;
ctrl|=(chan[i].active?0x80:0)|((s->isLoopable())?0x10:0)|(chan[i].freq>>8);
if (chan[i].keyOn) {
unsigned int start=s->offYMZ280B;
unsigned int loop=0;
unsigned int loopStart=0;
unsigned int loopEnd=0;
unsigned int end=MIN(start+s->getCurBufLen(),getSampleMemCapacity()-1);
if (chan[i].audPos>0) {
switch (s->depth) {
case 3: start+=chan[i].audPos/2; break;
case 8: start+=chan[i].audPos; break;
case 16: start+=chan[i].audPos*2; break;
case DIV_SAMPLE_DEPTH_YMZ_ADPCM: start+=chan[i].audPos/2; break;
case DIV_SAMPLE_DEPTH_8BIT: start+=chan[i].audPos; break;
case DIV_SAMPLE_DEPTH_16BIT: start+=chan[i].audPos*2; break;
default: break;
}
start=MIN(start,end);
}
if (s->loopStart>=0) {
if (s->isLoopable()) {
switch (s->depth) {
case 3: loop=start+s->loopStart/2; break;
case 8: loop=start+s->loopStart; break;
case 16: loop=start+s->loopStart*2; break;
case DIV_SAMPLE_DEPTH_YMZ_ADPCM: loopStart=start+s->loopStart/2; loopEnd=start+s->loopEnd/2; break;
case DIV_SAMPLE_DEPTH_8BIT: loopStart=start+s->loopStart; loopEnd=start+s->loopEnd; break;
case DIV_SAMPLE_DEPTH_16BIT: loopStart=start+s->loopStart*2; loopEnd=start+s->loopEnd*2; break;
default: break;
}
loop=MIN(loop,end);
loopEnd=MIN(loopEnd,end);
loopStart=MIN(loopStart,loopEnd);
}
rWrite(0x01+i*4,ctrl&~0x80); // force keyoff first
rWrite(0x20+i*4,(start>>16)&0xff);
rWrite(0x21+i*4,(loop>>16)&0xff);
rWrite(0x22+i*4,(end>>16)&0xff);
rWrite(0x21+i*4,(loopStart>>16)&0xff);
rWrite(0x22+i*4,(loopEnd>>16)&0xff);
rWrite(0x23+i*4,(end>>16)&0xff);
rWrite(0x40+i*4,(start>>8)&0xff);
rWrite(0x41+i*4,(loop>>8)&0xff);
rWrite(0x42+i*4,(end>>8)&0xff);
rWrite(0x41+i*4,(loopStart>>8)&0xff);
rWrite(0x42+i*4,(loopEnd>>8)&0xff);
rWrite(0x43+i*4,(end>>8)&0xff);
rWrite(0x60+i*4,start&0xff);
rWrite(0x61+i*4,loop&0xff);
rWrite(0x62+i*4,end&0xff);
rWrite(0x61+i*4,loopStart&0xff);
rWrite(0x62+i*4,loopEnd&0xff);
rWrite(0x63+i*4,end&0xff);
if (!chan[i].std.vol.had) {
chan[i].outVol=chan[i].vol;

View File

@ -1204,17 +1204,17 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
blip_add_delta(samp_bb,i,samp_temp-samp_prevSample);
samp_prevSample=samp_temp;
if (sPreview.pos>=s->samples || (sPreview.pEnd>=0 && (int)sPreview.pos>=sPreview.pEnd)) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples && (int)sPreview.pos>=s->loopStart) {
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.pos>=s->samples || (sPreview.pEnd>=0 && (int)sPreview.pos>=sPreview.pEnd)) {
if (s->loopStart>=0 && s->loopStart<(int)s->samples && (int)sPreview.pos>=s->loopStart) {
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 {
} else if (sPreview.pos>=s->samples) {
sPreview.sample=-1;
}
}

View File

@ -38,6 +38,65 @@ DivSampleHistory::~DivSampleHistory() {
if (data!=NULL) delete[] data;
}
bool DivSample::isLoopable() {
return (loopStart>=0 && loopStart<loopEnd) && (loopEnd>loopStart && loopEnd<=(int)samples);
}
unsigned int DivSample::getEndPosition(DivSampleDepth depth) {
int end=loopEnd;
unsigned int len=samples;
switch (depth) {
case DIV_SAMPLE_DEPTH_1BIT:
end=(loopEnd+7)/8;
len=length1;
break;
case DIV_SAMPLE_DEPTH_1BIT_DPCM:
end=(loopEnd+7)/8;
len=lengthDPCM;
break;
case DIV_SAMPLE_DEPTH_YMZ_ADPCM:
end=(loopEnd+1)/2;
len=lengthZ;
break;
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM:
end=(loopEnd+1)/2;
len=lengthQSoundA;
break;
case DIV_SAMPLE_DEPTH_ADPCM_A:
end=(loopEnd+1)/2;
len=lengthA;
break;
case DIV_SAMPLE_DEPTH_ADPCM_B:
end=(loopEnd+1)/2;
len=lengthB;
break;
case DIV_SAMPLE_DEPTH_8BIT:
end=loopEnd;
len=length8;
break;
case DIV_SAMPLE_DEPTH_BRR:
end=9*((loopEnd+15)/16);
len=lengthBRR;
break;
case DIV_SAMPLE_DEPTH_VOX:
end=(loopEnd+1)/2;
len=lengthVOX;
break;
case DIV_SAMPLE_DEPTH_16BIT:
end=loopEnd*2;
len=length16;
break;
default:
break;
}
return isLoopable()?end:len;
}
void DivSample::setSampleCount(unsigned int count) {
samples=count;
if ((!isLoopable()) || loopEnd<0 || loopEnd>(int)samples) loopEnd=samples;
}
bool DivSample::save(const char* path) {
#ifndef HAVE_SNDFILE
logE("Furnace was not compiled with libsndfile!");
@ -53,7 +112,7 @@ bool DivSample::save(const char* path) {
si.channels=1;
si.samplerate=rate;
switch (depth) {
case 8: // 8-bit
case DIV_SAMPLE_DEPTH_8BIT: // 8-bit
si.format=SF_FORMAT_PCM_U8|SF_FORMAT_WAV;
break;
default: // 16-bit
@ -76,17 +135,17 @@ bool DivSample::save(const char* path) {
inst.detune = 50 - (pitch % 100);
inst.velocity_hi = 0x7f;
inst.key_hi = 0x7f;
if(loopStart != -1)
if(isLoopable())
{
inst.loop_count = 1;
inst.loops[0].mode = SF_LOOP_FORWARD;
inst.loops[0].start = loopStart;
inst.loops[0].end = samples;
inst.loops[0].end = loopEnd;
}
sf_command(f, SFC_SET_INSTRUMENT, &inst, sizeof(inst));
switch (depth) {
case 8: {
case DIV_SAMPLE_DEPTH_8BIT: {
// convert from signed to unsigned
unsigned char* buf=new unsigned char[length8];
for (size_t i=0; i<length8; i++) {
@ -108,65 +167,65 @@ bool DivSample::save(const char* path) {
}
// 16-bit memory is padded to 512, to make things easier for ADPCM-A/B.
bool DivSample::initInternal(unsigned char d, int count) {
bool DivSample::initInternal(DivSampleDepth d, int count) {
switch (d) {
case 0: // 1-bit
case DIV_SAMPLE_DEPTH_1BIT: // 1-bit
if (data1!=NULL) delete[] data1;
length1=(count+7)/8;
data1=new unsigned char[length1];
memset(data1,0,length1);
break;
case 1: // DPCM
case DIV_SAMPLE_DEPTH_1BIT_DPCM: // DPCM
if (dataDPCM!=NULL) delete[] dataDPCM;
lengthDPCM=(count+7)/8;
dataDPCM=new unsigned char[lengthDPCM];
memset(dataDPCM,0,lengthDPCM);
break;
case 3: // YMZ ADPCM
case DIV_SAMPLE_DEPTH_YMZ_ADPCM: // YMZ ADPCM
if (dataZ!=NULL) delete[] dataZ;
lengthZ=(count+1)/2;
// for padding AICA sample
dataZ=new unsigned char[(lengthZ+3)&(~0x03)];
memset(dataZ,0,(lengthZ+3)&(~0x03));
break;
case 4: // QSound ADPCM
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: // QSound ADPCM
if (dataQSoundA!=NULL) delete[] dataQSoundA;
lengthQSoundA=(count+1)/2;
dataQSoundA=new unsigned char[lengthQSoundA];
memset(dataQSoundA,0,lengthQSoundA);
break;
case 5: // ADPCM-A
case DIV_SAMPLE_DEPTH_ADPCM_A: // ADPCM-A
if (dataA!=NULL) delete[] dataA;
lengthA=(count+1)/2;
dataA=new unsigned char[(lengthA+255)&(~0xff)];
memset(dataA,0,(lengthA+255)&(~0xff));
break;
case 6: // ADPCM-B
case DIV_SAMPLE_DEPTH_ADPCM_B: // ADPCM-B
if (dataB!=NULL) delete[] dataB;
lengthB=(count+1)/2;
dataB=new unsigned char[(lengthB+255)&(~0xff)];
memset(dataB,0,(lengthB+255)&(~0xff));
break;
case 8: // 8-bit
case DIV_SAMPLE_DEPTH_8BIT: // 8-bit
if (data8!=NULL) delete[] data8;
length8=count;
// for padding X1-010 sample
data8=new signed char[(count+4095)&(~0xfff)];
memset(data8,0,(count+4095)&(~0xfff));
break;
case 9: // BRR
case DIV_SAMPLE_DEPTH_BRR: // BRR
if (dataBRR!=NULL) delete[] dataBRR;
lengthBRR=9*((count+15)/16);
dataBRR=new unsigned char[lengthBRR];
memset(dataBRR,0,lengthBRR);
break;
case 10: // VOX
case DIV_SAMPLE_DEPTH_VOX: // VOX
if (dataVOX!=NULL) delete[] dataVOX;
lengthVOX=(count+1)/2;
dataVOX=new unsigned char[lengthVOX];
memset(dataVOX,0,lengthVOX);
break;
case 16: // 16-bit
case DIV_SAMPLE_DEPTH_16BIT: // 16-bit
if (data16!=NULL) delete[] data16;
length16=count*2;
data16=new short[(count+511)&(~0x1ff)];
@ -180,34 +239,34 @@ bool DivSample::initInternal(unsigned char d, int count) {
bool DivSample::init(unsigned int count) {
if (!initInternal(depth,count)) return false;
samples=count;
setSampleCount(count);
return true;
}
bool DivSample::resize(unsigned int count) {
if (depth==8) {
if (depth==DIV_SAMPLE_DEPTH_8BIT) {
if (data8!=NULL) {
signed char* oldData8=data8;
data8=NULL;
initInternal(8,count);
initInternal(DIV_SAMPLE_DEPTH_8BIT,count);
memcpy(data8,oldData8,MIN(count,samples));
delete[] oldData8;
} else {
initInternal(8,count);
initInternal(DIV_SAMPLE_DEPTH_8BIT,count);
}
samples=count;
setSampleCount(count);
return true;
} else if (depth==16) {
} else if (depth==DIV_SAMPLE_DEPTH_16BIT) {
if (data16!=NULL) {
short* oldData16=data16;
data16=NULL;
initInternal(16,count);
initInternal(DIV_SAMPLE_DEPTH_16BIT,count);
memcpy(data16,oldData16,sizeof(short)*MIN(count,samples));
delete[] oldData16;
} else {
initInternal(16,count);
initInternal(DIV_SAMPLE_DEPTH_16BIT,count);
}
samples=count;
setSampleCount(count);
return true;
}
return false;
@ -218,11 +277,11 @@ bool DivSample::strip(unsigned int begin, unsigned int end) {
if (end>samples) end=samples;
int count=samples-(end-begin);
if (count<=0) return resize(0);
if (depth==8) {
if (depth==DIV_SAMPLE_DEPTH_8BIT) {
if (data8!=NULL) {
signed char* oldData8=data8;
data8=NULL;
initInternal(8,count);
initInternal(DIV_SAMPLE_DEPTH_8BIT,count);
if (begin>0) {
memcpy(data8,oldData8,begin);
}
@ -234,13 +293,13 @@ bool DivSample::strip(unsigned int begin, unsigned int end) {
// do nothing
return true;
}
samples=count;
setSampleCount(count);
return true;
} else if (depth==16) {
} else if (depth==DIV_SAMPLE_DEPTH_16BIT) {
if (data16!=NULL) {
short* oldData16=data16;
data16=NULL;
initInternal(16,count);
initInternal(DIV_SAMPLE_DEPTH_16BIT,count);
if (begin>0) {
memcpy(data16,oldData16,sizeof(short)*begin);
}
@ -252,7 +311,7 @@ bool DivSample::strip(unsigned int begin, unsigned int end) {
// do nothing
return true;
}
samples=count;
setSampleCount(count);
return true;
}
return false;
@ -262,31 +321,31 @@ bool DivSample::trim(unsigned int begin, unsigned int end) {
int count=end-begin;
if (count==0) return true;
if (begin==0 && end==samples) return true;
if (depth==8) {
if (depth==DIV_SAMPLE_DEPTH_8BIT) {
if (data8!=NULL) {
signed char* oldData8=data8;
data8=NULL;
initInternal(8,count);
initInternal(DIV_SAMPLE_DEPTH_8BIT,count);
memcpy(data8,oldData8+begin,count);
delete[] oldData8;
} else {
// do nothing
return true;
}
samples=count;
setSampleCount(count);
return true;
} else if (depth==16) {
} else if (depth==DIV_SAMPLE_DEPTH_16BIT) {
if (data16!=NULL) {
short* oldData16=data16;
data16=NULL;
initInternal(16,count);
initInternal(DIV_SAMPLE_DEPTH_16BIT,count);
memcpy(data16,&(oldData16[begin]),sizeof(short)*count);
delete[] oldData16;
} else {
// do nothing
return true;
}
samples=count;
setSampleCount(count);
return true;
}
return false;
@ -294,11 +353,11 @@ bool DivSample::trim(unsigned int begin, unsigned int end) {
bool DivSample::insert(unsigned int pos, unsigned int length) {
unsigned int count=samples+length;
if (depth==8) {
if (depth==DIV_SAMPLE_DEPTH_8BIT) {
if (data8!=NULL) {
signed char* oldData8=data8;
data8=NULL;
initInternal(8,count);
initInternal(DIV_SAMPLE_DEPTH_8BIT,count);
if (pos>0) {
memcpy(data8,oldData8,pos);
}
@ -307,15 +366,15 @@ bool DivSample::insert(unsigned int pos, unsigned int length) {
}
delete[] oldData8;
} else {
initInternal(8,count);
initInternal(DIV_SAMPLE_DEPTH_8BIT,count);
}
samples=count;
setSampleCount(count);
return true;
} else if (depth==16) {
} else if (depth==DIV_SAMPLE_DEPTH_16BIT) {
if (data16!=NULL) {
short* oldData16=data16;
data16=NULL;
initInternal(16,count);
initInternal(DIV_SAMPLE_DEPTH_16BIT,count);
if (pos>0) {
memcpy(data16,oldData16,sizeof(short)*pos);
}
@ -324,9 +383,9 @@ bool DivSample::insert(unsigned int pos, unsigned int length) {
}
delete[] oldData16;
} else {
initInternal(16,count);
initInternal(DIV_SAMPLE_DEPTH_16BIT,count);
}
samples=count;
setSampleCount(count);
return true;
}
return false;
@ -337,15 +396,15 @@ bool DivSample::insert(unsigned int pos, unsigned int length) {
int finalCount=(double)samples*(r/(double)rate); \
signed char* oldData8=data8; \
short* oldData16=data16; \
if (depth==16) { \
if (depth==DIV_SAMPLE_DEPTH_16BIT) { \
if (data16!=NULL) { \
data16=NULL; \
initInternal(16,finalCount); \
initInternal(DIV_SAMPLE_DEPTH_16BIT,finalCount); \
} \
} else if (depth==8) { \
} else if (depth==DIV_SAMPLE_DEPTH_8BIT) { \
if (data8!=NULL) { \
data8=NULL; \
initInternal(8,finalCount); \
initInternal(DIV_SAMPLE_DEPTH_8BIT,finalCount); \
} \
} else { \
return false; \
@ -353,19 +412,20 @@ bool DivSample::insert(unsigned int pos, unsigned int length) {
#define RESAMPLE_END \
if (loopStart>=0) loopStart=(double)loopStart*(r/(double)rate); \
if (loopEnd>=0) loopEnd=(double)loopEnd*(r/(double)rate); \
centerRate=(int)((double)centerRate*(r/(double)rate)); \
rate=r; \
samples=finalCount; \
if (depth==16) { \
if (depth==DIV_SAMPLE_DEPTH_16BIT) { \
delete[] oldData16; \
} else if (depth==8) { \
} else if (depth==DIV_SAMPLE_DEPTH_8BIT) { \
delete[] oldData8; \
}
bool DivSample::resampleNone(double r) {
RESAMPLE_BEGIN;
if (depth==16) {
if (depth==DIV_SAMPLE_DEPTH_16BIT) {
for (int i=0; i<finalCount; i++) {
unsigned int pos=(unsigned int)((double)i*((double)rate/r));
if (pos>=samples) {
@ -374,7 +434,7 @@ bool DivSample::resampleNone(double r) {
data16[i]=oldData16[pos];
}
}
} else if (depth==8) {
} else if (depth==DIV_SAMPLE_DEPTH_8BIT) {
for (int i=0; i<finalCount; i++) {
unsigned int pos=(unsigned int)((double)i*((double)rate/r));
if (pos>=samples) {
@ -396,7 +456,7 @@ bool DivSample::resampleLinear(double r) {
unsigned int posInt=0;
double factor=(double)rate/r;
if (depth==16) {
if (depth==DIV_SAMPLE_DEPTH_16BIT) {
for (int i=0; i<finalCount; i++) {
short s1=(posInt>=samples)?0:oldData16[posInt];
short s2=(posInt+1>=samples)?((loopStart>=0 && loopStart<(int)samples)?oldData16[loopStart]:0):oldData16[posInt+1];
@ -409,7 +469,7 @@ bool DivSample::resampleLinear(double r) {
posInt++;
}
}
} else if (depth==8) {
} else if (depth==DIV_SAMPLE_DEPTH_8BIT) {
for (int i=0; i<finalCount; i++) {
short s1=(posInt>=samples)?0:oldData8[posInt];
short s2=(posInt+1>=samples)?((loopStart>=0 && loopStart<(int)samples)?oldData8[loopStart]:0):oldData8[posInt+1];
@ -436,7 +496,7 @@ bool DivSample::resampleCubic(double r) {
double factor=(double)rate/r;
float* cubicTable=DivFilterTables::getCubicTable();
if (depth==16) {
if (depth==DIV_SAMPLE_DEPTH_16BIT) {
for (int i=0; i<finalCount; i++) {
unsigned int n=((unsigned int)(posFrac*1024.0))&1023;
float* t=&cubicTable[n<<2];
@ -456,7 +516,7 @@ bool DivSample::resampleCubic(double r) {
posInt++;
}
}
} else if (depth==8) {
} else if (depth==DIV_SAMPLE_DEPTH_8BIT) {
for (int i=0; i<finalCount; i++) {
unsigned int n=((unsigned int)(posFrac*1024.0))&1023;
float* t=&cubicTable[n<<2];
@ -493,7 +553,7 @@ bool DivSample::resampleBlep(double r) {
memset(s,0,16*sizeof(float));
if (depth==16) {
if (depth==DIV_SAMPLE_DEPTH_16BIT) {
memset(data16,0,finalCount*sizeof(short));
for (int i=0; i<finalCount; i++) {
if (posInt<samples) {
@ -529,7 +589,7 @@ bool DivSample::resampleBlep(double r) {
}
}
}
} else if (depth==8) {
} else if (depth==DIV_SAMPLE_DEPTH_8BIT) {
memset(data8,0,finalCount);
for (int i=0; i<finalCount; i++) {
if (posInt<samples) {
@ -582,7 +642,7 @@ bool DivSample::resampleSinc(double r) {
memset(s,0,16*sizeof(float));
if (depth==16) {
if (depth==DIV_SAMPLE_DEPTH_16BIT) {
for (int i=0; i<finalCount+8; i++) {
unsigned int n=((unsigned int)(posFrac*8192.0))&8191;
float result=0;
@ -607,7 +667,7 @@ bool DivSample::resampleSinc(double r) {
s[15]=(posInt>=samples)?0:oldData16[posInt];
}
}
} else if (depth==8) {
} else if (depth==DIV_SAMPLE_DEPTH_8BIT) {
for (int i=0; i<finalCount+8; i++) {
unsigned int n=((unsigned int)(posFrac*8192.0))&8191;
float result=0;
@ -639,7 +699,7 @@ bool DivSample::resampleSinc(double r) {
}
bool DivSample::resample(double r, int filter) {
if (depth!=8 && depth!=16) return false;
if (depth!=DIV_SAMPLE_DEPTH_8BIT && depth!=DIV_SAMPLE_DEPTH_16BIT) return false;
switch (filter) {
case DIV_RESAMPLE_NONE:
return resampleNone(r);
@ -669,15 +729,15 @@ bool DivSample::resample(double r, int filter) {
void DivSample::render() {
// step 1: convert to 16-bit if needed
if (depth!=16) {
if (!initInternal(16,samples)) return;
if (depth!=DIV_SAMPLE_DEPTH_16BIT) {
if (!initInternal(DIV_SAMPLE_DEPTH_16BIT,samples)) return;
switch (depth) {
case 0: // 1-bit
case DIV_SAMPLE_DEPTH_1BIT: // 1-bit
for (unsigned int i=0; i<samples; i++) {
data16[i]=((data1[i>>3]>>(i&7))&1)?0x7fff:-0x7fff;
}
break;
case 1: { // DPCM
case DIV_SAMPLE_DEPTH_1BIT_DPCM: { // DPCM
int accum=0;
for (unsigned int i=0; i<samples; i++) {
accum+=((dataDPCM[i>>3]>>(i&7))&1)?1:-1;
@ -687,27 +747,27 @@ void DivSample::render() {
}
break;
}
case 3: // YMZ ADPCM
case DIV_SAMPLE_DEPTH_YMZ_ADPCM: // YMZ ADPCM
ymz_decode(dataZ,data16,samples);
break;
case 4: // QSound ADPCM
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: // QSound ADPCM
bs_decode(dataQSoundA,data16,samples);
break;
case 5: // ADPCM-A
case DIV_SAMPLE_DEPTH_ADPCM_A: // ADPCM-A
yma_decode(dataA,data16,samples);
break;
case 6: // ADPCM-B
case DIV_SAMPLE_DEPTH_ADPCM_B: // ADPCM-B
ymb_decode(dataB,data16,samples);
break;
case 8: // 8-bit PCM
case DIV_SAMPLE_DEPTH_8BIT: // 8-bit PCM
for (unsigned int i=0; i<samples; i++) {
data16[i]=data8[i]<<8;
}
break;
case 9: // BRR
case DIV_SAMPLE_DEPTH_BRR: // BRR
// TODO!
break;
case 10: // VOX
case DIV_SAMPLE_DEPTH_VOX: // VOX
oki_decode(dataVOX,data16,samples);
break;
default:
@ -716,16 +776,16 @@ void DivSample::render() {
}
// step 2: render to other formats
if (depth!=0) { // 1-bit
if (!initInternal(0,samples)) return;
if (depth!=DIV_SAMPLE_DEPTH_1BIT) { // 1-bit
if (!initInternal(DIV_SAMPLE_DEPTH_1BIT,samples)) return;
for (unsigned int i=0; i<samples; i++) {
if (data16[i]>0) {
data1[i>>3]|=1<<(i&7);
}
}
}
if (depth!=1) { // DPCM
if (!initInternal(1,samples)) return;
if (depth!=DIV_SAMPLE_DEPTH_1BIT_DPCM) { // DPCM
if (!initInternal(DIV_SAMPLE_DEPTH_1BIT_DPCM,samples)) return;
int accum=63;
for (unsigned int i=0; i<samples; i++) {
int next=((unsigned short)(data16[i]^0x8000))>>9;
@ -739,84 +799,88 @@ void DivSample::render() {
if (accum>127) accum=127;
}
}
if (depth!=3) { // YMZ ADPCM
if (!initInternal(3,samples)) return;
if (depth!=DIV_SAMPLE_DEPTH_YMZ_ADPCM) { // YMZ ADPCM
if (!initInternal(DIV_SAMPLE_DEPTH_YMZ_ADPCM,samples)) return;
ymz_encode(data16,dataZ,(samples+7)&(~0x7));
}
if (depth!=4) { // QSound ADPCM
if (!initInternal(4,samples)) return;
if (depth!=DIV_SAMPLE_DEPTH_QSOUND_ADPCM) { // QSound ADPCM
if (!initInternal(DIV_SAMPLE_DEPTH_QSOUND_ADPCM,samples)) return;
bs_encode(data16,dataQSoundA,samples);
}
// TODO: pad to 256.
if (depth!=5) { // ADPCM-A
if (!initInternal(5,samples)) return;
if (depth!=DIV_SAMPLE_DEPTH_ADPCM_A) { // ADPCM-A
if (!initInternal(DIV_SAMPLE_DEPTH_ADPCM_A,samples)) return;
yma_encode(data16,dataA,(samples+511)&(~0x1ff));
}
if (depth!=6) { // ADPCM-B
if (!initInternal(6,samples)) return;
if (depth!=DIV_SAMPLE_DEPTH_ADPCM_B) { // ADPCM-B
if (!initInternal(DIV_SAMPLE_DEPTH_ADPCM_B,samples)) return;
ymb_encode(data16,dataB,(samples+511)&(~0x1ff));
}
if (depth!=8) { // 8-bit PCM
if (!initInternal(8,samples)) return;
if (depth!=DIV_SAMPLE_DEPTH_8BIT) { // 8-bit PCM
if (!initInternal(DIV_SAMPLE_DEPTH_8BIT,samples)) return;
for (unsigned int i=0; i<samples; i++) {
data8[i]=data16[i]>>8;
}
}
// TODO: BRR!
if (depth!=10) { // VOX
if (!initInternal(10,samples)) return;
if (depth!=DIV_SAMPLE_DEPTH_VOX) { // VOX
if (!initInternal(DIV_SAMPLE_DEPTH_VOX,samples)) return;
oki_encode(data16,dataVOX,samples);
}
}
void* DivSample::getCurBuf() {
switch (depth) {
case 0:
case DIV_SAMPLE_DEPTH_1BIT:
return data1;
case 1:
case DIV_SAMPLE_DEPTH_1BIT_DPCM:
return dataDPCM;
case 3:
case DIV_SAMPLE_DEPTH_YMZ_ADPCM:
return dataZ;
case 4:
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM:
return dataQSoundA;
case 5:
case DIV_SAMPLE_DEPTH_ADPCM_A:
return dataA;
case 6:
case DIV_SAMPLE_DEPTH_ADPCM_B:
return dataB;
case 8:
case DIV_SAMPLE_DEPTH_8BIT:
return data8;
case 9:
case DIV_SAMPLE_DEPTH_BRR:
return dataBRR;
case 10:
case DIV_SAMPLE_DEPTH_VOX:
return dataVOX;
case 16:
case DIV_SAMPLE_DEPTH_16BIT:
return data16;
default:
return NULL;
}
return NULL;
}
unsigned int DivSample::getCurBufLen() {
switch (depth) {
case 0:
case DIV_SAMPLE_DEPTH_1BIT:
return length1;
case 1:
case DIV_SAMPLE_DEPTH_1BIT_DPCM:
return lengthDPCM;
case 3:
case DIV_SAMPLE_DEPTH_YMZ_ADPCM:
return lengthZ;
case 4:
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM:
return lengthQSoundA;
case 5:
case DIV_SAMPLE_DEPTH_ADPCM_A:
return lengthA;
case 6:
case DIV_SAMPLE_DEPTH_ADPCM_B:
return lengthB;
case 8:
case DIV_SAMPLE_DEPTH_8BIT:
return length8;
case 9:
case DIV_SAMPLE_DEPTH_BRR:
return lengthBRR;
case 10:
case DIV_SAMPLE_DEPTH_VOX:
return lengthVOX;
case 16:
case DIV_SAMPLE_DEPTH_16BIT:
return length16;
default:
return 0;
}
return 0;
}
@ -831,9 +895,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);
h=new DivSampleHistory(duplicate,getCurBufLen(),samples,depth,rate,centerRate,loopStart,loopEnd);
} else {
h=new DivSampleHistory(depth,rate,centerRate,loopStart);
h=new DivSampleHistory(depth,rate,centerRate,loopStart,loopEnd);
}
if (!doNotPush) {
while (!redoHist.empty()) {
@ -863,7 +927,8 @@ DivSampleHistory* DivSample::prepareUndo(bool data, bool doNotPush) {
} \
rate=h->rate; \
centerRate=h->centerRate; \
loopStart=h->loopStart;
loopStart=h->loopStart; \
loopEnd=h->loopEnd;
int DivSample::undo() {

View File

@ -17,9 +17,28 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _SAMPLE_H
#define _SAMPLE_H
#pragma once
#include "../ta-utils.h"
#include <deque>
enum DivSampleDepth: unsigned char {
DIV_SAMPLE_DEPTH_1BIT=0,
DIV_SAMPLE_DEPTH_1BIT_DPCM=1,
DIV_SAMPLE_DEPTH_YMZ_ADPCM=3,
DIV_SAMPLE_DEPTH_QSOUND_ADPCM=4,
DIV_SAMPLE_DEPTH_ADPCM_A=5,
DIV_SAMPLE_DEPTH_ADPCM_B=6,
DIV_SAMPLE_DEPTH_8BIT=8,
DIV_SAMPLE_DEPTH_BRR=9,
DIV_SAMPLE_DEPTH_VOX=10,
DIV_SAMPLE_DEPTH_16BIT=16,
DIV_SAMPLE_DEPTH_MAX // boundary for sample depth
};
enum DivResampleFilters {
DIV_RESAMPLE_NONE=0,
DIV_RESAMPLE_LINEAR,
@ -32,10 +51,10 @@ enum DivResampleFilters {
struct DivSampleHistory {
unsigned char* data;
unsigned int length, samples;
unsigned char depth;
int rate, centerRate, loopStart;
DivSampleDepth depth;
int rate, centerRate, loopStart, loopEnd;
bool hasSample;
DivSampleHistory(void* d, unsigned int l, unsigned int s, unsigned char de, int r, int cr, int ls):
DivSampleHistory(void* d, unsigned int l, unsigned int s, DivSampleDepth de, int r, int cr, int ls, int le):
data((unsigned char*)d),
length(l),
samples(s),
@ -43,8 +62,9 @@ struct DivSampleHistory {
rate(r),
centerRate(cr),
loopStart(ls),
loopEnd(le),
hasSample(true) {}
DivSampleHistory(unsigned char de, int r, int cr, int ls):
DivSampleHistory(DivSampleDepth de, int r, int cr, int ls, int le):
data(NULL),
length(0),
samples(0),
@ -52,13 +72,14 @@ struct DivSampleHistory {
rate(r),
centerRate(cr),
loopStart(ls),
loopEnd(le),
hasSample(false) {}
~DivSampleHistory();
};
struct DivSample {
String name;
int rate, centerRate, loopStart, loopOffP;
int rate, centerRate, loopStart, loopEnd, loopOffP;
// valid values are:
// - 0: ZX Spectrum overlay drum (1-bit)
// - 1: 1-bit NES DPCM (1-bit)
@ -70,7 +91,7 @@ struct DivSample {
// - 9: BRR (SNES)
// - 10: VOX ADPCM
// - 16: 16-bit PCM
unsigned char depth;
DivSampleDepth depth;
// these are the new data structures.
signed char* data8; // 8
@ -93,6 +114,23 @@ struct DivSample {
std::deque<DivSampleHistory*> undoHist;
std::deque<DivSampleHistory*> redoHist;
/**
* check if sample is loopable.
* @return whether it is loopable.
*/
bool isLoopable();
/**
* get sample end position
* @return the samples end position.
*/
unsigned int getEndPosition(DivSampleDepth depth=DIV_SAMPLE_DEPTH_MAX);
/**
* @warning DO NOT USE - internal functions
*/
void setSampleCount(unsigned int count);
/**
* @warning DO NOT USE - internal functions
*/
@ -116,7 +154,7 @@ struct DivSample {
* @param count number of samples.
* @return whether it was successful.
*/
bool initInternal(unsigned char d, int count);
bool initInternal(DivSampleDepth d, int count);
/**
* initialize sample data. make sure you have set `depth` before doing so.
@ -212,8 +250,9 @@ struct DivSample {
rate(32000),
centerRate(8363),
loopStart(-1),
loopEnd(-1),
loopOffP(0),
depth(16),
depth(DIV_SAMPLE_DEPTH_16BIT),
data8(NULL),
data16(NULL),
data1(NULL),
@ -253,3 +292,5 @@ struct DivSample {
samples(0) {}
~DivSample();
};
#endif

View File

@ -512,7 +512,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(streamID);
w->writeS(write.val); // sample number
w->writeC((sample->loopStart==0)|(sampleDir[streamID]?0x10:0)); // flags
if (sample->loopStart>0 && !sampleDir[streamID]) {
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) {
size_t memPos=0;
for (int i=0; i<song.sampleLen; i++) {
DivSample* sample=song.sample[i];
unsigned int alignedSize=(sample->length8+0xff)&(~0xff);
unsigned int alignedSize=(sample->getEndPosition(DIV_SAMPLE_DEPTH_8BIT)+0xff)&(~0xff);
if (alignedSize>65536) alignedSize=65536;
if ((memPos&0xff0000)!=((memPos+alignedSize)&0xff0000)) {
memPos=(memPos+0xffff)&0xff0000;
@ -1559,8 +1559,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
sample->offSegaPCM=memPos;
unsigned int readPos=0;
for (unsigned int j=0; j<alignedSize; j++) {
if (readPos>=sample->length8) {
if (sample->loopStart>=0 && sample->loopStart<(int)sample->length8) {
if (readPos>=sample->getEndPosition(DIV_SAMPLE_DEPTH_8BIT)) {
if (sample->isLoopable()) {
readPos=sample->loopStart;
pcmMem[memPos++]=((unsigned char)sample->data8[readPos]+0x80);
} else {
@ -1663,7 +1663,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
memcpy(sampleMem,writeZ280[i]->getSampleMem(),sampleMemLen);
for (int i=0; i<song.sampleLen; i++) {
DivSample* s=song.sample[i];
if (s->depth==16) {
if (s->depth==DIV_SAMPLE_DEPTH_16BIT) {
unsigned int pos=s->offYMZ280B;
for (unsigned int j=0; j<s->samples; j++) {
unsigned char lo=sampleMem[pos+j*2];
@ -1871,12 +1871,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
if (loopSample[nextToTouch]<song.sampleLen) {
DivSample* sample=song.sample[loopSample[nextToTouch]];
// insert loop
if (sample->loopStart<(int)sample->length8) {
if (sample->loopStart<(int)sample->getEndPosition(DIV_SAMPLE_DEPTH_8BIT)) {
w->writeC(0x93);
w->writeC(nextToTouch);
w->writeI(sample->off8+sample->loopStart);
w->writeC(0x81);
w->writeI(sample->length8-sample->loopStart);
w->writeI(sample->getEndPosition(DIV_SAMPLE_DEPTH_8BIT)-sample->loopStart);
}
}
loopSample[nextToTouch]=-1;

View File

@ -18,6 +18,7 @@
*/
#include "gui.h"
#include "guiConst.h"
#include "debug.h"
#include "IconsFontAwesome4.h"
#include <SDL_timer.h>
@ -154,12 +155,19 @@ void FurnaceGUI::drawDebug() {
ImGui::Text("rate: %d",sample->rate);
ImGui::Text("centerRate: %d",sample->centerRate);
ImGui::Text("loopStart: %d",sample->loopStart);
ImGui::Text("loopEnd: %d", sample->loopEnd);
ImGui::Text("loopOffP: %d",sample->loopOffP);
ImGui::Text("depth: %d",sample->depth);
if (sampleDepths[sample->depth]!=NULL) {
ImGui::Text("depth: %d (%s)",(unsigned char)sample->depth,sampleDepths[sample->depth]);
} else {
ImGui::Text("depth: %d (<NULL!>)",(unsigned char)sample->depth);
}
ImGui::Text("length8: %d",sample->length8);
ImGui::Text("length16: %d",sample->length16);
ImGui::Text("length1: %d",sample->length1);
ImGui::Text("lengthDPCM: %d",sample->lengthDPCM);
ImGui::Text("lengthZ: %d",sample->lengthZ);
ImGui::Text("lengthQSoundA: %d",sample->lengthQSoundA);
ImGui::Text("lengthA: %d",sample->lengthA);
ImGui::Text("lengthB: %d",sample->lengthB);
@ -170,6 +178,7 @@ void FurnaceGUI::drawDebug() {
ImGui::Text("off16: %x",sample->off16);
ImGui::Text("off1: %x",sample->off1);
ImGui::Text("offDPCM: %x",sample->offDPCM);
ImGui::Text("offZ: %x",sample->offZ);
ImGui::Text("offQSoundA: %x",sample->offQSoundA);
ImGui::Text("offA: %x",sample->offA);
ImGui::Text("offB: %x",sample->offB);
@ -179,6 +188,8 @@ void FurnaceGUI::drawDebug() {
ImGui::Text("offQSound: %x",sample->offQSound);
ImGui::Text("offX1_010: %x",sample->offX1_010);
ImGui::Text("offSU: %x",sample->offSU);
ImGui::Text("offYMZ280B: %x",sample->offYMZ280B);
ImGui::Text("offRF5C68: %x",sample->offRF5C68);
ImGui::Text("samples: %d",sample->samples);
ImGui::TreePop();

View File

@ -709,6 +709,7 @@ void FurnaceGUI::doAction(int what) {
sample->centerRate=prevSample->centerRate;
sample->name=prevSample->name;
sample->loopStart=prevSample->loopStart;
sample->loopEnd=prevSample->loopEnd;
sample->depth=prevSample->depth;
if (sample->init(prevSample->samples)) {
if (prevSample->getCurBuf()!=NULL) {
@ -838,7 +839,7 @@ void FurnaceGUI::doAction(int what) {
if (!sample->insert(pos,sampleClipboardLen)) {
showError("couldn't paste! make sure your sample is 8 or 16-bit.");
} else {
if (sample->depth==8) {
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
for (size_t i=0; i<sampleClipboardLen; i++) {
sample->data8[pos+i]=sampleClipboard[i]>>8;
}
@ -864,7 +865,7 @@ void FurnaceGUI::doAction(int what) {
if (pos<0) pos=0;
e->lockEngine([this,sample,pos]() {
if (sample->depth==8) {
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
for (size_t i=0; i<sampleClipboardLen; i++) {
if (pos+i>=sample->samples) break;
sample->data8[pos+i]=sampleClipboard[i]>>8;
@ -894,7 +895,7 @@ void FurnaceGUI::doAction(int what) {
if (pos<0) pos=0;
e->lockEngine([this,sample,pos]() {
if (sample->depth==8) {
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
for (size_t i=0; i<sampleClipboardLen; i++) {
if (pos+i>=sample->samples) break;
int val=sample->data8[pos+i]+(sampleClipboard[i]>>8);
@ -948,7 +949,7 @@ void FurnaceGUI::doAction(int what) {
SAMPLE_OP_BEGIN;
float maxVal=0.0f;
if (sample->depth==16) {
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
for (unsigned int i=start; i<end; i++) {
float val=fabs((float)sample->data16[i]/32767.0f);
if (val>maxVal) maxVal=val;
@ -963,7 +964,7 @@ void FurnaceGUI::doAction(int what) {
sample->data16[i]=val;
}
}
} else if (sample->depth==8) {
} else if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
for (unsigned int i=start; i<end; i++) {
float val=fabs((float)sample->data8[i]/127.0f);
if (val>maxVal) maxVal=val;
@ -994,14 +995,14 @@ void FurnaceGUI::doAction(int what) {
e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN;
if (sample->depth==16) {
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
for (unsigned int i=start; i<end; i++) {
float val=sample->data16[i]*float(i-start)/float(end-start);
if (val<-32768) val=-32768;
if (val>32767) val=32767;
sample->data16[i]=val;
}
} else if (sample->depth==8) {
} else if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
for (unsigned int i=start; i<end; i++) {
float val=sample->data8[i]*float(i-start)/float(end-start);
if (val<-128) val=-128;
@ -1024,14 +1025,14 @@ void FurnaceGUI::doAction(int what) {
e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN;
if (sample->depth==16) {
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
for (unsigned int i=start; i<end; i++) {
float val=sample->data16[i]*float(end-i)/float(end-start);
if (val<-32768) val=-32768;
if (val>32767) val=32767;
sample->data16[i]=val;
}
} else if (sample->depth==8) {
} else if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
for (unsigned int i=start; i<end; i++) {
float val=sample->data8[i]*float(end-i)/float(end-start);
if (val<-128) val=-128;
@ -1058,11 +1059,11 @@ void FurnaceGUI::doAction(int what) {
e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN;
if (sample->depth==16) {
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
for (unsigned int i=start; i<end; i++) {
sample->data16[i]=0;
}
} else if (sample->depth==8) {
} else if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
for (unsigned int i=start; i<end; i++) {
sample->data8[i]=0;
}
@ -1116,7 +1117,7 @@ void FurnaceGUI::doAction(int what) {
e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN;
if (sample->depth==16) {
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
for (unsigned int i=start; i<end; i++) {
unsigned int ri=end-i-1+start;
if (ri<=i) break;
@ -1124,7 +1125,7 @@ void FurnaceGUI::doAction(int what) {
sample->data16[ri]^=sample->data16[i];
sample->data16[i]^=sample->data16[ri];
}
} else if (sample->depth==8) {
} else if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
for (unsigned int i=start; i<end; i++) {
unsigned int ri=end-i-1+start;
if (ri<=i) break;
@ -1148,12 +1149,12 @@ void FurnaceGUI::doAction(int what) {
e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN;
if (sample->depth==16) {
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
for (unsigned int i=start; i<end; i++) {
sample->data16[i]=-sample->data16[i];
if (sample->data16[i]==-32768) sample->data16[i]=32767;
}
} else if (sample->depth==8) {
} else if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
for (unsigned int i=start; i<end; i++) {
sample->data8[i]=-sample->data8[i];
if (sample->data16[i]==-128) sample->data16[i]=127;
@ -1174,11 +1175,11 @@ void FurnaceGUI::doAction(int what) {
e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN;
if (sample->depth==16) {
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
for (unsigned int i=start; i<end; i++) {
sample->data16[i]^=0x8000;
}
} else if (sample->depth==8) {
} else if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
for (unsigned int i=start; i<end; i++) {
sample->data8[i]^=0x80;
}
@ -1261,8 +1262,8 @@ void FurnaceGUI::doAction(int what) {
e->lockEngine([this,sample]() {
SAMPLE_OP_BEGIN;
sample->trim(0,end);
sample->loopStart=start;
sample->loopEnd=end;
updateSampleTex=true;
e->renderSamples();

View File

@ -116,7 +116,7 @@ const char* insTypes[DIV_INS_MAX+1]={
NULL
};
const char* sampleDepths[17]={
const char* sampleDepths[DIV_SAMPLE_DEPTH_MAX]={
"1-bit PCM",
"1-bit DPCM",
NULL,

View File

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

View File

@ -41,7 +41,7 @@ void FurnaceGUI::drawSampleEdit() {
} else {
DivSample* sample=e->song.sample[curSample];
String sampleType="Invalid";
if (sample->depth<17) {
if (sample->depth<DIV_SAMPLE_DEPTH_MAX) {
if (sampleDepths[sample->depth]!=NULL) {
sampleType=sampleDepths[sample->depth];
}
@ -61,11 +61,11 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("##SampleType",sampleType.c_str())) {
for (int i=0; i<17; i++) {
for (int i=0; i<DIV_SAMPLE_DEPTH_MAX; i++) {
if (sampleDepths[i]==NULL) continue;
if (ImGui::Selectable(sampleDepths[i])) {
sample->prepareUndo(true);
sample->depth=i;
sample->depth=(DivSampleDepth)i;
e->renderSamplesP();
updateSampleTex=true;
MARK_MODIFIED;
@ -93,22 +93,43 @@ void FurnaceGUI::drawSampleEdit() {
}
ImGui::TableNextColumn();
bool doLoop=(sample->loopStart>=0);
bool doLoop=(sample->isLoopable());
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
if (doLoop) {
sample->loopStart=0;
sample->loopEnd=sample->samples;
} else {
sample->loopStart=-1;
sample->loopEnd=sample->samples;
}
updateSampleTex=true;
}
if (doLoop) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Loop Start");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputInt("##LoopPosition",&sample->loopStart,1,10)) { MARK_MODIFIED
if (sample->loopStart<0 || sample->loopStart>=(int)sample->samples) {
if (ImGui::InputInt("##LoopStartPosition",&sample->loopStart,1,10)) { MARK_MODIFIED
if (sample->loopStart<0) {
sample->loopStart=0;
}
if (sample->loopStart>sample->loopEnd) {
sample->loopStart=sample->loopEnd;
}
updateSampleTex=true;
}
ImGui::TableNextColumn();
ImGui::Text("Loop End");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputInt("##LoopEndPosition",&sample->loopEnd,1,10)) { MARK_MODIFIED
if (sample->loopEnd<sample->loopStart) {
sample->loopEnd=sample->loopStart;
}
if (sample->loopEnd>=(int)sample->samples) {
sample->loopEnd=sample->samples;
}
updateSampleTex=true;
}
}
@ -123,7 +144,7 @@ void FurnaceGUI::drawSampleEdit() {
*/
ImGui::Separator();
ImGui::BeginDisabled(sample->depth!=8 && sample->depth!=16);
ImGui::BeginDisabled(sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT);
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(!sampleDragMode));
if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) {
@ -275,14 +296,14 @@ void FurnaceGUI::drawSampleEdit() {
SAMPLE_OP_BEGIN;
float vol=amplifyVol/100.0f;
if (sample->depth==16) {
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
for (unsigned int i=start; i<end; i++) {
float val=sample->data16[i]*vol;
if (val<-32768) val=-32768;
if (val>32767) val=32767;
sample->data16[i]=val;
}
} else if (sample->depth==8) {
} else if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
for (unsigned int i=start; i<end; i++) {
float val=sample->data8[i]*vol;
if (val<-128) val=-128;
@ -466,7 +487,7 @@ void FurnaceGUI::drawSampleEdit() {
double power=(sampleFilterCutStart>sampleFilterCutEnd)?0.5:2.0;
if (sample->depth==16) {
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
for (unsigned int i=start; i<end; i++) {
double freq=sampleFilterCutStart+(sampleFilterCutEnd-sampleFilterCutStart)*pow(double(i-start)/double(end-start),power);
double cut=sin((freq/double(sample->rate))*M_PI);
@ -482,7 +503,7 @@ void FurnaceGUI::drawSampleEdit() {
if (val>32767) val=32767;
sample->data16[i]=val;
}
} else if (sample->depth==8) {
} else if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
for (unsigned int i=start; i<end; i++) {
double freq=sampleFilterCutStart+(sampleFilterCutEnd-sampleFilterCutStart)*pow(double(i-start)/double(end-start),power);
double cut=sin((freq/double(sample->rate))*M_PI);
@ -574,11 +595,11 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("##SampleType",sampleType.c_str())) {
for (int i=0; i<17; i++) {
for (int i=0; i<DIV_SAMPLE_DEPTH_MAX; i++) {
if (sampleDepths[i]==NULL) continue;
if (ImGui::Selectable(sampleDepths[i])) {
sample->prepareUndo(true);
sample->depth=i;
sample->depth=(DivSampleDepth)i;
e->renderSamplesP();
updateSampleTex=true;
MARK_MODIFIED;
@ -607,22 +628,43 @@ void FurnaceGUI::drawSampleEdit() {
}
ImGui::TableNextColumn();
bool doLoop=(sample->loopStart>=0);
bool doLoop=(sample->isLoopable());
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
if (doLoop) {
sample->loopStart=0;
sample->loopEnd=sample->samples;
} else {
sample->loopStart=-1;
sample->loopEnd=sample->samples;
}
updateSampleTex=true;
}
if (doLoop) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Loop Start");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputInt("##LoopPosition",&sample->loopStart,1,10)) { MARK_MODIFIED
if (sample->loopStart<0 || sample->loopStart>=(int)sample->samples) {
if (ImGui::InputInt("##LoopStartPosition",&sample->loopStart,1,10)) { MARK_MODIFIED
if (sample->loopStart<0) {
sample->loopStart=0;
}
if (sample->loopStart>sample->loopEnd) {
sample->loopStart=sample->loopEnd;
}
updateSampleTex=true;
}
ImGui::TableNextColumn();
ImGui::Text("Loop End");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputInt("##LoopEndPosition",&sample->loopEnd,1,10)) { MARK_MODIFIED
if (sample->loopEnd<sample->loopStart) {
sample->loopEnd=sample->loopStart;
}
if (sample->loopEnd>=(int)sample->samples) {
sample->loopEnd=sample->samples;
}
updateSampleTex=true;
}
}
@ -637,7 +679,7 @@ void FurnaceGUI::drawSampleEdit() {
*/
ImGui::Separator();
ImGui::BeginDisabled(sample->depth!=8 && sample->depth!=16);
ImGui::BeginDisabled(sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT);
ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(!sampleDragMode));
if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) {
@ -820,7 +862,7 @@ void FurnaceGUI::drawSampleEdit() {
double power=(sampleFilterCutStart>sampleFilterCutEnd)?0.5:2.0;
if (sample->depth==16) {
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
for (unsigned int i=start; i<end; i++) {
double freq=sampleFilterCutStart+(sampleFilterCutEnd-sampleFilterCutStart)*pow(double(i-start)/double(end-start),power);
double cut=sin((freq/double(sample->rate))*M_PI);
@ -836,7 +878,7 @@ void FurnaceGUI::drawSampleEdit() {
if (val>32767) val=32767;
sample->data16[i]=val;
}
} else if (sample->depth==8) {
} else if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
for (unsigned int i=start; i<end; i++) {
double freq=sampleFilterCutStart+(sampleFilterCutEnd-sampleFilterCutStart)*pow(double(i-start)/double(end-start),power);
double cut=sin((freq/double(sample->rate))*M_PI);
@ -890,14 +932,14 @@ void FurnaceGUI::drawSampleEdit() {
SAMPLE_OP_BEGIN;
float vol=amplifyVol/100.0f;
if (sample->depth==16) {
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
for (unsigned int i=start; i<end; i++) {
float val=sample->data16[i]*vol;
if (val<-32768) val=-32768;
if (val>32767) val=32767;
sample->data16[i]=val;
}
} else if (sample->depth==8) {
} else if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
for (unsigned int i=start; i<end; i++) {
float val=sample->data8[i]*vol;
if (val<-128) val=-128;
@ -1138,7 +1180,7 @@ void FurnaceGUI::drawSampleEdit() {
for (int i=0; i<availY; i++) {
for (int j=0; j<availX; j++) {
int scaledPos=samplePos+(j*sampleZoom);
if (sample->loopStart>=0 && sample->loopStart<(int)sample->samples && scaledPos>=sample->loopStart) {
if (sample->isLoopable() && (scaledPos>=sample->loopStart && scaledPos<=sample->loopEnd)) {
data[i*availX+j]=bgColorLoop;
} else {
data[i*availX+j]=bgColor;
@ -1158,7 +1200,7 @@ void FurnaceGUI::drawSampleEdit() {
if (xCoarse>=sample->samples) break;
int y1, y2;
int totalAdvance=0;
if (sample->depth==8) {
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
y1=((unsigned char)sample->data8[xCoarse]^0x80)*availY/256;
} else {
y1=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536;
@ -1171,7 +1213,7 @@ void FurnaceGUI::drawSampleEdit() {
totalAdvance+=xAdvanceCoarse;
if (xCoarse>=sample->samples) break;
do {
if (sample->depth==8) {
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
y2=((unsigned char)sample->data8[xCoarse]^0x80)*availY/256;
} else {
y2=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536;
@ -1209,11 +1251,11 @@ void FurnaceGUI::drawSampleEdit() {
sampleSelStart=0;
sampleSelEnd=sample->samples;
} else {
if (sample->samples>0 && (sample->depth==16 || sample->depth==8)) {
if (sample->samples>0 && (sample->depth==DIV_SAMPLE_DEPTH_16BIT || sample->depth==DIV_SAMPLE_DEPTH_8BIT)) {
sampleDragStart=rectMin;
sampleDragAreaSize=rectSize;
sampleDrag16=(sample->depth==16);
sampleDragTarget=(sample->depth==16)?((void*)sample->data16):((void*)sample->data8);
sampleDrag16=(sample->depth==DIV_SAMPLE_DEPTH_16BIT);
sampleDragTarget=(sample->depth==DIV_SAMPLE_DEPTH_16BIT)?((void*)sample->data16):((void*)sample->data8);
sampleDragLen=sample->samples;
sampleDragActive=true;
sampleSelStart=-1;
@ -1312,7 +1354,7 @@ void FurnaceGUI::drawSampleEdit() {
posX=samplePos+pos.x*sampleZoom;
if (posX>(int)sample->samples) posX=-1;
}
posY=(0.5-pos.y/rectSize.y)*((sample->depth==8)?255:32767);
posY=(0.5-pos.y/rectSize.y)*((sample->depth==DIV_SAMPLE_DEPTH_8BIT)?255:32767);
if (posX>=0) {
statusBar+=fmt::sprintf(" | (%d, %d)",posX,posY);
}
@ -1362,7 +1404,7 @@ void FurnaceGUI::drawSampleEdit() {
}
}
if (sample->depth!=8 && sample->depth!=16) {
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) {
statusBar="Non-8/16-bit samples cannot be edited without prior conversion.";
}