mirror of
https://github.com/tildearrow/furnace.git
synced 2025-01-03 14:11:11 +00:00
sample system rewrite! **PLEASE READ**
this commit mostly rewrites the sample system. as of now samples can be ADPCM, 8-bit, BRR or 16-bit or something... consider this VERY EXPERIMENTAL. if you find any issues REPORT THEM immediately. it's nearly 4am...
This commit is contained in:
parent
3542229448
commit
1e98f0c4a1
19 changed files with 545 additions and 390 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -16,3 +16,6 @@
|
||||||
[submodule "extern/fmt"]
|
[submodule "extern/fmt"]
|
||||||
path = extern/fmt
|
path = extern/fmt
|
||||||
url = https://github.com/fmtlib/fmt.git
|
url = https://github.com/fmtlib/fmt.git
|
||||||
|
[submodule "extern/adpcm"]
|
||||||
|
path = extern/adpcm
|
||||||
|
url = https://github.com/superctr/adpcm
|
||||||
|
|
|
@ -236,6 +236,12 @@ extern/SAASound/src/SAANoise.cpp
|
||||||
extern/SAASound/src/SAASndC.cpp
|
extern/SAASound/src/SAASndC.cpp
|
||||||
extern/SAASound/src/SAASound.cpp
|
extern/SAASound/src/SAASound.cpp
|
||||||
|
|
||||||
|
extern/adpcm/bs_codec.c
|
||||||
|
extern/adpcm/oki_codec.c
|
||||||
|
extern/adpcm/yma_codec.c
|
||||||
|
extern/adpcm/ymb_codec.c
|
||||||
|
extern/adpcm/ymz_codec.c
|
||||||
|
|
||||||
extern/Nuked-OPN2/ym3438.c
|
extern/Nuked-OPN2/ym3438.c
|
||||||
extern/opm/opm.c
|
extern/opm/opm.c
|
||||||
src/engine/platform/sound/sn76496.cpp
|
src/engine/platform/sound/sn76496.cpp
|
||||||
|
|
1
extern/adpcm
vendored
Submodule
1
extern/adpcm
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit ef7a217154badc3b99978ac481b268c8aab67bd8
|
|
@ -429,14 +429,26 @@ size | description
|
||||||
STR | sample name
|
STR | sample name
|
||||||
4 | length
|
4 | length
|
||||||
4 | rate
|
4 | rate
|
||||||
2 | volume
|
2 | volume (<58) or reserved
|
||||||
2 | pitch
|
2 | pitch (<58) or reserved
|
||||||
1 | depth
|
1 | depth
|
||||||
|
| - 0: ZX Spectrum overlay drum (1-bit)
|
||||||
|
| - 1: 1-bit NES DPCM (1-bit)
|
||||||
|
| - 4: QSound ADPCM
|
||||||
|
| - 5: ADPCM-A
|
||||||
|
| - 6: ADPCM-B
|
||||||
|
| - 7: X68000 ADPCM
|
||||||
|
| - 8: 8-bit PCM
|
||||||
|
| - 9: BRR (SNES)
|
||||||
|
| - 10: VOX
|
||||||
|
| - 16: 16-bit PCM
|
||||||
1 | reserved
|
1 | reserved
|
||||||
2 | C-4 rate (>=32) or reserved
|
2 | C-4 rate (>=32) or reserved
|
||||||
4 | loop point (>=19) or reserved
|
4 | loop point (>=19) or reserved
|
||||||
| - -1 means no loop
|
| - -1 means no loop
|
||||||
2?? | sample data (always 16-bit)
|
??? | sample data
|
||||||
|
| - version<58 size is length*2
|
||||||
|
| - version>=58 size is length
|
||||||
|
|
||||||
# pattern
|
# pattern
|
||||||
|
|
||||||
|
|
|
@ -434,26 +434,6 @@ void DivEngine::notifyWaveChange(int wave) {
|
||||||
isBusy.unlock();
|
isBusy.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ADPCM code attribution: https://wiki.neogeodev.org/index.php?title=ADPCM_codecs
|
|
||||||
|
|
||||||
static short adSteps[49]={
|
|
||||||
16, 17, 19, 21, 23, 25, 28, 31, 34, 37,
|
|
||||||
41, 45, 50, 55, 60, 66, 73, 80, 88, 97,
|
|
||||||
107, 118, 130, 143, 157, 173, 190, 209, 230, 253,
|
|
||||||
279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
|
|
||||||
724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
|
|
||||||
};
|
|
||||||
|
|
||||||
static int adStepSeek[16]={
|
|
||||||
-1, -1, -1, -1, 2, 5, 7, 9, -1, -1, -1, -1, 2, 5, 7, 9
|
|
||||||
};
|
|
||||||
|
|
||||||
static double samplePitches[11]={
|
|
||||||
0.1666666666, 0.2, 0.25, 0.333333333, 0.5,
|
|
||||||
1,
|
|
||||||
2, 3, 4, 5, 6
|
|
||||||
};
|
|
||||||
|
|
||||||
void DivEngine::renderSamplesP() {
|
void DivEngine::renderSamplesP() {
|
||||||
isBusy.lock();
|
isBusy.lock();
|
||||||
renderSamples();
|
renderSamples();
|
||||||
|
@ -463,117 +443,12 @@ void DivEngine::renderSamplesP() {
|
||||||
void DivEngine::renderSamples() {
|
void DivEngine::renderSamples() {
|
||||||
sPreview.sample=-1;
|
sPreview.sample=-1;
|
||||||
sPreview.pos=0;
|
sPreview.pos=0;
|
||||||
if (jediTable==NULL) {
|
|
||||||
jediTable=new int[16*49];
|
|
||||||
for (int step=0; step<49; step++) {
|
|
||||||
for (int nib=0; nib<16; nib++) {
|
|
||||||
int value=(2*(nib&0x07)+1)*adSteps[step]/8;
|
|
||||||
jediTable[step*16+nib]=((nib&0x08)!=0)?-value:value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0; i<song.sampleLen; i++) {
|
for (int i=0; i<song.sampleLen; i++) {
|
||||||
DivSample* s=song.sample[i];
|
song.sample[i]->render();
|
||||||
if (s->rendLength!=0) {
|
|
||||||
delete[] s->rendData;
|
|
||||||
delete[] s->adpcmRendData;
|
|
||||||
}
|
|
||||||
s->rendLength=(double)s->length/samplePitches[s->pitch];
|
|
||||||
if (s->rendLength==0) {
|
|
||||||
s->adpcmRendLength=0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
s->rendData=new short[s->rendLength];
|
|
||||||
size_t adpcmLen=((s->rendLength>>1)+255)&0xffffff00;
|
|
||||||
if (adpcmLen>1048576) adpcmLen=1048576;
|
|
||||||
s->adpcmRendLength=adpcmLen;
|
|
||||||
s->adpcmRendData=new unsigned char[adpcmLen];
|
|
||||||
memset(s->adpcmRendData,0,adpcmLen);
|
|
||||||
|
|
||||||
// step 1: render to PCM
|
|
||||||
unsigned int k=0;
|
|
||||||
float mult=(float)(s->vol)/50.0f;
|
|
||||||
for (double j=0; j<s->length; j+=samplePitches[s->pitch]) {
|
|
||||||
if (k>=s->rendLength) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (s->depth==8) {
|
|
||||||
float next=(float)(s->data[(unsigned int)j]-0x80)*mult;
|
|
||||||
s->rendData[k++]=fmin(fmax(next,-128),127);
|
|
||||||
} else {
|
|
||||||
float next=(float)s->data[(unsigned int)j]*mult;
|
|
||||||
s->rendData[k++]=fmin(fmax(next,-32768),32767);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// step 2: render to ADPCM
|
|
||||||
int acc=0;
|
|
||||||
int decstep=0;
|
|
||||||
int diff=0;
|
|
||||||
int step=0;
|
|
||||||
int predsample=0;
|
|
||||||
int index=0;
|
|
||||||
int prevsample=0;
|
|
||||||
int previndex=0;
|
|
||||||
for (unsigned int j=0; j<s->adpcmRendLength*2; j++) {
|
|
||||||
unsigned char encoded=0;
|
|
||||||
int tempstep=0;
|
|
||||||
|
|
||||||
predsample=prevsample;
|
|
||||||
index=previndex;
|
|
||||||
step=adSteps[index];
|
|
||||||
|
|
||||||
short sample=(j<s->rendLength)?((s->depth==16)?(s->rendData[j]>>4):(s->rendData[j]<<4)):0;
|
|
||||||
if (sample>0x7d0) sample=0x7d0;
|
|
||||||
if (sample<-0x7d0) sample=-0x7d0;
|
|
||||||
diff=sample-predsample;
|
|
||||||
if (diff>=0) {
|
|
||||||
encoded=0;
|
|
||||||
} else {
|
|
||||||
encoded=8;
|
|
||||||
diff=-diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
tempstep=step;
|
|
||||||
if (diff>=tempstep) {
|
|
||||||
encoded|=4;
|
|
||||||
diff-=tempstep;
|
|
||||||
}
|
|
||||||
tempstep>>=1;
|
|
||||||
if (diff>=tempstep) {
|
|
||||||
encoded|=2;
|
|
||||||
diff-=tempstep;
|
|
||||||
}
|
|
||||||
tempstep>>=1;
|
|
||||||
if (diff>=tempstep) encoded|=1;
|
|
||||||
|
|
||||||
acc+=jediTable[decstep+encoded];
|
|
||||||
/*if (acc>0x7ff || acc<-0x800) {
|
|
||||||
logW("clipping! %d\n",acc);
|
|
||||||
}*/
|
|
||||||
acc&=0xfff;
|
|
||||||
if (acc&0x800) acc|=~0xfff;
|
|
||||||
decstep+=adStepSeek[encoded&7]*16;
|
|
||||||
if (decstep<0) decstep=0;
|
|
||||||
if (decstep>48*16) decstep=48*16;
|
|
||||||
predsample=(short)acc;
|
|
||||||
|
|
||||||
index+=adStepSeek[encoded];
|
|
||||||
if (index<0) index=0;
|
|
||||||
if (index>48) index=48;
|
|
||||||
|
|
||||||
prevsample=predsample;
|
|
||||||
previndex=index;
|
|
||||||
|
|
||||||
if (j&1) {
|
|
||||||
s->adpcmRendData[j>>1]|=encoded;
|
|
||||||
} else {
|
|
||||||
s->adpcmRendData[j>>1]=encoded<<4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// step 3: allocate ADPCM samples
|
// step 3: allocate ADPCM samples
|
||||||
if (adpcmMem==NULL) adpcmMem=new unsigned char[16777216];
|
if (adpcmMem==NULL) adpcmMem=new unsigned char[16777216];
|
||||||
|
|
||||||
|
@ -631,6 +506,7 @@ void DivEngine::renderSamples() {
|
||||||
memPos+=length+16;
|
memPos+=length+16;
|
||||||
}
|
}
|
||||||
qsoundMemLen=memPos+256;
|
qsoundMemLen=memPos+256;
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivEngine::createNew() {
|
void DivEngine::createNew() {
|
||||||
|
@ -1901,22 +1777,23 @@ bool DivEngine::addSampleFromFile(const char* path) {
|
||||||
sample->name=sName;
|
sample->name=sName;
|
||||||
|
|
||||||
int index=0;
|
int index=0;
|
||||||
sample->length=si.frames;
|
if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8) {
|
||||||
sample->data=new short[si.frames];
|
sample->depth=8;
|
||||||
sample->depth=16;
|
} else {
|
||||||
sample->vol=50;
|
sample->depth=16;
|
||||||
sample->pitch=5;
|
}
|
||||||
|
sample->init(si.frames);
|
||||||
for (int i=0; i<si.frames*si.channels; i+=si.channels) {
|
for (int i=0; i<si.frames*si.channels; i+=si.channels) {
|
||||||
int averaged=0;
|
int averaged=0;
|
||||||
for (int j=0; j<si.channels; j++) {
|
for (int j=0; j<si.channels; j++) {
|
||||||
if (((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8)) {
|
averaged+=buf[i+j];
|
||||||
averaged+=buf[i+j];
|
|
||||||
} else {
|
|
||||||
averaged+=buf[i+j];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
averaged/=si.channels;
|
averaged/=si.channels;
|
||||||
sample->data[index++]=averaged;
|
if (((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8)) {
|
||||||
|
sample->data8[index++]=averaged;
|
||||||
|
} else {
|
||||||
|
sample->data16[index++]=averaged;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
delete[] buf;
|
delete[] buf;
|
||||||
sample->rate=si.samplerate;
|
sample->rate=si.samplerate;
|
||||||
|
|
|
@ -37,8 +37,8 @@
|
||||||
warnings+=(String("\n")+x); \
|
warnings+=(String("\n")+x); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DIV_VERSION "dev57"
|
#define DIV_VERSION "dev58"
|
||||||
#define DIV_ENGINE_VERSION 57
|
#define DIV_ENGINE_VERSION 58
|
||||||
|
|
||||||
enum DivStatusView {
|
enum DivStatusView {
|
||||||
DIV_STATUS_NOTHING=0,
|
DIV_STATUS_NOTHING=0,
|
||||||
|
@ -223,8 +223,6 @@ class DivEngine {
|
||||||
|
|
||||||
size_t totalProcessed;
|
size_t totalProcessed;
|
||||||
|
|
||||||
private: int* jediTable;
|
|
||||||
|
|
||||||
DivSystem systemFromFile(unsigned char val);
|
DivSystem systemFromFile(unsigned char val);
|
||||||
unsigned char systemToFile(DivSystem val);
|
unsigned char systemToFile(DivSystem val);
|
||||||
int dispatchCmd(DivCommand c);
|
int dispatchCmd(DivCommand c);
|
||||||
|
@ -680,7 +678,6 @@ class DivEngine {
|
||||||
metroPos(0),
|
metroPos(0),
|
||||||
metroAmp(0.0f),
|
metroAmp(0.0f),
|
||||||
totalProcessed(0),
|
totalProcessed(0),
|
||||||
jediTable(NULL),
|
|
||||||
oscBuf{NULL,NULL},
|
oscBuf{NULL,NULL},
|
||||||
oscSize(1),
|
oscSize(1),
|
||||||
adpcmMem(NULL),
|
adpcmMem(NULL),
|
||||||
|
|
|
@ -42,6 +42,12 @@ struct InflateBlock {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static double samplePitches[11]={
|
||||||
|
0.1666666666, 0.2, 0.25, 0.333333333, 0.5,
|
||||||
|
1,
|
||||||
|
2, 3, 4, 5, 6
|
||||||
|
};
|
||||||
|
|
||||||
bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
SafeReader reader=SafeReader(file,len);
|
SafeReader reader=SafeReader(file,len);
|
||||||
warnings="";
|
warnings="";
|
||||||
|
@ -607,9 +613,12 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
}
|
}
|
||||||
for (int i=0; i<ds.sampleLen; i++) {
|
for (int i=0; i<ds.sampleLen; i++) {
|
||||||
DivSample* sample=new DivSample;
|
DivSample* sample=new DivSample;
|
||||||
sample->length=reader.readI();
|
int length=reader.readI();
|
||||||
if (sample->length<0) {
|
int pitch=5;
|
||||||
logE("invalid sample length %d. are we doing something wrong?\n",sample->length);
|
int vol=50;
|
||||||
|
short* data;
|
||||||
|
if (length<0) {
|
||||||
|
logE("invalid sample length %d. are we doing something wrong?\n",length);
|
||||||
lastError="file is corrupt or unreadable at samples";
|
lastError="file is corrupt or unreadable at samples";
|
||||||
delete[] file;
|
delete[] file;
|
||||||
return false;
|
return false;
|
||||||
|
@ -619,31 +628,57 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
||||||
} else {
|
} else {
|
||||||
sample->name="";
|
sample->name="";
|
||||||
}
|
}
|
||||||
logD("%d name %s (%d)\n",i,sample->name.c_str(),sample->length);
|
logD("%d name %s (%d)\n",i,sample->name.c_str(),length);
|
||||||
if (ds.version<0x0b) {
|
sample->rate=22050;
|
||||||
sample->rate=22050;
|
if (ds.version>=0x0b) {
|
||||||
sample->pitch=5;
|
|
||||||
sample->vol=50;
|
|
||||||
} else {
|
|
||||||
sample->rate=fileToDivRate(reader.readC());
|
sample->rate=fileToDivRate(reader.readC());
|
||||||
sample->pitch=reader.readC();
|
pitch=reader.readC();
|
||||||
sample->vol=reader.readC();
|
vol=reader.readC();
|
||||||
}
|
}
|
||||||
logI("pitch and vol: %d %d\n",sample->pitch,sample->vol);
|
|
||||||
if (ds.version>0x15) {
|
if (ds.version>0x15) {
|
||||||
sample->depth=reader.readC();
|
sample->depth=reader.readC();
|
||||||
|
if (sample->depth!=8 && sample->depth!=16) {
|
||||||
|
logW("%d: sample depth is wrong! (%d)\n",i,sample->depth);
|
||||||
|
sample->depth=16;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
sample->depth=16;
|
sample->depth=16;
|
||||||
}
|
}
|
||||||
if (sample->length>0) {
|
if (length>0) {
|
||||||
if (ds.version<0x0b) {
|
if (ds.version<0x0b) {
|
||||||
sample->data=new short[1+(sample->length/2)];
|
data=new short[1+(length/2)];
|
||||||
reader.read(sample->data,sample->length);
|
reader.read(data,length);
|
||||||
sample->length/=2;
|
length/=2;
|
||||||
} else {
|
} else {
|
||||||
sample->data=new short[sample->length];
|
data=new short[length];
|
||||||
reader.read(sample->data,sample->length*2);
|
reader.read(data,length*2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pitch!=5) {
|
||||||
|
logD("%d: scaling from %d...\n",i,pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// render data
|
||||||
|
if (!sample->init((double)length/samplePitches[pitch])) {
|
||||||
|
logE("%d: error while initializing sample!\n",i);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int k=0;
|
||||||
|
float mult=(float)(vol)/50.0f;
|
||||||
|
for (double j=0; j<length; j+=samplePitches[pitch]) {
|
||||||
|
if (k>=sample->samples) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (sample->depth==8) {
|
||||||
|
float next=(float)(data[(unsigned int)j]-0x80)*mult;
|
||||||
|
sample->data8[k++]=fmin(fmax(next,-128),127);
|
||||||
|
} else {
|
||||||
|
float next=(float)data[(unsigned int)j]*mult;
|
||||||
|
sample->data16[k++]=fmin(fmax(next,-32768),32767);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] data;
|
||||||
}
|
}
|
||||||
ds.sample.push_back(sample);
|
ds.sample.push_back(sample);
|
||||||
}
|
}
|
||||||
|
@ -996,6 +1031,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
|
|
||||||
// read samples
|
// read samples
|
||||||
for (int i=0; i<ds.sampleLen; i++) {
|
for (int i=0; i<ds.sampleLen; i++) {
|
||||||
|
int vol=0;
|
||||||
|
int pitch=0;
|
||||||
|
|
||||||
reader.seek(samplePtr[i],SEEK_SET);
|
reader.seek(samplePtr[i],SEEK_SET);
|
||||||
reader.read(magic,4);
|
reader.read(magic,4);
|
||||||
if (strcmp(magic,"SMPL")!=0) {
|
if (strcmp(magic,"SMPL")!=0) {
|
||||||
|
@ -1008,10 +1046,14 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
DivSample* sample=new DivSample;
|
DivSample* sample=new DivSample;
|
||||||
|
|
||||||
sample->name=reader.readString();
|
sample->name=reader.readString();
|
||||||
sample->length=reader.readI();
|
sample->samples=reader.readI();
|
||||||
sample->rate=reader.readI();
|
sample->rate=reader.readI();
|
||||||
sample->vol=reader.readS();
|
if (ds.version<58) {
|
||||||
sample->pitch=reader.readS();
|
vol=reader.readS();
|
||||||
|
pitch=reader.readS();
|
||||||
|
} else {
|
||||||
|
reader.readI();
|
||||||
|
}
|
||||||
sample->depth=reader.readC();
|
sample->depth=reader.readC();
|
||||||
|
|
||||||
// reserved
|
// reserved
|
||||||
|
@ -1030,8 +1072,42 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
reader.readI();
|
reader.readI();
|
||||||
}
|
}
|
||||||
|
|
||||||
sample->data=new short[sample->length];
|
if (ds.version>=58) { // modern sample
|
||||||
reader.read(sample->data,2*sample->length);
|
sample->init(sample->samples);
|
||||||
|
reader.read(sample->getCurBuf(),sample->getCurBufLen());
|
||||||
|
} else { // legacy sample
|
||||||
|
int length=sample->samples;
|
||||||
|
short* data=new short[length];
|
||||||
|
reader.read(data,2*length);
|
||||||
|
|
||||||
|
if (pitch!=5) {
|
||||||
|
logD("%d: scaling from %d...\n",i,pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// render data
|
||||||
|
if (sample->depth!=8 && sample->depth!=16) {
|
||||||
|
logW("%d: sample depth is wrong! (%d)\n",i,sample->depth);
|
||||||
|
sample->depth=16;
|
||||||
|
}
|
||||||
|
sample->init(sample->samples);
|
||||||
|
|
||||||
|
unsigned int k=0;
|
||||||
|
float mult=(float)(vol)/50.0f;
|
||||||
|
for (double j=0; j<length; j+=samplePitches[pitch]) {
|
||||||
|
if (k>=sample->samples) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (sample->depth==8) {
|
||||||
|
float next=(float)(data[(unsigned int)j]-0x80)*mult;
|
||||||
|
sample->data8[k++]=fmin(fmax(next,-128),127);
|
||||||
|
} else {
|
||||||
|
float next=(float)data[(unsigned int)j]*mult;
|
||||||
|
sample->data16[k++]=fmin(fmax(next,-32768),32767);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] data;
|
||||||
|
}
|
||||||
|
|
||||||
ds.sample.push_back(sample);
|
ds.sample.push_back(sample);
|
||||||
}
|
}
|
||||||
|
@ -1390,16 +1466,15 @@ SafeWriter* DivEngine::saveFur() {
|
||||||
w->writeI(0);
|
w->writeI(0);
|
||||||
|
|
||||||
w->writeString(sample->name,false);
|
w->writeString(sample->name,false);
|
||||||
w->writeI(sample->length);
|
w->writeI(sample->samples);
|
||||||
w->writeI(sample->rate);
|
w->writeI(sample->rate);
|
||||||
w->writeS(sample->vol);
|
w->writeI(0); // reserved (for now)
|
||||||
w->writeS(sample->pitch);
|
|
||||||
w->writeC(sample->depth);
|
w->writeC(sample->depth);
|
||||||
w->writeC(0);
|
w->writeC(0);
|
||||||
w->writeS(sample->centerRate);
|
w->writeS(sample->centerRate);
|
||||||
w->writeI(sample->loopStart);
|
w->writeI(sample->loopStart);
|
||||||
|
|
||||||
w->write(sample->data,sample->length*2);
|
w->write(sample->getCurBuf(),sample->getCurBufLen());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PATTERN
|
/// PATTERN
|
||||||
|
@ -1728,13 +1803,14 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
||||||
|
|
||||||
w->writeC(song.sample.size());
|
w->writeC(song.sample.size());
|
||||||
for (DivSample* i: song.sample) {
|
for (DivSample* i: song.sample) {
|
||||||
w->writeI(i->length);
|
w->writeI(i->samples);
|
||||||
w->writeString(i->name,true);
|
w->writeString(i->name,true);
|
||||||
w->writeC(divToFileRate(i->rate));
|
w->writeC(divToFileRate(i->rate));
|
||||||
w->writeC(i->pitch);
|
w->writeC(5);
|
||||||
w->writeC(i->vol);
|
w->writeC(50);
|
||||||
w->writeC(i->depth);
|
// i'm too lazy to deal with .dmf's weird way of storing 8-bit samples
|
||||||
w->write(i->data,2*i->length);
|
w->writeC(16);
|
||||||
|
w->write(i->data16,i->length16);
|
||||||
}
|
}
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
|
|
|
@ -72,14 +72,10 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le
|
||||||
chan[i].audSub-=AMIGA_DIVIDER;
|
chan[i].audSub-=AMIGA_DIVIDER;
|
||||||
if (chan[i].audSub<0) {
|
if (chan[i].audSub<0) {
|
||||||
DivSample* s=parent->song.sample[chan[i].sample];
|
DivSample* s=parent->song.sample[chan[i].sample];
|
||||||
if (s->rendLength>0) {
|
if (s->samples>0) {
|
||||||
if (s->depth==8) {
|
chan[i].audDat=s->data8[chan[i].audPos++];
|
||||||
chan[i].audDat=s->rendData[chan[i].audPos++];
|
if (chan[i].audPos>=s->samples || chan[i].audPos>=131071) {
|
||||||
} else {
|
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
|
||||||
chan[i].audDat=s->rendData[chan[i].audPos++]>>8;
|
|
||||||
}
|
|
||||||
if (chan[i].audPos>=s->rendLength || chan[i].audPos>=131071) {
|
|
||||||
if (s->loopStart>=0 && s->loopStart<=(int)s->rendLength) {
|
|
||||||
chan[i].audPos=s->loopStart;
|
chan[i].audPos=s->loopStart;
|
||||||
} else {
|
} else {
|
||||||
chan[i].sample=-1;
|
chan[i].sample=-1;
|
||||||
|
|
|
@ -90,16 +90,12 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s
|
||||||
dacPeriod-=6;
|
dacPeriod-=6;
|
||||||
if (dacPeriod<1) {
|
if (dacPeriod<1) {
|
||||||
DivSample* s=parent->song.sample[dacSample];
|
DivSample* s=parent->song.sample[dacSample];
|
||||||
if (s->rendLength>0) {
|
if (s->samples>0) {
|
||||||
if (!isMuted[5]) {
|
if (!isMuted[5]) {
|
||||||
if (s->depth==8) {
|
immWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
|
||||||
immWrite(0x2a,(unsigned char)s->rendData[dacPos]+0x80);
|
|
||||||
} else {
|
|
||||||
immWrite(0x2a,((unsigned short)s->rendData[dacPos]+0x8000)>>8);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (++dacPos>=s->rendLength) {
|
if (++dacPos>=s->samples) {
|
||||||
if (s->loopStart>=0 && s->loopStart<=(int)s->rendLength) {
|
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
|
||||||
dacPos=s->loopStart;
|
dacPos=s->loopStart;
|
||||||
} else {
|
} else {
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
|
@ -164,16 +160,12 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
|
||||||
dacPeriod-=24;
|
dacPeriod-=24;
|
||||||
if (dacPeriod<1) {
|
if (dacPeriod<1) {
|
||||||
DivSample* s=parent->song.sample[dacSample];
|
DivSample* s=parent->song.sample[dacSample];
|
||||||
if (s->rendLength>0) {
|
if (s->samples>0) {
|
||||||
if (!isMuted[5]) {
|
if (!isMuted[5]) {
|
||||||
if (s->depth==8) {
|
immWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
|
||||||
immWrite(0x2a,(unsigned char)s->rendData[dacPos]+0x80);
|
|
||||||
} else {
|
|
||||||
immWrite(0x2a,((unsigned short)s->rendData[dacPos]+0x8000)>>8);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (++dacPos>=s->rendLength) {
|
if (++dacPos>=s->samples) {
|
||||||
if (s->loopStart>=0 && s->loopStart<=(int)s->rendLength) {
|
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
|
||||||
dacPos=s->loopStart;
|
dacPos=s->loopStart;
|
||||||
} else {
|
} else {
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
|
|
|
@ -76,16 +76,12 @@ void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len)
|
||||||
dacPeriod+=dacRate;
|
dacPeriod+=dacRate;
|
||||||
if (dacPeriod>=rate) {
|
if (dacPeriod>=rate) {
|
||||||
DivSample* s=parent->song.sample[dacSample];
|
DivSample* s=parent->song.sample[dacSample];
|
||||||
if (s->rendLength>0) {
|
if (s->samples>0) {
|
||||||
if (!isMuted[4]) {
|
if (!isMuted[4]) {
|
||||||
if (s->depth==8) {
|
rWrite(0x4011,((unsigned char)s->data8[dacPos]+0x80)>>1);
|
||||||
rWrite(0x4011,((unsigned char)s->rendData[dacPos]+0x80)>>1);
|
|
||||||
} else {
|
|
||||||
rWrite(0x4011,((unsigned short)s->rendData[dacPos]+0x8000)>>9);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (++dacPos>=s->rendLength) {
|
if (++dacPos>=s->samples) {
|
||||||
if (s->loopStart>=0 && s->loopStart<=(int)s->rendLength) {
|
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
|
||||||
dacPos=s->loopStart;
|
dacPos=s->loopStart;
|
||||||
} else {
|
} else {
|
||||||
dacSample=-1;
|
dacSample=-1;
|
||||||
|
|
|
@ -82,21 +82,16 @@ 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->song.sample[chan[i].dacSample];
|
DivSample* s=parent->song.sample[chan[i].dacSample];
|
||||||
if (s->rendLength<=0) {
|
if (s->samples<=0) {
|
||||||
chan[i].dacSample=-1;
|
chan[i].dacSample=-1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
chWrite(i,0x07,0);
|
chWrite(i,0x07,0);
|
||||||
if (s->depth==8) {
|
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->rendData[chan[i].dacPos]+0x80)>>3));
|
|
||||||
} else {
|
|
||||||
chWrite(i,0x04,0xdf);
|
|
||||||
chWrite(i,0x06,(((unsigned short)s->rendData[chan[i].dacPos]+0x8000)>>11));
|
|
||||||
}
|
|
||||||
chan[i].dacPos++;
|
chan[i].dacPos++;
|
||||||
if (chan[i].dacPos>=s->rendLength) {
|
if (chan[i].dacPos>=s->samples) {
|
||||||
if (s->loopStart>=0 && s->loopStart<=(int)s->rendLength) {
|
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
|
||||||
chan[i].dacPos=s->loopStart;
|
chan[i].dacPos=s->loopStart;
|
||||||
} else {
|
} else {
|
||||||
chan[i].dacSample=-1;
|
chan[i].dacSample=-1;
|
||||||
|
|
|
@ -296,18 +296,18 @@ void DivPlatformQSound::tick() {
|
||||||
} else {
|
} else {
|
||||||
off=(double)s->centerRate/24038.0/16.0;
|
off=(double)s->centerRate/24038.0/16.0;
|
||||||
}
|
}
|
||||||
qsound_bank = 0x8000 | (s->rendOffQsound >> 16);
|
qsound_bank = 0x8000 | (s->offQSound >> 16);
|
||||||
qsound_addr = s->rendOffQsound & 0xffff;
|
qsound_addr = s->offQSound & 0xffff;
|
||||||
|
|
||||||
int length = s->length;
|
int length = s->samples;
|
||||||
if (length > 65536 - 16) {
|
if (length > 65536 - 16) {
|
||||||
length = 65536 - 16;
|
length = 65536 - 16;
|
||||||
}
|
}
|
||||||
if (s->loopStart == -1 || s->loopStart >= length) {
|
if (s->loopStart == -1 || s->loopStart >= length) {
|
||||||
qsound_end = s->rendOffQsound + length + 15;
|
qsound_end = s->offQSound + length + 15;
|
||||||
qsound_loop = 15;
|
qsound_loop = 15;
|
||||||
} else {
|
} else {
|
||||||
qsound_end = s->rendOffQsound + length;
|
qsound_end = s->offQSound + length;
|
||||||
qsound_loop = length - s->loopStart;
|
qsound_loop = length - s->loopStart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,22 +44,17 @@ 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->song.sample[chan[i].pcm.sample];
|
DivSample* s=parent->song.sample[chan[i].pcm.sample];
|
||||||
if (s->rendLength<=0) {
|
if (s->samples<=0) {
|
||||||
chan[i].pcm.sample=-1;
|
chan[i].pcm.sample=-1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!isMuted[i]) {
|
if (!isMuted[i]) {
|
||||||
if (s->depth==8) {
|
pcmL+=(s->data8[chan[i].pcm.pos>>8]*chan[i].chVolL);
|
||||||
pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL);
|
pcmR+=(s->data8[chan[i].pcm.pos>>8]*chan[i].chVolR);
|
||||||
pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR);
|
|
||||||
} else {
|
|
||||||
pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL)>>8;
|
|
||||||
pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR)>>8;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
chan[i].pcm.pos+=chan[i].pcm.freq;
|
chan[i].pcm.pos+=chan[i].pcm.freq;
|
||||||
if (chan[i].pcm.pos>=(s->rendLength<<8)) {
|
if (chan[i].pcm.pos>=(s->samples<<8)) {
|
||||||
if (s->loopStart>=0 && s->loopStart<=(int)s->rendLength) {
|
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
|
||||||
chan[i].pcm.pos=s->loopStart<<8;
|
chan[i].pcm.pos=s->loopStart<<8;
|
||||||
} else {
|
} else {
|
||||||
chan[i].pcm.sample=-1;
|
chan[i].pcm.sample=-1;
|
||||||
|
@ -152,17 +147,17 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
||||||
chan[c.chan].furnacePCM=true;
|
chan[c.chan].furnacePCM=true;
|
||||||
if (dumpWrites) { // Sega PCM writes
|
if (dumpWrites) { // Sega PCM writes
|
||||||
DivSample* s=parent->song.sample[chan[c.chan].pcm.sample];
|
DivSample* s=parent->song.sample[chan[c.chan].pcm.sample];
|
||||||
addWrite(0x10086+(c.chan<<3),3+((s->rendOffP>>16)<<3));
|
addWrite(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3));
|
||||||
addWrite(0x10084+(c.chan<<3),(s->rendOffP)&0xff);
|
addWrite(0x10084+(c.chan<<3),(s->offSegaPCM)&0xff);
|
||||||
addWrite(0x10085+(c.chan<<3),(s->rendOffP>>8)&0xff);
|
addWrite(0x10085+(c.chan<<3),(s->offSegaPCM>>8)&0xff);
|
||||||
addWrite(0x10006+(c.chan<<3),MIN(255,((s->rendOffP&0xffff)+s->rendLength-1)>>8));
|
addWrite(0x10006+(c.chan<<3),MIN(255,((s->offSegaPCM&0xffff)+s->length8-1)>>8));
|
||||||
if (s->loopStart<0 || s->loopStart>=(int)s->rendLength) {
|
if (s->loopStart<0 || s->loopStart>=(int)s->length8) {
|
||||||
addWrite(0x10086+(c.chan<<3),2+((s->rendOffP>>16)<<3));
|
addWrite(0x10086+(c.chan<<3),2+((s->offSegaPCM>>16)<<3));
|
||||||
} else {
|
} else {
|
||||||
int loopPos=(s->rendOffP&0xffff)+s->loopStart+s->loopOffP;
|
int loopPos=(s->offSegaPCM&0xffff)+s->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->rendOffP>>16)<<3));
|
addWrite(0x10086+(c.chan<<3),((s->offSegaPCM>>16)<<3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -182,17 +177,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->song.sample[chan[c.chan].pcm.sample];
|
DivSample* s=parent->song.sample[chan[c.chan].pcm.sample];
|
||||||
addWrite(0x10086+(c.chan<<3),3+((s->rendOffP>>16)<<3));
|
addWrite(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3));
|
||||||
addWrite(0x10084+(c.chan<<3),(s->rendOffP)&0xff);
|
addWrite(0x10084+(c.chan<<3),(s->offSegaPCM)&0xff);
|
||||||
addWrite(0x10085+(c.chan<<3),(s->rendOffP>>8)&0xff);
|
addWrite(0x10085+(c.chan<<3),(s->offSegaPCM>>8)&0xff);
|
||||||
addWrite(0x10006+(c.chan<<3),MIN(255,((s->rendOffP&0xffff)+s->rendLength-1)>>8));
|
addWrite(0x10006+(c.chan<<3),MIN(255,((s->offSegaPCM&0xffff)+s->length8-1)>>8));
|
||||||
if (s->loopStart<0 || s->loopStart>=(int)s->rendLength) {
|
if (s->loopStart<0 || s->loopStart>=(int)s->length8) {
|
||||||
addWrite(0x10086+(c.chan<<3),2+((s->rendOffP>>16)<<3));
|
addWrite(0x10086+(c.chan<<3),2+((s->offSegaPCM>>16)<<3));
|
||||||
} else {
|
} else {
|
||||||
int loopPos=(s->rendOffP&0xffff)+s->loopStart+s->loopOffP;
|
int loopPos=(s->offSegaPCM&0xffff)+s->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->rendOffP>>16)<<3));
|
addWrite(0x10086+(c.chan<<3),((s->offSegaPCM>>16)<<3));
|
||||||
}
|
}
|
||||||
addWrite(0x10007+(c.chan<<3),chan[c.chan].pcm.freq);
|
addWrite(0x10007+(c.chan<<3),chan[c.chan].pcm.freq);
|
||||||
}
|
}
|
||||||
|
|
|
@ -437,9 +437,9 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
DivSample* s=parent->song.sample[12*sampleBank+c.value%12];
|
DivSample* s=parent->song.sample[12*sampleBank+c.value%12];
|
||||||
immWrite(0x110+c.chan-7,(s->rendOff>>8)&0xff);
|
immWrite(0x110+c.chan-7,(s->offA>>8)&0xff);
|
||||||
immWrite(0x118+c.chan-7,s->rendOff>>16);
|
immWrite(0x118+c.chan-7,s->offA>>16);
|
||||||
int end=s->rendOff+s->adpcmRendLength-1;
|
int end=s->offA+s->lengthA-1;
|
||||||
immWrite(0x120+c.chan-7,(end>>8)&0xff);
|
immWrite(0x120+c.chan-7,(end>>8)&0xff);
|
||||||
immWrite(0x128+c.chan-7,end>>16);
|
immWrite(0x128+c.chan-7,end>>16);
|
||||||
immWrite(0x108+(c.chan-7),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol));
|
immWrite(0x108+(c.chan-7),isMuted[c.chan]?0:((chan[c.chan].pan<<6)|chan[c.chan].vol));
|
||||||
|
|
|
@ -1132,24 +1132,24 @@ 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->rendLength) {
|
if (sPreview.pos>=s->samples) {
|
||||||
samp_temp=0;
|
samp_temp=0;
|
||||||
} else {
|
} else {
|
||||||
samp_temp=s->rendData[sPreview.pos++];
|
samp_temp=s->data16[sPreview.pos++];
|
||||||
}
|
}
|
||||||
if (s->depth==8) samp_temp<<=8;
|
if (s->depth==8) samp_temp<<=8;
|
||||||
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->rendLength) {
|
if (sPreview.pos>=s->samples) {
|
||||||
if (s->loopStart>=0 && s->loopStart<(int)s->rendLength) {
|
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
|
||||||
sPreview.pos=s->loopStart;
|
sPreview.pos=s->loopStart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sPreview.pos>=s->rendLength) {
|
if (sPreview.pos>=s->samples) {
|
||||||
if (s->loopStart>=0 && s->loopStart<(int)s->rendLength) {
|
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
|
||||||
sPreview.pos=s->loopStart;
|
sPreview.pos=s->loopStart;
|
||||||
} else {
|
} else {
|
||||||
sPreview.sample=-1;
|
sPreview.sample=-1;
|
||||||
|
|
|
@ -23,20 +23,24 @@
|
||||||
#include <sndfile.h>
|
#include <sndfile.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "../../extern/adpcm/bs_codec.h"
|
||||||
|
#include "../../extern/adpcm/oki_codec.h"
|
||||||
|
#include "../../extern/adpcm/yma_codec.h"
|
||||||
|
#include "../../extern/adpcm/ymb_codec.h"
|
||||||
|
#include "../../extern/adpcm/ymz_codec.h"
|
||||||
|
}
|
||||||
|
|
||||||
bool DivSample::save(const char* path) {
|
bool DivSample::save(const char* path) {
|
||||||
SNDFILE* f;
|
SNDFILE* f;
|
||||||
SF_INFO si;
|
SF_INFO si;
|
||||||
memset(&si,0,sizeof(SF_INFO));
|
memset(&si,0,sizeof(SF_INFO));
|
||||||
|
|
||||||
if (length<1) return false;
|
if (length16<1) return false;
|
||||||
|
|
||||||
si.channels=1;
|
si.channels=1;
|
||||||
si.samplerate=rate;
|
si.samplerate=rate;
|
||||||
if (depth==16) {
|
si.format=SF_FORMAT_PCM_16|SF_FORMAT_WAV;
|
||||||
si.format=SF_FORMAT_PCM_16|SF_FORMAT_WAV;
|
|
||||||
} else {
|
|
||||||
si.format=SF_FORMAT_PCM_U8|SF_FORMAT_WAV;
|
|
||||||
}
|
|
||||||
|
|
||||||
f=sf_open(path,SFM_WRITE,&si);
|
f=sf_open(path,SFM_WRITE,&si);
|
||||||
|
|
||||||
|
@ -58,28 +62,253 @@ bool DivSample::save(const char* path) {
|
||||||
inst.loop_count = 1;
|
inst.loop_count = 1;
|
||||||
inst.loops[0].mode = SF_LOOP_FORWARD;
|
inst.loops[0].mode = SF_LOOP_FORWARD;
|
||||||
inst.loops[0].start = loopStart;
|
inst.loops[0].start = loopStart;
|
||||||
inst.loops[0].end = length;
|
inst.loops[0].end = samples;
|
||||||
}
|
}
|
||||||
sf_command(f, SFC_SET_INSTRUMENT, &inst, sizeof(inst));
|
sf_command(f, SFC_SET_INSTRUMENT, &inst, sizeof(inst));
|
||||||
|
|
||||||
if (depth==16) {
|
sf_write_short(f,data16,length16);
|
||||||
sf_writef_short(f,data,length);
|
|
||||||
} else {
|
|
||||||
short* cbuf=new short[length];
|
|
||||||
for (int i=0; i<length; i++) {
|
|
||||||
cbuf[i]=(data[i]<<8)^0x8000;
|
|
||||||
}
|
|
||||||
sf_writef_short(f,cbuf,length);
|
|
||||||
delete[] cbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
sf_close(f);
|
sf_close(f);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DivSample::~DivSample() {
|
bool DivSample::initInternal(unsigned char d, int count) {
|
||||||
if (data) delete[] data;
|
switch (d) {
|
||||||
if (rendData) delete[] rendData;
|
case 0: // 1-bit
|
||||||
if (adpcmRendData) delete[] adpcmRendData;
|
if (data1!=NULL) delete[] data1;
|
||||||
|
length1=(count+7)/8;
|
||||||
|
data1=new unsigned char[length1];
|
||||||
|
memset(data1,0,length1);
|
||||||
|
break;
|
||||||
|
case 1: // DPCM
|
||||||
|
if (dataDPCM!=NULL) delete[] dataDPCM;
|
||||||
|
lengthDPCM=(count+7)/8;
|
||||||
|
dataDPCM=new unsigned char[lengthDPCM];
|
||||||
|
memset(dataDPCM,0,lengthDPCM);
|
||||||
|
break;
|
||||||
|
case 4: // 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
|
||||||
|
if (dataA!=NULL) delete[] dataA;
|
||||||
|
lengthA=(count+1)/2;
|
||||||
|
dataA=new unsigned char[lengthA];
|
||||||
|
memset(dataA,0,lengthA);
|
||||||
|
break;
|
||||||
|
case 6: // ADPCM-B
|
||||||
|
if (dataB!=NULL) delete[] dataB;
|
||||||
|
lengthB=(count+1)/2;
|
||||||
|
dataB=new unsigned char[lengthB];
|
||||||
|
memset(dataB,0,lengthB);
|
||||||
|
break;
|
||||||
|
case 7: // X68000 ADPCM
|
||||||
|
if (dataX68!=NULL) delete[] dataX68;
|
||||||
|
lengthX68=(count+1)/2;
|
||||||
|
dataX68=new unsigned char[lengthX68];
|
||||||
|
memset(dataX68,0,lengthX68);
|
||||||
|
break;
|
||||||
|
case 8: // 8-bit
|
||||||
|
if (data8!=NULL) delete[] data8;
|
||||||
|
length8=count;
|
||||||
|
data8=new signed char[length8];
|
||||||
|
memset(data8,0,length8);
|
||||||
|
break;
|
||||||
|
case 9: // BRR
|
||||||
|
if (dataBRR!=NULL) delete[] dataBRR;
|
||||||
|
lengthBRR=9*((count+15)/16);
|
||||||
|
dataBRR=new unsigned char[lengthBRR];
|
||||||
|
memset(dataBRR,0,lengthBRR);
|
||||||
|
break;
|
||||||
|
case 10: // VOX
|
||||||
|
if (dataVOX!=NULL) delete[] dataVOX;
|
||||||
|
lengthVOX=(count+1)/2;
|
||||||
|
dataVOX=new unsigned char[lengthVOX];
|
||||||
|
memset(dataVOX,0,lengthVOX);
|
||||||
|
break;
|
||||||
|
case 16: // 16-bit
|
||||||
|
if (data16!=NULL) delete[] data16;
|
||||||
|
length16=count*2;
|
||||||
|
data16=new short[count];
|
||||||
|
memset(data16,0,count*sizeof(short));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DivSample::init(unsigned int count) {
|
||||||
|
if (!initInternal(depth,count)) return false;
|
||||||
|
samples=count;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivSample::render() {
|
||||||
|
// step 1: convert to 16-bit if needed
|
||||||
|
if (depth!=16) {
|
||||||
|
if (!initInternal(16,samples)) return;
|
||||||
|
switch (depth) {
|
||||||
|
case 0: // 1-bit
|
||||||
|
for (unsigned int i=0; i<samples; i++) {
|
||||||
|
data16[i]=((data1[i>>3]>>(i&7))&1)?0x7fff:-0x7fff;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1: { // DPCM
|
||||||
|
int accum=0;
|
||||||
|
for (unsigned int i=0; i<samples; i++) {
|
||||||
|
accum+=((data1[i>>3]>>(i&7))&1)?1:-1;
|
||||||
|
if (accum>63) accum=63;
|
||||||
|
if (accum<-64) accum=-64;
|
||||||
|
data16[i]=accum*512;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: // QSound ADPCM
|
||||||
|
bs_decode(dataQSoundA,data16,samples);
|
||||||
|
break;
|
||||||
|
case 5: // ADPCM-A
|
||||||
|
yma_decode(dataA,data16,samples);
|
||||||
|
break;
|
||||||
|
case 6: // ADPCM-B
|
||||||
|
ymb_decode(dataB,data16,samples);
|
||||||
|
break;
|
||||||
|
case 7: // X6800 ADPCM
|
||||||
|
oki6258_decode(dataX68,data16,samples);
|
||||||
|
break;
|
||||||
|
case 8: // 8-bit PCM
|
||||||
|
for (unsigned int i=0; i<samples; i++) {
|
||||||
|
data16[i]=data8[i]<<8;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 9: // BRR
|
||||||
|
// TODO!
|
||||||
|
break;
|
||||||
|
case 10: // VOX
|
||||||
|
oki_decode(dataVOX,data16,samples);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// step 2: render to other formats
|
||||||
|
if (depth!=0) { // 1-bit
|
||||||
|
if (!initInternal(0,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;
|
||||||
|
int accum=63;
|
||||||
|
for (unsigned int i=0; i<samples; i++) {
|
||||||
|
int next=((unsigned short)(data16[i]^0x8000))>>9;
|
||||||
|
if (next>accum) {
|
||||||
|
dataDPCM[i>>3]|=1<<(i&7);
|
||||||
|
accum++;
|
||||||
|
} else {
|
||||||
|
accum--;
|
||||||
|
}
|
||||||
|
if (accum<0) accum=0;
|
||||||
|
if (accum>127) accum=127;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (depth!=4) { // QSound ADPCM
|
||||||
|
if (!initInternal(4,samples)) return;
|
||||||
|
bs_encode(data16,dataQSoundA,samples);
|
||||||
|
}
|
||||||
|
if (depth!=5) { // ADPCM-A
|
||||||
|
if (!initInternal(5,samples)) return;
|
||||||
|
yma_encode(data16,dataA,samples);
|
||||||
|
}
|
||||||
|
if (depth!=6) { // ADPCM-B
|
||||||
|
if (!initInternal(6,samples)) return;
|
||||||
|
ymb_encode(data16,dataB,samples);
|
||||||
|
}
|
||||||
|
if (depth!=7) { // X68000 ADPCM
|
||||||
|
if (!initInternal(7,samples)) return;
|
||||||
|
oki6258_encode(data16,dataX68,samples);
|
||||||
|
}
|
||||||
|
if (depth!=8) { // 8-bit PCM
|
||||||
|
if (!initInternal(8,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;
|
||||||
|
oki_encode(data16,dataVOX,samples);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* DivSample::getCurBuf() {
|
||||||
|
switch (depth) {
|
||||||
|
case 0:
|
||||||
|
return data1;
|
||||||
|
case 1:
|
||||||
|
return dataDPCM;
|
||||||
|
case 4:
|
||||||
|
return dataQSoundA;
|
||||||
|
case 5:
|
||||||
|
return dataA;
|
||||||
|
case 6:
|
||||||
|
return dataB;
|
||||||
|
case 7:
|
||||||
|
return dataX68;
|
||||||
|
case 8:
|
||||||
|
return data8;
|
||||||
|
case 9:
|
||||||
|
return dataBRR;
|
||||||
|
case 10:
|
||||||
|
return dataVOX;
|
||||||
|
case 16:
|
||||||
|
return data16;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int DivSample::getCurBufLen() {
|
||||||
|
switch (depth) {
|
||||||
|
case 0:
|
||||||
|
return length1;
|
||||||
|
case 1:
|
||||||
|
return lengthDPCM;
|
||||||
|
case 4:
|
||||||
|
return lengthQSoundA;
|
||||||
|
case 5:
|
||||||
|
return lengthA;
|
||||||
|
case 6:
|
||||||
|
return lengthB;
|
||||||
|
case 7:
|
||||||
|
return lengthX68;
|
||||||
|
case 8:
|
||||||
|
return length8;
|
||||||
|
case 9:
|
||||||
|
return lengthBRR;
|
||||||
|
case 10:
|
||||||
|
return lengthVOX;
|
||||||
|
case 16:
|
||||||
|
return length16;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DivSample::~DivSample() {
|
||||||
|
if (data8) delete[] data8;
|
||||||
|
if (data16) delete[] data16;
|
||||||
|
if (data1) delete[] data1;
|
||||||
|
if (dataDPCM) delete[] dataDPCM;
|
||||||
|
if (dataQSoundA) delete[] dataQSoundA;
|
||||||
|
if (dataA) delete[] dataA;
|
||||||
|
if (dataB) delete[] dataB;
|
||||||
|
if (dataX68) delete[] dataX68;
|
||||||
|
if (dataBRR) delete[] dataBRR;
|
||||||
|
if (dataVOX) delete[] dataVOX;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,12 +21,11 @@
|
||||||
|
|
||||||
struct DivSample {
|
struct DivSample {
|
||||||
String name;
|
String name;
|
||||||
int length, rate, centerRate, loopStart, loopOffP;
|
int rate, centerRate, loopStart, loopOffP;
|
||||||
signed char vol, pitch;
|
|
||||||
// valid values are:
|
// valid values are:
|
||||||
// - 0: ZX Spectrum overlay drum (1-bit)
|
// - 0: ZX Spectrum overlay drum (1-bit)
|
||||||
// - 1: 1-bit NES DPCM (1-bit)
|
// - 1: 1-bit NES DPCM (1-bit)
|
||||||
// - 4: QSound ADPCM ()
|
// - 4: QSound ADPCM
|
||||||
// - 5: ADPCM-A
|
// - 5: ADPCM-A
|
||||||
// - 6: ADPCM-B
|
// - 6: ADPCM-B
|
||||||
// - 7: X68000 ADPCM
|
// - 7: X68000 ADPCM
|
||||||
|
@ -36,45 +35,69 @@ struct DivSample {
|
||||||
// - 16: 16-bit PCM
|
// - 16: 16-bit PCM
|
||||||
unsigned char depth;
|
unsigned char depth;
|
||||||
|
|
||||||
// TODO: drop
|
|
||||||
short* data;
|
|
||||||
|
|
||||||
// these are the new data structures.
|
// these are the new data structures.
|
||||||
signed char* data8;
|
signed char* data8; // 8
|
||||||
short* data16;
|
short* data16; // 16
|
||||||
unsigned char* data1;
|
unsigned char* data1; // 0
|
||||||
unsigned char* dataDPCM;
|
unsigned char* dataDPCM; // 1
|
||||||
unsigned char* dataQSound;
|
unsigned char* dataQSoundA; // 4
|
||||||
unsigned char* dataA;
|
unsigned char* dataA; // 5
|
||||||
unsigned char* dataB;
|
unsigned char* dataB; // 6
|
||||||
unsigned char* dataX68;
|
unsigned char* dataX68; // 7
|
||||||
unsigned char* dataBRR;
|
unsigned char* dataBRR; // 9
|
||||||
unsigned char* dataVOX;
|
unsigned char* dataVOX; // 10
|
||||||
|
|
||||||
|
unsigned int length8, length16, length1, lengthDPCM, lengthQSoundA, lengthA, lengthB, lengthX68, lengthBRR, lengthVOX;
|
||||||
|
unsigned int off8, off16, off1, offDPCM, offQSoundA, offA, offB, offX68, offBRR, offVOX;
|
||||||
|
unsigned int offSegaPCM, offQSound;
|
||||||
|
|
||||||
unsigned int rendLength, adpcmRendLength, rendOff, rendOffP, rendOffContiguous, rendOffQsound;
|
unsigned int samples;
|
||||||
short* rendData;
|
|
||||||
unsigned char* adpcmRendData;
|
|
||||||
|
|
||||||
bool save(const char* path);
|
bool save(const char* path);
|
||||||
|
bool initInternal(unsigned char d, int count);
|
||||||
|
bool init(unsigned int count);
|
||||||
|
void render();
|
||||||
|
void* getCurBuf();
|
||||||
|
unsigned int getCurBufLen();
|
||||||
DivSample():
|
DivSample():
|
||||||
name(""),
|
name(""),
|
||||||
length(0),
|
|
||||||
rate(32000),
|
rate(32000),
|
||||||
centerRate(8363),
|
centerRate(8363),
|
||||||
loopStart(-1),
|
loopStart(-1),
|
||||||
loopOffP(0),
|
loopOffP(0),
|
||||||
vol(0),
|
|
||||||
pitch(0),
|
|
||||||
depth(16),
|
depth(16),
|
||||||
data(NULL),
|
data8(NULL),
|
||||||
rendLength(0),
|
data16(NULL),
|
||||||
adpcmRendLength(0),
|
data1(NULL),
|
||||||
rendOff(0),
|
dataDPCM(NULL),
|
||||||
rendOffP(0),
|
dataQSoundA(NULL),
|
||||||
rendOffContiguous(0),
|
dataA(NULL),
|
||||||
rendOffQsound(0),
|
dataB(NULL),
|
||||||
rendData(NULL),
|
dataX68(NULL),
|
||||||
adpcmRendData(NULL) {}
|
dataBRR(NULL),
|
||||||
|
dataVOX(NULL),
|
||||||
|
length8(0),
|
||||||
|
length16(0),
|
||||||
|
length1(0),
|
||||||
|
lengthDPCM(0),
|
||||||
|
lengthQSoundA(0),
|
||||||
|
lengthA(0),
|
||||||
|
lengthB(0),
|
||||||
|
lengthX68(0),
|
||||||
|
lengthBRR(0),
|
||||||
|
lengthVOX(0),
|
||||||
|
off8(0),
|
||||||
|
off16(0),
|
||||||
|
off1(0),
|
||||||
|
offDPCM(0),
|
||||||
|
offQSoundA(0),
|
||||||
|
offA(0),
|
||||||
|
offB(0),
|
||||||
|
offX68(0),
|
||||||
|
offBRR(0),
|
||||||
|
offVOX(0),
|
||||||
|
offSegaPCM(0),
|
||||||
|
offQSound(0),
|
||||||
|
samples(0) {}
|
||||||
~DivSample();
|
~DivSample();
|
||||||
};
|
};
|
||||||
|
|
|
@ -310,7 +310,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
||||||
w->writeS(write.val); // sample number
|
w->writeS(write.val); // sample number
|
||||||
w->writeC((sample->loopStart==0)); // flags
|
w->writeC((sample->loopStart==0)); // flags
|
||||||
if (sample->loopStart>0) {
|
if (sample->loopStart>0) {
|
||||||
loopTimer[streamID]=sample->rendLength;
|
loopTimer[streamID]=sample->length8;
|
||||||
loopSample[streamID]=write.val;
|
loopSample[streamID]=write.val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -848,8 +848,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) {
|
||||||
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];
|
||||||
logI("setting seek to %d\n",sampleSeek);
|
logI("setting seek to %d\n",sampleSeek);
|
||||||
sample->rendOffContiguous=sampleSeek;
|
sample->off8=sampleSeek;
|
||||||
sampleSeek+=sample->rendLength;
|
sampleSeek+=sample->length8;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (writeDACSamples) for (int i=0; i<song.sampleLen; i++) {
|
if (writeDACSamples) for (int i=0; i<song.sampleLen; i++) {
|
||||||
|
@ -857,15 +857,9 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) {
|
||||||
w->writeC(0x67);
|
w->writeC(0x67);
|
||||||
w->writeC(0x66);
|
w->writeC(0x66);
|
||||||
w->writeC(0);
|
w->writeC(0);
|
||||||
w->writeI(sample->rendLength);
|
w->writeI(sample->length8);
|
||||||
if (sample->depth==8) {
|
for (unsigned int j=0; j<sample->length8; j++) {
|
||||||
for (unsigned int j=0; j<sample->rendLength; j++) {
|
w->writeC((unsigned char)sample->data8[j]+0x80);
|
||||||
w->writeC((unsigned char)sample->rendData[j]+0x80);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (unsigned int j=0; j<sample->rendLength; j++) {
|
|
||||||
w->writeC(((unsigned short)sample->rendData[j]+0x8000)>>8);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -874,15 +868,9 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) {
|
||||||
w->writeC(0x67);
|
w->writeC(0x67);
|
||||||
w->writeC(0x66);
|
w->writeC(0x66);
|
||||||
w->writeC(7);
|
w->writeC(7);
|
||||||
w->writeI(sample->rendLength);
|
w->writeI(sample->length8);
|
||||||
if (sample->depth==8) {
|
for (unsigned int j=0; j<sample->length8; j++) {
|
||||||
for (unsigned int j=0; j<sample->rendLength; j++) {
|
w->writeC(((unsigned char)sample->data8[j]+0x80)>>1);
|
||||||
w->writeC(((unsigned char)sample->rendData[j]+0x80)>>1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (unsigned int j=0; j<sample->rendLength; j++) {
|
|
||||||
w->writeC(((unsigned short)sample->rendData[j]+0x8000)>>9);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -891,15 +879,9 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) {
|
||||||
w->writeC(0x67);
|
w->writeC(0x67);
|
||||||
w->writeC(0x66);
|
w->writeC(0x66);
|
||||||
w->writeC(5);
|
w->writeC(5);
|
||||||
w->writeI(sample->rendLength);
|
w->writeI(sample->length8);
|
||||||
if (sample->depth==8) {
|
for (unsigned int j=0; j<sample->length8; j++) {
|
||||||
for (unsigned int j=0; j<sample->rendLength; j++) {
|
w->writeC(((unsigned char)sample->data8[j]+0x80)>>3);
|
||||||
w->writeC(((unsigned char)sample->rendData[j]+0x80)>>3);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (unsigned int j=0; j<sample->rendLength; j++) {
|
|
||||||
w->writeC(((unsigned short)sample->rendData[j]+0x8000)>>11);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -909,47 +891,29 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) {
|
||||||
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];
|
||||||
if ((memPos&0xff0000)!=((memPos+sample->rendLength)&0xff0000)) {
|
if ((memPos&0xff0000)!=((memPos+sample->length8)&0xff0000)) {
|
||||||
memPos=(memPos+0xffff)&0xff0000;
|
memPos=(memPos+0xffff)&0xff0000;
|
||||||
}
|
}
|
||||||
if (memPos>=16777216) break;
|
if (memPos>=16777216) break;
|
||||||
sample->rendOffP=memPos;
|
sample->offSegaPCM=memPos;
|
||||||
unsigned int alignedSize=(sample->rendLength+0xff)&(~0xff);
|
unsigned int alignedSize=(sample->length8+0xff)&(~0xff);
|
||||||
unsigned int readPos=0;
|
unsigned int readPos=0;
|
||||||
if (alignedSize>65536) alignedSize=65536;
|
if (alignedSize>65536) alignedSize=65536;
|
||||||
if (sample->depth==8) {
|
for (unsigned int j=0; j<alignedSize; j++) {
|
||||||
for (unsigned int j=0; j<alignedSize; j++) {
|
if (readPos>=sample->length8) {
|
||||||
if (readPos>=sample->rendLength) {
|
if (sample->loopStart>=0 && sample->loopStart<(int)sample->length8) {
|
||||||
if (sample->loopStart>=0 && sample->loopStart<(int)sample->rendLength) {
|
readPos=sample->loopStart;
|
||||||
readPos=sample->loopStart;
|
pcmMem[memPos++]=((unsigned char)sample->data8[readPos]+0x80);
|
||||||
pcmMem[memPos++]=((unsigned char)sample->rendData[readPos]+0x80);
|
|
||||||
} else {
|
|
||||||
pcmMem[memPos++]=0x80;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
pcmMem[memPos++]=((unsigned char)sample->rendData[readPos]+0x80);
|
pcmMem[memPos++]=0x80;
|
||||||
}
|
}
|
||||||
readPos++;
|
} else {
|
||||||
if (memPos>=16777216) break;
|
pcmMem[memPos++]=((unsigned char)sample->data8[readPos]+0x80);
|
||||||
}
|
}
|
||||||
sample->loopOffP=readPos-sample->loopStart;
|
readPos++;
|
||||||
} else {
|
if (memPos>=16777216) break;
|
||||||
for (unsigned int j=0; j<alignedSize; j++) {
|
|
||||||
if (readPos>=sample->rendLength) {
|
|
||||||
if (sample->loopStart>=0 && sample->loopStart<(int)sample->rendLength) {
|
|
||||||
readPos=sample->loopStart;
|
|
||||||
pcmMem[memPos++]=(((unsigned short)sample->rendData[readPos]+0x8000)>>8);
|
|
||||||
} else {
|
|
||||||
pcmMem[memPos++]=0x80;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pcmMem[memPos++]=(((unsigned short)sample->rendData[readPos]+0x8000)>>8);
|
|
||||||
}
|
|
||||||
readPos++;
|
|
||||||
if (memPos>=16777216) break;
|
|
||||||
}
|
|
||||||
sample->loopOffP=readPos-sample->loopStart;
|
|
||||||
}
|
}
|
||||||
|
sample->loopOffP=readPos-sample->loopStart;
|
||||||
if (memPos>=16777216) break;
|
if (memPos>=16777216) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1137,12 +1101,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) {
|
||||||
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->rendLength) {
|
if (sample->loopStart<(int)sample->length8) {
|
||||||
w->writeC(0x93);
|
w->writeC(0x93);
|
||||||
w->writeC(nextToTouch);
|
w->writeC(nextToTouch);
|
||||||
w->writeI(sample->rendOffContiguous+sample->loopStart);
|
w->writeI(sample->off8+sample->loopStart);
|
||||||
w->writeC(0x81);
|
w->writeC(0x81);
|
||||||
w->writeI(sample->rendLength-sample->loopStart);
|
w->writeI(sample->length8-sample->loopStart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loopSample[nextToTouch]=-1;
|
loopSample[nextToTouch]=-1;
|
||||||
|
|
|
@ -1266,7 +1266,8 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
} else {
|
} else {
|
||||||
DivSample* sample=e->song.sample[curSample];
|
DivSample* sample=e->song.sample[curSample];
|
||||||
ImGui::InputText("Name",&sample->name);
|
ImGui::InputText("Name",&sample->name);
|
||||||
ImGui::Text("Length: %d",sample->length);
|
ImGui::Text("Length: %d",sample->samples);
|
||||||
|
ImGui::Text("Type: %d-bit",sample->depth);
|
||||||
if (ImGui::InputInt("Rate (Hz)",&sample->rate,10,200)) {
|
if (ImGui::InputInt("Rate (Hz)",&sample->rate,10,200)) {
|
||||||
if (sample->rate<100) sample->rate=100;
|
if (sample->rate<100) sample->rate=100;
|
||||||
if (sample->rate>96000) sample->rate=96000;
|
if (sample->rate>96000) sample->rate=96000;
|
||||||
|
@ -1287,19 +1288,11 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
if (doLoop) {
|
if (doLoop) {
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::InputInt("##LoopPosition",&sample->loopStart,1,10)) {
|
if (ImGui::InputInt("##LoopPosition",&sample->loopStart,1,10)) {
|
||||||
if (sample->loopStart<0 || sample->loopStart>=sample->length) {
|
if (sample->loopStart<0 || sample->loopStart>=(int)sample->samples) {
|
||||||
sample->loopStart=0;
|
sample->loopStart=0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ImGui::SliderScalar("Volume",ImGuiDataType_S8,&sample->vol,&_ZERO,&_ONE_HUNDRED,fmt::sprintf("%d%%%%",sample->vol*2).c_str())) {
|
|
||||||
if (sample->vol<0) sample->vol=0;
|
|
||||||
if (sample->vol>100) sample->vol=100;
|
|
||||||
}
|
|
||||||
if (ImGui::SliderScalar("Pitch",ImGuiDataType_S8,&sample->pitch,&_ZERO,&_TEN,pitchLabel[sample->pitch])) {
|
|
||||||
if (sample->pitch<0) sample->pitch=0;
|
|
||||||
if (sample->pitch>10) sample->pitch=10;
|
|
||||||
}
|
|
||||||
if (ImGui::Button("Apply")) {
|
if (ImGui::Button("Apply")) {
|
||||||
e->renderSamplesP();
|
e->renderSamplesP();
|
||||||
}
|
}
|
||||||
|
@ -1321,15 +1314,15 @@ void FurnaceGUI::drawSampleEdit() {
|
||||||
ImGui::Text("- sample loop start will be aligned to the nearest even sample on Amiga");
|
ImGui::Text("- sample loop start will be aligned to the nearest even sample on Amiga");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sample->length&1) {
|
if (sample->samples&1) {
|
||||||
considerations=true;
|
considerations=true;
|
||||||
ImGui::Text("- sample length will be aligned to the nearest even sample on Amiga");
|
ImGui::Text("- sample length will be aligned to the nearest even sample on Amiga");
|
||||||
}
|
}
|
||||||
if (sample->length>65535) {
|
if (sample->samples>65535) {
|
||||||
considerations=true;
|
considerations=true;
|
||||||
ImGui::Text("- maximum sample length on Sega PCM is 65536 samples");
|
ImGui::Text("- maximum sample length on Sega PCM is 65536 samples");
|
||||||
}
|
}
|
||||||
if (sample->length>2097151) {
|
if (sample->samples>2097151) {
|
||||||
considerations=true;
|
considerations=true;
|
||||||
ImGui::Text("- maximum sample length on Neo Geo ADPCM is 2097152 samples");
|
ImGui::Text("- maximum sample length on Neo Geo ADPCM is 2097152 samples");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue