mirror of
https://github.com/tildearrow/furnace.git
synced 2024-12-29 02:51:24 +00:00
add Furnace instrument loading
TODO: .dmp loading
This commit is contained in:
parent
969a5d203b
commit
8b79bf78a9
6 changed files with 231 additions and 138 deletions
|
@ -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; i<ds.insLen; i++) {
|
||||
DivInstrument* ins=new DivInstrument;
|
||||
reader.seek(insPtr[i],SEEK_SET);
|
||||
reader.read(magic,4);
|
||||
if (strcmp(magic,"INST")!=0) {
|
||||
logE("%d: invalid instrument header!\n",i);
|
||||
lastError="invalid instrument header!";
|
||||
|
||||
if (ins->readInsData(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()) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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(""),
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in a new issue