dev126 - new ins format

This commit is contained in:
tildearrow 2022-11-20 19:19:24 -05:00
parent 1b79f9d7f3
commit d6ae735ffe
11 changed files with 408 additions and 196 deletions

View file

@ -2348,142 +2348,24 @@ 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; DivSample* sample=new DivSample;
int pitch=0;
if (!reader.seek(samplePtr[i],SEEK_SET)) { if (!reader.seek(samplePtr[i],SEEK_SET)) {
logE("couldn't seek to sample %d!",i); logE("couldn't seek to sample %d!",i);
lastError=fmt::sprintf("couldn't seek to sample %d!",i); lastError=fmt::sprintf("couldn't seek to sample %d!",i);
ds.unload(); ds.unload();
delete sample;
delete[] file; delete[] file;
return false; return false;
} }
reader.read(magic,4); if (sample->readSampleData(reader,ds.version)!=DIV_DATA_SUCCESS) {
if (strcmp(magic,"SMPL")!=0 && strcmp(magic,"SMP2")!=0) { lastError="invalid sample header/data!";
logE("%d: invalid sample header!",i);
lastError="invalid sample header!";
ds.unload(); ds.unload();
delete sample;
delete[] file; delete[] file;
return false; return false;
} }
bool isNewSample=(strcmp(magic,"SMP2")==0);
reader.readI();
DivSample* sample=new DivSample;
logD("reading sample %d at %x...",i,samplePtr[i]);
if (!isNewSample) logV("(old sample)");
sample->name=reader.readString();
sample->samples=reader.readI();
if (!isNewSample) {
sample->loopEnd=sample->samples;
}
sample->rate=reader.readI();
if (isNewSample) {
sample->centerRate=reader.readI();
sample->depth=(DivSampleDepth)reader.readC();
if (ds.version>=123) {
sample->loopMode=(DivSampleLoopMode)reader.readC();
} else {
sample->loopMode=DIV_SAMPLE_LOOP_FORWARD;
reader.readC();
}
// reserved
reader.readC();
reader.readC();
sample->loopStart=reader.readI();
sample->loopEnd=reader.readI();
sample->loop=(sample->loopStart>=0)&&(sample->loopEnd>=0);
for (int i=0; i<4; i++) {
reader.readI();
}
} else {
if (ds.version<58) {
vol=reader.readS();
pitch=reader.readS();
} else {
reader.readI();
}
sample->depth=(DivSampleDepth)reader.readC();
// reserved
reader.readC();
// while version 32 stored this value, it was unused.
if (ds.version>=38) {
sample->centerRate=(unsigned short)reader.readS();
} else {
reader.readS();
}
if (ds.version>=19) {
sample->loopStart=reader.readI();
sample->loop=(sample->loopStart>=0)&&(sample->loopEnd>=0);
} else {
reader.readI();
}
}
if (ds.version>=58) { // modern sample
sample->init(sample->samples);
reader.read(sample->getCurBuf(),sample->getCurBufLen());
#ifdef TA_BIG_ENDIAN
// convert 16-bit samples to big-endian
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
unsigned char* sampleBuf=(unsigned char*)sample->getCurBuf();
size_t sampleBufLen=sample->getCurBufLen();
for (size_t pos=0; pos<sampleBufLen; pos+=2) {
sampleBuf[pos]^=sampleBuf[pos+1];
sampleBuf[pos+1]^=sampleBuf[pos];
sampleBuf[pos]^=sampleBuf[pos+1];
}
}
#endif
} else { // legacy sample
int length=sample->samples;
short* data=new short[length];
reader.read(data,2*length);
#ifdef TA_BIG_ENDIAN
// convert 16-bit samples to big-endian
for (int pos=0; pos<length; pos++) {
data[pos]=((unsigned short)data[pos]>>8)|((unsigned short)data[pos]<<8);
}
#endif
if (pitch!=5) {
logD("%d: scaling from %d...",i,pitch);
}
// render data
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=DIV_SAMPLE_DEPTH_16BIT;
}
sample->samples=(double)sample->samples/samplePitches[pitch];
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==DIV_SAMPLE_DEPTH_8BIT) {
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);
} }
@ -4635,45 +4517,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
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];
samplePtr.push_back(w->tell()); samplePtr.push_back(w->tell());
w->write("SMP2",4); sample->putSampleData(w);
blockStartSeek=w->tell();
w->writeI(0);
w->writeString(sample->name,false);
w->writeI(sample->samples);
w->writeI(sample->rate);
w->writeI(sample->centerRate);
w->writeC(sample->depth);
w->writeC(sample->loopMode);
w->writeC(0); // reserved
w->writeC(0);
w->writeI(sample->loop?sample->loopStart:-1);
w->writeI(sample->loop?sample->loopEnd:-1);
for (int i=0; i<4; i++) {
w->writeI(0xffffffff);
}
#ifdef TA_BIG_ENDIAN
// store 16-bit samples as little-endian
if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
unsigned char* sampleBuf=(unsigned char*)sample->getCurBuf();
size_t bufLen=sample->getCurBufLen();
for (size_t i=0; i<bufLen; i+=2) {
w->writeC(sampleBuf[i+1]);
w->writeC(sampleBuf[i]);
}
} else {
w->write(sample->getCurBuf(),sample->getCurBufLen());
}
#else
w->write(sample->getCurBuf(),sample->getCurBufLen());
#endif
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4);
w->seek(0,SEEK_END);
} }
/// PATTERN /// PATTERN

View file

@ -549,8 +549,6 @@ void DivInstrument::writeFeatureWS(SafeWriter* w) {
} }
size_t DivInstrument::writeFeatureSL(SafeWriter* w, std::vector<int>& list, const DivSong* song) { size_t DivInstrument::writeFeatureSL(SafeWriter* w, std::vector<int>& list, const DivSong* song) {
FEATURE_BEGIN("SL");
bool sampleUsed[256]; bool sampleUsed[256];
memset(sampleUsed,0,256*sizeof(bool)); memset(sampleUsed,0,256*sizeof(bool));
@ -572,6 +570,10 @@ size_t DivInstrument::writeFeatureSL(SafeWriter* w, std::vector<int>& list, cons
} }
} }
if (list.empty()) return 0;
FEATURE_BEGIN("SL");
w->writeC(list.size()); w->writeC(list.size());
for (int i: list) { for (int i: list) {
@ -591,8 +593,6 @@ size_t DivInstrument::writeFeatureSL(SafeWriter* w, std::vector<int>& list, cons
} }
size_t DivInstrument::writeFeatureWL(SafeWriter* w, std::vector<int>& list, const DivSong* song) { size_t DivInstrument::writeFeatureWL(SafeWriter* w, std::vector<int>& list, const DivSong* song) {
FEATURE_BEGIN("WL");
bool waveUsed[256]; bool waveUsed[256];
memset(waveUsed,0,256*sizeof(bool)); memset(waveUsed,0,256*sizeof(bool));
@ -617,6 +617,10 @@ size_t DivInstrument::writeFeatureWL(SafeWriter* w, std::vector<int>& list, cons
} }
} }
if (list.empty()) return 0;
FEATURE_BEGIN("WL");
w->writeC(list.size()); w->writeC(list.size());
for (int i: list) { for (int i: list) {
@ -687,9 +691,14 @@ void DivInstrument::writeFeatureX1(SafeWriter* w) {
void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) { void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
size_t blockStartSeek=0; size_t blockStartSeek=0;
size_t blockEndSeek=0; size_t blockEndSeek=0;
size_t slSeek=0;
size_t wlSeek=0;
std::vector<int> waveList; std::vector<int> waveList;
std::vector<int> sampleList; std::vector<int> sampleList;
std::vector<unsigned int> wavePtr;
std::vector<unsigned int> samplePtr;
if (fui) { if (fui) {
w->write("FINS",4); w->write("FINS",4);
} else { } else {
@ -747,17 +756,21 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
break; break;
case DIV_INS_AMIGA: case DIV_INS_AMIGA:
featureSM=true; featureSM=true;
if (!amiga.useWave) featureSL=true;
break; break;
case DIV_INS_PCE: case DIV_INS_PCE:
checkForWL=true; checkForWL=true;
featureSM=true; featureSM=true;
if (amiga.useSample) featureSL=true;
if (ws.enabled) featureWS=true; if (ws.enabled) featureWS=true;
break; break;
case DIV_INS_AY: case DIV_INS_AY:
featureSM=true; featureSM=true;
if (amiga.useSample) featureSL=true;
break; break;
case DIV_INS_AY8930: case DIV_INS_AY8930:
featureSM=true; featureSM=true;
if (amiga.useSample) featureSL=true;
break; break;
case DIV_INS_TIA: case DIV_INS_TIA:
break; break;
@ -769,6 +782,7 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
break; break;
case DIV_INS_VRC6: case DIV_INS_VRC6:
featureSM=true; featureSM=true;
if (amiga.useSample) featureSL=true;
break; break;
case DIV_INS_OPLL: case DIV_INS_OPLL:
featureFM=true; featureFM=true;
@ -807,36 +821,44 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
case DIV_INS_SWAN: case DIV_INS_SWAN:
checkForWL=true; checkForWL=true;
featureSM=true; featureSM=true;
if (amiga.useSample) featureSL=true;
if (ws.enabled) featureWS=true; if (ws.enabled) featureWS=true;
break; break;
case DIV_INS_MIKEY: case DIV_INS_MIKEY:
featureSM=true; featureSM=true;
if (amiga.useSample) featureSL=true;
break; break;
case DIV_INS_VERA: case DIV_INS_VERA:
break; break;
case DIV_INS_X1_010: case DIV_INS_X1_010:
checkForWL=true; checkForWL=true;
featureX1=true; featureX1=true;
featureSM=true;
if (amiga.useSample) featureSL=true;
if (ws.enabled) featureWS=true; if (ws.enabled) featureWS=true;
break; break;
case DIV_INS_VRC6_SAW: case DIV_INS_VRC6_SAW:
break; break;
case DIV_INS_ES5506: case DIV_INS_ES5506:
featureSM=true; featureSM=true;
featureSL=true;
featureES=true; featureES=true;
break; break;
case DIV_INS_MULTIPCM: case DIV_INS_MULTIPCM:
featureSM=true; featureSM=true;
featureSL=true;
featureMP=true; featureMP=true;
break; break;
case DIV_INS_SNES: case DIV_INS_SNES:
featureSM=true; featureSM=true;
if (!amiga.useWave) featureSL=true;
featureSN=true; featureSN=true;
checkForWL=true; checkForWL=true;
if (ws.enabled) featureWS=true; if (ws.enabled) featureWS=true;
break; break;
case DIV_INS_SU: case DIV_INS_SU:
featureSM=true; featureSM=true;
if (amiga.useSample) featureSL=true;
featureSU=true; featureSU=true;
break; break;
case DIV_INS_NAMCO: case DIV_INS_NAMCO:
@ -854,27 +876,35 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
break; break;
case DIV_INS_MSM6258: case DIV_INS_MSM6258:
featureSM=true; featureSM=true;
featureSL=true;
break; break;
case DIV_INS_MSM6295: case DIV_INS_MSM6295:
featureSM=true; featureSM=true;
featureSL=true;
break; break;
case DIV_INS_ADPCMA: case DIV_INS_ADPCMA:
featureSM=true; featureSM=true;
featureSL=true;
break; break;
case DIV_INS_ADPCMB: case DIV_INS_ADPCMB:
featureSM=true; featureSM=true;
featureSL=true;
break; break;
case DIV_INS_SEGAPCM: case DIV_INS_SEGAPCM:
featureSM=true; featureSM=true;
featureSL=true;
break; break;
case DIV_INS_QSOUND: case DIV_INS_QSOUND:
featureSM=true; featureSM=true;
featureSL=true;
break; break;
case DIV_INS_YMZ280B: case DIV_INS_YMZ280B:
featureSM=true; featureSM=true;
featureSL=true;
break; break;
case DIV_INS_RF5C68: case DIV_INS_RF5C68:
featureSM=true; featureSM=true;
featureSL=true;
break; break;
case DIV_INS_MSM5232: case DIV_INS_MSM5232:
break; break;
@ -1049,10 +1079,10 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
writeFeatureWS(w); writeFeatureWS(w);
} }
if (featureSL) { if (featureSL) {
writeFeatureSL(w,sampleList,song); slSeek=writeFeatureSL(w,sampleList,song);
} }
if (featureWL) { if (featureWL) {
writeFeatureWL(w,waveList,song); wlSeek=writeFeatureWL(w,waveList,song);
} }
if (featureMP) { if (featureMP) {
writeFeatureMP(w); writeFeatureMP(w);
@ -1069,7 +1099,44 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
if (fui && (featureSL || featureWL)) { if (fui && (featureSL || featureWL)) {
w->write("EN",2); w->write("EN",2);
// TODO: write wave/sample data here
if (wlSeek!=0 && !waveList.empty()) {
for (int i: waveList) {
if (i<0 || i>=(int)song->wave.size()) {
wavePtr.push_back(0);
continue;
}
DivWavetable* wave=song->wave[i];
wavePtr.push_back(w->tell());
wave->putWaveData(w);
}
w->seek(wlSeek,SEEK_SET);
for (unsigned int i: wavePtr) {
w->writeI(i);
}
w->seek(0,SEEK_END);
}
if (slSeek!=0 && !sampleList.empty()) {
for (int i: sampleList) {
if (i<0 || i>=(int)song->sample.size()) {
samplePtr.push_back(0);
continue;
}
DivSample* sample=song->sample[i];
samplePtr.push_back(w->tell());
sample->putSampleData(w);
}
w->seek(slSeek,SEEK_SET);
for (unsigned int i: samplePtr) {
w->writeI(i);
}
w->seek(0,SEEK_END);
}
} }
blockEndSeek=w->tell(); blockEndSeek=w->tell();
@ -2204,13 +2271,15 @@ void DivInstrument::readFeatureWS(SafeReader& reader) {
READ_FEAT_END; READ_FEAT_END;
} }
void DivInstrument::readFeatureSL(SafeReader& reader, const DivSong* song) { void DivInstrument::readFeatureSL(SafeReader& reader, DivSong* song, short version) {
READ_FEAT_BEGIN; READ_FEAT_BEGIN;
unsigned int samplePtr[256]; unsigned int samplePtr[256];
unsigned char sampleIndex[256]; unsigned char sampleIndex[256];
unsigned char sampleRemap[256];
memset(samplePtr,0,256*sizeof(unsigned int)); memset(samplePtr,0,256*sizeof(unsigned int));
memset(sampleIndex,0,256); memset(sampleIndex,0,256);
memset(sampleRemap,0,256);
unsigned char sampleCount=reader.readC(); unsigned char sampleCount=reader.readC();
@ -2223,20 +2292,53 @@ void DivInstrument::readFeatureSL(SafeReader& reader, const DivSong* song) {
size_t lastSeek=reader.tell(); size_t lastSeek=reader.tell();
// TODO: load samples here // load samples
for (int i=0; i<sampleCount; i++) {
reader.seek(samplePtr[i],SEEK_SET);
if (song->sample.size()>=256) {
break;
}
DivSample* sample=new DivSample;
int sampleCount=(int)song->sample.size();
DivDataErrors result=sample->readSampleData(reader,version);
if (result==DIV_DATA_SUCCESS) {
song->sample.push_back(sample);
song->sampleLen=sampleCount+1;
sampleRemap[sampleIndex[i]]=sampleCount;
} else {
delete sample;
sampleRemap[sampleIndex[i]]=0;
}
}
reader.seek(lastSeek,SEEK_CUR); reader.seek(lastSeek,SEEK_CUR);
// re-map samples
if (amiga.initSample>=0 && amiga.initSample<256) {
amiga.initSample=sampleRemap[amiga.initSample];
}
if (amiga.useNoteMap) {
for (int i=0; i<120; i++) {
if (amiga.noteMap[i].map>=0 && amiga.noteMap[i].map<256) {
amiga.noteMap[i].map=sampleRemap[amiga.noteMap[i].map];
}
}
}
READ_FEAT_END; READ_FEAT_END;
} }
void DivInstrument::readFeatureWL(SafeReader& reader, const DivSong* song) { void DivInstrument::readFeatureWL(SafeReader& reader, DivSong* song, short version) {
READ_FEAT_BEGIN; READ_FEAT_BEGIN;
unsigned int wavePtr[256]; unsigned int wavePtr[256];
unsigned char waveIndex[256]; unsigned char waveIndex[256];
unsigned char waveRemap[256];
memset(wavePtr,0,256*sizeof(unsigned int)); memset(wavePtr,0,256*sizeof(unsigned int));
memset(waveIndex,0,256); memset(waveIndex,0,256);
memset(waveRemap,0,256);
unsigned char waveCount=reader.readC(); unsigned char waveCount=reader.readC();
@ -2249,10 +2351,40 @@ void DivInstrument::readFeatureWL(SafeReader& reader, const DivSong* song) {
size_t lastSeek=reader.tell(); size_t lastSeek=reader.tell();
// TODO: load samples here // load wavetables
for (int i=0; i<waveCount; i++) {
reader.seek(wavePtr[i],SEEK_SET);
if (song->wave.size()>=256) {
break;
}
DivWavetable* wave=new DivWavetable;
int waveCount=(int)song->wave.size();
DivDataErrors result=wave->readWaveData(reader,version);
if (result==DIV_DATA_SUCCESS) {
song->wave.push_back(wave);
song->waveLen=waveCount+1;
waveRemap[waveIndex[i]]=waveCount;
} else {
delete wave;
waveRemap[waveIndex[i]]=0;
}
}
reader.seek(lastSeek,SEEK_CUR); reader.seek(lastSeek,SEEK_CUR);
// re-map wavetables
if (ws.enabled) {
if (ws.wave1>=0 && ws.wave1<256) ws.wave1=waveRemap[ws.wave1];
if (ws.effect&0x80) {
if (ws.wave2>=0 && ws.wave2<256) ws.wave2=waveRemap[ws.wave2];
}
}
if (n163.wave>=0 && n163.wave<256) n163.wave=waveRemap[n163.wave];
for (int i=0; i<std.waveMacro.len; i++) {
if (std.waveMacro.val[i]>=0 && std.waveMacro.val[i]<256) std.waveMacro.val[i]=waveRemap[std.waveMacro.val[i]];
}
READ_FEAT_END; READ_FEAT_END;
} }
@ -2358,9 +2490,9 @@ DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, b
} else if (memcmp(featCode,"WS",2)==0) { // WaveSynth } else if (memcmp(featCode,"WS",2)==0) { // WaveSynth
readFeatureWS(reader); readFeatureWS(reader);
} else if (memcmp(featCode,"SL",2)==0 && fui && song!=NULL) { // sample list } else if (memcmp(featCode,"SL",2)==0 && fui && song!=NULL) { // sample list
readFeatureSL(reader,NULL); readFeatureSL(reader,song,version);
} else if (memcmp(featCode,"WL",2)==0 && fui && song!=NULL) { // wave list } else if (memcmp(featCode,"WL",2)==0 && fui && song!=NULL) { // wave list
readFeatureWL(reader,NULL); readFeatureWL(reader,song,version);
} else if (memcmp(featCode,"MP",2)==0) { // MultiPCM } else if (memcmp(featCode,"MP",2)==0) { // MultiPCM
readFeatureMP(reader); readFeatureMP(reader);
} else if (memcmp(featCode,"SU",2)==0) { // Sound Unit } else if (memcmp(featCode,"SU",2)==0) { // Sound Unit
@ -3118,30 +3250,32 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version, DivS
return readInsDataOld(reader,version); return readInsDataOld(reader,version);
} }
bool DivInstrument::save(const char* path) { bool DivInstrument::save(const char* path, bool oldFormat, DivSong* song) {
SafeWriter* w=new SafeWriter(); SafeWriter* w=new SafeWriter();
w->init(); w->init();
//putInsData2(w,true); if (oldFormat) {
// write magic
w->write("-Furnace instr.-",16);
// write magic // write version
w->write("-Furnace instr.-",16); w->writeS(DIV_ENGINE_VERSION);
// write version // reserved
w->writeS(DIV_ENGINE_VERSION); w->writeS(0);
// reserved // pointer to data
w->writeS(0); w->writeI(32);
// pointer to data // currently reserved (TODO; wavetable and sample here)
w->writeI(32); w->writeS(0);
w->writeS(0);
w->writeI(0);
// currently reserved (TODO; wavetable and sample here) putInsData(w);
w->writeS(0); } else {
w->writeS(0); putInsData2(w,true,song);
w->writeI(0); }
putInsData(w);
FILE* outFile=ps_fopen(path,"wb"); FILE* outFile=ps_fopen(path,"wb");
if (outFile==NULL) { if (outFile==NULL) {

View file

@ -674,8 +674,8 @@ struct DivInstrument {
void readFeatureN1(SafeReader& reader); void readFeatureN1(SafeReader& reader);
void readFeatureFD(SafeReader& reader); void readFeatureFD(SafeReader& reader);
void readFeatureWS(SafeReader& reader); void readFeatureWS(SafeReader& reader);
void readFeatureSL(SafeReader& reader, const DivSong* song); void readFeatureSL(SafeReader& reader, DivSong* song, short version);
void readFeatureWL(SafeReader& reader, const DivSong* song); void readFeatureWL(SafeReader& reader, DivSong* song, short version);
void readFeatureMP(SafeReader& reader); void readFeatureMP(SafeReader& reader);
void readFeatureSU(SafeReader& reader); void readFeatureSU(SafeReader& reader);
void readFeatureES(SafeReader& reader); void readFeatureES(SafeReader& reader);
@ -707,9 +707,11 @@ struct DivInstrument {
/** /**
* save this instrument to a file. * save this instrument to a file.
* @param path file path. * @param path file path.
* @param oldFormat whether to save in legacy Furnace ins format.
* @param song if new format, a DivSong to read wavetables and samples.
* @return whether it was successful. * @return whether it was successful.
*/ */
bool save(const char* path); bool save(const char* path, bool oldFormat=false, DivSong* song=NULL);
/** /**
* save this instrument to a file in .dmp format. * save this instrument to a file in .dmp format.

View file

@ -39,6 +39,185 @@ DivSampleHistory::~DivSampleHistory() {
if (data!=NULL) delete[] data; if (data!=NULL) delete[] data;
} }
void DivSample::putSampleData(SafeWriter* w) {
size_t blockStartSeek, blockEndSeek;
w->write("SMP2",4);
blockStartSeek=w->tell();
w->writeI(0);
w->writeString(name,false);
w->writeI(samples);
w->writeI(rate);
w->writeI(centerRate);
w->writeC(depth);
w->writeC(loopMode);
w->writeC(0); // reserved
w->writeC(0);
w->writeI(loop?loopStart:-1);
w->writeI(loop?loopEnd:-1);
for (int i=0; i<4; i++) {
w->writeI(0xffffffff);
}
#ifdef TA_BIG_ENDIAN
// store 16-bit samples as little-endian
if (depth==DIV_SAMPLE_DEPTH_16BIT) {
unsigned char* sampleBuf=(unsigned char*)getCurBuf();
size_t bufLen=getCurBufLen();
for (size_t i=0; i<bufLen; i+=2) {
w->writeC(sampleBuf[i+1]);
w->writeC(sampleBuf[i]);
}
} else {
w->write(getCurBuf(),getCurBufLen());
}
#else
w->write(getCurBuf(),getCurBufLen());
#endif
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4);
w->seek(0,SEEK_END);
}
// Delek why
static double samplePitchesSD[11]={
0.1666666666, 0.2, 0.25, 0.333333333, 0.5,
1,
2, 3, 4, 5, 6
};
DivDataErrors DivSample::readSampleData(SafeReader& reader, short version) {
int vol=0;
int pitch=0;
char magic[4];
reader.read(magic,4);
if (strcmp(magic,"SMPL")!=0 && strcmp(magic,"SMP2")!=0) {
return DIV_DATA_INVALID_HEADER;
}
bool isNewSample=(strcmp(magic,"SMP2")==0);
reader.readI();
if (!isNewSample) logV("(old sample)");
name=reader.readString();
samples=reader.readI();
if (!isNewSample) {
loopEnd=samples;
}
rate=reader.readI();
if (isNewSample) {
centerRate=reader.readI();
depth=(DivSampleDepth)reader.readC();
if (version>=123) {
loopMode=(DivSampleLoopMode)reader.readC();
} else {
loopMode=DIV_SAMPLE_LOOP_FORWARD;
reader.readC();
}
// reserved
reader.readC();
reader.readC();
loopStart=reader.readI();
loopEnd=reader.readI();
loop=(loopStart>=0)&&(loopEnd>=0);
for (int i=0; i<4; i++) {
reader.readI();
}
} else {
if (version<58) {
vol=reader.readS();
pitch=reader.readS();
} else {
reader.readI();
}
depth=(DivSampleDepth)reader.readC();
// reserved
reader.readC();
// while version 32 stored this value, it was unused.
if (version>=38) {
centerRate=(unsigned short)reader.readS();
} else {
reader.readS();
}
if (version>=19) {
loopStart=reader.readI();
loop=(loopStart>=0)&&(loopEnd>=0);
} else {
reader.readI();
}
}
if (version>=58) { // modern sample
init(samples);
reader.read(getCurBuf(),getCurBufLen());
#ifdef TA_BIG_ENDIAN
// convert 16-bit samples to big-endian
if (depth==DIV_SAMPLE_DEPTH_16BIT) {
unsigned char* sampleBuf=(unsigned char*)getCurBuf();
size_t sampleBufLen=getCurBufLen();
for (size_t pos=0; pos<sampleBufLen; pos+=2) {
sampleBuf[pos]^=sampleBuf[pos+1];
sampleBuf[pos+1]^=sampleBuf[pos];
sampleBuf[pos]^=sampleBuf[pos+1];
}
}
#endif
} else { // legacy sample
int length=samples;
short* data=new short[length];
reader.read(data,2*length);
#ifdef TA_BIG_ENDIAN
// convert 16-bit samples to big-endian
for (int pos=0; pos<length; pos++) {
data[pos]=((unsigned short)data[pos]>>8)|((unsigned short)data[pos]<<8);
}
#endif
if (pitch!=5) {
logD("scaling from %d...",pitch);
}
// render data
if (depth!=DIV_SAMPLE_DEPTH_8BIT && depth!=DIV_SAMPLE_DEPTH_16BIT) {
logW("sample depth is wrong! (%d)",depth);
depth=DIV_SAMPLE_DEPTH_16BIT;
}
samples=(double)samples/samplePitchesSD[pitch];
init(samples);
unsigned int k=0;
float mult=(float)(vol)/50.0f;
for (double j=0; j<length; j+=samplePitchesSD[pitch]) {
if (k>=samples) {
break;
}
if (depth==DIV_SAMPLE_DEPTH_8BIT) {
float next=(float)(data[(unsigned int)j]-0x80)*mult;
data8[k++]=fmin(fmax(next,-128),127);
} else {
float next=(float)data[(unsigned int)j]*mult;
data16[k++]=fmin(fmax(next,-32768),32767);
}
}
delete[] data;
}
return DIV_DATA_SUCCESS;
}
bool DivSample::isLoopable() { bool DivSample::isLoopable() {
return loop && ((loopStart>=0 && loopStart<loopEnd) && (loopEnd>loopStart && loopEnd<=(int)samples)); return loop && ((loopStart>=0 && loopStart<loopEnd) && (loopEnd>loopStart && loopEnd<=(int)samples));
} }

View file

@ -21,6 +21,8 @@
#define _SAMPLE_H #define _SAMPLE_H
#include "../ta-utils.h" #include "../ta-utils.h"
#include "safeWriter.h"
#include "dataErrors.h"
#include <deque> #include <deque>
enum DivSampleLoopMode: unsigned char { enum DivSampleLoopMode: unsigned char {
@ -130,6 +132,20 @@ struct DivSample {
std::deque<DivSampleHistory*> undoHist; std::deque<DivSampleHistory*> undoHist;
std::deque<DivSampleHistory*> redoHist; std::deque<DivSampleHistory*> redoHist;
/**
* put sample data.
* @param w a SafeWriter.
*/
void putSampleData(SafeWriter* w);
/**
* read sample data.
* @param reader the reader.
* @param version the format version.
* @return a DivDataErrors.
*/
DivDataErrors readSampleData(SafeReader& reader, short version);
/** /**
* check if sample is loopable. * check if sample is loopable.
* @return whether it is loopable. * @return whether it is loopable.

View file

@ -132,6 +132,9 @@ void FurnaceGUI::drawInsList(bool asChild) {
if (ImGui::MenuItem("instrument")) { if (ImGui::MenuItem("instrument")) {
doAction(GUI_ACTION_INS_LIST_SAVE); doAction(GUI_ACTION_INS_LIST_SAVE);
} }
if (ImGui::MenuItem("instrument (legacy .fui)")) {
doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
}
if (ImGui::MenuItem("instrument (.dmp)")) { if (ImGui::MenuItem("instrument (.dmp)")) {
doAction(GUI_ACTION_INS_LIST_SAVE_DMP); doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
} }
@ -151,6 +154,9 @@ void FurnaceGUI::drawInsList(bool asChild) {
} }
} else { } else {
if (ImGui::BeginPopupContextItem("InsSaveFormats",ImGuiMouseButton_Right)) { if (ImGui::BeginPopupContextItem("InsSaveFormats",ImGuiMouseButton_Right)) {
if (ImGui::MenuItem("save in legacy format...")) {
doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
}
if (ImGui::MenuItem("save as .dmp...")) { if (ImGui::MenuItem("save as .dmp...")) {
doAction(GUI_ACTION_INS_LIST_SAVE_DMP); doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
} }
@ -434,6 +440,9 @@ void FurnaceGUI::drawInsList(bool asChild) {
if (ImGui::MenuItem("save")) { if (ImGui::MenuItem("save")) {
doAction(GUI_ACTION_INS_LIST_SAVE); doAction(GUI_ACTION_INS_LIST_SAVE);
} }
if (ImGui::MenuItem("save (legacy .fui)")) {
doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
}
if (ImGui::MenuItem("save (.dmp)")) { if (ImGui::MenuItem("save (.dmp)")) {
doAction(GUI_ACTION_INS_LIST_SAVE_DMP); doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
} }

View file

@ -599,6 +599,9 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_INS_LIST_SAVE: case GUI_ACTION_INS_LIST_SAVE:
if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE); if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE);
break; break;
case GUI_ACTION_INS_LIST_SAVE_OLD:
if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE_OLD);
break;
case GUI_ACTION_INS_LIST_SAVE_DMP: case GUI_ACTION_INS_LIST_SAVE_DMP:
if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE_DMP); if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE_DMP);
break; break;

View file

@ -1506,6 +1506,16 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
dpiScale dpiScale
); );
break; break;
case GUI_FILE_INS_SAVE_OLD:
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
hasOpened=fileDialog->openSave(
"Save Instrument",
{"Furnace instrument", "*.fui"},
"Furnace instrument{.fui}",
workingDirIns,
dpiScale
);
break;
case GUI_FILE_INS_SAVE_DMP: case GUI_FILE_INS_SAVE_DMP:
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
hasOpened=fileDialog->openSave( hasOpened=fileDialog->openSave(
@ -3855,6 +3865,7 @@ bool FurnaceGUI::loop() {
case GUI_FILE_INS_OPEN: case GUI_FILE_INS_OPEN:
case GUI_FILE_INS_OPEN_REPLACE: case GUI_FILE_INS_OPEN_REPLACE:
case GUI_FILE_INS_SAVE: case GUI_FILE_INS_SAVE:
case GUI_FILE_INS_SAVE_OLD:
case GUI_FILE_INS_SAVE_DMP: case GUI_FILE_INS_SAVE_DMP:
workingDirIns=fileDialog->getPath()+DIR_SEPARATOR_STR; workingDirIns=fileDialog->getPath()+DIR_SEPARATOR_STR;
break; break;
@ -3950,6 +3961,9 @@ bool FurnaceGUI::loop() {
if (curFileDialog==GUI_FILE_INS_SAVE) { if (curFileDialog==GUI_FILE_INS_SAVE) {
checkExtension(".fui"); checkExtension(".fui");
} }
if (curFileDialog==GUI_FILE_INS_SAVE_OLD) {
checkExtension(".fui");
}
if (curFileDialog==GUI_FILE_INS_SAVE_DMP) { if (curFileDialog==GUI_FILE_INS_SAVE_DMP) {
checkExtension(".dmp"); checkExtension(".dmp");
} }
@ -4041,7 +4055,12 @@ bool FurnaceGUI::loop() {
break; break;
case GUI_FILE_INS_SAVE: case GUI_FILE_INS_SAVE:
if (curIns>=0 && curIns<(int)e->song.ins.size()) { if (curIns>=0 && curIns<(int)e->song.ins.size()) {
e->song.ins[curIns]->save(copyOfName.c_str()); e->song.ins[curIns]->save(copyOfName.c_str(),false,&e->song);
}
break;
case GUI_FILE_INS_SAVE_OLD:
if (curIns>=0 && curIns<(int)e->song.ins.size()) {
e->song.ins[curIns]->save(copyOfName.c_str(),true);
} }
break; break;
case GUI_FILE_INS_SAVE_DMP: case GUI_FILE_INS_SAVE_DMP:

View file

@ -313,6 +313,7 @@ enum FurnaceGUIFileDialogs {
GUI_FILE_INS_OPEN, GUI_FILE_INS_OPEN,
GUI_FILE_INS_OPEN_REPLACE, GUI_FILE_INS_OPEN_REPLACE,
GUI_FILE_INS_SAVE, GUI_FILE_INS_SAVE,
GUI_FILE_INS_SAVE_OLD,
GUI_FILE_INS_SAVE_DMP, GUI_FILE_INS_SAVE_DMP,
GUI_FILE_WAVE_OPEN, GUI_FILE_WAVE_OPEN,
GUI_FILE_WAVE_OPEN_REPLACE, GUI_FILE_WAVE_OPEN_REPLACE,
@ -507,6 +508,7 @@ enum FurnaceGUIActions {
GUI_ACTION_INS_LIST_OPEN, GUI_ACTION_INS_LIST_OPEN,
GUI_ACTION_INS_LIST_OPEN_REPLACE, GUI_ACTION_INS_LIST_OPEN_REPLACE,
GUI_ACTION_INS_LIST_SAVE, GUI_ACTION_INS_LIST_SAVE,
GUI_ACTION_INS_LIST_SAVE_OLD,
GUI_ACTION_INS_LIST_SAVE_DMP, GUI_ACTION_INS_LIST_SAVE_DMP,
GUI_ACTION_INS_LIST_MOVE_UP, GUI_ACTION_INS_LIST_MOVE_UP,
GUI_ACTION_INS_LIST_MOVE_DOWN, GUI_ACTION_INS_LIST_MOVE_DOWN,

View file

@ -590,6 +590,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("INS_LIST_OPEN", "Open", 0), D("INS_LIST_OPEN", "Open", 0),
D("INS_LIST_OPEN_REPLACE", "Open (replace current)", 0), D("INS_LIST_OPEN_REPLACE", "Open (replace current)", 0),
D("INS_LIST_SAVE", "Save", 0), D("INS_LIST_SAVE", "Save", 0),
D("INS_LIST_SAVE_OLD", "Save (legacy .fui)", 0),
D("INS_LIST_SAVE_DMP", "Save (.dmp)", 0), D("INS_LIST_SAVE_DMP", "Save (.dmp)", 0),
D("INS_LIST_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP), D("INS_LIST_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP),
D("INS_LIST_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN), D("INS_LIST_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN),
@ -1084,4 +1085,4 @@ const char* chipCategoryNames[]={
"Special", "Special",
"Sample", "Sample",
NULL NULL
}; };

View file

@ -2167,6 +2167,9 @@ void FurnaceGUI::drawInsEdit() {
doAction(GUI_ACTION_INS_LIST_SAVE); doAction(GUI_ACTION_INS_LIST_SAVE);
} }
if (ImGui::BeginPopupContextItem("InsSaveFormats",ImGuiMouseButton_Right)) { if (ImGui::BeginPopupContextItem("InsSaveFormats",ImGuiMouseButton_Right)) {
if (ImGui::MenuItem("save in legacy format...")) {
doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
}
if (ImGui::MenuItem("save as .dmp...")) { if (ImGui::MenuItem("save as .dmp...")) {
doAction(GUI_ACTION_INS_LIST_SAVE_DMP); doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
} }