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
for (int i=0; i<ds.sampleLen; i++) {
int vol=0;
int pitch=0;
DivSample* sample=new DivSample;
if (!reader.seek(samplePtr[i],SEEK_SET)) {
logE("couldn't seek to sample %d!",i);
lastError=fmt::sprintf("couldn't seek to sample %d!",i);
ds.unload();
delete sample;
delete[] file;
return false;
}
reader.read(magic,4);
if (strcmp(magic,"SMPL")!=0 && strcmp(magic,"SMP2")!=0) {
logE("%d: invalid sample header!",i);
lastError="invalid sample header!";
if (sample->readSampleData(reader,ds.version)!=DIV_DATA_SUCCESS) {
lastError="invalid sample header/data!";
ds.unload();
delete sample;
delete[] file;
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);
}
@ -4635,45 +4517,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
for (int i=0; i<song.sampleLen; i++) {
DivSample* sample=song.sample[i];
samplePtr.push_back(w->tell());
w->write("SMP2",4);
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);
sample->putSampleData(w);
}
/// 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) {
FEATURE_BEGIN("SL");
bool sampleUsed[256];
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());
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) {
FEATURE_BEGIN("WL");
bool waveUsed[256];
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());
for (int i: list) {
@ -687,9 +691,14 @@ void DivInstrument::writeFeatureX1(SafeWriter* w) {
void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
size_t blockStartSeek=0;
size_t blockEndSeek=0;
size_t slSeek=0;
size_t wlSeek=0;
std::vector<int> waveList;
std::vector<int> sampleList;
std::vector<unsigned int> wavePtr;
std::vector<unsigned int> samplePtr;
if (fui) {
w->write("FINS",4);
} else {
@ -747,17 +756,21 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
break;
case DIV_INS_AMIGA:
featureSM=true;
if (!amiga.useWave) featureSL=true;
break;
case DIV_INS_PCE:
checkForWL=true;
featureSM=true;
if (amiga.useSample) featureSL=true;
if (ws.enabled) featureWS=true;
break;
case DIV_INS_AY:
featureSM=true;
if (amiga.useSample) featureSL=true;
break;
case DIV_INS_AY8930:
featureSM=true;
if (amiga.useSample) featureSL=true;
break;
case DIV_INS_TIA:
break;
@ -769,6 +782,7 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
break;
case DIV_INS_VRC6:
featureSM=true;
if (amiga.useSample) featureSL=true;
break;
case DIV_INS_OPLL:
featureFM=true;
@ -807,36 +821,44 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
case DIV_INS_SWAN:
checkForWL=true;
featureSM=true;
if (amiga.useSample) featureSL=true;
if (ws.enabled) featureWS=true;
break;
case DIV_INS_MIKEY:
featureSM=true;
if (amiga.useSample) featureSL=true;
break;
case DIV_INS_VERA:
break;
case DIV_INS_X1_010:
checkForWL=true;
featureX1=true;
featureSM=true;
if (amiga.useSample) featureSL=true;
if (ws.enabled) featureWS=true;
break;
case DIV_INS_VRC6_SAW:
break;
case DIV_INS_ES5506:
featureSM=true;
featureSL=true;
featureES=true;
break;
case DIV_INS_MULTIPCM:
featureSM=true;
featureSL=true;
featureMP=true;
break;
case DIV_INS_SNES:
featureSM=true;
if (!amiga.useWave) featureSL=true;
featureSN=true;
checkForWL=true;
if (ws.enabled) featureWS=true;
break;
case DIV_INS_SU:
featureSM=true;
if (amiga.useSample) featureSL=true;
featureSU=true;
break;
case DIV_INS_NAMCO:
@ -854,27 +876,35 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
break;
case DIV_INS_MSM6258:
featureSM=true;
featureSL=true;
break;
case DIV_INS_MSM6295:
featureSM=true;
featureSL=true;
break;
case DIV_INS_ADPCMA:
featureSM=true;
featureSL=true;
break;
case DIV_INS_ADPCMB:
featureSM=true;
featureSL=true;
break;
case DIV_INS_SEGAPCM:
featureSM=true;
featureSL=true;
break;
case DIV_INS_QSOUND:
featureSM=true;
featureSL=true;
break;
case DIV_INS_YMZ280B:
featureSM=true;
featureSL=true;
break;
case DIV_INS_RF5C68:
featureSM=true;
featureSL=true;
break;
case DIV_INS_MSM5232:
break;
@ -1049,10 +1079,10 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
writeFeatureWS(w);
}
if (featureSL) {
writeFeatureSL(w,sampleList,song);
slSeek=writeFeatureSL(w,sampleList,song);
}
if (featureWL) {
writeFeatureWL(w,waveList,song);
wlSeek=writeFeatureWL(w,waveList,song);
}
if (featureMP) {
writeFeatureMP(w);
@ -1069,7 +1099,44 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
if (fui && (featureSL || featureWL)) {
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();
@ -2204,13 +2271,15 @@ void DivInstrument::readFeatureWS(SafeReader& reader) {
READ_FEAT_END;
}
void DivInstrument::readFeatureSL(SafeReader& reader, const DivSong* song) {
void DivInstrument::readFeatureSL(SafeReader& reader, DivSong* song, short version) {
READ_FEAT_BEGIN;
unsigned int samplePtr[256];
unsigned char sampleIndex[256];
unsigned char sampleRemap[256];
memset(samplePtr,0,256*sizeof(unsigned int));
memset(sampleIndex,0,256);
memset(sampleRemap,0,256);
unsigned char sampleCount=reader.readC();
@ -2223,20 +2292,53 @@ void DivInstrument::readFeatureSL(SafeReader& reader, const DivSong* song) {
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);
// 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;
}
void DivInstrument::readFeatureWL(SafeReader& reader, const DivSong* song) {
void DivInstrument::readFeatureWL(SafeReader& reader, DivSong* song, short version) {
READ_FEAT_BEGIN;
unsigned int wavePtr[256];
unsigned char waveIndex[256];
unsigned char waveRemap[256];
memset(wavePtr,0,256*sizeof(unsigned int));
memset(waveIndex,0,256);
memset(waveRemap,0,256);
unsigned char waveCount=reader.readC();
@ -2249,10 +2351,40 @@ void DivInstrument::readFeatureWL(SafeReader& reader, const DivSong* song) {
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);
// 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;
}
@ -2358,9 +2490,9 @@ DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, b
} else if (memcmp(featCode,"WS",2)==0) { // WaveSynth
readFeatureWS(reader);
} 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
readFeatureWL(reader,NULL);
readFeatureWL(reader,song,version);
} else if (memcmp(featCode,"MP",2)==0) { // MultiPCM
readFeatureMP(reader);
} 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);
}
bool DivInstrument::save(const char* path) {
bool DivInstrument::save(const char* path, bool oldFormat, DivSong* song) {
SafeWriter* w=new SafeWriter();
w->init();
//putInsData2(w,true);
if (oldFormat) {
// write magic
w->write("-Furnace instr.-",16);
// write magic
w->write("-Furnace instr.-",16);
// write version
w->writeS(DIV_ENGINE_VERSION);
// write version
w->writeS(DIV_ENGINE_VERSION);
// reserved
w->writeS(0);
// reserved
w->writeS(0);
// pointer to data
w->writeI(32);
// pointer to data
w->writeI(32);
// currently reserved (TODO; wavetable and sample here)
w->writeS(0);
w->writeS(0);
w->writeI(0);
// currently reserved (TODO; wavetable and sample here)
w->writeS(0);
w->writeS(0);
w->writeI(0);
putInsData(w);
putInsData(w);
} else {
putInsData2(w,true,song);
}
FILE* outFile=ps_fopen(path,"wb");
if (outFile==NULL) {

View file

@ -674,8 +674,8 @@ struct DivInstrument {
void readFeatureN1(SafeReader& reader);
void readFeatureFD(SafeReader& reader);
void readFeatureWS(SafeReader& reader);
void readFeatureSL(SafeReader& reader, const DivSong* song);
void readFeatureWL(SafeReader& reader, const DivSong* song);
void readFeatureSL(SafeReader& reader, DivSong* song, short version);
void readFeatureWL(SafeReader& reader, DivSong* song, short version);
void readFeatureMP(SafeReader& reader);
void readFeatureSU(SafeReader& reader);
void readFeatureES(SafeReader& reader);
@ -707,9 +707,11 @@ struct DivInstrument {
/**
* save this instrument to a file.
* @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.
*/
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.

View file

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

View file

@ -21,6 +21,8 @@
#define _SAMPLE_H
#include "../ta-utils.h"
#include "safeWriter.h"
#include "dataErrors.h"
#include <deque>
enum DivSampleLoopMode: unsigned char {
@ -130,6 +132,20 @@ struct DivSample {
std::deque<DivSampleHistory*> undoHist;
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.
* @return whether it is loopable.

View file

@ -132,6 +132,9 @@ void FurnaceGUI::drawInsList(bool asChild) {
if (ImGui::MenuItem("instrument")) {
doAction(GUI_ACTION_INS_LIST_SAVE);
}
if (ImGui::MenuItem("instrument (legacy .fui)")) {
doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
}
if (ImGui::MenuItem("instrument (.dmp)")) {
doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
}
@ -151,6 +154,9 @@ void FurnaceGUI::drawInsList(bool asChild) {
}
} else {
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...")) {
doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
}
@ -434,6 +440,9 @@ void FurnaceGUI::drawInsList(bool asChild) {
if (ImGui::MenuItem("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)")) {
doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
}

View file

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

View file

@ -1506,6 +1506,16 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
dpiScale
);
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:
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
hasOpened=fileDialog->openSave(
@ -3855,6 +3865,7 @@ bool FurnaceGUI::loop() {
case GUI_FILE_INS_OPEN:
case GUI_FILE_INS_OPEN_REPLACE:
case GUI_FILE_INS_SAVE:
case GUI_FILE_INS_SAVE_OLD:
case GUI_FILE_INS_SAVE_DMP:
workingDirIns=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
@ -3950,6 +3961,9 @@ bool FurnaceGUI::loop() {
if (curFileDialog==GUI_FILE_INS_SAVE) {
checkExtension(".fui");
}
if (curFileDialog==GUI_FILE_INS_SAVE_OLD) {
checkExtension(".fui");
}
if (curFileDialog==GUI_FILE_INS_SAVE_DMP) {
checkExtension(".dmp");
}
@ -4041,7 +4055,12 @@ bool FurnaceGUI::loop() {
break;
case GUI_FILE_INS_SAVE:
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;
case GUI_FILE_INS_SAVE_DMP:

View file

@ -313,6 +313,7 @@ enum FurnaceGUIFileDialogs {
GUI_FILE_INS_OPEN,
GUI_FILE_INS_OPEN_REPLACE,
GUI_FILE_INS_SAVE,
GUI_FILE_INS_SAVE_OLD,
GUI_FILE_INS_SAVE_DMP,
GUI_FILE_WAVE_OPEN,
GUI_FILE_WAVE_OPEN_REPLACE,
@ -507,6 +508,7 @@ enum FurnaceGUIActions {
GUI_ACTION_INS_LIST_OPEN,
GUI_ACTION_INS_LIST_OPEN_REPLACE,
GUI_ACTION_INS_LIST_SAVE,
GUI_ACTION_INS_LIST_SAVE_OLD,
GUI_ACTION_INS_LIST_SAVE_DMP,
GUI_ACTION_INS_LIST_MOVE_UP,
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_REPLACE", "Open (replace current)", 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_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP),
D("INS_LIST_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN),
@ -1084,4 +1085,4 @@ const char* chipCategoryNames[]={
"Special",
"Sample",
NULL
};
};

View file

@ -2167,6 +2167,9 @@ void FurnaceGUI::drawInsEdit() {
doAction(GUI_ACTION_INS_LIST_SAVE);
}
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...")) {
doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
}