Merge branch 'master' of https://github.com/tildearrow/furnace into nmk112

This commit is contained in:
cam900 2023-10-21 15:15:10 +09:00
commit 15bdb19336
1785 changed files with 523362 additions and 4372 deletions

View file

@ -267,6 +267,29 @@ struct DivCommand {
value2(0) {}
};
struct DivPitchTable {
int pitch[(12*128)+1];
unsigned char linearity, blockBits;
bool period;
// get pitch
int get(int base, int pitch, int pitch2);
// linear: note
// non-linear: get(note,0,0)
int getBase(int note);
// calculate pitch table
void init(float tuning, double clock, double divider, int octave, unsigned char linear, bool isPeriod, unsigned char block=0);
DivPitchTable():
linearity(2),
blockBits(0),
period(false) {
memset(pitch,0,sizeof(pitch));
}
};
struct DivDelayedCommand {
int ticks;
DivCommand cmd;
@ -339,6 +362,40 @@ struct DivDispatchOscBuffer {
}
};
struct DivChannelPair {
const char* label;
// -1: none
signed char pairs[8];
DivChannelPair(const char* l, signed char p0, signed char p1, signed char p2, signed char p3, signed char p4, signed char p5, signed char p6, signed char p7):
label(l),
pairs{p0,p1,p2,p3,p4,p5,p6,p7} {}
DivChannelPair(const char* l, signed char p):
label(l),
pairs{p,-1,-1,-1,-1,-1,-1,-1} {}
DivChannelPair():
label(NULL),
pairs{-1,-1,-1,-1,-1,-1,-1,-1} {}
};
struct DivChannelModeHints {
const char* hint[4];
// valid types:
// - 0: disabled
// - 1: volume
// - 2: pitch
// - 3: panning
// - 4: ???
unsigned char type[4];
// up to 4
unsigned char count;
DivChannelModeHints():
hint{NULL,NULL,NULL,NULL},
type{0,0,0,0},
count(0) {}
};
class DivEngine;
class DivMacroInt;
@ -418,6 +475,21 @@ class DivDispatch {
*/
virtual unsigned short getPan(int chan);
/**
* get "paired" channels.
* @param chan the channel to query.
* @return a DivChannelPair.
*/
virtual DivChannelPair getPaired(int chan);
/**
* get channel mode hints.
* @param chan the channel to query.
* @return a DivChannelModeHints.
*/
virtual DivChannelModeHints getModeHints(int chan);
/**
* get currently playing sample (and its position).
* @param chan the channel.
@ -643,6 +715,11 @@ class DivDispatch {
*/
virtual void renderSamples(int sysID);
/**
* tell this DivDispatch that the tuning and/or pitch linearity has changed, and therefore the pitch table must be regenerated.
*/
virtual void notifyPitchTable();
/**
* initialize this DivDispatch.
* @param parent the parent DivEngine.
@ -669,6 +746,7 @@ class DivDispatch {
if (chipClock<getClockRangeMin()) chipClock=getClockRangeMin(); \
}
// NOTE: these definitions may be deprecated in the future. see DivPitchTable.
// pitch calculation:
// - a DivDispatch usually contains four variables per channel:
// - baseFreq: this changes on new notes, legato, arpeggio and slides.

View file

@ -150,6 +150,10 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
if (iter!=sysDef->postEffectHandlers.end()) {
return iter->second.description;
}
iter=sysDef->preEffectHandlers.find(effect);
if (iter!=sysDef->preEffectHandlers.end()) {
return iter->second.description;
}
}
break;
}
@ -541,6 +545,7 @@ void DivEngine::initSongWithDesc(const char* description, bool inBase64, bool ol
// extra attributes
song.subsong[0]->hz=c.getDouble("tickRate",60.0);
song.author=getConfString("defaultAuthorName","");
}
void DivEngine::createNew(const char* description, String sysName, bool inBase64) {
@ -1414,6 +1419,11 @@ void* DivEngine::getDispatchChanState(int ch) {
return disCont[dispatchOfChan[ch]].dispatch->getChanState(dispatchChanOfChan[ch]);
}
DivChannelPair DivEngine::getChanPaired(int ch) {
if (ch<0 || ch>=chans) return DivChannelPair();
return disCont[dispatchOfChan[ch]].dispatch->getPaired(dispatchChanOfChan[ch]);
}
unsigned char* DivEngine::getRegisterPool(int sys, int& size, int& depth) {
if (sys<0 || sys>=song.systemLen) return NULL;
if (disCont[sys].dispatch==NULL) return NULL;
@ -1908,11 +1918,13 @@ void DivEngine::recalcChans() {
memset(isInsTypePossible,0,DIV_INS_MAX*sizeof(bool));
for (int i=0; i<song.systemLen; i++) {
int chanCount=getChannelCount(song.system[i]);
int firstChan=chans;
chans+=chanCount;
for (int j=0; j<chanCount; j++) {
sysOfChan[chanIndex]=song.system[i];
dispatchOfChan[chanIndex]=i;
dispatchChanOfChan[chanIndex]=j;
dispatchFirstChan[chanIndex]=firstChan;
chanIndex++;
if (sysDefs[song.system[i]]!=NULL) {
@ -2295,6 +2307,65 @@ void DivEngine::unmuteAll() {
BUSY_END;
}
void DivEngine::dumpSongInfo() {
printf(
"SONG INFORMATION\n"
"- name: %s\n"
"- author: %s\n"
"- album: %s\n"
"- system: %s\n"
"- %d ins, %d waves, %d samples\n"
"<<<\n%s\n>>>\n\n",
song.name.c_str(),
song.author.c_str(),
song.category.c_str(),
song.systemName.c_str(),
song.insLen,
song.waveLen,
song.sampleLen,
song.notes.c_str()
);
printf("SUB-SONGS\n");
int index=0;
for (DivSubSong* i: song.subsong) {
printf(
"=== %d: %s\n"
"<<<\n%s\n>>>\n",
index,
i->name.c_str(),
i->notes.c_str()
);
index++;
}
if (!song.ins.empty()) {
printf("\nINSTRUMENTS\n");
index=0;
for (DivInstrument* i: song.ins) {
printf(
"- %d: %s\n",
index,
i->name.c_str()
);
index++;
}
}
if (!song.sample.empty()) {
printf("\nSAMPLES\n");
index=0;
for (DivSample* i: song.sample) {
printf(
"- %d: %s\n",
index,
i->name.c_str()
);
index++;
}
}
}
int DivEngine::addInstrument(int refChan, DivInstrumentType fallbackType) {
if (song.ins.size()>=256) return -1;
BUSY_BEGIN;
@ -3094,7 +3165,7 @@ void DivEngine::noteOff(int chan) {
BUSY_END;
}
void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
bool DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
bool isViable[DIV_MAX_CHANS];
bool canPlayAnyway=false;
bool notInViableChannel=false;
@ -3130,7 +3201,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
}
}
if (!canPlayAnyway) return;
if (!canPlayAnyway) return false;
// 2. find a free channel
do {
@ -3138,7 +3209,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
chan[finalChan].midiNote=note;
chan[finalChan].midiAge=midiAgeCounter++;
pendingNotes.push_back(DivNoteEvent(finalChan,ins,note,vol,true));
return;
return true;
}
if (++finalChan>=chans) {
finalChan=0;
@ -3159,6 +3230,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
chan[candidate].midiNote=note;
chan[candidate].midiAge=midiAgeCounter++;
pendingNotes.push_back(DivNoteEvent(candidate,ins,note,vol,true));
return true;
}
void DivEngine::autoNoteOff(int ch, int note, int vol) {
@ -3201,10 +3273,11 @@ void DivEngine::setOrder(unsigned char order) {
BUSY_END;
}
void DivEngine::updateSysFlags(int system, bool restart) {
void DivEngine::updateSysFlags(int system, bool restart, bool render) {
BUSY_BEGIN_SOFT;
disCont[system].dispatch->setFlags(song.systemFlags[system]);
disCont[system].setRates(got.rate);
if (render) renderSamples();
// patchbay
if (song.patchbayAuto) {
@ -3620,7 +3693,8 @@ bool DivEngine::deinitAudioBackend(bool dueToSwitchMaster) {
return true;
}
void DivEngine::preInit() {
bool DivEngine::preInit(bool noSafeMode) {
bool wantSafe=false;
// register systems
if (!systemsRegistered) registerSystems();
@ -3628,6 +3702,13 @@ void DivEngine::preInit() {
initConfDir();
logD("config path: %s",configPath.c_str());
if (!noSafeMode) {
String safeModePath=configPath+DIR_SEPARATOR_STR+"safemode";
if (touchFile(safeModePath.c_str())==-EEXIST) {
wantSafe=true;
}
}
String logPath=configPath+DIR_SEPARATOR_STR+"furnace.log";
startLogFile(logPath.c_str());
@ -3641,6 +3722,17 @@ void DivEngine::preInit() {
SDL_SetHint("SDL_HINT_AUDIODRIVER",audioDriver.c_str());
}
#endif
if (wantSafe) {
logW("requesting safe mode.");
}
return wantSafe;
}
void DivEngine::everythingOK() {
String safeModePath=configPath+DIR_SEPARATOR_STR+"safemode";
deleteFile(safeModePath.c_str());
}
bool DivEngine::init() {

View file

@ -52,10 +52,10 @@ class DivWorkPool;
#define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock();
#define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false;
//#define DIV_UNSTABLE
#define DIV_UNSTABLE
#define DIV_VERSION "0.6"
#define DIV_ENGINE_VERSION 181
#define DIV_VERSION "dev184"
#define DIV_ENGINE_VERSION 184
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02
@ -105,7 +105,7 @@ struct DivChannelState {
int vibratoDepth, vibratoRate, vibratoPos, vibratoPosGiant, vibratoDir, vibratoFine;
int tremoloDepth, tremoloRate, tremoloPos;
unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR, lastVibrato, lastPorta;
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff, releasing;
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp;
bool wentThroughNote, goneThroughNote;
@ -154,6 +154,7 @@ struct DivChannelState {
keyOff(false),
nowYouCanStop(true),
stopOnOff(false),
releasing(false),
arpYield(false),
delayLocked(false),
inPorta(false),
@ -595,6 +596,7 @@ class DivEngine {
DivSystem sysOfChan[DIV_MAX_CHANS];
int dispatchOfChan[DIV_MAX_CHANS];
int dispatchChanOfChan[DIV_MAX_CHANS];
int dispatchFirstChan[DIV_MAX_CHANS];
bool keyHit[DIV_MAX_CHANS];
float* oscBuf[DIV_MAX_OUTPUTS];
float oscSize;
@ -879,6 +881,9 @@ class DivEngine {
// get ext value
unsigned char getExtValue();
// dump song info to stdout
void dumpSongInfo();
// is playing
bool isPlaying();
@ -983,7 +988,8 @@ class DivEngine {
// stop note
void noteOff(int chan);
void autoNoteOn(int chan, int ins, int note, int vol=-1);
// returns whether it could
bool autoNoteOn(int chan, int ins, int note, int vol=-1);
void autoNoteOff(int chan, int note, int vol=-1);
void autoNoteOffAll();
@ -994,7 +1000,7 @@ class DivEngine {
void setOrder(unsigned char order);
// update system flags
void updateSysFlags(int system, bool restart);
void updateSysFlags(int system, bool restart, bool render);
// set Hz
void setSongRate(float hz);
@ -1008,6 +1014,9 @@ class DivEngine {
// get dispatch channel state
void* getDispatchChanState(int chan);
// get channel pairs
DivChannelPair getChanPaired(int chan);
// get register pool
unsigned char* getRegisterPool(int sys, int& size, int& depth);
@ -1190,12 +1199,15 @@ class DivEngine {
// quit dispatch
void quitDispatch();
// pre-initialize the engine.
void preInit();
// pre-initialize the engine. returns whether Furnace should run in safe mode.
bool preInit(bool noSafeMode=true);
// initialize the engine.
bool init();
// confirm that the engine is running (delete safe mode file).
void everythingOK();
// terminate the engine.
bool quit();
@ -1319,6 +1331,7 @@ class DivEngine {
mu5ROM(NULL) {
memset(isMuted,0,DIV_MAX_CHANS*sizeof(bool));
memset(keyHit,0,DIV_MAX_CHANS*sizeof(bool));
memset(dispatchFirstChan,0,DIV_MAX_CHANS*sizeof(int));
memset(dispatchChanOfChan,0,DIV_MAX_CHANS*sizeof(int));
memset(dispatchOfChan,0,DIV_MAX_CHANS*sizeof(int));
memset(sysOfChan,0,DIV_MAX_CHANS*sizeof(int));

View file

@ -184,6 +184,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.snNoLowPeriods=true;
ds.disableSampleMacro=true;
ds.preNoteNoEffect=true;
ds.oldDPCM=true;
ds.delayBehavior=0;
ds.jumpTreatment=2;
@ -1856,6 +1857,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version<168) {
ds.preNoteNoEffect=true;
}
if (ds.version<183) {
ds.oldDPCM=true;
}
if (ds.version<184) {
ds.resetArpPhaseOnNewNote=false;
}
ds.isDMF=false;
reader.readS(); // reserved
@ -2374,7 +2381,17 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} else {
reader.readC();
}
for (int i=0; i<5; i++) {
if (ds.version>=183) {
ds.oldDPCM=reader.readC();
} else {
reader.readC();
}
if (ds.version>=184) {
ds.resetArpPhaseOnNewNote=reader.readC();
} else {
reader.readC();
}
for (int i=0; i<3; i++) {
reader.readC();
}
}
@ -5438,7 +5455,9 @@ SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) {
w->writeC(song.brokenPortaLegato);
w->writeC(song.brokenFMOff);
w->writeC(song.preNoteNoEffect);
for (int i=0; i<5; i++) {
w->writeC(song.oldDPCM);
w->writeC(song.resetArpPhaseOnNewNote);
for (int i=0; i<3; i++) {
w->writeC(0);
}

View file

@ -291,7 +291,7 @@ void DivInstrument::writeFeatureFM(SafeWriter* w, bool fui) {
FEATURE_END;
}
void DivInstrument::writeMacro(SafeWriter* w, const DivInstrumentMacro& m, unsigned char macroCode) {
void DivInstrument::writeMacro(SafeWriter* w, const DivInstrumentMacro& m) {
if (!m.len) return;
// determine word size
@ -314,12 +314,12 @@ void DivInstrument::writeMacro(SafeWriter* w, const DivInstrumentMacro& m, unsig
wordSize=192; // 32-bit signed
}
w->writeC(macroCode);
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|wordSize);
w->writeC((m.open&0x3f)|wordSize);
w->writeC(m.delay);
w->writeC(m.speed);
@ -355,26 +355,26 @@ void DivInstrument::writeFeatureMA(SafeWriter* w) {
w->writeS(8);
// write macros
writeMacro(w,std.volMacro,0);
writeMacro(w,std.arpMacro,1);
writeMacro(w,std.dutyMacro,2);
writeMacro(w,std.waveMacro,3);
writeMacro(w,std.pitchMacro,4);
writeMacro(w,std.ex1Macro,5);
writeMacro(w,std.ex2Macro,6);
writeMacro(w,std.ex3Macro,7);
writeMacro(w,std.algMacro,8);
writeMacro(w,std.fbMacro,9);
writeMacro(w,std.fmsMacro,10);
writeMacro(w,std.amsMacro,11);
writeMacro(w,std.panLMacro,12);
writeMacro(w,std.panRMacro,13);
writeMacro(w,std.phaseResetMacro,14);
writeMacro(w,std.ex4Macro,15);
writeMacro(w,std.ex5Macro,16);
writeMacro(w,std.ex6Macro,17);
writeMacro(w,std.ex7Macro,18);
writeMacro(w,std.ex8Macro,19);
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);
@ -471,26 +471,26 @@ void DivInstrument::writeFeatureOx(SafeWriter* w, int ope) {
// write macros
const DivInstrumentSTD::OpMacro& o=std.opMacros[ope];
writeMacro(w,o.amMacro,0);
writeMacro(w,o.arMacro,1);
writeMacro(w,o.drMacro,2);
writeMacro(w,o.multMacro,3);
writeMacro(w,o.rrMacro,4);
writeMacro(w,o.slMacro,5);
writeMacro(w,o.tlMacro,6);
writeMacro(w,o.dt2Macro,7);
writeMacro(w,o.rsMacro,8);
writeMacro(w,o.dtMacro,9);
writeMacro(w,o.d2rMacro,10);
writeMacro(w,o.ssgMacro,11);
writeMacro(w,o.damMacro,12);
writeMacro(w,o.dvbMacro,13);
writeMacro(w,o.egtMacro,14);
writeMacro(w,o.kslMacro,15);
writeMacro(w,o.susMacro,16);
writeMacro(w,o.vibMacro,17);
writeMacro(w,o.wsMacro,18);
writeMacro(w,o.ksrMacro,19);
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);
@ -719,6 +719,21 @@ void DivInstrument::writeFeatureX1(SafeWriter* w) {
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::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bool insName) {
size_t blockStartSeek=0;
size_t blockEndSeek=0;
@ -761,6 +776,7 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
bool featureSU=false;
bool featureES=false;
bool featureX1=false;
bool featureNE=false;
bool checkForWL=false;
@ -904,6 +920,9 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
featureFM=true;
break;
case DIV_INS_NES:
featureSM=true;
featureNE=true;
featureSL=true;
break;
case DIV_INS_MSM6258:
featureSM=true;
@ -993,6 +1012,7 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
}
if (amiga!=defaultIns.amiga) {
featureSM=true;
featureNE=true;
}
if (snes!=defaultIns.snes) {
featureSN=true;
@ -1157,6 +1177,9 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
if (featureX1) {
writeFeatureX1(w);
}
if (featureNE) {
writeFeatureNE(w);
}
if (fui && (featureSL || featureWL)) {
w->write("EN",2);
@ -2024,7 +2047,7 @@ void DivInstrument::readFeatureMA(SafeReader& reader, short version) {
target->mode=reader.readC();
unsigned char wordSize=reader.readC();
target->open=wordSize&7;
target->open=wordSize&15;
wordSize>>=6;
target->delay=reader.readC();
@ -2416,7 +2439,7 @@ void DivInstrument::readFeatureSL(SafeReader& reader, DivSong* song, short versi
}
}
reader.seek(lastSeek,SEEK_CUR);
reader.seek(lastSeek,SEEK_SET);
// re-map samples
if (amiga.initSample>=0 && amiga.initSample<256) {
@ -2475,7 +2498,7 @@ void DivInstrument::readFeatureWL(SafeReader& reader, DivSong* song, short versi
}
}
reader.seek(lastSeek,SEEK_CUR);
reader.seek(lastSeek,SEEK_SET);
// re-map wavetables
if (ws.enabled) {
@ -2541,6 +2564,21 @@ void DivInstrument::readFeatureX1(SafeReader& reader, short version) {
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;
}
DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song) {
unsigned char featCode[2];
@ -2606,6 +2644,8 @@ DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, b
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 (song==NULL && (memcmp(featCode,"SL",2)==0 || (memcmp(featCode,"WL",2)==0))) {
// nothing

View file

@ -89,6 +89,52 @@ enum DivInstrumentType: unsigned short {
DIV_INS_NULL
};
enum DivMacroType: unsigned char {
DIV_MACRO_VOL=0,
DIV_MACRO_ARP,
DIV_MACRO_DUTY,
DIV_MACRO_WAVE,
DIV_MACRO_PITCH,
DIV_MACRO_EX1,
DIV_MACRO_EX2,
DIV_MACRO_EX3,
DIV_MACRO_ALG,
DIV_MACRO_FB,
DIV_MACRO_FMS,
DIV_MACRO_AMS,
DIV_MACRO_PAN_LEFT,
DIV_MACRO_PAN_RIGHT,
DIV_MACRO_PHASE_RESET,
DIV_MACRO_EX4,
DIV_MACRO_EX5,
DIV_MACRO_EX6,
DIV_MACRO_EX7,
DIV_MACRO_EX8
};
enum DivMacroTypeOp: unsigned char {
DIV_MACRO_OP_AM=32,
DIV_MACRO_OP_AR,
DIV_MACRO_OP_DR,
DIV_MACRO_OP_MULT,
DIV_MACRO_OP_RR,
DIV_MACRO_OP_SL,
DIV_MACRO_OP_TL,
DIV_MACRO_OP_DT2,
DIV_MACRO_OP_RS,
DIV_MACRO_OP_DT,
DIV_MACRO_OP_D2R,
DIV_MACRO_OP_SSG,
DIV_MACRO_OP_DAM,
DIV_MACRO_OP_DVB,
DIV_MACRO_OP_EGT,
DIV_MACRO_OP_KSL,
DIV_MACRO_OP_SUS,
DIV_MACRO_OP_VIB,
DIV_MACRO_OP_WS,
DIV_MACRO_OP_KSR,
};
// FM operator structure:
// - OPN:
// - AM, AR, DR, MULT, RR, SL, TL, RS, DT, D2R, SSG-EG
@ -198,19 +244,20 @@ struct DivInstrumentFM {
// this is getting out of hand
struct DivInstrumentMacro {
String name;
int val[256];
unsigned int mode;
unsigned char open;
unsigned char len, delay, speed, loop, rel;
// 0-31: normal
// 32+: operator (top 3 bits select operator, starting from 1)
unsigned char macroType;
// the following variables are used by the GUI and not saved in the file
int vScroll, vZoom;
int typeMemory[16];
unsigned char lenMemory;
explicit DivInstrumentMacro(const String& n, bool initOpen=false):
name(n),
explicit DivInstrumentMacro(unsigned char initType, bool initOpen=false):
mode(0),
open(initOpen),
len(0),
@ -218,6 +265,7 @@ struct DivInstrumentMacro {
speed(1),
loop(255),
rel(255),
macroType(initType),
vScroll(0),
vZoom(-1),
lenMemory(0) {
@ -271,33 +319,57 @@ struct DivInstrumentSTD {
DivInstrumentMacro wsMacro;
DivInstrumentMacro ksrMacro;
OpMacro():
amMacro("am"), arMacro("ar"), drMacro("dr"), multMacro("mult"),
rrMacro("rr"), slMacro("sl"), tlMacro("tl",true), dt2Macro("dt2"),
rsMacro("rs"), dtMacro("dt"), d2rMacro("d2r"), ssgMacro("ssg"),
damMacro("dam"), dvbMacro("dvb"), egtMacro("egt"), kslMacro("ksl"),
susMacro("sus"), vibMacro("vib"), wsMacro("ws"), ksrMacro("ksr") {}
amMacro(DIV_MACRO_OP_AM), arMacro(DIV_MACRO_OP_AR), drMacro(DIV_MACRO_OP_DR), multMacro(DIV_MACRO_OP_MULT),
rrMacro(DIV_MACRO_OP_RR), slMacro(DIV_MACRO_OP_SL), tlMacro(DIV_MACRO_OP_TL,true), dt2Macro(DIV_MACRO_OP_DT2),
rsMacro(DIV_MACRO_OP_RS), dtMacro(DIV_MACRO_OP_DT), d2rMacro(DIV_MACRO_OP_D2R), ssgMacro(DIV_MACRO_OP_SSG),
damMacro(DIV_MACRO_OP_DAM), dvbMacro(DIV_MACRO_OP_DVB), egtMacro(DIV_MACRO_OP_EGT), kslMacro(DIV_MACRO_OP_KSL),
susMacro(DIV_MACRO_OP_SUS), vibMacro(DIV_MACRO_OP_VIB), wsMacro(DIV_MACRO_OP_WS), ksrMacro(DIV_MACRO_OP_KSR) {}
} opMacros[4];
DivInstrumentSTD():
volMacro("vol",true),
arpMacro("arp"),
dutyMacro("duty"),
waveMacro("wave"),
pitchMacro("pitch"),
ex1Macro("ex1"),
ex2Macro("ex2"),
ex3Macro("ex3"),
algMacro("alg"),
fbMacro("fb"),
fmsMacro("fms"),
amsMacro("ams"),
panLMacro("panL"),
panRMacro("panR"),
phaseResetMacro("phaseReset"),
ex4Macro("ex4"),
ex5Macro("ex5"),
ex6Macro("ex6"),
ex7Macro("ex7"),
ex8Macro("ex8") {}
volMacro(DIV_MACRO_VOL,true),
arpMacro(DIV_MACRO_ARP),
dutyMacro(DIV_MACRO_DUTY),
waveMacro(DIV_MACRO_WAVE),
pitchMacro(DIV_MACRO_PITCH),
ex1Macro(DIV_MACRO_EX1),
ex2Macro(DIV_MACRO_EX2),
ex3Macro(DIV_MACRO_EX3),
algMacro(DIV_MACRO_ALG),
fbMacro(DIV_MACRO_FB),
fmsMacro(DIV_MACRO_FMS),
amsMacro(DIV_MACRO_AMS),
panLMacro(DIV_MACRO_PAN_LEFT),
panRMacro(DIV_MACRO_PAN_RIGHT),
phaseResetMacro(DIV_MACRO_PHASE_RESET),
ex4Macro(DIV_MACRO_EX4),
ex5Macro(DIV_MACRO_EX5),
ex6Macro(DIV_MACRO_EX6),
ex7Macro(DIV_MACRO_EX7),
ex8Macro(DIV_MACRO_EX8) {
for (int i=0; i<4; i++) {
opMacros[i].amMacro.macroType=DIV_MACRO_OP_AM+(i<<5);
opMacros[i].arMacro.macroType=DIV_MACRO_OP_AR+(i<<5);
opMacros[i].drMacro.macroType=DIV_MACRO_OP_DR+(i<<5);
opMacros[i].multMacro.macroType=DIV_MACRO_OP_MULT+(i<<5);
opMacros[i].rrMacro.macroType=DIV_MACRO_OP_RR+(i<<5);
opMacros[i].slMacro.macroType=DIV_MACRO_OP_SL+(i<<5);
opMacros[i].tlMacro.macroType=DIV_MACRO_OP_TL+(i<<5);
opMacros[i].dt2Macro.macroType=DIV_MACRO_OP_DT2+(i<<5);
opMacros[i].rsMacro.macroType=DIV_MACRO_OP_RS+(i<<5);
opMacros[i].dtMacro.macroType=DIV_MACRO_OP_DT+(i<<5);
opMacros[i].d2rMacro.macroType=DIV_MACRO_OP_D2R+(i<<5);
opMacros[i].ssgMacro.macroType=DIV_MACRO_OP_SSG+(i<<5);
opMacros[i].damMacro.macroType=DIV_MACRO_OP_DAM+(i<<5);
opMacros[i].dvbMacro.macroType=DIV_MACRO_OP_DVB+(i<<5);
opMacros[i].egtMacro.macroType=DIV_MACRO_OP_EGT+(i<<5);
opMacros[i].kslMacro.macroType=DIV_MACRO_OP_KSL+(i<<5);
opMacros[i].susMacro.macroType=DIV_MACRO_OP_SUS+(i<<5);
opMacros[i].vibMacro.macroType=DIV_MACRO_OP_VIB+(i<<5);
opMacros[i].wsMacro.macroType=DIV_MACRO_OP_WS+(i<<5);
opMacros[i].ksrMacro.macroType=DIV_MACRO_OP_KSR+(i<<5);
}
}
};
struct DivInstrumentGB {
@ -380,9 +452,13 @@ struct DivInstrumentAmiga {
struct SampleMap {
int freq;
short map;
SampleMap(int f=0, short m=-1):
signed char dpcmFreq;
signed char dpcmDelta;
SampleMap(int f=0, short m=-1, signed char df=15, signed char dd=-1):
freq(f),
map(m) {}
map(m),
dpcmFreq(df),
dpcmDelta(dd) {}
};
short initSample;
bool useNoteMap;
@ -410,8 +486,8 @@ struct DivInstrumentAmiga {
}
/**
* get the sample frequency at specified note.
* @return the frequency, or -1 if not using note map.
* get the sample playback note at specified note.
* @return the note, or -1 if not using note map.
*/
inline int getFreq(int note) {
if (useNoteMap) {
@ -422,6 +498,32 @@ struct DivInstrumentAmiga {
return note;
}
/**
* get the DPCM pitch at specified note.
* @return the pitch, or -1 if not using note map.
*/
inline signed char getDPCMFreq(int note) {
if (useNoteMap) {
if (note<0) note=0;
if (note>119) note=119;
return noteMap[note].dpcmFreq;
}
return -1;
}
/**
* get the DPCM delta counter value at specified note.
* @return the delta counter value, or -1 if not using note map.
*/
inline signed char getDPCMDelta(int note) {
if (useNoteMap) {
if (note<0) note=0;
if (note>119) note=119;
return noteMap[note].dpcmDelta;
}
return -1;
}
DivInstrumentAmiga():
initSample(0),
useNoteMap(false),
@ -668,7 +770,7 @@ struct DivInstrument {
/**
* these are internal functions.
*/
void writeMacro(SafeWriter* w, const DivInstrumentMacro& m, unsigned char macroCode);
void writeMacro(SafeWriter* w, const DivInstrumentMacro& m);
void writeFeatureNA(SafeWriter* w);
void writeFeatureFM(SafeWriter* w, bool fui);
void writeFeatureMA(SafeWriter* w);
@ -687,6 +789,7 @@ struct DivInstrument {
void writeFeatureSU(SafeWriter* w);
void writeFeatureES(SafeWriter* w);
void writeFeatureX1(SafeWriter* w);
void writeFeatureNE(SafeWriter* w);
void readFeatureNA(SafeReader& reader, short version);
void readFeatureFM(SafeReader& reader, short version);
@ -706,6 +809,7 @@ struct DivInstrument {
void readFeatureSU(SafeReader& reader, short version);
void readFeatureES(SafeReader& reader, short version);
void readFeatureX1(SafeReader& reader, short version);
void readFeatureNE(SafeReader& reader, short version);
DivDataErrors readInsDataOld(SafeReader& reader, short version);
DivDataErrors readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song);

View file

@ -42,7 +42,8 @@ void DivMacroStruct::prepare(DivInstrumentMacro& source, DivEngine* e) {
has=had=actualHad=will=true;
mode=source.mode;
type=(source.open>>1)&3;
linger=(source.name=="vol" && e->song.volMacroLinger);
activeRelease=source.open&8;
linger=(source.macroType==DIV_MACRO_VOL && e->song.volMacroLinger);
lfoPos=LFO_PHASE;
}
@ -57,6 +58,10 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic
return;
}
if (released && type==1 && lastPos<3) delay=0;
if (released && type==0 && pos<source.rel && source.rel<source.len && activeRelease) {
delay=0;
pos=source.rel;
}
if (delay>0) {
delay--;
if (!linger) had=false;
@ -419,108 +424,58 @@ void DivMacroInt::notifyInsDeletion(DivInstrument* which) {
}
}
// randomly-generated
constexpr unsigned int hashTable[256]={
0x0718657, 0xe904eb33, 0x14b2da2b, 0x0ef67ca9,
0x0f0559a, 0x4142065a, 0x4d9ab4ba, 0x3cdd601a,
0x6635aca, 0x2c41ab72, 0xf98e8d31, 0x1003ee63,
0x3fd9fb5, 0x30734d16, 0xe8964431, 0x29bb9b79,
0x817f580, 0xfe083b9e, 0x974b5e85, 0x3b5729c2,
0x2afea96, 0xf1573b4b, 0x308a1024, 0xaa94b92d,
0x693fa93, 0x547ba3da, 0xac4f206c, 0x93f72ea9,
0xcc44001, 0x37e27670, 0xf35a63d0, 0xd1cdbb92,
0x7c7ee24, 0xfa267ee9, 0xf9cd9956, 0x6a6768d4,
0x9e6a108, 0xf6ca4bd0, 0xa53cba9f, 0x526a523a,
0xf46f0c8, 0xf131bd4c, 0x82800d48, 0xabff9214,
0x40eabd4, 0xea0ef8f7, 0xdc3968d6, 0x54c3cb63,
0x8855023, 0xaab73861, 0xff0bea2c, 0x139b9765,
0x4a21279, 0x6b2aa29a, 0xf147cc3f, 0xc42edc1a,
0xfe2f86f, 0x6d352047, 0xd3cac3e4, 0x35e5c389,
0xe923727, 0x12fe3b32, 0x204295c5, 0x254a8b7a,
0xc1d995d, 0x26a512d2, 0xa3e34033, 0x9a968df0,
0x53447ed, 0x36cf4077, 0x189b03a7, 0x558790e8,
0x01f921a, 0x840f260c, 0x93dd2b86, 0x12f69cb0,
0x117d93a, 0xcb2cbc2b, 0xd41e3aed, 0x5ff6ec75,
0x607290d, 0xd41adb92, 0x64f94ba7, 0xaff720f7,
0x6bf1d5d, 0xc8e36c6d, 0x7095bab5, 0xdfbf7b0d,
0x01ddeea, 0xe8f262da, 0xf589512f, 0xc2ecac5d,
0xbe29d98, 0xff8b5a2e, 0x18e7279e, 0x6ad24dcb,
0x2b3b9b1, 0x6f5227d8, 0x076d7553, 0x6c5856e2,
0x995f655, 0xe9fcf5a6, 0x83671b70, 0xaf3aed1e,
0xac340f0, 0x5c7008b4, 0x14651282, 0x8bf855b9,
0x4a933af, 0x829b87f1, 0x9a673070, 0xb19da64f,
0x77d8f36, 0x584c9fdc, 0xa9e52c0d, 0x6da5e13d,
0xae1051f, 0xe85e976f, 0xfeac2d9a, 0x19c46754,
0x1cba6f3, 0xaf21bc31, 0x16b6a8d4, 0xe08b0fdb,
0x97e6e54, 0x5da499ae, 0xab472e19, 0xc2491f2e,
0xc08c563, 0xe91b131b, 0xc8e22451, 0x6995c8fe,
0x7042718, 0x01043738, 0xc7d88b28, 0x2d9f330f,
0x4b3aae5, 0xf1e705ba, 0xc5b8ee59, 0xa8ba4e8f,
0x55f65a2, 0xa1899e41, 0x296243c8, 0x1e502bf2,
0x20080de, 0x841d2239, 0x37b082af, 0xbdd7f7da,
0x4075090, 0x1dc7dc49, 0x5cd3c69a, 0x7fb13b62,
0xb382bf1, 0xa0cfbc2f, 0x9eca4dc1, 0xb9355453,
0x5d0dd24, 0x834f4d8e, 0xe9b136b2, 0xe7b8738d,
0x1c91d41, 0x8cb3ddb5, 0xdc600590, 0x607cff55,
0x2ca7675, 0x4622a8e4, 0x9340e414, 0xcb44928a,
0xa9e791c, 0x68849920, 0xc5b5fcd8, 0xbc352269,
0x3ab13cf, 0xaa3cbbd0, 0x1abacc64, 0x623b5b49,
0xcc8c4c3, 0x3c8f2f70, 0x3e584a28, 0x9316d24d,
0xfe315a2, 0x10f0ba7a, 0xed15a523, 0x4f987369,
0x7aa4a4a, 0x90eaf98f, 0xcf0af610, 0x1b38f4e7,
0x19df72d, 0xd8306808, 0xd54e25ac, 0x76b79c6d,
0x58110cf, 0x06a3e5f2, 0x873a6039, 0xf52684e3,
0xecf39c3, 0x7cbb2759, 0xe280d361, 0x91e8471a,
0xa67cdd3, 0x17cac3be, 0xfc9eff1f, 0x71abdf49,
0x6168624, 0xb68f86f7, 0x67a8e72a, 0xe746911d,
0xca48fd7, 0x8f3cc436, 0x3a3851a8, 0x30a7e26e,
0xca49308, 0xb598ef74, 0x49ef167a, 0xa9e17632,
0x0f7308a, 0xf156efed, 0xcf799645, 0xbae4b85a,
0xecba3fe, 0xd97f861d, 0xc164af62, 0xb1aca42f,
0xf249576, 0x83d1bf4e, 0x2f486a9c, 0xd3b53cc2,
0x17d7c26, 0xd95ddae1, 0x76c1a2f5, 0xf8af6782,
0xdbaece4, 0x010b2b53, 0x049be200, 0xd9fd0d1a,
0x37d7e6c, 0x5b848651, 0x203c98c7, 0x669681b0,
0x683086f, 0xdd0ee8ab, 0x5dbe008b, 0xe5d0690d,
0x23dd758, 0x6b34acbc, 0x4b2b3e65, 0xcc7b56c1,
0x196b0a0, 0x7b065105, 0xb731b01a, 0xd37daa16,
0xf77816b, 0x3c9fa546, 0x81dfadb8, 0x39b1fb8b
};
#define CONSIDER(x,y) case (y&0x1f): return &x; break;
constexpr unsigned int NAME_HASH(const char* name) {
unsigned int nameHash=0xffffffff;
for (const char* i=name; *i; i++) {
nameHash=(nameHash>>8)^hashTable[(unsigned char)*i];
DivMacroStruct* DivMacroInt::structByType(unsigned char type) {
if (type>=0x20) {
unsigned char o=((type>>5)-1)&3;
switch (type&0x1f) {
CONSIDER(op[o].am,DIV_MACRO_OP_AM)
CONSIDER(op[o].ar,DIV_MACRO_OP_AR)
CONSIDER(op[o].dr,DIV_MACRO_OP_DR)
CONSIDER(op[o].mult,DIV_MACRO_OP_MULT)
CONSIDER(op[o].rr,DIV_MACRO_OP_RR)
CONSIDER(op[o].sl,DIV_MACRO_OP_SL)
CONSIDER(op[o].tl,DIV_MACRO_OP_TL)
CONSIDER(op[o].dt2,DIV_MACRO_OP_DT2)
CONSIDER(op[o].rs,DIV_MACRO_OP_RS)
CONSIDER(op[o].dt,DIV_MACRO_OP_DT)
CONSIDER(op[o].d2r,DIV_MACRO_OP_D2R)
CONSIDER(op[o].ssg,DIV_MACRO_OP_SSG)
CONSIDER(op[o].dam,DIV_MACRO_OP_DAM)
CONSIDER(op[o].dvb,DIV_MACRO_OP_DVB)
CONSIDER(op[o].egt,DIV_MACRO_OP_EGT)
CONSIDER(op[o].ksl,DIV_MACRO_OP_KSL)
CONSIDER(op[o].sus,DIV_MACRO_OP_SUS)
CONSIDER(op[o].vib,DIV_MACRO_OP_VIB)
CONSIDER(op[o].ws,DIV_MACRO_OP_WS)
CONSIDER(op[o].ksr,DIV_MACRO_OP_KSR)
}
return NULL;
}
return nameHash;
}
#define CONSIDER(x) case NAME_HASH(#x): return &x; break;
DivMacroStruct* DivMacroInt::structByName(const String& name) {
unsigned int hash=NAME_HASH(name.c_str());
switch (hash) {
CONSIDER(vol)
CONSIDER(arp)
CONSIDER(duty)
CONSIDER(wave)
CONSIDER(pitch)
CONSIDER(ex1)
CONSIDER(ex2)
CONSIDER(ex3)
CONSIDER(alg)
CONSIDER(fb)
CONSIDER(fms)
CONSIDER(ams)
CONSIDER(panL)
CONSIDER(panR)
CONSIDER(phaseReset)
CONSIDER(ex4)
CONSIDER(ex5)
CONSIDER(ex6)
CONSIDER(ex7)
CONSIDER(ex8)
switch (type) {
CONSIDER(vol,DIV_MACRO_VOL)
CONSIDER(arp,DIV_MACRO_ARP)
CONSIDER(duty,DIV_MACRO_DUTY)
CONSIDER(wave,DIV_MACRO_WAVE)
CONSIDER(pitch,DIV_MACRO_PITCH)
CONSIDER(ex1,DIV_MACRO_EX1)
CONSIDER(ex2,DIV_MACRO_EX2)
CONSIDER(ex3,DIV_MACRO_EX3)
CONSIDER(alg,DIV_MACRO_ALG)
CONSIDER(fb,DIV_MACRO_FB)
CONSIDER(fms,DIV_MACRO_FMS)
CONSIDER(ams,DIV_MACRO_AMS)
CONSIDER(panL,DIV_MACRO_PAN_LEFT)
CONSIDER(panR,DIV_MACRO_PAN_RIGHT)
CONSIDER(phaseReset,DIV_MACRO_PHASE_RESET)
CONSIDER(ex4,DIV_MACRO_EX4)
CONSIDER(ex5,DIV_MACRO_EX5)
CONSIDER(ex6,DIV_MACRO_EX6)
CONSIDER(ex7,DIV_MACRO_EX7)
CONSIDER(ex8,DIV_MACRO_EX8)
}
return NULL;

View file

@ -27,8 +27,9 @@ class DivEngine;
struct DivMacroStruct {
int pos, lastPos, lfoPos, delay;
int val;
bool has, had, actualHad, finished, will, linger, began, masked;
bool has, had, actualHad, finished, will, linger, began, masked, activeRelease;
unsigned int mode, type;
unsigned char macroType;
void doMacro(DivInstrumentMacro& source, bool released, bool tick);
void init() {
pos=lastPos=lfoPos=mode=type=delay=0;
@ -39,7 +40,7 @@ struct DivMacroStruct {
val=0;
}
void prepare(DivInstrumentMacro& source, DivEngine* e);
DivMacroStruct():
DivMacroStruct(unsigned char mType):
pos(0),
lastPos(0),
lfoPos(0),
@ -53,8 +54,10 @@ struct DivMacroStruct {
linger(false),
began(true),
masked(false),
activeRelease(false),
mode(0),
type(0) {}
type(0),
macroType(mType) {}
};
class DivMacroInt {
@ -81,26 +84,26 @@ class DivMacroInt {
DivMacroStruct dam, dvb, egt, ksl;
DivMacroStruct sus, vib, ws, ksr;
IntOp():
am(),
ar(),
dr(),
mult(),
rr(),
sl(),
tl(),
dt2(),
rs(),
dt(),
d2r(),
ssg(),
dam(),
dvb(),
egt(),
ksl(),
sus(),
vib(),
ws(),
ksr() {}
am(DIV_MACRO_OP_AM),
ar(DIV_MACRO_OP_AR),
dr(DIV_MACRO_OP_DR),
mult(DIV_MACRO_OP_MULT),
rr(DIV_MACRO_OP_RR),
sl(DIV_MACRO_OP_SL),
tl(DIV_MACRO_OP_TL),
dt2(DIV_MACRO_OP_DT2),
rs(DIV_MACRO_OP_RS),
dt(DIV_MACRO_OP_DT),
d2r(DIV_MACRO_OP_D2R),
ssg(DIV_MACRO_OP_SSG),
dam(DIV_MACRO_OP_DAM),
dvb(DIV_MACRO_OP_DVB),
egt(DIV_MACRO_OP_EGT),
ksl(DIV_MACRO_OP_KSL),
sus(DIV_MACRO_OP_SUS),
vib(DIV_MACRO_OP_VIB),
ws(DIV_MACRO_OP_WS),
ksr(DIV_MACRO_OP_KSR) {}
} op[4];
// state
@ -140,11 +143,11 @@ class DivMacroInt {
void notifyInsDeletion(DivInstrument* which);
/**
* get DivMacroStruct by macro name.
* @param which the macro name.
* get DivMacroStruct by macro type.
* @param which the macro type.
* @return a DivMacroStruct, or NULL if none found.
*/
DivMacroStruct* structByName(const String& name);
DivMacroStruct* structByType(unsigned char which);
DivMacroInt():
e(NULL),
@ -152,26 +155,26 @@ class DivMacroInt {
macroListLen(0),
subTick(1),
released(false),
vol(),
arp(),
duty(),
wave(),
pitch(),
ex1(),
ex2(),
ex3(),
alg(),
fb(),
fms(),
ams(),
panL(),
panR(),
phaseReset(),
ex4(),
ex5(),
ex6(),
ex7(),
ex8(),
vol(DIV_MACRO_VOL),
arp(DIV_MACRO_ARP),
duty(DIV_MACRO_DUTY),
wave(DIV_MACRO_WAVE),
pitch(DIV_MACRO_PITCH),
ex1(DIV_MACRO_EX1),
ex2(DIV_MACRO_EX2),
ex3(DIV_MACRO_EX3),
alg(DIV_MACRO_ALG),
fb(DIV_MACRO_FB),
fms(DIV_MACRO_FMS),
ams(DIV_MACRO_AMS),
panL(DIV_MACRO_PAN_LEFT),
panR(DIV_MACRO_PAN_RIGHT),
phaseReset(DIV_MACRO_PHASE_RESET),
ex4(DIV_MACRO_EX4),
ex5(DIV_MACRO_EX5),
ex6(DIV_MACRO_EX6),
ex7(DIV_MACRO_EX7),
ex8(DIV_MACRO_EX8),
hasRelease(false) {
memset(macroList,0,128*sizeof(void*));
memset(macroSource,0,128*sizeof(void*));

23
src/engine/pitchTable.cpp Normal file
View file

@ -0,0 +1,23 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 "dispatch.h"

View file

@ -37,6 +37,14 @@ unsigned short DivDispatch::getPan(int chan) {
return 0;
}
DivChannelPair DivDispatch::getPaired(int chan) {
return DivChannelPair();
}
DivChannelModeHints DivDispatch::getModeHints(int chan) {
return DivChannelModeHints();
}
DivMacroInt* DivDispatch::getChanMacroInt(int chan) {
return NULL;
}
@ -191,6 +199,9 @@ void DivDispatch::renderSamples(int sysID) {
}
void DivDispatch::notifyPitchTable() {
}
int DivDispatch::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
return 0;
}

View file

@ -271,6 +271,14 @@ void DivPlatformC140::tick(bool sysTick) {
}
}
} else {
switch (bankType) {
case 0:
bank=((bank&8)<<2)|(bank&7);
break;
case 1:
bank=((bank&0x18)<<1)|(bank&7);
break;
}
rWrite(0x04+(i<<4),bank);
}
rWrite(0x06+(i<<4),(start>>8)&0xff);
@ -548,7 +556,15 @@ const void* DivPlatformC140::getSampleMem(int index) {
}
size_t DivPlatformC140::getSampleMemCapacity(int index) {
return index == 0 ? (is219?524288:16777216) : 0;
if (index!=0) return 0;
if (is219) return 524288;
switch (bankType) {
case 0:
return 2097152;
case 1:
return 4194304;
}
return 16777216;
}
size_t DivPlatformC140::getSampleMemUsage(int index) {
@ -562,7 +578,7 @@ bool DivPlatformC140::isSampleLoaded(int index, int sample) {
}
void DivPlatformC140::renderSamples(int sysID) {
memset(sampleMem,0,getSampleMemCapacity());
memset(sampleMem,0,is219?524288:16777216);
memset(sampleOff,0,256*sizeof(unsigned int));
memset(sampleLoaded,0,256*sizeof(bool));
@ -701,6 +717,10 @@ void DivPlatformC140::setFlags(const DivConfig& flags) {
CHECK_CUSTOM_CLOCK;
rate=chipClock/192;
}
bankType=flags.getInt("bankType",0);
if (!is219) {
c140_bank_type(&c140,bankType);
}
for (int i=0; i<totalChans; i++) {
oscBuf[i]->rate=rate;
}
@ -710,12 +730,13 @@ int DivPlatformC140::init(DivEngine* p, int channels, int sugRate, const DivConf
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
bankType=2;
for (int i=0; i<totalChans; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
sampleMem=new unsigned char[getSampleMemCapacity()];
sampleMem=new unsigned char[is219?524288:16777216];
sampleMemLen=0;
if (is219) {
c219_init(&c219);

View file

@ -60,6 +60,7 @@ class DivPlatformC140: public DivDispatch {
bool is219;
int totalChans;
unsigned char groupBank[4];
unsigned char bankType;
unsigned char* sampleMem;
size_t sampleMemLen;

View file

@ -1057,7 +1057,11 @@ DivMacroInt* DivPlatformES5506::getChanMacroInt(int ch) {
}
unsigned short DivPlatformES5506::getPan(int ch) {
return ((chan[ch].lVol>>4)<<8)|(chan[ch].rVol>>4);
float expL=255.0f*pow(((float)(chan[ch].resLVol>>4))/4095.0f,4.0f);
float expR=255.0f*pow(((float)(chan[ch].resRVol>>4))/4095.0f,4.0f);
if (expL>255.0f) expL=255.0f;
if (expR>255.0f) expR=255.0f;
return (((unsigned int)expL)<<8)|((unsigned int)expR);
}
void DivPlatformES5506::reset() {

View file

@ -343,6 +343,10 @@ void DivPlatformNES::tick(bool sysTick) {
} else {
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
}
if (nextDPCMDelta>=0) {
rWrite(0x4011,nextDPCMDelta);
nextDPCMDelta=-1;
}
rWrite(0x4012,(dpcmAddr>>6)&0xff);
rWrite(0x4013,dpcmLen&0xff);
rWrite(0x4015,31);
@ -373,11 +377,38 @@ int DivPlatformNES::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON:
if (c.chan==4) { // PCM
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
if (ins->type==DIV_INS_AMIGA) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_NES);
if (ins->type==DIV_INS_AMIGA || (ins->type==DIV_INS_NES && !parent->song.oldDPCM)) {
if (ins->type==DIV_INS_NES) {
if (!dpcmMode) {
dpcmMode=true;
if (dumpWrites) addWrite(0xffff0002,0);
dacSample=-1;
rWrite(0x4015,15);
rWrite(0x4010,0);
rWrite(0x4012,0);
rWrite(0x4013,0);
rWrite(0x4015,31);
}
if (ins->amiga.useNoteMap) {
nextDPCMFreq=ins->amiga.getDPCMFreq(c.value);
if (nextDPCMFreq<0 || nextDPCMFreq>15) nextDPCMFreq=lastDPCMFreq;
lastDPCMFreq=nextDPCMFreq;
nextDPCMDelta=ins->amiga.getDPCMDelta(c.value);
} else {
if (c.value==DIV_NOTE_NULL) {
nextDPCMFreq=lastDPCMFreq;
} else {
nextDPCMFreq=c.value&15;
}
}
}
if (c.value!=DIV_NOTE_NULL) {
dacSample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
if (ins->type==DIV_INS_AMIGA) {
c.value=ins->amiga.getFreq(c.value);
}
}
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
dacSample=-1;
@ -452,7 +483,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_NES));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
@ -614,7 +645,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_NES));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
@ -700,6 +731,8 @@ void DivPlatformNES::reset() {
goingToLoop=false;
countMode=false;
nextDPCMFreq=-1;
nextDPCMDelta=-1;
lastDPCMFreq=15;
linearCount=255;
if (useNP) {

View file

@ -54,6 +54,8 @@ class DivPlatformNES: public DivDispatch {
unsigned char apuType;
unsigned char linearCount;
signed char nextDPCMFreq;
signed char nextDPCMDelta;
signed char lastDPCMFreq;
bool dpcmMode;
bool dpcmModeDefault;
bool dacAntiClickOn;

View file

@ -1582,6 +1582,15 @@ unsigned short DivPlatformOPL::getPan(int ch) {
return ((chan[ch].pan&1)<<8)|((chan[ch].pan&2)>>1);
}
DivChannelPair DivPlatformOPL::getPaired(int ch) {
if (oplType==3 && ch<12 && !(ch&1)) {
if (chan[ch].fourOp) {
return DivChannelPair("4OP",ch+1);
}
}
return DivChannelPair();
}
DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) {
if (oplType==759 || chipType==8950) {
if (ch>=totalChans+1) return NULL;

View file

@ -115,6 +115,7 @@ class DivPlatformOPL: public DivDispatch {
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
unsigned short getPan(int chan);
DivChannelPair getPaired(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -406,6 +406,24 @@ DivMacroInt* DivPlatformPOKEY::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivChannelPair DivPlatformPOKEY::getPaired(int ch) {
switch (ch) {
case 0:
if (audctl&4) return DivChannelPair("filter",2);
break;
case 1:
if (audctl&16) return DivChannelPair("16-bit",0);
break;
case 2:
if (audctl&8) return DivChannelPair("16-bit",3);
break;
case 3:
if (audctl&2) return DivChannelPair("filter",1);
break;
}
return DivChannelPair();
}
DivDispatchOscBuffer* DivPlatformPOKEY::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -65,6 +65,7 @@ class DivPlatformPOKEY: public DivDispatch {
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivChannelPair getPaired(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -472,7 +472,7 @@ int DivPlatformSNES::dispatch(DivCommand c) {
break;
case DIV_CMD_SNES_INVERT:
chan[c.chan].invertL=(c.value>>4);
chan[c.chan].invertR=c.chan&15;
chan[c.chan].invertR=c.value&15;
chan[c.chan].shallWriteVol=true;
break;
case DIV_CMD_SNES_GAIN_MODE:
@ -703,6 +703,13 @@ unsigned short DivPlatformSNES::getPan(int ch) {
return (chan[ch].panL<<8)|chan[ch].panR;
}
DivChannelPair DivPlatformSNES::getPaired(int ch) {
if (chan[ch].pitchMod) {
return DivChannelPair("mod",(ch-1)&7);
}
return DivChannelPair();
}
DivSamplePos DivPlatformSNES::getSamplePos(int ch) {
if (ch>=8) return DivSamplePos();
if (!chan[ch].active) return DivSamplePos();

View file

@ -101,6 +101,7 @@ class DivPlatformSNES: public DivDispatch {
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
unsigned short getPan(int chan);
DivChannelPair getPaired(int chan);
DivSamplePos getSamplePos(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();

View file

@ -333,6 +333,11 @@ void c219_reset(struct c219_t *c219)
}
}
// TILDEARROW
void c140_bank_type(struct c140_t *c140, unsigned char type) {
c140->bank_type = type;
}
void c140_write(struct c140_t *c140, const unsigned short addr, const unsigned char data)
{
// voice register
@ -345,7 +350,16 @@ void c140_write(struct c140_t *c140, const unsigned short addr, const unsigned c
case 0x1: voice->lvol = data; break;
case 0x2: voice->freq = (voice->freq & ~0xff00) | (unsigned int)(data << 8); break;
case 0x3: voice->freq = (voice->freq & ~0x00ff) | data; break;
case 0x4: voice->bank = data; break;
case 0x4: { // TILDEARROW
if (c140->bank_type == 0) {
voice->bank = ((data&0x20)>>2)|(data&7);
} else if (c140->bank_type == 1) {
voice->bank = ((data&0x30)>>1)|(data&7);
} else {
voice->bank = data;
}
break;
}
case 0x5:
voice->compressed = c140_bit(data, 3);
voice->loop = c140_bit(data, 4);

View file

@ -5,7 +5,7 @@
MODIFIED Namco C140/C219 sound emulator - MODIFIED VERSION
by cam900
MODIFICATION by tildearrow - adds muting function
MODIFICATION by tildearrow - adds muting function AND VGM banking
THIS IS NOT THE ORIGINAL VERSION - you can find the original one in
commit 72d04777c013988ed8cf6da27c62a9d784a59dff
@ -78,6 +78,7 @@ struct c140_t
signed int lout, rout;
signed short mulaw[256];
signed short *sample_mem;
unsigned char bank_type;
};
struct c219_t
@ -106,6 +107,8 @@ void c140_write(struct c140_t *c140, const unsigned short addr, const unsigned c
void c219_write(struct c219_t *c219, const unsigned short addr, const unsigned char data);
void c140_bank_type(struct c140_t *c140, unsigned char type);
void c140_init(struct c140_t *c140);
void c219_init(struct c219_t *c219);

View file

@ -447,6 +447,7 @@ void DivPlatformSoundUnit::forceIns() {
chWrite(i,0x03,chan[i].pan);
writeControl(i);
writeControlUpper(i);
chWrite(i,0x08,chan[i].duty);
}
}

View file

@ -591,8 +591,10 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].scheduledSlideReset=true;
}
dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF_ENV,i));
chan[i].releasing=true;
} else if (pat->data[whatRow][0]==102) { // env release
dispatchCmd(DivCommand(DIV_CMD_ENV_RELEASE,i));
chan[i].releasing=true;
} else if (!(pat->data[whatRow][0]==0 && pat->data[whatRow][1]==0)) {
chan[i].oldNote=chan[i].note;
chan[i].note=pat->data[whatRow][0]+((signed char)pat->data[whatRow][1])*12;
@ -1067,6 +1069,10 @@ void DivEngine::processRow(int i, bool afterDelay) {
}
} else if (!chan[i].noteOnInhibit) {
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note,chan[i].volume>>8));
chan[i].releasing=false;
if (song.resetArpPhaseOnNewNote) {
chan[i].arpStage=-1;
}
chan[i].goneThroughNote=true;
chan[i].wentThroughNote=true;
keyHit[i]=true;
@ -1366,6 +1372,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
//dispatchCmd(DivCommand(DIV_CMD_VOLUME,note.channel,(note.volume*(chan[note.channel].volMax>>8))/127));
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,note.channel,note.note));
keyHit[note.channel]=true;
chan[note.channel].releasing=false;
chan[note.channel].noteOnInhibit=true;
chan[note.channel].lastIns=note.ins;
} else {
@ -1604,7 +1611,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
}
if (totalTicks>=1000000) {
totalTicks-=1000000;
totalSeconds++;
if (totalSeconds<0x7fffffff) totalSeconds++;
cmdsPerSecond=totalCmds-lastCmds;
lastCmds=totalCmds;
}
@ -1856,7 +1863,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
output->midiIn->queue.pop();
}
// process audio
// process sample/wave preview
if ((sPreview.sample>=0 && sPreview.sample<(int)song.sample.size()) || (sPreview.wave>=0 && sPreview.wave<(int)song.wave.size())) {
unsigned int samp_bbOff=0;
unsigned int prevAvail=blip_samples_avail(samp_bb);
@ -2003,7 +2010,9 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
memset(samp_bbOut,0,size*sizeof(short));
}
if (playing && !halted) {
// process audio
bool mustPlay=playing && !halted;
if (mustPlay) {
// logic starts here
for (int i=0; i<song.systemLen; i++) {
// TODO: we may have a problem here
@ -2132,6 +2141,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
renderPool->wait();
}
// process metronome
if (metroBufLen<size || metroBuf==NULL) {
if (metroBuf!=NULL) delete[] metroBuf;
metroBuf=new float[size];
@ -2140,7 +2150,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
memset(metroBuf,0,metroBufLen*sizeof(float));
if (playing && !halted && metronome) {
if (mustPlay && metronome) {
for (size_t i=0; i<size; i++) {
if (metroTick[i]) {
if (metroTick[i]==2) {
@ -2222,6 +2232,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
// nothing/invalid
}
// dump to oscillator buffer
for (unsigned int i=0; i<size; i++) {
for (int j=0; j<outChans; j++) {
if (oscBuf[j]==NULL) continue;
@ -2231,6 +2242,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
}
oscSize=size;
// force mono audio (if enabled)
if (forceMono && outChans>1) {
for (size_t i=0; i<size; i++) {
float chanSum=out[0][i];
@ -2243,6 +2255,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
}
}
}
// clamp output (if enabled)
if (clampSamples) {
for (size_t i=0; i<size; i++) {
for (int j=0; j<outChans; j++) {

View file

@ -377,6 +377,8 @@ struct DivSong {
bool brokenPortaLegato;
bool brokenFMOff;
bool preNoteNoEffect;
bool oldDPCM;
bool resetArpPhaseOnNewNote;
std::vector<DivInstrument*> ins;
std::vector<DivWavetable*> wave;
@ -496,7 +498,9 @@ struct DivSong {
patchbayAuto(true),
brokenPortaLegato(false),
brokenFMOff(false),
preNoteNoEffect(false) {
preNoteNoEffect(false),
oldDPCM(false),
resetArpPhaseOnNewNote(false) {
for (int i=0; i<DIV_MAX_CHIPS; i++) {
system[i]=DIV_SYSTEM_NULL;
systemVol[i]=1.0;

View file

@ -674,7 +674,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_PCE]=new DivSysDef(
"PC Engine/TurboGrafx-16", NULL, 0x05, 0x05, 6, false, true, 0x161, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
"an '80's game console with a wavetable sound chip, popular in Japan.",
"an '80s game console with a wavetable sound chip, popular in Japan.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6"},
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6"},
{DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE},
@ -691,12 +691,12 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_NES]=new DivSysDef(
"NES (Ricoh 2A03)", NULL, 0x06, 0x06, 5, false, true, 0x161, false, (1U<<DIV_SAMPLE_DEPTH_1BIT_DPCM)|(1U<<DIV_SAMPLE_DEPTH_8BIT),
"also known as Famicom in Japan, it's the most well-known game console of the '80's.",
"also known as Famicom in Japan, it's the most well-known game console of the '80s.",
{"Pulse 1", "Pulse 2", "Triangle", "Noise", "DPCM"},
{"S1", "S2", "TR", "NO", "DMC"},
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE, DIV_CH_PCM},
{DIV_INS_NES, DIV_INS_NES, DIV_INS_NES, DIV_INS_NES, DIV_INS_AMIGA},
{},
{DIV_INS_NES, DIV_INS_NES, DIV_INS_NES, DIV_INS_NES, DIV_INS_NES},
{DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA},
{
{0x11, {DIV_CMD_NES_DMC, "11xx: Write to delta modulation counter (0 to 7F)"}},
{0x12, {DIV_CMD_STD_NOISE_MODE, "12xx: Set duty cycle/noise mode (pulse: 0 to 3; noise: 0 or 1)"}},
@ -792,7 +792,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_AMIGA]=new DivSysDef(
"Amiga", NULL, 0x81, 0, 4, false, true, 0, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
"a computer from the '80's with full sampling capabilities, giving it a sound ahead of its time.",
"a computer from the '80s with full sampling capabilities, giving it a sound ahead of its time.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4"},
{"CH1", "CH2", "CH3", "CH4"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
@ -1617,7 +1617,7 @@ void DivEngine::registerSystems() {
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM},
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL_DRUMS, DIV_INS_OPL_DRUMS, DIV_INS_OPL_DRUMS, DIV_INS_OPL_DRUMS, DIV_INS_ADPCMB},
{DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA},
fmEffectHandlerMap,
fmOPLDrumsEffectHandlerMap,
fmOPLPostEffectHandlerMap
);

View file

@ -1834,6 +1834,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
CHIP_VOL(40,1.0);
willExport[i]=true;
writeC140[0]=disCont[i].dispatch;
c140Type=(song.systemFlags[i].getInt("bankType",0)==1)?1:0;
} else if (!(hasC140&0x40000000)) {
isSecond[i]=true;
CHIP_VOL_SECOND(40,1.0);

View file

@ -89,6 +89,7 @@ void DivEngine::runExportThread() {
if (lastLoopPos>-1 && i>=lastLoopPos && totalLoops>=exportLoopCount) {
logD("start fading out...");
isFadingOut=true;
if (fadeOutSamples==0) break;
}
}
}
@ -197,6 +198,7 @@ void DivEngine::runExportThread() {
if (lastLoopPos>-1 && j>=lastLoopPos && totalLoops>=exportLoopCount) {
logD("start fading out...");
isFadingOut=true;
if (fadeOutSamples==0) break;
}
}
}
@ -239,7 +241,6 @@ void DivEngine::runExportThread() {
outBuf[0]=new float[EXPORT_BUFSIZE];
outBuf[1]=new float[EXPORT_BUFSIZE];
outBuf[2]=new float[EXPORT_BUFSIZE*2];
int loopCount=remainingLoops;
logI("rendering to files...");
@ -281,11 +282,7 @@ void DivEngine::runExportThread() {
lastLoopPos=-1;
totalLoops=0;
isFadingOut=false;
if (exportFadeOut<=0.01) {
remainingLoops=loopCount;
} else {
remainingLoops=-1;
}
remainingLoops=-1;
playSub(false);
while (playing) {
@ -311,6 +308,7 @@ void DivEngine::runExportThread() {
if (lastLoopPos>-1 && j>=lastLoopPos && totalLoops>=exportLoopCount) {
logD("start fading out...");
isFadingOut=true;
if (fadeOutSamples==0) break;
}
}
}
@ -399,11 +397,7 @@ bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode,
stop();
repeatPattern=false;
setOrder(0);
if (exportFadeOut<=0.01) {
remainingLoops=loops;
} else {
remainingLoops=-1;
}
remainingLoops=-1;
if (shallSwitchCores()) {
bool isMutedBefore[DIV_MAX_CHANS];