/** * Furnace Tracker - multi-system chiptune tracker * Copyright (C) 2021-2024 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "dataErrors.h" #include "engine.h" #include "instrument.h" #include "../ta-log.h" #include "../fileutils.h" const DivInstrument defaultIns; #define _C(x) x==other.x bool DivInstrumentFM::operator==(const DivInstrumentFM& other) { return ( _C(alg) && _C(fb) && _C(fms) && _C(ams) && _C(fms2) && _C(ams2) && _C(ops) && _C(opllPreset) && _C(fixedDrums) && _C(kickFreq) && _C(snareHatFreq) && _C(tomTopFreq) && _C(op[0]) && _C(op[1]) && _C(op[2]) && _C(op[3]) ); } bool DivInstrumentFM::Operator::operator==(const DivInstrumentFM::Operator& other) { return ( _C(enable) && _C(am) && _C(ar) && _C(dr) && _C(mult) && _C(rr) && _C(sl) && _C(tl) && _C(dt2) && _C(rs) && _C(dt) && _C(d2r) && _C(ssgEnv) && _C(dam) && _C(dvb) && _C(egt) && _C(ksl) && _C(sus) && _C(vib) && _C(ws) && _C(ksr) && _C(kvs) ); } bool DivInstrumentGB::operator==(const DivInstrumentGB& other) { return ( _C(envVol) && _C(envDir) && _C(envLen) && _C(soundLen) && _C(hwSeqLen) && _C(softEnv) && _C(alwaysInit) && _C(doubleWave) ); } bool DivInstrumentC64::operator==(const DivInstrumentC64& other) { return ( _C(triOn) && _C(sawOn) && _C(pulseOn) && _C(noiseOn) && _C(a) && _C(d) && _C(s) && _C(r) && _C(duty) && _C(ringMod) && _C(oscSync) && _C(toFilter) && _C(initFilter) && _C(dutyIsAbs) && _C(filterIsAbs) && _C(noTest) && _C(res) && _C(cut) && _C(hp) && _C(lp) && _C(bp) && _C(ch3off) ); } bool DivInstrumentAmiga::operator==(const DivInstrumentAmiga& other) { return ( _C(initSample) && _C(useNoteMap) && _C(useSample) && _C(useWave) && _C(waveLen) ); } bool DivInstrumentX1_010::operator==(const DivInstrumentX1_010& other) { return _C(bankSlot); } bool DivInstrumentN163::operator==(const DivInstrumentN163& other) { return ( _C(wave) && _C(wavePos) && _C(waveLen) && _C(waveMode) && _C(perChanPos) && _C(wavePosCh[0]) && _C(wavePosCh[1]) && _C(wavePosCh[2]) && _C(wavePosCh[3]) && _C(wavePosCh[4]) && _C(wavePosCh[5]) && _C(wavePosCh[6]) && _C(wavePosCh[7]) && _C(waveLenCh[0]) && _C(waveLenCh[1]) && _C(waveLenCh[2]) && _C(waveLenCh[3]) && _C(waveLenCh[4]) && _C(waveLenCh[5]) && _C(waveLenCh[6]) && _C(waveLenCh[7]) ); } bool DivInstrumentFDS::operator==(const DivInstrumentFDS& other) { return ( (memcmp(modTable,other.modTable,32)==0) && _C(modSpeed) && _C(modDepth) && _C(initModTableWithFirstWave) ); } bool DivInstrumentMultiPCM::operator==(const DivInstrumentMultiPCM& other) { return ( _C(ar) && _C(d1r) && _C(dl) && _C(d2r) && _C(rr) && _C(rc) && _C(lfo) && _C(vib) && _C(am) ); } bool DivInstrumentWaveSynth::operator==(const DivInstrumentWaveSynth& other) { return ( _C(wave1) && _C(wave2) && _C(rateDivider) && _C(effect) && _C(oneShot) && _C(enabled) && _C(global) && _C(speed) && _C(param1) && _C(param2) && _C(param3) && _C(param4) ); } bool DivInstrumentSoundUnit::operator==(const DivInstrumentSoundUnit& other) { return ( _C(switchRoles) && _C(hwSeqLen) ); } bool DivInstrumentES5506::operator==(const DivInstrumentES5506& other) { return ( _C(filter.mode) && _C(filter.k1) && _C(filter.k2) && _C(envelope.ecount) && _C(envelope.lVRamp) && _C(envelope.rVRamp) && _C(envelope.k1Ramp) && _C(envelope.k2Ramp) && _C(envelope.k1Slow) && _C(envelope.k2Slow) ); } bool DivInstrumentSNES::operator==(const DivInstrumentSNES& other) { return ( _C(useEnv) && _C(sus) && _C(gainMode) && _C(gain) && _C(a) && _C(d) && _C(s) && _C(r) && _C(d2) ); } bool DivInstrumentESFM::operator==(const DivInstrumentESFM& other) { return ( _C(noise) && _C(op[0]) && _C(op[1]) && _C(op[2]) && _C(op[3]) ); } bool DivInstrumentESFM::Operator::operator==(const DivInstrumentESFM::Operator& other) { return ( _C(delay) && _C(outLvl) && _C(modIn) && _C(left) && _C(right) && _C(fixed) && _C(ct) && _C(dt) ); } bool DivInstrumentPowerNoise::operator==(const DivInstrumentPowerNoise& other) { return _C(octave); } #undef _C #define CONSIDER(x,t) \ case t: \ return &x; \ break; DivInstrumentMacro* DivInstrumentSTD::macroByType(DivMacroType type) { switch (type) { CONSIDER(volMacro,DIV_MACRO_VOL) CONSIDER(arpMacro,DIV_MACRO_ARP) CONSIDER(dutyMacro,DIV_MACRO_DUTY) CONSIDER(waveMacro,DIV_MACRO_WAVE) CONSIDER(pitchMacro,DIV_MACRO_PITCH) CONSIDER(ex1Macro,DIV_MACRO_EX1) CONSIDER(ex2Macro,DIV_MACRO_EX2) CONSIDER(ex3Macro,DIV_MACRO_EX3) CONSIDER(algMacro,DIV_MACRO_ALG) CONSIDER(fbMacro,DIV_MACRO_FB) CONSIDER(fmsMacro,DIV_MACRO_FMS) CONSIDER(amsMacro,DIV_MACRO_AMS) CONSIDER(panLMacro,DIV_MACRO_PAN_LEFT) CONSIDER(panRMacro,DIV_MACRO_PAN_RIGHT) CONSIDER(phaseResetMacro,DIV_MACRO_PHASE_RESET) CONSIDER(ex4Macro,DIV_MACRO_EX4) CONSIDER(ex5Macro,DIV_MACRO_EX5) CONSIDER(ex6Macro,DIV_MACRO_EX6) CONSIDER(ex7Macro,DIV_MACRO_EX7) CONSIDER(ex8Macro,DIV_MACRO_EX8) } return NULL; } #undef CONSIDER #define FEATURE_BEGIN(x) \ w->write(x,2); \ size_t featStartSeek=w->tell(); \ w->writeS(0); #define FEATURE_END \ size_t featEndSeek=w->tell(); \ w->seek(featStartSeek,SEEK_SET); \ w->writeS(featEndSeek-featStartSeek-2); \ w->seek(featEndSeek,SEEK_SET); void DivInstrument::writeFeatureNA(SafeWriter* w) { FEATURE_BEGIN("NA"); w->writeString(name,false); FEATURE_END; } void DivInstrument::writeFeatureFM(SafeWriter* w, bool fui) { FEATURE_BEGIN("FM"); int opCount=4; if (fui) { if (type==DIV_INS_OPLL) { opCount=2; } else if (type==DIV_INS_OPL) { opCount=(fm.ops==4)?4:2; } } w->writeC( (fm.op[3].enable?128:0)| (fm.op[2].enable?64:0)| (fm.op[1].enable?32:0)| (fm.op[0].enable?16:0)| opCount ); // base data w->writeC(((fm.alg&7)<<4)|(fm.fb&7)); w->writeC(((fm.fms2&7)<<5)|((fm.ams&3)<<3)|(fm.fms&7)); w->writeC(((fm.ams2&3)<<6)|((fm.ops==4)?32:0)|(fm.opllPreset&31)); // operator data for (int i=0; iwriteC((op.ksr?128:0)|((op.dt&7)<<4)|(op.mult&15)); w->writeC((op.sus?128:0)|(op.tl&127)); w->writeC(((op.rs&3)<<6)|(op.vib?32:0)|(op.ar&31)); w->writeC((op.am?128:0)|((op.ksl&3)<<5)|(op.dr&31)); w->writeC((op.egt?128:0)|((op.kvs&3)<<5)|(op.d2r&31)); w->writeC(((op.sl&15)<<4)|(op.rr&15)); w->writeC(((op.dvb&15)<<4)|(op.ssgEnv&15)); w->writeC(((op.dam&7)<<5)|((op.dt2&3)<<3)|(op.ws&7)); } FEATURE_END; } void DivInstrument::writeMacro(SafeWriter* w, const DivInstrumentMacro& m) { if (!m.len) return; // determine word size int macroMin=0x7fffffff; int macroMax=0x80000000; for (int i=0; imacroMax) macroMax=m.val[i]; } unsigned char wordSize=192; // 32-bit if (macroMin>=0 && macroMax<=255) { wordSize=0; // 8-bit unsigned } else if (macroMin>=-128 && macroMax<=127) { wordSize=64; // 8-bit signed } else if (macroMin>=-32768 && macroMax<=32767) { wordSize=128; // 16-bit signed } else { wordSize=192; // 32-bit signed } w->writeC(m.macroType&31); w->writeC(m.len); w->writeC(m.loop); w->writeC(m.rel); w->writeC(m.mode); w->writeC((m.open&0x3f)|wordSize); w->writeC(m.delay); w->writeC(m.speed); switch (wordSize) { case 0: for (int i=0; iwriteC((unsigned char)m.val[i]); } break; case 64: for (int i=0; iwriteC((signed char)m.val[i]); } break; case 128: for (int i=0; iwriteS((short)m.val[i]); } break; default: // 192 for (int i=0; iwriteI(m.val[i]); } break; } } void DivInstrument::writeFeatureMA(SafeWriter* w) { FEATURE_BEGIN("MA"); // if you update the macro header, please update this value as well. // it's the length. w->writeS(8); // write macros writeMacro(w,std.volMacro); writeMacro(w,std.arpMacro); writeMacro(w,std.dutyMacro); writeMacro(w,std.waveMacro); writeMacro(w,std.pitchMacro); writeMacro(w,std.ex1Macro); writeMacro(w,std.ex2Macro); writeMacro(w,std.ex3Macro); writeMacro(w,std.algMacro); writeMacro(w,std.fbMacro); writeMacro(w,std.fmsMacro); writeMacro(w,std.amsMacro); writeMacro(w,std.panLMacro); writeMacro(w,std.panRMacro); writeMacro(w,std.phaseResetMacro); writeMacro(w,std.ex4Macro); writeMacro(w,std.ex5Macro); writeMacro(w,std.ex6Macro); writeMacro(w,std.ex7Macro); writeMacro(w,std.ex8Macro); // "stop reading" code w->writeC(-1); FEATURE_END; } void DivInstrument::writeFeature64(SafeWriter* w) { FEATURE_BEGIN("64"); w->writeC( (c64.dutyIsAbs?0x80:0)| (c64.initFilter?0x40:0)| (c64.toFilter?0x10:0)| (c64.noiseOn?8:0)| (c64.pulseOn?4:0)| (c64.sawOn?2:0)| (c64.triOn?1:0) ); w->writeC( (c64.oscSync?0x80:0)| (c64.ringMod?0x40:0)| (c64.noTest?0x20:0)| (c64.filterIsAbs?0x10:0)| (c64.ch3off?8:0)| (c64.bp?4:0)| (c64.hp?2:0)| (c64.lp?1:0) ); w->writeC(((c64.a&15)<<4)|(c64.d&15)); w->writeC(((c64.s&15)<<4)|(c64.r&15)); w->writeS(c64.duty); w->writeS((unsigned short)((c64.cut&2047)|(c64.res<<12))); FEATURE_END; } void DivInstrument::writeFeatureGB(SafeWriter* w) { FEATURE_BEGIN("GB"); w->writeC(((gb.envLen&7)<<5)|(gb.envDir?16:0)|(gb.envVol&15)); w->writeC(gb.soundLen); w->writeC( (gb.doubleWave?4:0)| (gb.alwaysInit?2:0)| (gb.softEnv?1:0) ); w->writeC(gb.hwSeqLen); for (int i=0; iwriteC(gb.hwSeq[i].cmd); w->writeS(gb.hwSeq[i].data); } FEATURE_END; } void DivInstrument::writeFeatureSM(SafeWriter* w) { FEATURE_BEGIN("SM"); w->writeS(amiga.initSample); w->writeC( (amiga.useWave?4:0)| (amiga.useSample?2:0)| (amiga.useNoteMap?1:0) ); w->writeC(amiga.waveLen); if (amiga.useNoteMap) { for (int note=0; note<120; note++) { w->writeS(amiga.noteMap[note].freq); w->writeS(amiga.noteMap[note].map); } } FEATURE_END; } void DivInstrument::writeFeatureOx(SafeWriter* w, int ope) { char opCode[3]; opCode[0]='O'; opCode[1]='1'+ope; opCode[2]=0; FEATURE_BEGIN(opCode); // if you update the macro header, please update this value as well. // it's the length. w->writeS(8); // write macros const DivInstrumentSTD::OpMacro& o=std.opMacros[ope]; writeMacro(w,o.amMacro); writeMacro(w,o.arMacro); writeMacro(w,o.drMacro); writeMacro(w,o.multMacro); writeMacro(w,o.rrMacro); writeMacro(w,o.slMacro); writeMacro(w,o.tlMacro); writeMacro(w,o.dt2Macro); writeMacro(w,o.rsMacro); writeMacro(w,o.dtMacro); writeMacro(w,o.d2rMacro); writeMacro(w,o.ssgMacro); writeMacro(w,o.damMacro); writeMacro(w,o.dvbMacro); writeMacro(w,o.egtMacro); writeMacro(w,o.kslMacro); writeMacro(w,o.susMacro); writeMacro(w,o.vibMacro); writeMacro(w,o.wsMacro); writeMacro(w,o.ksrMacro); // "stop reading" code w->writeC(-1); FEATURE_END; } void DivInstrument::writeFeatureLD(SafeWriter* w) { FEATURE_BEGIN("LD"); w->writeC(fm.fixedDrums); w->writeS(fm.kickFreq); w->writeS(fm.snareHatFreq); w->writeS(fm.tomTopFreq); FEATURE_END; } void DivInstrument::writeFeatureSN(SafeWriter* w) { FEATURE_BEGIN("SN"); w->writeC(((snes.d&7)<<4)|(snes.a&15)); w->writeC(((snes.s&7)<<5)|(snes.r&31)); w->writeC( (snes.useEnv?16:0)| (snes.sus?8:0)| (snes.gainMode) ); w->writeC(snes.gain); w->writeC(((snes.sus&3)<<5)|(snes.d2&31)); FEATURE_END; } void DivInstrument::writeFeatureN1(SafeWriter* w) { FEATURE_BEGIN("N1"); w->writeI(n163.wave); w->writeC(n163.wavePos); w->writeC(n163.waveLen); w->writeC(n163.waveMode); w->writeC(n163.perChanPos); if (n163.perChanPos) { for (int i=0; i<8; i++) { w->writeC(n163.wavePosCh[i]); } for (int i=0; i<8; i++) { w->writeC(n163.waveLenCh[i]); } } FEATURE_END; } void DivInstrument::writeFeatureFD(SafeWriter* w) { FEATURE_BEGIN("FD"); w->writeI(fds.modSpeed); w->writeI(fds.modDepth); w->writeC(fds.initModTableWithFirstWave); w->write(fds.modTable,32); FEATURE_END; } void DivInstrument::writeFeatureWS(SafeWriter* w) { FEATURE_BEGIN("WS"); w->writeI(ws.wave1); w->writeI(ws.wave2); w->writeC(ws.rateDivider); w->writeC(ws.effect); w->writeC(ws.enabled); w->writeC(ws.global); w->writeC(ws.speed); w->writeC(ws.param1); w->writeC(ws.param2); w->writeC(ws.param3); w->writeC(ws.param4); FEATURE_END; } size_t DivInstrument::writeFeatureSL(SafeWriter* w, std::vector& list, const DivSong* song) { bool sampleUsed[256]; memset(sampleUsed,0,256*sizeof(bool)); if (amiga.initSample>=0 && amiga.initSample<(int)song->sample.size()) { sampleUsed[amiga.initSample]=true; } if (amiga.useNoteMap) { for (int i=0; i<120; i++) { if (amiga.noteMap[i].map>=0 && amiga.noteMap[i].map<(int)song->sample.size()) { sampleUsed[amiga.noteMap[i].map]=true; } } } for (size_t i=0; isample.size(); i++) { if (sampleUsed[i]) { list.push_back(i); } } if (list.empty()) return 0; FEATURE_BEGIN("SL"); w->writeC(list.size()); for (int i: list) { w->writeC(i); } size_t ret=w->tell(); // pointers (these will be filled later) for (size_t i=0; iwriteI(0); } FEATURE_END; return ret; } size_t DivInstrument::writeFeatureWL(SafeWriter* w, std::vector& list, const DivSong* song) { bool waveUsed[256]; memset(waveUsed,0,256*sizeof(bool)); for (int i=0; i=0 && std.waveMacro.val[i]<(int)song->wave.size()) { waveUsed[std.waveMacro.val[i]]=true; } } if (ws.enabled) { if (ws.wave1>=0 && ws.wave1<(int)song->wave.size()) { waveUsed[ws.wave1]=true; } if ((ws.effect&0x80) && ws.wave2>=0 && ws.wave2<(int)song->wave.size()) { waveUsed[ws.wave2]=true; } } for (size_t i=0; iwave.size(); i++) { if (waveUsed[i]) { list.push_back(i); } } if (list.empty()) return 0; FEATURE_BEGIN("WL"); w->writeC(list.size()); for (int i: list) { w->writeC(i); } size_t ret=w->tell(); // pointers (these will be filled later) for (size_t i=0; iwriteI(0); } FEATURE_END; return ret; } void DivInstrument::writeFeatureMP(SafeWriter* w) { FEATURE_BEGIN("MP"); w->writeC(multipcm.ar); w->writeC(multipcm.d1r); w->writeC(multipcm.dl); w->writeC(multipcm.d2r); w->writeC(multipcm.rr); w->writeC(multipcm.rc); w->writeC(multipcm.lfo); w->writeC(multipcm.vib); w->writeC(multipcm.am); FEATURE_END; } void DivInstrument::writeFeatureSU(SafeWriter* w) { FEATURE_BEGIN("SU"); w->writeC(su.switchRoles); w->writeC(su.hwSeqLen); for (int i=0; iwriteC(su.hwSeq[i].cmd); w->writeC(su.hwSeq[i].bound); w->writeC(su.hwSeq[i].val); w->writeS(su.hwSeq[i].speed); } FEATURE_END; } void DivInstrument::writeFeatureES(SafeWriter* w) { FEATURE_BEGIN("ES"); w->writeC(es5506.filter.mode); w->writeS(es5506.filter.k1); w->writeS(es5506.filter.k2); w->writeS(es5506.envelope.ecount); w->writeC(es5506.envelope.lVRamp); w->writeC(es5506.envelope.rVRamp); w->writeC(es5506.envelope.k1Ramp); w->writeC(es5506.envelope.k2Ramp); w->writeC(es5506.envelope.k1Slow); w->writeC(es5506.envelope.k2Slow); FEATURE_END; } void DivInstrument::writeFeatureX1(SafeWriter* w) { FEATURE_BEGIN("X1"); w->writeI(x1_010.bankSlot); FEATURE_END; } void DivInstrument::writeFeatureNE(SafeWriter* w) { FEATURE_BEGIN("NE"); w->writeC(amiga.useNoteMap?1:0); if (amiga.useNoteMap) { for (int note=0; note<120; note++) { w->writeC(amiga.noteMap[note].dpcmFreq); w->writeC(amiga.noteMap[note].dpcmDelta); } } FEATURE_END; } void DivInstrument::writeFeatureEF(SafeWriter* w) { FEATURE_BEGIN("EF"); w->writeC(esfm.noise&3); for (int i=0; i<4; i++) { DivInstrumentESFM::Operator& op=esfm.op[i]; w->writeC(((op.delay&7)<<5)|((op.outLvl&7)<<2)|((op.right&1)<<1)|(op.left&1)); w->writeC((op.fixed&1)<<3|(op.modIn&7)); w->writeC(op.ct); w->writeC(op.dt); } FEATURE_END; } void DivInstrument::writeFeaturePN(SafeWriter* w) { FEATURE_BEGIN("PN"); w->writeC(powernoise.octave); FEATURE_END; } void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bool insName) { size_t blockStartSeek=0; size_t blockEndSeek=0; size_t slSeek=0; size_t wlSeek=0; std::vector waveList; std::vector sampleList; std::vector wavePtr; std::vector samplePtr; if (fui) { w->write("FINS",4); } else { w->write("INS2",4); blockStartSeek=w->tell(); w->writeI(0); } w->writeS(DIV_ENGINE_VERSION); w->writeC(type); w->writeC(0); // write features bool featureNA=false; bool featureFM=false; bool featureMA=false; bool feature64=false; bool featureGB=false; bool featureSM=false; bool featureOx[4]; bool featureLD=false; bool featureSN=false; bool featureN1=false; bool featureFD=false; bool featureWS=false; bool featureSL=false; bool featureWL=false; bool featureMP=false; bool featureSU=false; bool featureES=false; bool featureX1=false; bool featureNE=false; bool featureEF=false; bool featurePN=false; bool checkForWL=false; featureOx[0]=false; featureOx[1]=false; featureOx[2]=false; featureOx[3]=false; // turn on base features if .fui if (fui) { switch (type) { case DIV_INS_STD: break; case DIV_INS_FM: featureFM=true; break; case DIV_INS_GB: featureGB=true; checkForWL=true; if (ws.enabled) featureWS=true; break; case DIV_INS_C64: feature64=true; 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; case DIV_INS_SAA1099: break; case DIV_INS_VIC: break; case DIV_INS_PET: break; case DIV_INS_VRC6: featureSM=true; if (amiga.useSample) featureSL=true; break; case DIV_INS_OPLL: featureFM=true; if (fm.fixedDrums) featureLD=true; break; case DIV_INS_OPL: featureFM=true; if (fm.fixedDrums) featureLD=true; break; case DIV_INS_FDS: checkForWL=true; featureFD=true; if (ws.enabled) featureWS=true; break; case DIV_INS_VBOY: checkForWL=true; featureFD=true; if (ws.enabled) featureWS=true; break; case DIV_INS_N163: checkForWL=true; featureN1=true; if (ws.enabled) featureWS=true; break; case DIV_INS_SCC: checkForWL=true; if (ws.enabled) featureWS=true; break; case DIV_INS_OPZ: featureFM=true; break; case DIV_INS_POKEY: break; case DIV_INS_BEEPER: break; 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: checkForWL=true; if (ws.enabled) featureWS=true; break; case DIV_INS_OPL_DRUMS: featureFM=true; if (fm.fixedDrums) featureLD=true; break; case DIV_INS_OPM: featureFM=true; break; case DIV_INS_NES: featureSM=true; featureNE=true; featureSL=true; 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; case DIV_INS_T6W28: break; case DIV_INS_K007232: featureSM=true; featureSL=true; break; case DIV_INS_GA20: featureSM=true; featureSL=true; break; case DIV_INS_POKEMINI: break; case DIV_INS_SM8521: checkForWL=true; if (ws.enabled) featureWS=true; break; case DIV_INS_PV1000: break; case DIV_INS_K053260: featureSM=true; featureSL=true; break; case DIV_INS_TED: break; case DIV_INS_C140: featureSM=true; featureSL=true; break; case DIV_INS_C219: featureSM=true; featureSL=true; break; case DIV_INS_ESFM: featureFM=true; featureEF=true; break; case DIV_INS_POWERNOISE: featurePN=true; break; case DIV_INS_POWERNOISE_SLOPE: featurePN=true; break; case DIV_INS_DAVE: break; case DIV_INS_NDS: featureSM=true; if (amiga.useSample) featureSL=true; break; case DIV_INS_GBA_DMA: featureSM=true; featureSL=true; break; case DIV_INS_GBA_MINMOD: featureSM=true; featureSL=true; break; case DIV_INS_BIFURCATOR: break; case DIV_INS_MAX: break; case DIV_INS_NULL: break; } } else { // turn on features depending on what is set // almost 40 years of C++, and there still isn't an official way to easily compare two structs. // even Java, which many regard as having a slow runtime, has .equals(). if (fm!=defaultIns.fm) { featureFM=true; featureLD=true; } if (c64!=defaultIns.c64) { feature64=true; } if (gb!=defaultIns.gb) { featureGB=true; } if (amiga!=defaultIns.amiga) { featureSM=true; featureNE=true; } if (snes!=defaultIns.snes) { featureSN=true; } if (n163!=defaultIns.n163) { featureN1=true; } if (fds!=defaultIns.fds) { featureFD=true; } if (ws!=defaultIns.ws) { featureWS=true; } if (multipcm!=defaultIns.multipcm) { featureMP=true; } if (su!=defaultIns.su) { featureSU=true; } if (es5506!=defaultIns.es5506) { featureES=true; } if (x1_010!=defaultIns.x1_010) { featureX1=true; } if (esfm!=defaultIns.esfm) { featureEF=true; } if (powernoise!=defaultIns.powernoise) { featurePN=true; } } // check ins name if (!name.empty() && insName) { featureNA=true; } // check macros if (std.volMacro.len || std.arpMacro.len || std.dutyMacro.len || std.waveMacro.len || std.pitchMacro.len || std.ex1Macro.len || std.ex2Macro.len || std.ex3Macro.len || std.algMacro.len || std.fbMacro.len || std.fmsMacro.len || std.amsMacro.len || std.panLMacro.len || std.panRMacro.len || std.phaseResetMacro.len || std.ex4Macro.len || std.ex5Macro.len || std.ex6Macro.len || std.ex7Macro.len || std.ex8Macro.len) { featureMA=true; } // check whether to write wavetable list if (checkForWL && fui) { if (std.waveMacro.len || ws.enabled) { featureWL=true; } } if (featureFM || !fui) { // check FM macros int opCount=4; bool storeExtendedAsWell=true; if (fui) { if (type==DIV_INS_OPLL) { opCount=2; } else if (type==DIV_INS_OPL) { opCount=(fm.ops==4)?4:2; } else if (type==DIV_INS_FM || type==DIV_INS_OPM) { storeExtendedAsWell=false; } } for (int i=0; iwrite("EN",2); 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); } } if (!fui) { w->write("EN",2); } blockEndSeek=w->tell(); if (!fui) { w->seek(blockStartSeek,SEEK_SET); w->writeI(blockEndSeek-blockStartSeek-4); } w->seek(0,SEEK_END); } #define READ_FEAT_BEGIN \ unsigned short featLen=reader.readS(); \ size_t endOfFeat=reader.tell()+featLen; #define READ_FEAT_END \ if (reader.tell()>4)&7; fm.fb=next&7; next=reader.readC(); fm.fms2=(next>>5)&7; fm.ams=(next>>3)&3; fm.fms=next&7; next=reader.readC(); fm.ams2=(next>>6)&3; fm.ops=(next&32)?4:2; fm.opllPreset=next&31; // read operators for (int i=0; i>4)&7; op.mult=next&15; next=reader.readC(); op.sus=(next&128)?1:0; op.tl=next&127; next=reader.readC(); op.rs=(next>>6)&3; op.vib=(next&32)?1:0; op.ar=next&31; next=reader.readC(); op.am=(next&128)?1:0; op.ksl=(next>>5)&3; op.dr=next&31; next=reader.readC(); op.egt=(next&128)?1:0; op.kvs=(next>>5)&3; op.d2r=next&31; next=reader.readC(); op.sl=(next>>4)&15; op.rr=next&15; next=reader.readC(); op.dvb=(next>>4)&15; op.ssgEnv=next&15; next=reader.readC(); op.dam=(next>>5)&7; op.dt2=(next>>3)&3; op.ws=next&7; } READ_FEAT_END; } void DivInstrument::readFeatureMA(SafeReader& reader, short version) { READ_FEAT_BEGIN; unsigned short macroHeaderLen=reader.readS(); DivInstrumentMacro* target=&std.volMacro; while (reader.tell()len=reader.readC(); target->loop=reader.readC(); target->rel=reader.readC(); target->mode=reader.readC(); unsigned char wordSize=reader.readC(); target->open=wordSize&15; wordSize>>=6; target->delay=reader.readC(); target->speed=reader.readC(); reader.seek(endOfMacroHeader,SEEK_SET); // read macro switch (wordSize) { case 0: for (int i=0; ilen; i++) { target->val[i]=(unsigned char)reader.readC(); } break; case 1: for (int i=0; ilen; i++) { target->val[i]=(signed char)reader.readC(); } break; case 2: for (int i=0; ilen; i++) { target->val[i]=reader.readS(); } break; default: for (int i=0; ilen; i++) { target->val[i]=reader.readI(); } break; } } if (version<193) { if (type==DIV_INS_AY || type==DIV_INS_AY8930) { for (int j=0; j>4)&15; c64.d=next&15; next=reader.readC(); c64.s=(next>>4)&15; c64.r=next&15; c64.duty=reader.readS()&4095; unsigned short cr=reader.readS(); c64.cut=cr&2047; c64.res=cr>>12; READ_FEAT_END; } void DivInstrument::readFeatureGB(SafeReader& reader, short version) { READ_FEAT_BEGIN; unsigned char next=reader.readC(); gb.envLen=(next>>5)&7; gb.envDir=(next&16)?1:0; gb.envVol=next&15; gb.soundLen=reader.readC(); next=reader.readC(); if (version>=196) gb.doubleWave=next&4; gb.alwaysInit=next&2; gb.softEnv=next&1; gb.hwSeqLen=reader.readC(); for (int i=0; ilen=reader.readC(); target->loop=reader.readC(); target->rel=reader.readC(); target->mode=reader.readC(); unsigned char wordSize=reader.readC(); target->open=wordSize&7; wordSize>>=6; target->delay=reader.readC(); target->speed=reader.readC(); reader.seek(endOfMacroHeader,SEEK_SET); // read macro switch (wordSize) { case 0: for (int i=0; ilen; i++) { target->val[i]=(unsigned char)reader.readC(); } break; case 1: for (int i=0; ilen; i++) { target->val[i]=(signed char)reader.readC(); } break; case 2: for (int i=0; ilen; i++) { target->val[i]=reader.readS(); } break; default: for (int i=0; ilen; i++) { target->val[i]=reader.readI(); } break; } // <167 TL macro compat if (macroCode==6 && version<167) { if (target->open&6) { for (int j=0; j<2; j++) { target->val[j]^=0x7f; } } else { for (int j=0; jlen; j++) { target->val[j]^=0x7f; } } } } READ_FEAT_END; } void DivInstrument::readFeatureLD(SafeReader& reader, short version) { READ_FEAT_BEGIN; fm.fixedDrums=reader.readC(); fm.kickFreq=reader.readS(); fm.snareHatFreq=reader.readS(); fm.tomTopFreq=reader.readS(); READ_FEAT_END; } void DivInstrument::readFeatureSN(SafeReader& reader, short version) { READ_FEAT_BEGIN; unsigned char next=reader.readC(); snes.d=(next>>4)&7; snes.a=next&15; next=reader.readC(); snes.s=(next>>5)&7; snes.r=next&31; next=reader.readC(); snes.useEnv=next&16; snes.sus=(next&8)?1:0; snes.gainMode=(DivInstrumentSNES::GainMode)(next&7); if (snes.gainMode==1 || snes.gainMode==2 || snes.gainMode==3) snes.gainMode=DivInstrumentSNES::GAIN_MODE_DIRECT; snes.gain=reader.readC(); if (version>=131) { next=reader.readC(); snes.sus=(next>>5)&3; snes.d2=next&31; } READ_FEAT_END; } void DivInstrument::readFeatureN1(SafeReader& reader, short version) { READ_FEAT_BEGIN; n163.wave=reader.readI(); n163.wavePos=(unsigned char)reader.readC(); n163.waveLen=(unsigned char)reader.readC(); n163.waveMode=(unsigned char)reader.readC(); if (version>=164) { n163.perChanPos=reader.readC(); if (n163.perChanPos) { for (int i=0; i<8; i++) { n163.wavePosCh[i]=(unsigned char)reader.readC(); } for (int i=0; i<8; i++) { n163.waveLenCh[i]=(unsigned char)reader.readC(); } } } READ_FEAT_END; } void DivInstrument::readFeatureFD(SafeReader& reader, short version) { READ_FEAT_BEGIN; fds.modSpeed=reader.readI(); fds.modDepth=reader.readI(); fds.initModTableWithFirstWave=reader.readC(); reader.read(fds.modTable,32); READ_FEAT_END; } void DivInstrument::readFeatureWS(SafeReader& reader, short version) { READ_FEAT_BEGIN; ws.wave1=reader.readI(); ws.wave2=reader.readI(); ws.rateDivider=reader.readC(); ws.effect=reader.readC(); ws.enabled=reader.readC(); ws.global=reader.readC(); ws.speed=reader.readC(); ws.param1=reader.readC(); ws.param2=reader.readC(); ws.param3=reader.readC(); ws.param4=reader.readC(); READ_FEAT_END; } 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(); for (int i=0; isample.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_SET); // 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, 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(); for (int i=0; iwave.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_SET); // 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=0 && std.waveMacro.val[i]<256) std.waveMacro.val[i]=waveRemap[std.waveMacro.val[i]]; } READ_FEAT_END; } void DivInstrument::readFeatureMP(SafeReader& reader, short version) { READ_FEAT_BEGIN; multipcm.ar=reader.readC(); multipcm.d1r=reader.readC(); multipcm.dl=reader.readC(); multipcm.d2r=reader.readC(); multipcm.rr=reader.readC(); multipcm.rc=reader.readC(); multipcm.lfo=reader.readC(); multipcm.vib=reader.readC(); multipcm.am=reader.readC(); READ_FEAT_END; } void DivInstrument::readFeatureSU(SafeReader& reader, short version) { READ_FEAT_BEGIN; su.switchRoles=reader.readC(); if (version>=185) { su.hwSeqLen=reader.readC(); for (int i=0; i>5)&7; op.outLvl=(next>>2)&7; op.right=(next>>1)&1; op.left=next&1; next=reader.readC(); op.modIn=next&7; op.fixed=(next>>3)&1; op.ct=reader.readC(); op.dt=reader.readC(); } READ_FEAT_END; } void DivInstrument::readFeaturePN(SafeReader& reader, short version) { READ_FEAT_BEGIN; powernoise.octave=reader.readC(); READ_FEAT_END; } DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song) { unsigned char featCode[2]; bool volIsCutoff=false; int dataLen=reader.size()-4; if (!fui) { dataLen=reader.readI(); } dataLen+=reader.tell(); logV("data length: %d",dataLen); reader.readS(); // format version. ignored. type=(DivInstrumentType)reader.readS(); // feature reading loop while ((int)reader.tell()=60) { fm.opllPreset=reader.readC(); } else { 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(); if (version>=114) { op.enable=reader.readC(); } else { reader.readC(); } if (version>=115) { op.kvs=reader.readC(); } else { op.kvs=2; reader.readC(); } // reserved for (int k=0; k<10; 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(); 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(); if (version>=82) { amiga.useWave=reader.readC(); amiga.waveLen=(unsigned char)reader.readC(); } else { reader.readC(); reader.readC(); } // reserved for (int k=0; k<12; k++) reader.readC(); // standard std.volMacro.len=reader.readI(); std.arpMacro.len=reader.readI(); std.dutyMacro.len=reader.readI(); std.waveMacro.len=reader.readI(); if (version>=17) { std.pitchMacro.len=reader.readI(); std.ex1Macro.len=reader.readI(); std.ex2Macro.len=reader.readI(); std.ex3Macro.len=reader.readI(); } std.volMacro.loop=reader.readI(); std.arpMacro.loop=reader.readI(); std.dutyMacro.loop=reader.readI(); std.waveMacro.loop=reader.readI(); if (version>=17) { std.pitchMacro.loop=reader.readI(); std.ex1Macro.loop=reader.readI(); std.ex2Macro.loop=reader.readI(); std.ex3Macro.loop=reader.readI(); } std.arpMacro.mode=reader.readC(); // these 3 were macro heights before but they are not used anymore int oldVolHeight=reader.readC(); int oldDutyHeight=reader.readC(); reader.readC(); // oldWaveHeight READ_MACRO_VALS(std.volMacro.val,std.volMacro.len); READ_MACRO_VALS(std.arpMacro.val,std.arpMacro.len); READ_MACRO_VALS(std.dutyMacro.val,std.dutyMacro.len); READ_MACRO_VALS(std.waveMacro.val,std.waveMacro.len); if (version<31) { if (!std.arpMacro.mode) for (int j=0; j=17) { READ_MACRO_VALS(std.pitchMacro.val,std.pitchMacro.len); READ_MACRO_VALS(std.ex1Macro.val,std.ex1Macro.len); READ_MACRO_VALS(std.ex2Macro.val,std.ex2Macro.len); READ_MACRO_VALS(std.ex3Macro.val,std.ex3Macro.len); } else { if (type==DIV_INS_STD) { if (oldVolHeight==31) { type=DIV_INS_PCE; } if (oldDutyHeight==31) { type=DIV_INS_AY; } } } // FM macros if (version>=29) { std.algMacro.len=reader.readI(); std.fbMacro.len=reader.readI(); std.fmsMacro.len=reader.readI(); std.amsMacro.len=reader.readI(); std.algMacro.loop=reader.readI(); std.fbMacro.loop=reader.readI(); std.fmsMacro.loop=reader.readI(); std.amsMacro.loop=reader.readI(); std.volMacro.open=reader.readC(); std.arpMacro.open=reader.readC(); std.dutyMacro.open=reader.readC(); std.waveMacro.open=reader.readC(); std.pitchMacro.open=reader.readC(); std.ex1Macro.open=reader.readC(); std.ex2Macro.open=reader.readC(); std.ex3Macro.open=reader.readC(); std.algMacro.open=reader.readC(); std.fbMacro.open=reader.readC(); std.fmsMacro.open=reader.readC(); std.amsMacro.open=reader.readC(); READ_MACRO_VALS(std.algMacro.val,std.algMacro.len); READ_MACRO_VALS(std.fbMacro.val,std.fbMacro.len); READ_MACRO_VALS(std.fmsMacro.val,std.fmsMacro.len); READ_MACRO_VALS(std.amsMacro.val,std.amsMacro.len); for (int i=0; i<4; i++) { DivInstrumentSTD::OpMacro& op=std.opMacros[i]; op.amMacro.len=reader.readI(); op.arMacro.len=reader.readI(); op.drMacro.len=reader.readI(); op.multMacro.len=reader.readI(); op.rrMacro.len=reader.readI(); op.slMacro.len=reader.readI(); op.tlMacro.len=reader.readI(); op.dt2Macro.len=reader.readI(); op.rsMacro.len=reader.readI(); op.dtMacro.len=reader.readI(); op.d2rMacro.len=reader.readI(); op.ssgMacro.len=reader.readI(); op.amMacro.loop=reader.readI(); op.arMacro.loop=reader.readI(); op.drMacro.loop=reader.readI(); op.multMacro.loop=reader.readI(); op.rrMacro.loop=reader.readI(); op.slMacro.loop=reader.readI(); op.tlMacro.loop=reader.readI(); op.dt2Macro.loop=reader.readI(); op.rsMacro.loop=reader.readI(); op.dtMacro.loop=reader.readI(); op.d2rMacro.loop=reader.readI(); op.ssgMacro.loop=reader.readI(); op.amMacro.open=reader.readC(); op.arMacro.open=reader.readC(); op.drMacro.open=reader.readC(); op.multMacro.open=reader.readC(); op.rrMacro.open=reader.readC(); op.slMacro.open=reader.readC(); op.tlMacro.open=reader.readC(); op.dt2Macro.open=reader.readC(); op.rsMacro.open=reader.readC(); op.dtMacro.open=reader.readC(); op.d2rMacro.open=reader.readC(); op.ssgMacro.open=reader.readC(); } // FM macro low 8 bits for (int i=0; i<4; i++) { DivInstrumentSTD::OpMacro& op=std.opMacros[i]; for (int j=0; j=44) { std.volMacro.rel=reader.readI(); std.arpMacro.rel=reader.readI(); std.dutyMacro.rel=reader.readI(); std.waveMacro.rel=reader.readI(); std.pitchMacro.rel=reader.readI(); std.ex1Macro.rel=reader.readI(); std.ex2Macro.rel=reader.readI(); std.ex3Macro.rel=reader.readI(); std.algMacro.rel=reader.readI(); std.fbMacro.rel=reader.readI(); std.fmsMacro.rel=reader.readI(); std.amsMacro.rel=reader.readI(); for (int i=0; i<4; i++) { DivInstrumentSTD::OpMacro& op=std.opMacros[i]; op.amMacro.rel=reader.readI(); op.arMacro.rel=reader.readI(); op.drMacro.rel=reader.readI(); op.multMacro.rel=reader.readI(); op.rrMacro.rel=reader.readI(); op.slMacro.rel=reader.readI(); op.tlMacro.rel=reader.readI(); op.dt2Macro.rel=reader.readI(); op.rsMacro.rel=reader.readI(); op.dtMacro.rel=reader.readI(); op.d2rMacro.rel=reader.readI(); op.ssgMacro.rel=reader.readI(); } } // extended op macros if (version>=61) { for (int i=0; i<4; i++) { DivInstrumentSTD::OpMacro& op=std.opMacros[i]; op.damMacro.len=reader.readI(); op.dvbMacro.len=reader.readI(); op.egtMacro.len=reader.readI(); op.kslMacro.len=reader.readI(); op.susMacro.len=reader.readI(); op.vibMacro.len=reader.readI(); op.wsMacro.len=reader.readI(); op.ksrMacro.len=reader.readI(); op.damMacro.loop=reader.readI(); op.dvbMacro.loop=reader.readI(); op.egtMacro.loop=reader.readI(); op.kslMacro.loop=reader.readI(); op.susMacro.loop=reader.readI(); op.vibMacro.loop=reader.readI(); op.wsMacro.loop=reader.readI(); op.ksrMacro.loop=reader.readI(); op.damMacro.rel=reader.readI(); op.dvbMacro.rel=reader.readI(); op.egtMacro.rel=reader.readI(); op.kslMacro.rel=reader.readI(); op.susMacro.rel=reader.readI(); op.vibMacro.rel=reader.readI(); op.wsMacro.rel=reader.readI(); op.ksrMacro.rel=reader.readI(); op.damMacro.open=reader.readC(); op.dvbMacro.open=reader.readC(); op.egtMacro.open=reader.readC(); op.kslMacro.open=reader.readC(); op.susMacro.open=reader.readC(); op.vibMacro.open=reader.readC(); op.wsMacro.open=reader.readC(); op.ksrMacro.open=reader.readC(); } for (int i=0; i<4; i++) { DivInstrumentSTD::OpMacro& op=std.opMacros[i]; for (int j=0; j=63) { fm.fixedDrums=reader.readC(); reader.readC(); // reserved fm.kickFreq=reader.readS(); fm.snareHatFreq=reader.readS(); fm.tomTopFreq=reader.readS(); } // clear noise macro if PCE instrument and version<63 if (version<63 && type==DIV_INS_PCE) { std.dutyMacro.len=0; std.dutyMacro.loop=255; std.dutyMacro.rel=255; } // clear wave macro if OPLL instrument and version<70 if (version<70 && type==DIV_INS_OPLL) { std.waveMacro.len=0; std.waveMacro.loop=255; std.waveMacro.rel=255; } // sample map if (version>=67) { amiga.useNoteMap=reader.readC(); if (amiga.useNoteMap) { for (int note=0; note<120; note++) { amiga.noteMap[note].freq=reader.readI(); } for (int note=0; note<120; note++) { amiga.noteMap[note].map=reader.readS(); } if (version<152) { for (int note=0; note<120; note++) { amiga.noteMap[note].freq=note; } } } } // N163 if (version>=73) { n163.wave=reader.readI(); n163.wavePos=(unsigned char)reader.readC(); n163.waveLen=(unsigned char)reader.readC(); n163.waveMode=(unsigned char)reader.readC(); reader.readC(); // reserved } if (version>=76) { // more macros std.panLMacro.len=reader.readI(); std.panRMacro.len=reader.readI(); std.phaseResetMacro.len=reader.readI(); std.ex4Macro.len=reader.readI(); std.ex5Macro.len=reader.readI(); std.ex6Macro.len=reader.readI(); std.ex7Macro.len=reader.readI(); std.ex8Macro.len=reader.readI(); std.panLMacro.loop=reader.readI(); std.panRMacro.loop=reader.readI(); std.phaseResetMacro.loop=reader.readI(); std.ex4Macro.loop=reader.readI(); std.ex5Macro.loop=reader.readI(); std.ex6Macro.loop=reader.readI(); std.ex7Macro.loop=reader.readI(); std.ex8Macro.loop=reader.readI(); std.panLMacro.rel=reader.readI(); std.panRMacro.rel=reader.readI(); std.phaseResetMacro.rel=reader.readI(); std.ex4Macro.rel=reader.readI(); std.ex5Macro.rel=reader.readI(); std.ex6Macro.rel=reader.readI(); std.ex7Macro.rel=reader.readI(); std.ex8Macro.rel=reader.readI(); std.panLMacro.open=reader.readC(); std.panRMacro.open=reader.readC(); std.phaseResetMacro.open=reader.readC(); std.ex4Macro.open=reader.readC(); std.ex5Macro.open=reader.readC(); std.ex6Macro.open=reader.readC(); std.ex7Macro.open=reader.readC(); std.ex8Macro.open=reader.readC(); READ_MACRO_VALS(std.panLMacro.val,std.panLMacro.len); READ_MACRO_VALS(std.panRMacro.val,std.panRMacro.len); READ_MACRO_VALS(std.phaseResetMacro.val,std.phaseResetMacro.len); READ_MACRO_VALS(std.ex4Macro.val,std.ex4Macro.len); READ_MACRO_VALS(std.ex5Macro.val,std.ex5Macro.len); READ_MACRO_VALS(std.ex6Macro.val,std.ex6Macro.len); READ_MACRO_VALS(std.ex7Macro.val,std.ex7Macro.len); READ_MACRO_VALS(std.ex8Macro.val,std.ex8Macro.len); // FDS fds.modSpeed=reader.readI(); fds.modDepth=reader.readI(); fds.initModTableWithFirstWave=reader.readC(); reader.readC(); // reserved reader.readC(); reader.readC(); reader.read(fds.modTable,32); } // OPZ if (version>=77) { fm.fms2=reader.readC(); fm.ams2=reader.readC(); } // wave synth if (version>=79) { ws.wave1=reader.readI(); ws.wave2=reader.readI(); ws.rateDivider=reader.readC(); ws.effect=reader.readC(); ws.enabled=reader.readC(); ws.global=reader.readC(); ws.speed=reader.readC(); ws.param1=reader.readC(); ws.param2=reader.readC(); ws.param3=reader.readC(); ws.param4=reader.readC(); } // other macro modes if (version>=84) { std.volMacro.mode=reader.readC(); std.dutyMacro.mode=reader.readC(); std.waveMacro.mode=reader.readC(); std.pitchMacro.mode=reader.readC(); std.ex1Macro.mode=reader.readC(); std.ex2Macro.mode=reader.readC(); std.ex3Macro.mode=reader.readC(); std.algMacro.mode=reader.readC(); std.fbMacro.mode=reader.readC(); std.fmsMacro.mode=reader.readC(); std.amsMacro.mode=reader.readC(); std.panLMacro.mode=reader.readC(); std.panRMacro.mode=reader.readC(); std.phaseResetMacro.mode=reader.readC(); std.ex4Macro.mode=reader.readC(); std.ex5Macro.mode=reader.readC(); std.ex6Macro.mode=reader.readC(); std.ex7Macro.mode=reader.readC(); std.ex8Macro.mode=reader.readC(); } // C64 no test if (version>=89) { c64.noTest=reader.readC(); } // MultiPCM if (version>=93) { multipcm.ar=reader.readC(); multipcm.d1r=reader.readC(); multipcm.dl=reader.readC(); multipcm.d2r=reader.readC(); multipcm.rr=reader.readC(); multipcm.rc=reader.readC(); multipcm.lfo=reader.readC(); multipcm.vib=reader.readC(); multipcm.am=reader.readC(); // reserved for (int k=0; k<23; k++) reader.readC(); } // Sound Unit if (version>=104) { amiga.useSample=reader.readC(); su.switchRoles=reader.readC(); } // GB hardware sequence if (version>=105) { gb.hwSeqLen=reader.readC(); for (int i=0; i=106) { gb.softEnv=reader.readC(); gb.alwaysInit=reader.readC(); } // ES5506 if (version>=107) { es5506.filter.mode=(DivInstrumentES5506::Filter::FilterMode)reader.readC(); es5506.filter.k1=reader.readS(); es5506.filter.k2=reader.readS(); es5506.envelope.ecount=reader.readS(); es5506.envelope.lVRamp=reader.readC(); es5506.envelope.rVRamp=reader.readC(); es5506.envelope.k1Ramp=reader.readC(); es5506.envelope.k2Ramp=reader.readC(); es5506.envelope.k1Slow=reader.readC(); es5506.envelope.k2Slow=reader.readC(); } // SNES if (version>=109) { snes.useEnv=reader.readC(); if (version<118) { // why why why reader.readC(); reader.readC(); } else { snes.gainMode=(DivInstrumentSNES::GainMode)reader.readC(); snes.gain=reader.readC(); } snes.a=reader.readC(); snes.d=reader.readC(); snes.s=reader.readC(); snes.sus=(snes.s&8)?1:0; snes.s&=7; snes.r=reader.readC(); } // macro speed/delay if (version>=111) { std.volMacro.speed=reader.readC(); std.arpMacro.speed=reader.readC(); std.dutyMacro.speed=reader.readC(); std.waveMacro.speed=reader.readC(); std.pitchMacro.speed=reader.readC(); std.ex1Macro.speed=reader.readC(); std.ex2Macro.speed=reader.readC(); std.ex3Macro.speed=reader.readC(); std.algMacro.speed=reader.readC(); std.fbMacro.speed=reader.readC(); std.fmsMacro.speed=reader.readC(); std.amsMacro.speed=reader.readC(); std.panLMacro.speed=reader.readC(); std.panRMacro.speed=reader.readC(); std.phaseResetMacro.speed=reader.readC(); std.ex4Macro.speed=reader.readC(); std.ex5Macro.speed=reader.readC(); std.ex6Macro.speed=reader.readC(); std.ex7Macro.speed=reader.readC(); std.ex8Macro.speed=reader.readC(); std.volMacro.delay=reader.readC(); std.arpMacro.delay=reader.readC(); std.dutyMacro.delay=reader.readC(); std.waveMacro.delay=reader.readC(); std.pitchMacro.delay=reader.readC(); std.ex1Macro.delay=reader.readC(); std.ex2Macro.delay=reader.readC(); std.ex3Macro.delay=reader.readC(); std.algMacro.delay=reader.readC(); std.fbMacro.delay=reader.readC(); std.fmsMacro.delay=reader.readC(); std.amsMacro.delay=reader.readC(); std.panLMacro.delay=reader.readC(); std.panRMacro.delay=reader.readC(); std.phaseResetMacro.delay=reader.readC(); std.ex4Macro.delay=reader.readC(); std.ex5Macro.delay=reader.readC(); std.ex6Macro.delay=reader.readC(); std.ex7Macro.delay=reader.readC(); std.ex8Macro.delay=reader.readC(); // op macro speed/delay for (int i=0; i<4; i++) { DivInstrumentSTD::OpMacro& op=std.opMacros[i]; op.amMacro.speed=reader.readC(); op.arMacro.speed=reader.readC(); op.drMacro.speed=reader.readC(); op.multMacro.speed=reader.readC(); op.rrMacro.speed=reader.readC(); op.slMacro.speed=reader.readC(); op.tlMacro.speed=reader.readC(); op.dt2Macro.speed=reader.readC(); op.rsMacro.speed=reader.readC(); op.dtMacro.speed=reader.readC(); op.d2rMacro.speed=reader.readC(); op.ssgMacro.speed=reader.readC(); op.damMacro.speed=reader.readC(); op.dvbMacro.speed=reader.readC(); op.egtMacro.speed=reader.readC(); op.kslMacro.speed=reader.readC(); op.susMacro.speed=reader.readC(); op.vibMacro.speed=reader.readC(); op.wsMacro.speed=reader.readC(); op.ksrMacro.speed=reader.readC(); op.amMacro.delay=reader.readC(); op.arMacro.delay=reader.readC(); op.drMacro.delay=reader.readC(); op.multMacro.delay=reader.readC(); op.rrMacro.delay=reader.readC(); op.slMacro.delay=reader.readC(); op.tlMacro.delay=reader.readC(); op.dt2Macro.delay=reader.readC(); op.rsMacro.delay=reader.readC(); op.dtMacro.delay=reader.readC(); op.d2rMacro.delay=reader.readC(); op.ssgMacro.delay=reader.readC(); op.damMacro.delay=reader.readC(); op.dvbMacro.delay=reader.readC(); op.egtMacro.delay=reader.readC(); op.kslMacro.delay=reader.readC(); op.susMacro.delay=reader.readC(); op.vibMacro.delay=reader.readC(); op.wsMacro.delay=reader.readC(); op.ksrMacro.delay=reader.readC(); } } // old arp macro format if (version<112) { if (std.arpMacro.mode) { std.arpMacro.mode=0; for (int i=0; i=std.arpMacro.len || (std.arpMacro.rel>std.arpMacro.loop && std.arpMacro.rel0 && !(std.ex3Macro.open&6)) { if (std.ex4Macro.len>0 && std.ex4Macro.len=std.ex3Macro.len) { std.ex4Macro.val[i]|=(std.ex3Macro.val[std.ex3Macro.len-1]&3)<<1; } else { std.ex4Macro.val[i]|=(std.ex3Macro.val[i]&3)<<1; } } } std.ex4Macro.len=maxLen; std.ex3Macro=DivInstrumentMacro(DIV_MACRO_EX3); } bool DivInstrument::save(const char* path, DivSong* song, bool writeInsName) { SafeWriter* w=new SafeWriter(); w->init(); putInsData2(w,true,song,writeInsName); 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; } 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 Genesis and Neo Geo apart w->writeC(0x02); w->writeC(1); break; case DIV_INS_STD: w->writeC(0x03); w->writeC(0); break; case DIV_INS_NES: w->writeC(0x06); 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(0x05); w->writeC(0); break; case DIV_INS_OPLL: // ??? w->writeC(0x13); w->writeC(1); break; case DIV_INS_OPM: w->writeC(0x08); 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_OPM || 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; iwriteI(std.volMacro.val[i]); } if (std.volMacro.len>0) w->writeC(std.volMacro.loop); } bool arpMacroMode=false; int arpMacroHowManyFixed=0; int realArpMacroLen=std.arpMacro.len; for (int j=0; j=std.arpMacro.len-1) { arpMacroMode=true; } if (std.arpMacro.len>0) { if (arpMacroMode && std.arpMacro.val[std.arpMacro.len-1]==0 && std.arpMacro.loop>=std.arpMacro.len) { realArpMacroLen--; } } if (realArpMacroLen>127) realArpMacroLen=127; w->writeC(realArpMacroLen); if (arpMacroMode) { for (int j=0; jwriteI(std.arpMacro.val[j]^0x40000000); } else { w->writeI(std.arpMacro.val[j]); } } } else { for (int j=0; jwriteI((std.arpMacro.val[j]^0x40000000)+12); } else { w->writeI(std.arpMacro.val[j]+12); } } } if (realArpMacroLen>0) { w->writeC(std.arpMacro.loop); } w->writeC(arpMacroMode); w->writeC(std.dutyMacro.len); for (int i=0; iwriteI(std.dutyMacro.val[i]); } if (std.dutyMacro.len>0) w->writeC(std.dutyMacro.loop); w->writeC(std.waveMacro.len); for (int i=0; iwriteI(std.waveMacro.val[i]-1); } else { w->writeI(std.waveMacro.val[i]); } } 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(0); // this was 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; }