furnace/src/engine/instrument.cpp
alederer c5310d1855
Instrument editor undo (minus FixedQueue change) (#2094)
* add undo to instrument editor (check for diffs on the current DivInstrument in insEdit, record them in a stack)

* style fixes

* accidentally left some logs in

* typo in style fix

* cheat to avoid warning -Werror=class-memaccess on linux

* warn instead of assert on case where MemPatch application would exceed size of target buffer (which should never happen, if you're applying the patch to the same type it was generated from)

* instrument editor undo: don't check delta if no user input has come in that could potentially have dirtied the editor

* don't run a delta against cached instrument if not insEditOpen

* revert fixedQueue to before my 'fix' (if i touch it again i'll add unit tests)

* explicitly cast to (DivInstrumentPOD*) when memsetting DivInstrumentPOD in DivInstrument constructor, rather than relying on implicit memory layout

* use delete[] instead of free (whoops)

* MemPatch/DivInstrumentUndoStep -- remove clear() function (ambiguous whether it should free data, it only existed to set data to null after the swap, so just do that directly now). Also set data to null after delete.

* DivInstrument -- fix dangling undo-step pointers being created on duplicate (potentially leading to use-after-free), fix undo-step objects being shamelessly leaked

---------

Co-authored-by: Adam Lederer <adam@adamlederer.com>
2024-08-23 15:17:19 -05:00

3489 lines
85 KiB
C++

/**
* 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);
}
bool DivInstrumentSID2::operator==(const DivInstrumentSID2& other) {
return (
_C(volume) &&
_C(mixMode) &&
_C(noiseMode)
);
}
#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; i<opCount; i++) {
DivInstrumentFM::Operator& op=fm.op[i];
w->writeC((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;
}
bool MemPatch::calcDiff(const void* pre, const void* post, size_t inputSize) {
bool diffValid=false;
size_t firstDiff=0;
size_t lastDiff=0;
const unsigned char* preBytes=(const unsigned char*)pre;
const unsigned char* postBytes=(const unsigned char*)post;
// @NOTE: consider/profile using a memcmp==0 check to early-out, if it's potentially faster
// for the common case, which is no change
for (size_t ii=0; ii<inputSize; ++ii) {
if (preBytes[ii] != postBytes[ii]) {
lastDiff=ii;
firstDiff=diffValid ? firstDiff : ii;
diffValid=true;
}
}
if (diffValid) {
offset=firstDiff;
size=lastDiff - firstDiff + 1;
data=new unsigned char[size];
// the diff is to make pre into post (MemPatch is general, not specific to
// undo), so copy from postBytes
memcpy(data, postBytes+offset, size);
}
return diffValid;
}
void MemPatch::applyAndReverse(void* target, size_t targetSize) {
if (size==0) return;
if (offset+size>targetSize) {
logW("MemPatch (offset %d, size %d) exceeds target size (%d), can't apply!",offset,size,targetSize);
return;
}
unsigned char* targetBytes=(unsigned char*)target;
// swap this->data and its segment on target
for (size_t ii=0; ii<size; ++ii) {
unsigned char tmp=targetBytes[offset+ii];
targetBytes[offset+ii] = data[ii];
data[ii] = tmp;
}
}
void DivInstrumentUndoStep::applyAndReverse(DivInstrument* target) {
if (nameValid) {
name.swap(target->name);
}
podPatch.applyAndReverse((DivInstrumentPOD*)target, sizeof(DivInstrumentPOD));
}
bool DivInstrumentUndoStep::makeUndoPatch(size_t processTime_, const DivInstrument* pre, const DivInstrument* post) {
processTime=processTime_;
// create the patch that will make post into pre
podPatch.calcDiff((const DivInstrumentPOD*)post, (const DivInstrumentPOD*)pre, sizeof(DivInstrumentPOD));
if (pre->name!=post->name) {
nameValid=true;
name=pre->name;
}
return nameValid || podPatch.isValid();
}
bool DivInstrument::recordUndoStepIfChanged(size_t processTime, const DivInstrument* old) {
DivInstrumentUndoStep step;
// generate a patch to go back to old
if (step.makeUndoPatch(processTime, old, this)) {
// make room
if (undoHist.size()>=undoHist.capacity()) {
delete undoHist.front();
undoHist.pop_front();
}
// clear redo
while (!redoHist.empty()) {
delete redoHist.back();
redoHist.pop_back();
}
DivInstrumentUndoStep* stepPtr=new DivInstrumentUndoStep;
*stepPtr=step;
step.podPatch.data=NULL; // don't let it delete the data ptr that's been copied!
undoHist.push_back(stepPtr);
// logI("DivInstrument::undoHist push (%u off, %u size)", stepPtr->podPatch.offset, stepPtr->podPatch.size);
return true;
}
return false;
}
int DivInstrument::undo() {
if (undoHist.empty()) return 0;
DivInstrumentUndoStep* step=undoHist.back();
undoHist.pop_back();
// logI("DivInstrument::undo (%u off, %u size)", step->podPatch.offset, step->podPatch.size);
step->applyAndReverse(this);
// make room
if (redoHist.size()>=redoHist.capacity()) {
DivInstrumentUndoStep* step=redoHist.front();
delete step;
redoHist.pop_front();
}
redoHist.push_back(step);
return 1;
}
int DivInstrument::redo() {
if (redoHist.empty()) return 0;
DivInstrumentUndoStep* step = redoHist.back();
redoHist.pop_back();
// logI("DivInstrument::redo (%u off, %u size)", step->podPatch.offset, step->podPatch.size);
step->applyAndReverse(this);
// make room
if (undoHist.size()>=undoHist.capacity()) {
DivInstrumentUndoStep* step=undoHist.front();
delete step;
undoHist.pop_front();
}
undoHist.push_back(step);
return 1;
}
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; i<m.len; i++) {
if (m.val[i]<macroMin) macroMin=m.val[i];
if (m.val[i]>macroMax) 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; i<m.len; i++) {
w->writeC((unsigned char)m.val[i]);
}
break;
case 64:
for (int i=0; i<m.len; i++) {
w->writeC((signed char)m.val[i]);
}
break;
case 128:
for (int i=0; i<m.len; i++) {
w->writeS((short)m.val[i]);
}
break;
default: // 192
for (int i=0; i<m.len; i++) {
w->writeI(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&4095)|((c64.res&15)<<12)));
w->writeC((c64.res>>4)&15);
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; i<gb.hwSeqLen; i++) {
w->writeC(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<int>& 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; i<song->sample.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; i<list.size(); i++) {
w->writeI(0);
}
FEATURE_END;
return ret;
}
size_t DivInstrument::writeFeatureWL(SafeWriter* w, std::vector<int>& list, const DivSong* song) {
bool waveUsed[256];
memset(waveUsed,0,256*sizeof(bool));
for (int i=0; i<std.waveMacro.len; i++) {
if (std.waveMacro.val[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; i<song->wave.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; i<list.size(); i++) {
w->writeI(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; i<su.hwSeqLen; i++) {
w->writeC(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::writeFeatureS2(SafeWriter* w) {
FEATURE_BEGIN("S2");
w->writeC((sid2.volume&15)|((sid2.mixMode&3)<<4)|((sid2.noiseMode&3)<<6));
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<int> waveList;
std::vector<int> sampleList;
std::vector<unsigned int> wavePtr;
std::vector<unsigned int> 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 featureS2=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_SID2:
feature64=true;
featureS2=true;
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;
}
if (sid2!=defaultIns.sid2) {
featureS2=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; i<opCount; i++) {
const DivInstrumentSTD::OpMacro& m=std.opMacros[i];
if (m.amMacro.len ||
m.arMacro.len ||
m.drMacro.len ||
m.multMacro.len ||
m.rrMacro.len ||
m.slMacro.len ||
m.tlMacro.len ||
m.dt2Macro.len ||
m.rsMacro.len ||
m.dtMacro.len ||
m.d2rMacro.len ||
m.ssgMacro.len) {
featureOx[i]=true;
}
if (storeExtendedAsWell) {
if (m.damMacro.len ||
m.dvbMacro.len ||
m.egtMacro.len ||
m.kslMacro.len ||
m.susMacro.len ||
m.vibMacro.len ||
m.wsMacro.len ||
m.ksrMacro.len) {
featureOx[i]=true;
}
}
}
}
// write features
if (featureNA) {
writeFeatureNA(w);
}
if (featureFM) {
writeFeatureFM(w,fui);
}
if (featureMA) {
writeFeatureMA(w);
}
if (feature64) {
writeFeature64(w);
}
if (featureGB) {
writeFeatureGB(w);
}
if (featureSM) {
writeFeatureSM(w);
}
for (int i=0; i<4; i++) {
if (featureOx[i]) {
writeFeatureOx(w,i);
}
}
if (featureLD) {
writeFeatureLD(w);
}
if (featureSN) {
writeFeatureSN(w);
}
if (featureN1) {
writeFeatureN1(w);
}
if (featureFD) {
writeFeatureFD(w);
}
if (featureWS) {
writeFeatureWS(w);
}
if (featureSL) {
slSeek=writeFeatureSL(w,sampleList,song);
}
if (featureWL) {
wlSeek=writeFeatureWL(w,waveList,song);
}
if (featureMP) {
writeFeatureMP(w);
}
if (featureSU) {
writeFeatureSU(w);
}
if (featureES) {
writeFeatureES(w);
}
if (featureX1) {
writeFeatureX1(w);
}
if (featureNE) {
writeFeatureNE(w);
}
if (featureEF) {
writeFeatureEF(w);
}
if (featurePN) {
writeFeaturePN(w);
}
if (featureS2) {
writeFeatureS2(w);
}
if (fui && (featureSL || featureWL)) {
w->write("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()<endOfFeat) reader.seek(endOfFeat,SEEK_SET);
void DivInstrument::readFeatureNA(SafeReader& reader, short version) {
READ_FEAT_BEGIN;
name=reader.readString();
READ_FEAT_END;
}
void DivInstrument::readFeatureFM(SafeReader& reader, short version) {
READ_FEAT_BEGIN;
unsigned char opCount=reader.readC();
fm.op[0].enable=(opCount&16);
fm.op[1].enable=(opCount&32);
fm.op[2].enable=(opCount&64);
fm.op[3].enable=(opCount&128);
opCount&=15;
unsigned char next=reader.readC();
fm.alg=(next>>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<opCount; i++) {
DivInstrumentFM::Operator& op=fm.op[i];
next=reader.readC();
op.ksr=(next&128)?1:0;
op.dt=(next>>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();
if (macroHeaderLen==0) {
logW("invalid macro header length!");
READ_FEAT_END;
return;
}
DivInstrumentMacro* target=&std.volMacro;
while (reader.tell()<endOfFeat) {
size_t endOfMacroHeader=reader.tell()+macroHeaderLen;
unsigned char macroCode=reader.readC();
// end of macro list
if (macroCode==255) break;
switch (macroCode) {
case 0:
target=&std.volMacro;
break;
case 1:
target=&std.arpMacro;
break;
case 2:
target=&std.dutyMacro;
break;
case 3:
target=&std.waveMacro;
break;
case 4:
target=&std.pitchMacro;
break;
case 5:
target=&std.ex1Macro;
break;
case 6:
target=&std.ex2Macro;
break;
case 7:
target=&std.ex3Macro;
break;
case 8:
target=&std.algMacro;
break;
case 9:
target=&std.fbMacro;
break;
case 10:
target=&std.fmsMacro;
break;
case 11:
target=&std.amsMacro;
break;
case 12:
target=&std.panLMacro;
break;
case 13:
target=&std.panRMacro;
break;
case 14:
target=&std.phaseResetMacro;
break;
case 15:
target=&std.ex4Macro;
break;
case 16:
target=&std.ex5Macro;
break;
case 17:
target=&std.ex6Macro;
break;
case 18:
target=&std.ex7Macro;
break;
case 19:
target=&std.ex8Macro;
break;
default:
logW("invalid macro code %d!");
break;
}
target->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; i<target->len; i++) {
target->val[i]=(unsigned char)reader.readC();
}
break;
case 1:
for (int i=0; i<target->len; i++) {
target->val[i]=(signed char)reader.readC();
}
break;
case 2:
for (int i=0; i<target->len; i++) {
target->val[i]=reader.readS();
}
break;
default:
for (int i=0; i<target->len; i++) {
target->val[i]=reader.readI();
}
break;
}
}
if (version<193) {
if (type==DIV_INS_AY || type==DIV_INS_AY8930) {
for (int j=0; j<std.waveMacro.len; j++) {
std.waveMacro.val[j]++;
}
}
}
READ_FEAT_END;
}
void DivInstrument::readFeature64(SafeReader& reader, bool& volIsCutoff, short version) {
READ_FEAT_BEGIN;
unsigned char next=reader.readC();
c64.dutyIsAbs=next&128;
c64.initFilter=next&64;
volIsCutoff=next&32;
c64.toFilter=next&16;
c64.noiseOn=next&8;
c64.pulseOn=next&4;
c64.sawOn=next&2;
c64.triOn=next&1;
next=reader.readC();
c64.oscSync=(next&128)?1:0;
c64.ringMod=(next&64)?1:0;
c64.noTest=next&32;
c64.filterIsAbs=next&16;
c64.ch3off=next&8;
c64.bp=next&4;
c64.hp=next&2;
c64.lp=next&1;
next=reader.readC();
c64.a=(next>>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&4095;
c64.res=cr>>12;
if (version>=199) {
c64.res|=((unsigned char)reader.readC())<<4;
}
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; i<gb.hwSeqLen; i++) {
gb.hwSeq[i].cmd=reader.readC();
gb.hwSeq[i].data=reader.readS();
}
READ_FEAT_END;
}
void DivInstrument::readFeatureSM(SafeReader& reader, short version) {
READ_FEAT_BEGIN;
amiga.initSample=reader.readS();
unsigned char next=reader.readC();
amiga.useWave=next&4;
amiga.useSample=next&2;
amiga.useNoteMap=next&1;
amiga.waveLen=(unsigned char)reader.readC();
if (amiga.useNoteMap) {
for (int note=0; note<120; note++) {
amiga.noteMap[note].freq=reader.readS();
amiga.noteMap[note].map=reader.readS();
}
if (version<152) {
for (int note=0; note<120; note++) {
amiga.noteMap[note].freq=note;
}
}
}
READ_FEAT_END;
}
void DivInstrument::readFeatureOx(SafeReader& reader, int op, short version) {
READ_FEAT_BEGIN;
unsigned short macroHeaderLen=reader.readS();
if (macroHeaderLen==0) {
logW("invalid macro header length!");
READ_FEAT_END;
return;
}
DivInstrumentMacro* target=&std.opMacros[op].amMacro;
while (reader.tell()<endOfFeat) {
size_t endOfMacroHeader=reader.tell()+macroHeaderLen;
unsigned char macroCode=reader.readC();
// end of macro list
if (macroCode==255) break;
switch (macroCode) {
case 0:
target=&std.opMacros[op].amMacro;
break;
case 1:
target=&std.opMacros[op].arMacro;
break;
case 2:
target=&std.opMacros[op].drMacro;
break;
case 3:
target=&std.opMacros[op].multMacro;
break;
case 4:
target=&std.opMacros[op].rrMacro;
break;
case 5:
target=&std.opMacros[op].slMacro;
break;
case 6:
target=&std.opMacros[op].tlMacro;
break;
case 7:
target=&std.opMacros[op].dt2Macro;
break;
case 8:
target=&std.opMacros[op].rsMacro;
break;
case 9:
target=&std.opMacros[op].dtMacro;
break;
case 10:
target=&std.opMacros[op].d2rMacro;
break;
case 11:
target=&std.opMacros[op].ssgMacro;
break;
case 12:
target=&std.opMacros[op].damMacro;
break;
case 13:
target=&std.opMacros[op].dvbMacro;
break;
case 14:
target=&std.opMacros[op].egtMacro;
break;
case 15:
target=&std.opMacros[op].kslMacro;
break;
case 16:
target=&std.opMacros[op].susMacro;
break;
case 17:
target=&std.opMacros[op].vibMacro;
break;
case 18:
target=&std.opMacros[op].wsMacro;
break;
case 19:
target=&std.opMacros[op].ksrMacro;
break;
}
target->len=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; i<target->len; i++) {
target->val[i]=(unsigned char)reader.readC();
}
break;
case 1:
for (int i=0; i<target->len; i++) {
target->val[i]=(signed char)reader.readC();
}
break;
case 2:
for (int i=0; i<target->len; i++) {
target->val[i]=reader.readS();
}
break;
default:
for (int i=0; i<target->len; 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; j<target->len; 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; i<sampleCount; i++) {
sampleIndex[i]=reader.readC();
}
for (int i=0; i<sampleCount; i++) {
samplePtr[i]=reader.readI();
}
size_t lastSeek=reader.tell();
// 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_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; i<waveCount; i++) {
waveIndex[i]=reader.readC();
}
for (int i=0; i<waveCount; i++) {
wavePtr[i]=reader.readI();
}
size_t lastSeek=reader.tell();
// 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_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<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;
}
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<su.hwSeqLen; i++) {
su.hwSeq[i].cmd=reader.readC();
su.hwSeq[i].bound=reader.readC();
su.hwSeq[i].val=reader.readC();
su.hwSeq[i].speed=reader.readS();
}
}
READ_FEAT_END;
}
void DivInstrument::readFeatureES(SafeReader& reader, short version) {
READ_FEAT_BEGIN;
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();
READ_FEAT_END;
}
void DivInstrument::readFeatureX1(SafeReader& reader, short version) {
READ_FEAT_BEGIN;
x1_010.bankSlot=reader.readI();
READ_FEAT_END;
}
void DivInstrument::readFeatureNE(SafeReader& reader, short version) {
READ_FEAT_BEGIN;
amiga.useNoteMap=reader.readC();
if (amiga.useNoteMap) {
for (int note=0; note<120; note++) {
amiga.noteMap[note].dpcmFreq=reader.readC();
amiga.noteMap[note].dpcmDelta=reader.readC();
}
}
READ_FEAT_END;
}
void DivInstrument::readFeatureEF(SafeReader& reader, short version) {
READ_FEAT_BEGIN;
unsigned char next=reader.readC();
esfm.noise=next&3;
for (int i=0; i<4; i++) {
DivInstrumentESFM::Operator& op=esfm.op[i];
next=reader.readC();
op.delay=(next>>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;
}
void DivInstrument::readFeatureS2(SafeReader& reader, short version) {
READ_FEAT_BEGIN;
unsigned char next=reader.readC();
sid2.volume=next&0xf;
sid2.mixMode=(next>>4)&3;
sid2.noiseMode=next>>6;
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()<dataLen) {
// read feature code
reader.read(featCode,2);
logV("- %c%c",featCode[0],featCode[1]);
if (memcmp(featCode,"EN",2)==0) { // end of instrument
break;
} else if (memcmp(featCode,"NA",2)==0) { // name
readFeatureNA(reader,version);
} else if (memcmp(featCode,"FM",2)==0) { // FM
readFeatureFM(reader,version);
} else if (memcmp(featCode,"MA",2)==0) { // macros
readFeatureMA(reader,version);
} else if (memcmp(featCode,"64",2)==0) { // C64
readFeature64(reader,volIsCutoff,version);
} else if (memcmp(featCode,"GB",2)==0) { // Game Boy
readFeatureGB(reader,version);
} else if (memcmp(featCode,"SM",2)==0) { // sample
readFeatureSM(reader,version);
} else if (memcmp(featCode,"O1",2)==0) { // op1 macros
readFeatureOx(reader,0,version);
} else if (memcmp(featCode,"O2",2)==0) { // op2 macros
readFeatureOx(reader,1,version);
} else if (memcmp(featCode,"O3",2)==0) { // op3 macros
readFeatureOx(reader,2,version);
} else if (memcmp(featCode,"O4",2)==0) { // op4 macros
readFeatureOx(reader,3,version);
} else if (memcmp(featCode,"LD",2)==0) { // OPL drums
readFeatureLD(reader,version);
} else if (memcmp(featCode,"SN",2)==0) { // SNES
readFeatureSN(reader,version);
} else if (memcmp(featCode,"N1",2)==0) { // Namco 163
readFeatureN1(reader,version);
} else if (memcmp(featCode,"FD",2)==0) { // FDS/VB
readFeatureFD(reader,version);
} else if (memcmp(featCode,"WS",2)==0) { // WaveSynth
readFeatureWS(reader,version);
} else if (memcmp(featCode,"SL",2)==0 && fui && song!=NULL) { // sample list
readFeatureSL(reader,song,version);
} else if (memcmp(featCode,"WL",2)==0 && fui && song!=NULL) { // wave list
readFeatureWL(reader,song,version);
} else if (memcmp(featCode,"MP",2)==0) { // MultiPCM
readFeatureMP(reader,version);
} else if (memcmp(featCode,"SU",2)==0) { // Sound Unit
readFeatureSU(reader,version);
} else if (memcmp(featCode,"ES",2)==0) { // ES5506
readFeatureES(reader,version);
} else if (memcmp(featCode,"X1",2)==0) { // X1-010
readFeatureX1(reader,version);
} else if (memcmp(featCode,"NE",2)==0) { // NES (DPCM)
readFeatureNE(reader,version);
} else if (memcmp(featCode,"EF",2)==0) { // ESFM
readFeatureEF(reader,version);
} else if (memcmp(featCode,"PN",2)==0) { // PowerNoise
readFeaturePN(reader,version);
} else if (memcmp(featCode,"S2",2)==0) { // SID2
readFeatureS2(reader,version);
} else {
if (song==NULL && (memcmp(featCode,"SL",2)==0 || (memcmp(featCode,"WL",2)==0))) {
// nothing
} else {
logW("unknown feature code %c%c!",featCode[0],featCode[1]);
}
// skip feature
unsigned short skip=reader.readS();
reader.seek(skip,SEEK_CUR);
}
}
// <187 C64 cutoff macro compatibility
if (type==DIV_INS_C64 && volIsCutoff && version<187) {
memcpy(&std.algMacro,&std.volMacro,sizeof(DivInstrumentMacro));
std.algMacro.macroType=DIV_MACRO_ALG;
std.volMacro=DivInstrumentMacro(DIV_MACRO_VOL,true);
if (!c64.filterIsAbs) {
for (int i=0; i<std.algMacro.len; i++) {
std.algMacro.val[i]=-std.algMacro.val[i];
}
}
}
// <187 special/test/gate merge
if (type==DIV_INS_C64 && version<187) {
convertC64SpecialMacro();
}
return DIV_DATA_SUCCESS;
}
#define READ_MACRO_VALS(x,y) \
for (int macroValPos=0; macroValPos<y; macroValPos++) x[macroValPos]=reader.readI();
DivDataErrors DivInstrument::readInsDataOld(SafeReader &reader, short version) {
bool volIsCutoff=false;
reader.readI(); // length. ignored.
reader.readS(); // format version. ignored.
type=(DivInstrumentType)reader.readC();
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();
if (version>=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<std.arpMacro.len; j++) {
std.arpMacro.val[j]-=12;
}
}
if (type==DIV_INS_C64 && version<87) {
if (volIsCutoff && !c64.filterIsAbs) for (int j=0; j<std.volMacro.len; j++) {
std.volMacro.val[j]-=18;
}
if (!c64.dutyIsAbs) for (int j=0; j<std.dutyMacro.len; j++) {
std.dutyMacro.val[j]-=12;
}
}
if (version<193) {
if (type==DIV_INS_AY || type==DIV_INS_AY8930) {
for (int j=0; j<std.waveMacro.len; j++) {
std.waveMacro.val[j]++;
}
}
}
if (version>=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<op.amMacro.len; j++) {
op.amMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.arMacro.len; j++) {
op.arMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.drMacro.len; j++) {
op.drMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.multMacro.len; j++) {
op.multMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.rrMacro.len; j++) {
op.rrMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.slMacro.len; j++) {
op.slMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.tlMacro.len; j++) {
op.tlMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.dt2Macro.len; j++) {
op.dt2Macro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.rsMacro.len; j++) {
op.rsMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.dtMacro.len; j++) {
op.dtMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.d2rMacro.len; j++) {
op.d2rMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.ssgMacro.len; j++) {
op.ssgMacro.val[j]=(unsigned char)reader.readC();
}
}
}
// release points
if (version>=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<op.damMacro.len; j++) {
op.damMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.dvbMacro.len; j++) {
op.dvbMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.egtMacro.len; j++) {
op.egtMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.kslMacro.len; j++) {
op.kslMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.susMacro.len; j++) {
op.susMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.vibMacro.len; j++) {
op.vibMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.wsMacro.len; j++) {
op.wsMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.ksrMacro.len; j++) {
op.ksrMacro.val[j]=(unsigned char)reader.readC();
}
}
}
// OPL drum data
if (version>=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<gb.hwSeqLen; i++) {
gb.hwSeq[i].cmd=reader.readC();
gb.hwSeq[i].data=reader.readS();
}
}
// GB additional flags
if (version>=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; i++) {
std.arpMacro.val[i]^=0x40000000;
}
if ((std.arpMacro.loop>=std.arpMacro.len || (std.arpMacro.rel>std.arpMacro.loop && std.arpMacro.rel<std.arpMacro.len)) && std.arpMacro.len<255) {
std.arpMacro.val[std.arpMacro.len++]=0;
}
}
}
// <167 TL macro compat
if (version<167) {
for (int i=0; i<4; i++) {
if (std.opMacros[i].tlMacro.open&6) {
for (int j=0; j<2; j++) {
std.opMacros[i].tlMacro.val[j]^=0x7f;
}
} else {
for (int j=0; j<std.opMacros[i].tlMacro.len; j++) {
std.opMacros[i].tlMacro.val[j]^=0x7f;
}
}
}
}
// <187 C64 cutoff macro compatibility
if (type==DIV_INS_C64 && volIsCutoff && version<187) {
memcpy(&std.algMacro,&std.volMacro,sizeof(DivInstrumentMacro));
std.algMacro.macroType=DIV_MACRO_ALG;
std.volMacro=DivInstrumentMacro(DIV_MACRO_VOL,true);
if (!c64.filterIsAbs) {
for (int i=0; i<std.algMacro.len; i++) {
std.algMacro.val[i]=-std.algMacro.val[i];
}
}
}
// <187 special/test/gate merge
if (type==DIV_INS_C64 && version<187) {
convertC64SpecialMacro();
}
return DIV_DATA_SUCCESS;
}
DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version, DivSong* song) {
// 0: old (INST)
// 1: new (INS2, length)
// 2: new (FINS, no length)
int type=-1;
char magic[4];
reader.read(magic,4);
if (memcmp(magic,"INST",4)==0) {
type=0;
} else if (memcmp(magic,"INS2",4)==0) {
type=1;
} else if (memcmp(magic,"IN2B",4)==0) { // DIV_FUR_VARIANT_B
type=1;
} else if (memcmp(magic,"FINS",4)==0) {
type=2;
} else if (memcmp(magic,"FINB",4)==0) { // DIV_FUR_VARIANT_B
type=2;
} else {
logE("invalid instrument header!");
return DIV_DATA_INVALID_HEADER;
}
if (type==1 || type==2) {
logV("reading new instrument data...");
return readInsDataNew(reader,version,type==2,song);
}
return readInsDataOld(reader,version);
}
void DivInstrument::convertC64SpecialMacro() {
// merge special and test/gate macros into new special macro
int maxLen=MAX(std.ex3Macro.len,std.ex4Macro.len);
// skip if ex4 is not a sequence macro
if (std.ex4Macro.open&6) return;
// move ex4 macro up and fill in gate
for (int i=0; i<std.ex4Macro.len; i++) {
std.ex4Macro.val[i]=(std.ex4Macro.val[i]&1)?9:1;
}
// merge ex3 into ex4 if viable to
if (std.ex3Macro.len>0 && !(std.ex3Macro.open&6)) {
if (std.ex4Macro.len>0 && std.ex4Macro.len<maxLen) {
for (int i=std.ex4Macro.len; i<maxLen; i++) {
std.ex4Macro.val[i]=std.ex3Macro.val[std.ex4Macro.len-1];
}
} else {
for (int i=0; i<maxLen; i++) {
std.ex4Macro.val[i]=1;
}
}
for (int i=0; i<maxLen; i++) {
if (i>=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; i<std.volMacro.len; i++) {
w->writeI(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; j++) {
if ((std.arpMacro.val[j]&0xc0000000)==0x40000000 || (std.arpMacro.val[j]&0xc0000000)==0x80000000) {
arpMacroHowManyFixed++;
}
}
if (arpMacroHowManyFixed>=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; j<realArpMacroLen; j++) {
if ((std.arpMacro.val[j]&0xc0000000)==0x40000000 || (std.arpMacro.val[j]&0xc0000000)==0x80000000) {
w->writeI(std.arpMacro.val[j]^0x40000000);
} else {
w->writeI(std.arpMacro.val[j]);
}
}
} else {
for (int j=0; j<realArpMacroLen; j++) {
if ((std.arpMacro.val[j]&0xc0000000)==0x40000000 || (std.arpMacro.val[j]&0xc0000000)==0x80000000) {
w->writeI((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; i<std.dutyMacro.len; i++) {
w->writeI(std.dutyMacro.val[i]);
}
if (std.dutyMacro.len>0) w->writeC(std.dutyMacro.loop);
w->writeC(std.waveMacro.len);
for (int i=0; i<std.waveMacro.len; i++) {
if (type==DIV_INS_AY) {
w->writeI(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;
}
DivInstrument::~DivInstrument() {
// free undoHist/redoHist
while (!undoHist.empty()) {
delete undoHist.back();
undoHist.pop_back();
}
while (!redoHist.empty()) {
delete redoHist.back();
redoHist.pop_back();
}
}
DivInstrument::DivInstrument( const DivInstrument& ins ) {
// undo/redo history is specifically not copied
*(DivInstrumentPOD*)this=ins;
name=ins.name;
}
DivInstrument& DivInstrument::operator=( const DivInstrument& ins ) {
// undo/redo history is specifically not copied
*(DivInstrumentPOD*)this=ins;
name=ins.name;
return *this;
}