From 8b79bf78a928dff7ffda66b2e4bd19a76748995c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 22 Jan 2022 00:14:48 -0500 Subject: [PATCH] add Furnace instrument loading TODO: .dmp loading --- src/engine/engine.cpp | 217 +++++++++++++++----------------------- src/engine/engine.h | 2 +- src/engine/instrument.cpp | 140 ++++++++++++++++++++++++ src/engine/instrument.h | 3 + src/engine/song.h | 2 +- src/gui/gui.cpp | 5 +- 6 files changed, 231 insertions(+), 138 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 89d8958b..9d651b25 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -651,7 +651,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { delete[] file; return false; } - ds.version=reader.readC(); + ds.version=(unsigned char)reader.readC(); logI("module version %d (0x%.2x)\n",ds.version,ds.version); if (ds.version>0x18) { logW("this version is not supported by Furnace yet!\n"); @@ -1282,143 +1282,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { // read instruments for (int i=0; ireadInsData(reader,ds.version)!=DIV_DATA_SUCCESS) { + lastError="invalid instrument header/data!"; + delete ins; delete[] file; return false; } - reader.readI(); - DivInstrument* ins=new DivInstrument; - - reader.readS(); // format version. ignored. - ins->type=(DivInstrumentType)reader.readC(); - ins->mode=(ins->type==DIV_INS_FM); - reader.readC(); - ins->name=reader.readString(); - - // FM - ins->fm.alg=reader.readC(); - ins->fm.fb=reader.readC(); - ins->fm.fms=reader.readC(); - ins->fm.ams=reader.readC(); - ins->fm.ops=reader.readC(); - reader.readC(); - reader.readC(); - reader.readC(); - - for (int j=0; j<4; j++) { - DivInstrumentFM::Operator& op=ins->fm.op[j]; - op.am=reader.readC(); - op.ar=reader.readC(); - op.dr=reader.readC(); - op.mult=reader.readC(); - op.rr=reader.readC(); - op.sl=reader.readC(); - op.tl=reader.readC(); - op.dt2=reader.readC(); - op.rs=reader.readC(); - op.dt=reader.readC(); - op.d2r=reader.readC(); - op.ssgEnv=reader.readC(); - - op.dam=reader.readC(); - op.dvb=reader.readC(); - op.egt=reader.readC(); - op.ksl=reader.readC(); - op.sus=reader.readC(); - op.vib=reader.readC(); - op.ws=reader.readC(); - op.ksr=reader.readC(); - - // reserved - for (int k=0; k<12; k++) reader.readC(); - } - - // GB - ins->gb.envVol=reader.readC(); - ins->gb.envDir=reader.readC(); - ins->gb.envLen=reader.readC(); - ins->gb.soundLen=reader.readC(); - - // C64 - ins->c64.triOn=reader.readC(); - ins->c64.sawOn=reader.readC(); - ins->c64.pulseOn=reader.readC(); - ins->c64.noiseOn=reader.readC(); - ins->c64.a=reader.readC(); - ins->c64.d=reader.readC(); - ins->c64.s=reader.readC(); - ins->c64.r=reader.readC(); - ins->c64.duty=reader.readS(); - ins->c64.ringMod=reader.readC(); - ins->c64.oscSync=reader.readC(); - ins->c64.toFilter=reader.readC(); - ins->c64.initFilter=reader.readC(); - ins->c64.volIsCutoff=reader.readC(); - ins->c64.res=reader.readC(); - ins->c64.lp=reader.readC(); - ins->c64.bp=reader.readC(); - ins->c64.hp=reader.readC(); - ins->c64.ch3off=reader.readC(); - ins->c64.cut=reader.readS(); - ins->c64.dutyIsAbs=reader.readC(); - ins->c64.filterIsAbs=reader.readC(); - - // Amiga - ins->amiga.initSample=reader.readS(); - // reserved - for (int k=0; k<14; k++) reader.readC(); - - // standard - ins->std.volMacroLen=reader.readI(); - ins->std.arpMacroLen=reader.readI(); - ins->std.dutyMacroLen=reader.readI(); - ins->std.waveMacroLen=reader.readI(); - if (ds.version>=17) { - ins->std.pitchMacroLen=reader.readI(); - ins->std.ex1MacroLen=reader.readI(); - ins->std.ex2MacroLen=reader.readI(); - ins->std.ex3MacroLen=reader.readI(); - } - ins->std.volMacroLoop=reader.readI(); - ins->std.arpMacroLoop=reader.readI(); - ins->std.dutyMacroLoop=reader.readI(); - ins->std.waveMacroLoop=reader.readI(); - if (ds.version>=17) { - ins->std.pitchMacroLoop=reader.readI(); - ins->std.ex1MacroLoop=reader.readI(); - ins->std.ex2MacroLoop=reader.readI(); - ins->std.ex3MacroLoop=reader.readI(); - } - ins->std.arpMacroMode=reader.readC(); - ins->std.volMacroHeight=reader.readC(); - ins->std.dutyMacroHeight=reader.readC(); - ins->std.waveMacroHeight=reader.readC(); - if (ins->std.volMacroHeight==0) ins->std.volMacroHeight=15; - if (ins->std.dutyMacroHeight==0) ins->std.dutyMacroHeight=3; - if (ins->std.waveMacroHeight==0) ins->std.waveMacroHeight=63; - reader.read(ins->std.volMacro,4*ins->std.volMacroLen); - reader.read(ins->std.arpMacro,4*ins->std.arpMacroLen); - reader.read(ins->std.dutyMacro,4*ins->std.dutyMacroLen); - reader.read(ins->std.waveMacro,4*ins->std.waveMacroLen); - if (ds.version>=17) { - reader.read(ins->std.pitchMacro,4*ins->std.pitchMacroLen); - reader.read(ins->std.ex1Macro,4*ins->std.ex1MacroLen); - reader.read(ins->std.ex2Macro,4*ins->std.ex2MacroLen); - reader.read(ins->std.ex3Macro,4*ins->std.ex3MacroLen); - } else { - if (ins->type==DIV_INS_STD) { - if (ins->std.volMacroHeight==31) { - ins->type=DIV_INS_PCE; - } - if (ins->std.dutyMacroHeight==31) { - ins->type=DIV_INS_AY; - } - } - } ds.ins.push_back(ins); } @@ -3050,6 +2922,83 @@ int DivEngine::addInstrument(int refChan) { return insCount; } +bool DivEngine::addInstrumentFromFile(const char *path) { + FILE* f=ps_fopen(path,"rb"); + if (f==NULL) { + return false; + } + unsigned char* buf; + ssize_t len; + if (fseek(f,0,SEEK_END)!=0) { + fclose(f); + return false; + } + len=ftell(f); + if (len<0) { + fclose(f); + return false; + } + if (len==0) { + fclose(f); + return false; + } + if (fseek(f,0,SEEK_SET)!=0) { + fclose(f); + return false; + } + buf=new unsigned char[len]; + if (fread(buf,1,len,f)!=(size_t)len) { + logW("did not read entire instrument file buffer!\n"); + delete[] buf; + return false; + } + fclose(f); + + SafeReader reader=SafeReader(buf,len); + + unsigned char magic[16]; + bool isFurnaceInstr=false; + try { + reader.read(magic,16); + if (memcmp("-Furnace instr.-",magic,16)==0) { + isFurnaceInstr=true; + } + } catch (EndOfFileException e) { + reader.seek(0,SEEK_SET); + } + + DivInstrument* ins=new DivInstrument; + if (isFurnaceInstr) { + try { + short version=reader.readS(); + reader.readS(); // reserved + + unsigned int dataPtr=reader.readI(); + reader.seek(dataPtr,SEEK_SET); + + if (ins->readInsData(reader,version)!=DIV_DATA_SUCCESS) { + lastError="invalid wavetable header/data!"; + delete ins; + delete[] buf; + return false; + } + } catch (EndOfFileException e) { + lastError="premature end of file"; + logE("premature end of file!\n"); + delete[] buf; + return false; + } + } else { // read as .dmp + } + + isBusy.lock(); + int insCount=(int)song.ins.size(); + song.ins.push_back(ins); + song.insLen=insCount+1; + isBusy.unlock(); + return true; +} + void DivEngine::delInstrument(int index) { isBusy.lock(); if (index>=0 && index<(int)song.ins.size()) { diff --git a/src/engine/engine.h b/src/engine/engine.h index 460cf363..7d127333 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -376,7 +376,7 @@ class DivEngine { int addInstrument(int refChan=0); // add instrument from file - int addInstrumentFromFile(const char* path); + bool addInstrumentFromFile(const char* path); // delete instrument void delInstrument(int index); diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index fa1a3e01..6def88e2 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -1,3 +1,4 @@ +#include "dataErrors.h" #include "engine.h" #include "instrument.h" #include "../ta-log.h" @@ -137,6 +138,145 @@ void DivInstrument::putInsData(SafeWriter* w) { } } +DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { + char magic[4]; + reader.read(magic,4); + if (memcmp(magic,"INST",4)!=0) { + logE("invalid instrument header!\n"); + return DIV_DATA_INVALID_HEADER; + } + reader.readI(); + + reader.readS(); // format version. ignored. + type=(DivInstrumentType)reader.readC(); + mode=(type==DIV_INS_FM); + reader.readC(); + name=reader.readString(); + + // FM + fm.alg=reader.readC(); + fm.fb=reader.readC(); + fm.fms=reader.readC(); + fm.ams=reader.readC(); + fm.ops=reader.readC(); + reader.readC(); + reader.readC(); + reader.readC(); + + for (int j=0; j<4; j++) { + DivInstrumentFM::Operator& op=fm.op[j]; + op.am=reader.readC(); + op.ar=reader.readC(); + op.dr=reader.readC(); + op.mult=reader.readC(); + op.rr=reader.readC(); + op.sl=reader.readC(); + op.tl=reader.readC(); + op.dt2=reader.readC(); + op.rs=reader.readC(); + op.dt=reader.readC(); + op.d2r=reader.readC(); + op.ssgEnv=reader.readC(); + + op.dam=reader.readC(); + op.dvb=reader.readC(); + op.egt=reader.readC(); + op.ksl=reader.readC(); + op.sus=reader.readC(); + op.vib=reader.readC(); + op.ws=reader.readC(); + op.ksr=reader.readC(); + + // reserved + for (int k=0; k<12; k++) reader.readC(); + } + + // GB + gb.envVol=reader.readC(); + gb.envDir=reader.readC(); + gb.envLen=reader.readC(); + gb.soundLen=reader.readC(); + + // C64 + c64.triOn=reader.readC(); + c64.sawOn=reader.readC(); + c64.pulseOn=reader.readC(); + c64.noiseOn=reader.readC(); + c64.a=reader.readC(); + c64.d=reader.readC(); + c64.s=reader.readC(); + c64.r=reader.readC(); + c64.duty=reader.readS(); + c64.ringMod=reader.readC(); + c64.oscSync=reader.readC(); + c64.toFilter=reader.readC(); + c64.initFilter=reader.readC(); + c64.volIsCutoff=reader.readC(); + c64.res=reader.readC(); + c64.lp=reader.readC(); + c64.bp=reader.readC(); + c64.hp=reader.readC(); + c64.ch3off=reader.readC(); + c64.cut=reader.readS(); + c64.dutyIsAbs=reader.readC(); + c64.filterIsAbs=reader.readC(); + + // Amiga + amiga.initSample=reader.readS(); + // reserved + for (int k=0; k<14; k++) reader.readC(); + + // standard + std.volMacroLen=reader.readI(); + std.arpMacroLen=reader.readI(); + std.dutyMacroLen=reader.readI(); + std.waveMacroLen=reader.readI(); + if (version>=17) { + std.pitchMacroLen=reader.readI(); + std.ex1MacroLen=reader.readI(); + std.ex2MacroLen=reader.readI(); + std.ex3MacroLen=reader.readI(); + } + std.volMacroLoop=reader.readI(); + std.arpMacroLoop=reader.readI(); + std.dutyMacroLoop=reader.readI(); + std.waveMacroLoop=reader.readI(); + if (version>=17) { + std.pitchMacroLoop=reader.readI(); + std.ex1MacroLoop=reader.readI(); + std.ex2MacroLoop=reader.readI(); + std.ex3MacroLoop=reader.readI(); + } + std.arpMacroMode=reader.readC(); + std.volMacroHeight=reader.readC(); + std.dutyMacroHeight=reader.readC(); + std.waveMacroHeight=reader.readC(); + if (std.volMacroHeight==0) std.volMacroHeight=15; + if (std.dutyMacroHeight==0) std.dutyMacroHeight=3; + if (std.waveMacroHeight==0) std.waveMacroHeight=63; + reader.read(std.volMacro,4*std.volMacroLen); + reader.read(std.arpMacro,4*std.arpMacroLen); + reader.read(std.dutyMacro,4*std.dutyMacroLen); + reader.read(std.waveMacro,4*std.waveMacroLen); + if (version>=17) { + reader.read(std.pitchMacro,4*std.pitchMacroLen); + reader.read(std.ex1Macro,4*std.ex1MacroLen); + reader.read(std.ex2Macro,4*std.ex2MacroLen); + reader.read(std.ex3Macro,4*std.ex3MacroLen); + } else { + if (type==DIV_INS_STD) { + if (std.volMacroHeight==31) { + type=DIV_INS_PCE; + } + if (std.dutyMacroHeight==31) { + type=DIV_INS_AY; + } + } + } + + return DIV_DATA_SUCCESS; +} + bool DivInstrument::save(const char* path) { SafeWriter* w=new SafeWriter(); w->init(); diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 01b2d6f6..1b351087 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -1,6 +1,7 @@ #ifndef _INSTRUMENT_H #define _INSTRUMENT_H #include "safeWriter.h" +#include "dataErrors.h" #include "../ta-utils.h" enum DivInstrumentType { @@ -247,7 +248,9 @@ struct DivInstrument { DivInstrumentGB gb; DivInstrumentC64 c64; DivInstrumentAmiga amiga; + void putInsData(SafeWriter* w); + DivDataErrors readInsData(SafeReader& reader, short version); bool save(const char* path); DivInstrument(): name(""), diff --git a/src/engine/song.h b/src/engine/song.h index c3d8e6a5..90f3de2d 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -82,7 +82,7 @@ struct DivSong { // - possibly the first version that could save // - basic format, no system number, 16 instruments, one speed, YMU759-only // - if somebody manages to find a version 2 or even 1 module, please tell me as it will be worth more than a luxury vehicle - unsigned char version; + unsigned short version; // system DivSystem system[32]; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 3e94381d..76c7a6d4 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3316,13 +3316,13 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save File","Furnace song{.fur},DefleMask module{.dmf}",workingDir); break; case GUI_FILE_INS_OPEN: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Load Instrument","Furnace instrument{.fui},DefleMask preset{.dmp},.*",workingDir); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Load Instrument","compatible files{.fui,.dmp},.*",workingDir); break; case GUI_FILE_INS_SAVE: ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save Instrument","Furnace instrument{.fui}",workingDir); break; case GUI_FILE_WAVE_OPEN: - ImGuiFileDialog::Instance()->OpenModal("FileDialog","Load Wavetable","Furnace wavetable{.fuw},DefleMask wavetable{.dmw},.*",workingDir); + ImGuiFileDialog::Instance()->OpenModal("FileDialog","Load Wavetable","compatible files{.fuw,dmw},.*",workingDir); break; case GUI_FILE_WAVE_SAVE: ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save Wavetable","Furnace wavetable{.fuw}",workingDir); @@ -3967,6 +3967,7 @@ bool FurnaceGUI::loop() { exportAudio(copyOfName,DIV_EXPORT_MODE_MANY_CHAN); break; case GUI_FILE_INS_OPEN: + e->addInstrumentFromFile(copyOfName.c_str()); break; case GUI_FILE_WAVE_OPEN: e->addWaveFromFile(copyOfName.c_str());