add ability to save ins/wave as .dmp/.dmw
also saving wavetables as raw data
This commit is contained in:
parent
041a76ad81
commit
02fb5abc02
|
@ -1150,3 +1150,148 @@ bool DivInstrument::save(const char* path) {
|
|||
w->finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DivInstrument::saveDMP(const char* path) {
|
||||
SafeWriter* w=new SafeWriter();
|
||||
w->init();
|
||||
|
||||
// write version
|
||||
w->writeC(11);
|
||||
|
||||
// guess the system
|
||||
switch (type) {
|
||||
case DIV_INS_FM:
|
||||
// we can't tell between Genesis, Neo Geo and Arcade ins type yet
|
||||
w->writeC(0x02);
|
||||
w->writeC(1);
|
||||
break;
|
||||
case DIV_INS_STD:
|
||||
// we can't tell between SMS and NES ins type yet
|
||||
w->writeC(0x03);
|
||||
w->writeC(0);
|
||||
break;
|
||||
case DIV_INS_GB:
|
||||
w->writeC(0x04);
|
||||
w->writeC(0);
|
||||
break;
|
||||
case DIV_INS_C64:
|
||||
w->writeC(0x07);
|
||||
w->writeC(0);
|
||||
break;
|
||||
case DIV_INS_PCE:
|
||||
w->writeC(0x06);
|
||||
w->writeC(0);
|
||||
break;
|
||||
case DIV_INS_OPLL:
|
||||
// ???
|
||||
w->writeC(0x13);
|
||||
w->writeC(1);
|
||||
break;
|
||||
case DIV_INS_OPZ:
|
||||
// data will be lost
|
||||
w->writeC(0x08);
|
||||
w->writeC(1);
|
||||
break;
|
||||
case DIV_INS_FDS:
|
||||
// ???
|
||||
w->writeC(0x06);
|
||||
w->writeC(0);
|
||||
break;
|
||||
default:
|
||||
// not supported by .dmp
|
||||
w->finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type==DIV_INS_FM || type==DIV_INS_OPLL || type==DIV_INS_OPZ) {
|
||||
w->writeC(fm.fms);
|
||||
w->writeC(fm.fb);
|
||||
w->writeC(fm.alg);
|
||||
w->writeC(fm.ams);
|
||||
|
||||
// TODO: OPLL params
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=fm.op[i];
|
||||
w->writeC(op.mult);
|
||||
w->writeC(op.tl);
|
||||
w->writeC(op.ar);
|
||||
w->writeC(op.dr);
|
||||
w->writeC(op.sl);
|
||||
w->writeC(op.rr);
|
||||
w->writeC(op.am);
|
||||
w->writeC(op.rs);
|
||||
w->writeC(op.dt|(op.dt2<<4));
|
||||
w->writeC(op.d2r);
|
||||
w->writeC(op.ssgEnv);
|
||||
}
|
||||
} else {
|
||||
if (type!=DIV_INS_GB) {
|
||||
w->writeC(std.volMacro.len);
|
||||
for (int i=0; i<std.volMacro.len; i++) {
|
||||
w->writeI(std.volMacro.val[i]);
|
||||
}
|
||||
if (std.volMacro.len>0) w->writeC(std.volMacro.loop);
|
||||
}
|
||||
|
||||
w->writeC(std.arpMacro.len);
|
||||
for (int i=0; i<std.arpMacro.len; i++) {
|
||||
w->writeI(std.arpMacro.val[i]+12);
|
||||
}
|
||||
if (std.arpMacro.len>0) w->writeC(std.arpMacro.loop);
|
||||
w->writeC(std.arpMacro.mode);
|
||||
|
||||
w->writeC(std.dutyMacro.len);
|
||||
for (int i=0; i<std.dutyMacro.len; i++) {
|
||||
w->writeI(std.dutyMacro.val[i]+12);
|
||||
}
|
||||
if (std.dutyMacro.len>0) w->writeC(std.dutyMacro.loop);
|
||||
|
||||
w->writeC(std.waveMacro.len);
|
||||
for (int i=0; i<std.waveMacro.len; i++) {
|
||||
w->writeI(std.waveMacro.val[i]+12);
|
||||
}
|
||||
if (std.waveMacro.len>0) w->writeC(std.waveMacro.loop);
|
||||
|
||||
if (type==DIV_INS_C64) {
|
||||
w->writeC(c64.triOn);
|
||||
w->writeC(c64.sawOn);
|
||||
w->writeC(c64.pulseOn);
|
||||
w->writeC(c64.noiseOn);
|
||||
w->writeC(c64.a);
|
||||
w->writeC(c64.d);
|
||||
w->writeC(c64.s);
|
||||
w->writeC(c64.r);
|
||||
w->writeC((c64.duty*100)/4095);
|
||||
w->writeC(c64.ringMod);
|
||||
w->writeC(c64.oscSync);
|
||||
w->writeC(c64.toFilter);
|
||||
w->writeC(c64.volIsCutoff);
|
||||
w->writeC(c64.initFilter);
|
||||
w->writeC(c64.res);
|
||||
w->writeC((c64.cut*100)/2047);
|
||||
w->writeC(c64.hp);
|
||||
w->writeC(c64.lp);
|
||||
w->writeC(c64.bp);
|
||||
w->writeC(c64.ch3off);
|
||||
}
|
||||
if (type==DIV_INS_GB) {
|
||||
w->writeC(gb.envVol);
|
||||
w->writeC(gb.envDir);
|
||||
w->writeC(gb.envLen);
|
||||
w->writeC(gb.soundLen);
|
||||
}
|
||||
}
|
||||
|
||||
FILE* outFile=ps_fopen(path,"wb");
|
||||
if (outFile==NULL) {
|
||||
logE("could not save instrument: %s!",strerror(errno));
|
||||
w->finish();
|
||||
return false;
|
||||
}
|
||||
if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
|
||||
logW("did not write entire instrument!");
|
||||
}
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -500,6 +500,13 @@ struct DivInstrument {
|
|||
* @return whether it was successful.
|
||||
*/
|
||||
bool save(const char* path);
|
||||
|
||||
/**
|
||||
* save this instrument to a file in .dmp format.
|
||||
* @param path file path.
|
||||
* @return whether it was successful.
|
||||
*/
|
||||
bool saveDMP(const char* path);
|
||||
DivInstrument():
|
||||
name(""),
|
||||
type(DIV_INS_FM) {
|
||||
|
|
|
@ -92,3 +92,65 @@ bool DivWavetable::save(const char* path) {
|
|||
w->finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DivWavetable::saveDMW(const char* path) {
|
||||
SafeWriter* w=new SafeWriter();
|
||||
w->init();
|
||||
|
||||
// write width
|
||||
w->writeI(len);
|
||||
|
||||
// check height
|
||||
w->writeC(max);
|
||||
if (max==255) {
|
||||
// write as new format (because 0xff means that)
|
||||
w->writeC(1); // format version
|
||||
w->writeC(max); // actual height
|
||||
|
||||
// waveform data
|
||||
for (int i=0; i<len; i++) {
|
||||
w->writeI(data[i]&0xff);
|
||||
}
|
||||
} else {
|
||||
// write as old format
|
||||
for (int i=0; i<len; i++) {
|
||||
w->writeC(data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
FILE* outFile=ps_fopen(path,"wb");
|
||||
if (outFile==NULL) {
|
||||
logE("could not save wavetable: %s!",strerror(errno));
|
||||
w->finish();
|
||||
return false;
|
||||
}
|
||||
if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
|
||||
logW("did not write entire wavetable!");
|
||||
}
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DivWavetable::saveRaw(const char* path) {
|
||||
SafeWriter* w=new SafeWriter();
|
||||
w->init();
|
||||
|
||||
// waveform data
|
||||
for (int i=0; i<len; i++) {
|
||||
w->writeC(data[i]);
|
||||
}
|
||||
|
||||
FILE* outFile=ps_fopen(path,"wb");
|
||||
if (outFile==NULL) {
|
||||
logE("could not save wavetable: %s!",strerror(errno));
|
||||
w->finish();
|
||||
return false;
|
||||
}
|
||||
if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
|
||||
logW("did not write entire wavetable!");
|
||||
}
|
||||
fclose(outFile);
|
||||
w->finish();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,20 @@ struct DivWavetable {
|
|||
* @return whether it was successful.
|
||||
*/
|
||||
bool save(const char* path);
|
||||
|
||||
/**
|
||||
* save this wavetable to a file in .dmw format.
|
||||
* @param path file path.
|
||||
* @return whether it was successful.
|
||||
*/
|
||||
bool saveDMW(const char* path);
|
||||
|
||||
/**
|
||||
* save this wavetable to a file in raw format.
|
||||
* @param path file path.
|
||||
* @return whether it was successful.
|
||||
*/
|
||||
bool saveRaw(const char* path);
|
||||
DivWavetable():
|
||||
len(32),
|
||||
min(0),
|
||||
|
@ -56,4 +70,4 @@ struct DivWavetable {
|
|||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1313,8 +1313,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
|
||||
hasOpened=fileDialog->openSave(
|
||||
"Save Instrument",
|
||||
{"Furnace instrument", "*.fui"},
|
||||
"Furnace instrument{.fui}",
|
||||
{"Furnace instrument", "*.fui",
|
||||
"DefleMask preset", "*.dmp"},
|
||||
"Furnace instrument{.fui},DefleMask preset{.dmp}",
|
||||
workingDirIns,
|
||||
dpiScale
|
||||
);
|
||||
|
@ -1335,8 +1336,10 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
if (!dirExists(workingDirWave)) workingDirWave=getHomeDir();
|
||||
hasOpened=fileDialog->openSave(
|
||||
"Save Wavetable",
|
||||
{"Furnace wavetable", ".fuw"},
|
||||
"Furnace wavetable{.fuw}",
|
||||
{"Furnace wavetable", ".fuw",
|
||||
"DefleMask wavetable", ".dmw",
|
||||
"raw data", ".raw"},
|
||||
"Furnace wavetable{.fuw},DefleMask wavetable{.dmw},raw data{.raw}",
|
||||
workingDirWave,
|
||||
dpiScale
|
||||
);
|
||||
|
@ -1921,6 +1924,15 @@ void FurnaceGUI::processDrags(int dragX, int dragY) {
|
|||
fileName+=fallback; \
|
||||
}
|
||||
|
||||
#define checkExtensionTriple(x,y,z,fallback) \
|
||||
String lowerCase=fileName; \
|
||||
for (char& i: lowerCase) { \
|
||||
if (i>='A' && i<='Z') i+='a'-'A'; \
|
||||
} \
|
||||
if (lowerCase.size()<4 || (lowerCase.rfind(x)!=lowerCase.size()-4 && lowerCase.rfind(y)!=lowerCase.size()-4 && lowerCase.rfind(z)!=lowerCase.size()-4)) { \
|
||||
fileName+=fallback; \
|
||||
}
|
||||
|
||||
#define drawOpMask(m) \
|
||||
ImGui::PushFont(patFont); \
|
||||
ImGui::PushID("om_" #m); \
|
||||
|
@ -3365,10 +3377,21 @@ bool FurnaceGUI::loop() {
|
|||
checkExtension(".wav");
|
||||
}
|
||||
if (curFileDialog==GUI_FILE_INS_SAVE) {
|
||||
checkExtension(".fui");
|
||||
// we can't tell whether the user chose .fui or .dmp in the system file picker
|
||||
const char* fallbackExt=(settings.sysFileDialog || ImGuiFileDialog::Instance()->GetCurrentFilter()=="Furnace instrument")?".fui":".dmp";
|
||||
checkExtensionDual(".fui",".dmp",fallbackExt);
|
||||
}
|
||||
if (curFileDialog==GUI_FILE_WAVE_SAVE) {
|
||||
checkExtension(".fuw");
|
||||
// same thing here
|
||||
const char* fallbackExt=".fuw";
|
||||
if (!settings.sysFileDialog) {
|
||||
if (ImGuiFileDialog::Instance()->GetCurrentFilter()=="raw data") {
|
||||
fallbackExt=".raw";
|
||||
} else if (ImGuiFileDialog::Instance()->GetCurrentFilter()=="DefleMask wavetable") {
|
||||
fallbackExt=".dmw";
|
||||
}
|
||||
}
|
||||
checkExtensionTriple(".fuw",".dmw",".raw",fallbackExt);
|
||||
}
|
||||
if (curFileDialog==GUI_FILE_EXPORT_VGM) {
|
||||
checkExtension(".vgm");
|
||||
|
@ -3451,12 +3474,34 @@ 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());
|
||||
String lowerCase=fileName;
|
||||
for (char& i: lowerCase) {
|
||||
if (i>='A' && i<='Z') i+='a'-'A';
|
||||
}
|
||||
if ((lowerCase.size()<4 || lowerCase.rfind(".dmp")!=lowerCase.size()-4)) {
|
||||
e->song.ins[curIns]->save(copyOfName.c_str());
|
||||
} else {
|
||||
if (!e->song.ins[curIns]->saveDMP(copyOfName.c_str())) {
|
||||
showError("error while saving instrument! make sure your instrument is compatible.");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GUI_FILE_WAVE_SAVE:
|
||||
if (curWave>=0 && curWave<(int)e->song.wave.size()) {
|
||||
e->song.wave[curWave]->save(copyOfName.c_str());
|
||||
String lowerCase=fileName;
|
||||
for (char& i: lowerCase) {
|
||||
if (i>='A' && i<='Z') i+='a'-'A';
|
||||
}
|
||||
if (lowerCase.size()<4) {
|
||||
e->song.wave[curWave]->save(copyOfName.c_str());
|
||||
} else if (lowerCase.rfind(".dmw")==lowerCase.size()-4) {
|
||||
e->song.wave[curWave]->saveDMW(copyOfName.c_str());
|
||||
} else if (lowerCase.rfind(".raw")==lowerCase.size()-4) {
|
||||
e->song.wave[curWave]->saveRaw(copyOfName.c_str());
|
||||
} else {
|
||||
e->song.wave[curWave]->save(copyOfName.c_str());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GUI_FILE_SAMPLE_OPEN: {
|
||||
|
|
Loading…
Reference in New Issue