/** * 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. */ #ifndef _ENGINE_H #define _ENGINE_H #include "config.h" #include "instrument.h" #include "song.h" #include "dispatch.h" #include "effect.h" #include "export.h" #include "dataErrors.h" #include "safeWriter.h" #include "cmdStream.h" #include "../audio/taAudio.h" #include "blip_buf.h" #include #include #include #include "../fixedQueue.h" class DivWorkPool; #define addWarning(x) \ if (warnings.empty()) { \ warnings+=x; \ } else { \ warnings+=(String("\n")+x); \ } #define BUSY_BEGIN softLocked=false; isBusy.lock(); #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; #define EXTERN_BUSY_BEGIN e->softLocked=false; e->isBusy.lock(); #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_VERSION "dev189" #define DIV_ENGINE_VERSION 189 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 #define DIV_VERSION_S3M 0xff03 #define DIV_VERSION_FTM 0xff04 enum DivStatusView { DIV_STATUS_NOTHING=0, DIV_STATUS_PATTERN, DIV_STATUS_COMMANDS }; enum DivAudioEngines { DIV_AUDIO_JACK=0, DIV_AUDIO_SDL=1, DIV_AUDIO_PORTAUDIO=2, DIV_AUDIO_NULL=126, DIV_AUDIO_DUMMY=127 }; enum DivAudioExportModes { DIV_EXPORT_MODE_ONE=0, DIV_EXPORT_MODE_MANY_SYS, DIV_EXPORT_MODE_MANY_CHAN }; enum DivHaltPositions { DIV_HALT_NONE=0, DIV_HALT_TICK, DIV_HALT_ROW, DIV_HALT_PATTERN, DIV_HALT_BREAKPOINT }; enum DivMIDIModes { DIV_MIDI_MODE_OFF=0, DIV_MIDI_MODE_NOTE, DIV_MIDI_MODE_LIGHT_SHOW }; struct DivChannelState { std::vector delayed; int note, oldNote, lastIns, pitch, portaSpeed, portaNote; int volume, volSpeed, cut, rowDelay, volMax; int delayOrder, delayRow, retrigSpeed, retrigTick; 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, releasing; bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp; bool wentThroughNote, goneThroughNote; int midiNote, curMidiNote, midiPitch; size_t midiAge; bool midiAftertouch; DivChannelState(): note(-1), oldNote(-1), lastIns(-1), pitch(0), portaSpeed(-1), portaNote(-1), volume(0x7f00), volSpeed(0), cut(-1), rowDelay(0), volMax(0), delayOrder(0), delayRow(0), retrigSpeed(0), retrigTick(0), vibratoDepth(0), vibratoRate(0), vibratoPos(0), vibratoPosGiant(0), vibratoDir(0), vibratoFine(15), tremoloDepth(0), tremoloRate(0), tremoloPos(0), arp(0), arpStage(-1), arpTicks(1), panL(255), panR(255), panRL(0), panRR(0), lastVibrato(0), lastPorta(0), doNote(false), legato(false), portaStop(false), keyOn(false), keyOff(false), nowYouCanStop(true), stopOnOff(false), releasing(false), arpYield(false), delayLocked(false), inPorta(false), scheduledSlideReset(false), shorthandPorta(false), wasShorthandPorta(false), noteOnInhibit(false), resetArp(false), wentThroughNote(false), goneThroughNote(false), midiNote(-1), curMidiNote(-1), midiPitch(-1), midiAge(0), midiAftertouch(false) {} }; struct DivNoteEvent { signed char channel; unsigned char ins; signed char note, volume; bool on, nop, pad1, pad2; DivNoteEvent(int c, int i, int n, int v, bool o): channel(c), ins(i), note(n), volume(v), on(o), nop(false), pad1(false), pad2(false) {} DivNoteEvent(): channel(-1), ins(0), note(0), volume(-1), on(false), nop(true), pad1(false), pad2(false) {} }; struct DivDispatchContainer { DivDispatch* dispatch; blip_buffer_t* bb[DIV_MAX_OUTPUTS]; size_t bbInLen, runtotal, runLeft, runPos, lastAvail; int temp[DIV_MAX_OUTPUTS], prevSample[DIV_MAX_OUTPUTS]; short* bbInMapped[DIV_MAX_OUTPUTS]; short* bbIn[DIV_MAX_OUTPUTS]; short* bbOut[DIV_MAX_OUTPUTS]; bool lowQuality, dcOffCompensation, hiPass; double rateMemory; // used in multi-thread int cycles; unsigned int size; void setRates(double gotRate); void setQuality(bool lowQual, bool dcHiPass); void grow(size_t size); void acquire(size_t offset, size_t count); void flush(size_t count); void fillBuf(size_t runtotal, size_t offset, size_t size); void clear(); void init(DivSystem sys, DivEngine* eng, int chanCount, double gotRate, const DivConfig& flags, bool isRender=false); void quit(); DivDispatchContainer(): dispatch(NULL), bbInLen(0), runtotal(0), runLeft(0), runPos(0), lastAvail(0), lowQuality(false), dcOffCompensation(false), hiPass(true), rateMemory(0.0), cycles(0), size(0) { memset(bb,0,DIV_MAX_OUTPUTS*sizeof(blip_buffer_t*)); memset(temp,0,DIV_MAX_OUTPUTS*sizeof(int)); memset(prevSample,0,DIV_MAX_OUTPUTS*sizeof(int)); memset(bbIn,0,DIV_MAX_OUTPUTS*sizeof(short*)); memset(bbInMapped,0,DIV_MAX_OUTPUTS*sizeof(short*)); memset(bbOut,0,DIV_MAX_OUTPUTS*sizeof(short*)); } }; struct DivEffectContainer { DivEffect* effect; float* in[DIV_MAX_OUTPUTS]; float* out[DIV_MAX_OUTPUTS]; size_t inLen, outLen; void preAcquire(size_t count); void acquire(size_t count); bool init(DivEffectType effectType, DivEngine* eng, double rate, unsigned short version, const unsigned char* data, size_t len); void quit(); DivEffectContainer(): effect(NULL), inLen(0), outLen(0) { memset(in,0,DIV_MAX_OUTPUTS*sizeof(float*)); memset(out,0,DIV_MAX_OUTPUTS*sizeof(float*)); } }; typedef int EffectValConversion(unsigned char,unsigned char); struct EffectHandler { DivDispatchCmds dispatchCmd; const char* description; EffectValConversion* val; EffectValConversion* val2; EffectHandler( DivDispatchCmds dispatchCmd_, const char* description_, EffectValConversion val_=NULL, EffectValConversion val2_=NULL ): dispatchCmd(dispatchCmd_), description(description_), val(val_), val2(val2_) {} }; struct DivDoNotHandleEffect { }; typedef std::unordered_map EffectHandlerMap; struct DivSysDef { const char* name; const char* nameJ; const char* description; unsigned char id; unsigned char id_DMF; int channels; bool isFM, isSTD, isCompound; // width 0: variable // height 0: no wavetable support unsigned short waveWidth, waveHeight; unsigned int vgmVersion; unsigned int sampleFormatMask; const char* chanNames[DIV_MAX_CHANS]; const char* chanShortNames[DIV_MAX_CHANS]; int chanTypes[DIV_MAX_CHANS]; // 0: primary // 1: alternate (usually PCM) DivInstrumentType chanInsType[DIV_MAX_CHANS][2]; const EffectHandlerMap effectHandlers; const EffectHandlerMap postEffectHandlers; const EffectHandlerMap preEffectHandlers; DivSysDef( const char* sysName, const char* sysNameJ, unsigned char fileID, unsigned char fileID_DMF, int chans, bool isFMChip, bool isSTDChip, unsigned int vgmVer, bool compound, unsigned int formatMask, unsigned short waveWid, unsigned short waveHei, const char* desc, std::initializer_list chNames, std::initializer_list chShortNames, std::initializer_list chTypes, std::initializer_list chInsType1, std::initializer_list chInsType2={}, const EffectHandlerMap fxHandlers_={}, const EffectHandlerMap postFxHandlers_={}, const EffectHandlerMap preFxHandlers_={}): name(sysName), nameJ(sysNameJ), description(desc), id(fileID), id_DMF(fileID_DMF), channels(chans), isFM(isFMChip), isSTD(isSTDChip), isCompound(compound), waveWidth(waveWid), waveHeight(waveHei), vgmVersion(vgmVer), sampleFormatMask(formatMask), effectHandlers(fxHandlers_), postEffectHandlers(postFxHandlers_), preEffectHandlers(preFxHandlers_) { memset(chanNames,0,DIV_MAX_CHANS*sizeof(void*)); memset(chanShortNames,0,DIV_MAX_CHANS*sizeof(void*)); memset(chanTypes,0,DIV_MAX_CHANS*sizeof(int)); for (int i=0; i=DIV_MAX_CHANS) break; } index=0; for (const char* i: chShortNames) { chanShortNames[index++]=i; if (index>=DIV_MAX_CHANS) break; } index=0; for (int i: chTypes) { chanTypes[index++]=i; if (index>=DIV_MAX_CHANS) break; } index=0; for (DivInstrumentType i: chInsType1) { chanInsType[index++][0]=i; if (index>=DIV_MAX_CHANS) break; } index=0; for (DivInstrumentType i: chInsType2) { chanInsType[index++][1]=i; if (index>=DIV_MAX_CHANS) break; } } }; enum DivChanTypes { DIV_CH_FM=0, DIV_CH_PULSE=1, DIV_CH_NOISE=2, DIV_CH_WAVE=3, DIV_CH_PCM=4, DIV_CH_OP=5 }; extern const char* cmdName[]; class DivEngine { DivDispatchContainer disCont[DIV_MAX_CHIPS]; TAAudio* output; TAAudioDesc want, got; String exportPath; std::thread* exportThread; int chans; bool active; bool lowQuality; bool dcHiPass; bool playing; bool freelance; bool shallStop, shallStopSched; bool endOfSong; bool consoleMode; bool extValuePresent; bool repeatPattern; bool metronome; bool exporting; bool stopExport; bool halted; bool forceMono; bool clampSamples; bool cmdStreamEnabled; bool softLocked; bool firstTick; bool skipping; bool midiIsDirect; bool lowLatency; bool systemsRegistered; bool hasLoadedSomething; bool midiOutClock; bool midiOutTime; bool midiOutProgramChange; int midiOutMode; int midiOutTimeRate; int softLockCount; int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats, curSpeed; size_t curSubSongIndex; size_t bufferPos; double divider; int cycles; double clockDrift; int midiClockCycles; double midiClockDrift; int midiTimeCycles; double midiTimeDrift; int stepPlay; int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, curMidiClock, curMidiTime, totalCmds, lastCmds, cmdsPerSecond, globalPitch; int curMidiTimePiece, curMidiTimeCode; unsigned char extValue, pendingMetroTick; DivGroovePattern speeds; short tempoAccum; DivStatusView view; DivHaltPositions haltOn; DivChannelState chan[DIV_MAX_CHANS]; DivAudioEngines audioEngine; DivAudioExportModes exportMode; double exportFadeOut; DivConfig conf; FixedQueue pendingNotes; // bitfield unsigned char walked[8192]; bool isMuted[DIV_MAX_CHANS]; std::mutex isBusy, saveLock, playPosLock; String configPath; String configFile; String lastError; String warnings; std::vector audioDevs; std::vector midiIns; std::vector midiOuts; std::vector cmdStream; std::vector possibleInsTypes; std::vector effectInst; static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS]; static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS]; static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS]; DivCSPlayer* cmdStreamInt; struct SamplePreview { double rate; int sample; int wave; int pos; int pBegin, pEnd; int rateMul, posSub; bool dir; SamplePreview(): rate(0.0), sample(-1), wave(-1), pos(0), pBegin(-1), pEnd(-1), rateMul(1), posSub(0), dir(false) {} } sPreview; short vibTable[64]; short tremTable[128]; int reversePitchTable[4096]; int pitchTable[4096]; short effectSlotMap[4096]; int midiBaseChan; bool midiPoly; size_t midiAgeCounter; blip_buffer_t* samp_bb; size_t samp_bbInLen; int samp_temp, samp_prevSample; short* samp_bbIn; short* samp_bbOut; unsigned char* metroTick; size_t metroTickLen; float* metroBuf; size_t metroBufLen; float metroFreq, metroPos; float metroAmp; float metroVol; float previewVol; size_t totalProcessed; unsigned int renderPoolThreads; DivWorkPool* renderPool; // MIDI stuff std::function midiCallback=[](const TAMidiMessage&) -> int {return -2;}; void processRowPre(int i); void processRow(int i, bool afterDelay); void nextOrder(); void nextRow(); void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream); // returns true if end of song. bool nextTick(bool noAccum=false, bool inhibitLowLat=false); bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal); bool perSystemPostEffect(int ch, unsigned char effect, unsigned char effectVal); bool perSystemPreEffect(int ch, unsigned char effect, unsigned char effectVal); void recalcChans(); void reset(); void playSub(bool preserveDrift, int goalRow=0); void runMidiClock(int totalCycles=1); void runMidiTime(int totalCycles=1); bool shallSwitchCores(); void testFunction(); bool loadDMF(unsigned char* file, size_t len); bool loadFur(unsigned char* file, size_t len); bool loadMod(unsigned char* file, size_t len); bool loadS3M(unsigned char* file, size_t len); bool loadFTM(unsigned char* file, size_t len); bool loadFC(unsigned char* file, size_t len); void loadDMP(SafeReader& reader, std::vector& ret, String& stripPath); void loadTFI(SafeReader& reader, std::vector& ret, String& stripPath); void loadVGI(SafeReader& reader, std::vector& ret, String& stripPath); void loadS3I(SafeReader& reader, std::vector& ret, String& stripPath); void loadSBI(SafeReader& reader, std::vector& ret, String& stripPath); void loadOPLI(SafeReader& reader, std::vector& ret, String& stripPath); void loadOPNI(SafeReader& reader, std::vector& ret, String& stripPath); void loadY12(SafeReader& reader, std::vector& ret, String& stripPath); void loadBNK(SafeReader& reader, std::vector& ret, String& stripPath); void loadGYB(SafeReader& reader, std::vector& ret, String& stripPath); void loadOPM(SafeReader& reader, std::vector& ret, String& stripPath); void loadFF(SafeReader& reader, std::vector& ret, String& stripPath); void loadWOPL(SafeReader& reader, std::vector& ret, String& stripPath); void loadWOPN(SafeReader& reader, std::vector& ret, String& stripPath); int loadSampleROM(String path, ssize_t expectedSize, unsigned char*& ret); bool initAudioBackend(); bool deinitAudioBackend(bool dueToSwitchMaster=false); void registerSystems(); void initSongWithDesc(const char* description, bool inBase64=true, bool oldVol=false); void exchangeIns(int one, int two); void exchangeWave(int one, int two); void exchangeSample(int one, int two); void swapChannels(int src, int dest); void stompChannel(int ch); // recalculate patchbay (UNSAFE) void recalcPatchbay(); // change song (UNSAFE) void changeSong(size_t songIndex); // move an asset void moveAsset(std::vector& dir, int before, int after); // remove an asset void removeAsset(std::vector& dir, int entry); // read/write asset dir void putAssetDirData(SafeWriter* w, std::vector& dir); DivDataErrors readAssetDirData(SafeReader& reader, std::vector& dir); // add every export method here friend class DivROMExport; friend class DivExportAmigaValidation; public: DivSong song; DivOrders* curOrders; DivChannelData* curPat; DivSubSong* curSubSong; DivInstrument* tempIns; 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; int oscReadPos, oscWritePos; int tickMult; int lastNBIns, lastNBOuts, lastNBSize; std::atomic processTime; void runExportThread(); void nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size); DivInstrument* getIns(int index, DivInstrumentType fallbackType=DIV_INS_FM); DivWavetable* getWave(int index); DivSample* getSample(int index); DivDispatch* getDispatch(int index); // parse old system setup description String decodeSysDesc(String desc); // start fresh void createNew(const char* description, String sysName, bool inBase64=true); void createNewFromDefaults(); // load a file. bool load(unsigned char* f, size_t length); // play a binary command stream. bool playStream(unsigned char* f, size_t length); // save as .dmf. SafeWriter* saveDMF(unsigned char version); // save as .fur. // if notPrimary is true then the song will not be altered SafeWriter* saveFur(bool notPrimary=false, bool newPatternFormat=true); // build a ROM file (TODO). // specify system to build ROM for. std::vector buildROM(DivROMExportOptions sys); // dump to VGM. // set trailingTicks to: // - 0 to add one tick of trailing // - x to add x+1 ticks of trailing // - -1 to auto-determine trailing // - -2 to add a whole loop of trailing SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false, int trailingTicks=-1); // dump to ZSM. SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true, bool optimize=true); // dump command stream. SafeWriter* saveCommand(bool binary=false); // export to text SafeWriter* saveText(bool separatePatterns=true); // export to an audio file bool saveAudio(const char* path, int loops, DivAudioExportModes mode, double fadeOutTime=0.0); // wait for audio export to finish void waitAudioFile(); // stop audio file export bool haltAudioFile(); // return back to playback cores if necessary void finishAudioFile(); // notify instrument parameter change void notifyInsChange(int ins); // notify wavetable change void notifyWaveChange(int wave); // dispatch a command int dispatchCmd(DivCommand c); // get system IDs static DivSystem systemFromFileFur(unsigned char val); static unsigned char systemToFileFur(DivSystem val); static DivSystem systemFromFileDMF(unsigned char val); static unsigned char systemToFileDMF(DivSystem val); // convert old flags static void convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivSystem sys); // check whether an asset directory is complete (UNSAFE) void checkAssetDir(std::vector& dir, size_t entries); // benchmark (returns time in seconds) double benchmarkPlayback(); double benchmarkSeek(); // returns the minimum VGM version which may carry the specified system, or 0 if none. int minVGMVersion(DivSystem which); // determine and setup config dir void initConfDir(); // save config bool saveConf(); // load config bool loadConf(); // get a config value bool getConfBool(String key, bool fallback); int getConfInt(String key, int fallback); float getConfFloat(String key, float fallback); double getConfDouble(String key, double fallback); String getConfString(String key, String fallback); // set a config value void setConf(String key, bool value); void setConf(String key, int value); void setConf(String key, float value); void setConf(String key, double value); void setConf(String key, const char* value); void setConf(String key, String value); // get whether config value exists bool hasConf(String key); // calculate base frequency/period double calcBaseFreq(double clock, double divider, int note, bool period); // calculate base frequency in f-num/block format int calcBaseFreqFNumBlock(double clock, double divider, int note, int bits); // calculate frequency/period int calcFreq(int base, int pitch, int arp, bool arpFixed, bool period=false, int octave=0, int pitch2=0, double clock=1.0, double divider=1.0, int blockBits=0); // calculate arpeggio int calcArp(int note, int arp, int offset=0); // convert panning formats int convertPanSplitToLinear(unsigned int val, unsigned char bits, int range); int convertPanSplitToLinearLR(unsigned char left, unsigned char right, int range); unsigned int convertPanLinearToSplit(int val, unsigned char bits, int range); // find song loop position void walkSong(int& loopOrder, int& loopRow, int& loopEnd); // play (returns whether successful) bool play(); // play to row (returns whether successful) bool playToRow(int row); // play by one row void stepOne(int row); // stop void stop(); // reset playback state void syncReset(); // sample preview query bool isPreviewingSample(); int getSamplePreviewSample(); int getSamplePreviewPos(); double getSamplePreviewRate(); // set sample preview volume (1.0 = 100%) void setSamplePreviewVol(float vol); // trigger sample preview void previewSample(int sample, int note=-1, int pStart=-1, int pEnd=-1); void stopSamplePreview(); // trigger wave preview void previewWave(int wave, int note); void stopWavePreview(); // trigger sample preview void previewSampleNoLock(int sample, int note=-1, int pStart=-1, int pEnd=-1); void stopSamplePreviewNoLock(); // trigger wave preview void previewWaveNoLock(int wave, int note); void stopWavePreviewNoLock(); // get config path String getConfigPath(); // get sys channel count int getChannelCount(DivSystem sys); // get channel count int getTotalChannelCount(); // get instrument types available for use std::vector& getPossibleInsTypes(); // get effect description const char* getEffectDesc(unsigned char effect, int chan, bool notNull=false); // get channel type // - 0: FM // - 1: pulse // - 2: noise // - 3: wave/other // - 4: PCM // - 5: FM operator int getChannelType(int ch); // get preferred instrument type DivInstrumentType getPreferInsType(int ch); // get alternate instrument type DivInstrumentType getPreferInsSecondType(int ch); // get song system name String getSongSystemLegacyName(DivSong& ds, bool isMultiSystemAcceptable=true); // get sys name const char* getSystemName(DivSystem sys); // get japanese system name const char* getSystemNameJ(DivSystem sys); // get sys definition const DivSysDef* getSystemDef(DivSystem sys); // convert sample rate format int fileToDivRate(int frate); int divToFileRate(int drate); // get effective sample rate int getEffectiveSampleRate(int rate); // is FM system bool isFMSystem(DivSystem sys); // is STD system bool isSTDSystem(DivSystem sys); // is channel muted bool isChannelMuted(int chan); // toggle mute void toggleMute(int chan); // toggle solo void toggleSolo(int chan); // set mute status void muteChannel(int chan, bool mute); // unmute all void unmuteAll(); // get channel name const char* getChannelName(int chan); // get channel short name const char* getChannelShortName(int chan); // get channel max volume int getMaxVolumeChan(int chan); // get current order unsigned char getOrder(); // get current row int getRow(); // synchronous get order/row void getPlayPos(int& order, int& row); // get beat/bar int getElapsedBars(); int getElapsedBeats(); // get current subsong size_t getCurrentSubSong(); // get speeds const DivGroovePattern& getSpeeds(); // get Hz float getHz(); // get current Hz float getCurHz(); // get time int getTotalTicks(); // 1/1000000th of a second int getTotalSeconds(); // get repeat pattern bool getRepeatPattern(); // set repeat pattern void setRepeatPattern(bool value); // has ext value bool hasExtValue(); // get ext value unsigned char getExtValue(); // dump song info to stdout void dumpSongInfo(); // is playing bool isPlaying(); // is running bool isRunning(); // is stepping bool isStepping(); // is exporting bool isExporting(); // add instrument int addInstrument(int refChan=0, DivInstrumentType fallbackType=DIV_INS_STD); // add instrument from pointer int addInstrumentPtr(DivInstrument* which); // get instrument from file // if the returned vector is empty then there was an error. std::vector instrumentFromFile(const char* path, bool loadAssets=true, bool readInsName=true); // load temporary instrument void loadTempIns(DivInstrument* which); // delete instrument void delInstrument(int index); void delInstrumentUnsafe(int index); // add wavetable int addWave(); // add wavetable from pointer int addWavePtr(DivWavetable* which); // get wavetable from file DivWavetable* waveFromFile(const char* path, bool loadRaw=true); // delete wavetable void delWave(int index); void delWaveUnsafe(int index); // add sample int addSample(); // add sample from pointer int addSamplePtr(DivSample* which); // get sample from file DivSample* sampleFromFile(const char* path); // get raw sample DivSample* sampleFromFileRaw(const char* path, DivSampleDepth depth, int channels, bool bigEndian, bool unsign, bool swapNibbles); // delete sample void delSample(int index); void delSampleUnsafe(int index, bool render=true); // add order void addOrder(int pos, bool duplicate, bool where); // deep clone orders void deepCloneOrder(int pos, bool where); // delete order void deleteOrder(int pos); // move order up void moveOrderUp(int& pos); // move order down void moveOrderDown(int& pos); // move thing up bool moveInsUp(int which); bool moveWaveUp(int which); bool moveSampleUp(int which); // move thing down bool moveInsDown(int which); bool moveWaveDown(int which); bool moveSampleDown(int which); // automatic patchbay void autoPatchbay(); void autoPatchbayP(); // connect in patchbay // returns false if connection already made bool patchConnect(unsigned int src, unsigned int dest); // disconnect in patchbay // returns false if connection doesn't exist bool patchDisconnect(unsigned int src, unsigned int dest); // disconnect all in patchbay void patchDisconnectAll(unsigned int portSet); // play note void noteOn(int chan, int ins, int note, int vol=-1); // stop note void noteOff(int chan); // 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(); // set whether autoNoteIn is mono or poly void setAutoNotePoly(bool poly); // go to order void setOrder(unsigned char order); // update system flags void updateSysFlags(int system, bool restart, bool render); // set Hz void setSongRate(float hz); // set remaining loops. -1 means loop forever. void setLoops(int loops); // get channel state DivChannelState* getChanState(int chan); // get dispatch channel state void* getDispatchChanState(int chan); // get channel pairs DivChannelPair getChanPaired(int chan); // get channel mode hints DivChannelModeHints getChanModeHints(int chan); // get register pool unsigned char* getRegisterPool(int sys, int& size, int& depth); // get macro interpreter DivMacroInt* getMacroInt(int chan); // get channel panning unsigned short getChanPan(int chan); // get sample position DivSamplePos getSamplePos(int chan); // get osc buffer DivDispatchOscBuffer* getOscBuffer(int chan); // enable command stream dumping void enableCommandStream(bool enable); // get command stream void getCommandStream(std::vector& where); // set the audio system. void setAudio(DivAudioEngines which); // set the view mode. void setView(DivStatusView which); // get available audio devices std::vector& getAudioDevices(); // get available MIDI inputs std::vector& getMidiIns(); // get available MIDI inputs std::vector& getMidiOuts(); // rescan audio devices void rescanAudioDevices(); // set the console mode. void setConsoleMode(bool enable); // get metronome bool getMetronome(); // set metronome void setMetronome(bool enable); // set metronome volume (1.0 = 100%) void setMetronomeVol(float vol); // get buffer position int getBufferPos(); // halt now void halt(); // resume from halt void resume(); // halt on next something void haltWhen(DivHaltPositions when); // is engine halted bool isHalted(); // get register cheatsheet const char** getRegisterSheet(int sys); // load sample ROMs int loadSampleROMs(); // get the sample format mask unsigned int getSampleFormatMask(); // UNSAFE render samples - only execute when locked void renderSamples(int whichSample=-1); // public render samples // values for whichSample // -2: don't render anything - just update chip sample memory // -1: render all samples // >=0: render specific sample void renderSamplesP(int whichSample=-1); // public swap channels void swapChannelsP(int src, int dest); // public change song void changeSongP(size_t index); // add subsong int addSubSong(); // duplicate subsong int duplicateSubSong(int index); // remove subsong bool removeSubSong(int index); // move subsong void moveSubSongUp(size_t index); void moveSubSongDown(size_t index); // clear all subsong data void clearSubSongs(); // optimize assets void delUnusedIns(); void delUnusedWaves(); void delUnusedSamples(); // change system void changeSystem(int index, DivSystem which, bool preserveOrder=true); // add system bool addSystem(DivSystem which); // remove system bool removeSystem(int index, bool preserveOrder=true); // move system bool swapSystem(int src, int dest, bool preserveOrder=true); // add effect bool addEffect(DivEffectType which); // remove effect bool removeEffect(int index); // write to register on system void poke(int sys, unsigned int addr, unsigned short val); // write to register on system void poke(int sys, std::vector& wlist); // get last error String getLastError(); // get warnings String getWarnings(); // get debug info String getPlaybackDebugInfo(); // switch master bool switchMaster(bool full=false); // set MIDI base channel void setMidiBaseChan(int chan); // set MIDI direct channel map void setMidiDirect(bool value); // set MIDI input callback // if the specified function returns -2, note feedback will be inhibited. void setMidiCallback(std::function what); // send MIDI message bool sendMidiMessage(TAMidiMessage& msg); // perform secure/sync operation void synchronized(const std::function& what); // perform secure/sync song operation void lockSave(const std::function& what); // perform secure/sync song operation (and lock audio too) void lockEngine(const std::function& what); // get audio desc want TAAudioDesc& getAudioDescWant(); // get audio desc TAAudioDesc& getAudioDescGot(); // init dispatch void initDispatch(bool isRender=false); // quit dispatch void quitDispatch(); // 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(); unsigned char* yrw801ROM; unsigned char* tg100ROM; unsigned char* mu5ROM; DivEngine(): output(NULL), exportThread(NULL), chans(0), active(false), lowQuality(false), dcHiPass(true), playing(false), freelance(false), shallStop(false), shallStopSched(false), endOfSong(false), consoleMode(false), extValuePresent(false), repeatPattern(false), metronome(false), exporting(false), stopExport(false), halted(false), forceMono(false), cmdStreamEnabled(false), softLocked(false), firstTick(false), skipping(false), midiIsDirect(false), lowLatency(false), systemsRegistered(false), hasLoadedSomething(false), midiOutClock(false), midiOutTime(false), midiOutProgramChange(false), midiOutMode(DIV_MIDI_MODE_NOTE), midiOutTimeRate(0), softLockCount(0), subticks(0), ticks(0), curRow(0), curOrder(0), prevRow(0), prevOrder(0), remainingLoops(-1), totalLoops(0), lastLoopPos(0), exportLoopCount(0), nextSpeed(3), elapsedBars(0), elapsedBeats(0), curSpeed(0), curSubSongIndex(0), bufferPos(0), divider(60), cycles(0), clockDrift(0), midiClockCycles(0), midiClockDrift(0), midiTimeCycles(0), midiTimeDrift(0), stepPlay(0), changeOrd(-1), changePos(0), totalSeconds(0), totalTicks(0), totalTicksR(0), curMidiClock(0), curMidiTime(0), totalCmds(0), lastCmds(0), cmdsPerSecond(0), globalPitch(0), curMidiTimePiece(0), curMidiTimeCode(0), extValue(0), pendingMetroTick(0), tempoAccum(0), view(DIV_STATUS_NOTHING), haltOn(DIV_HALT_NONE), audioEngine(DIV_AUDIO_NULL), exportMode(DIV_EXPORT_MODE_ONE), exportFadeOut(0.0), cmdStreamInt(NULL), midiBaseChan(0), midiPoly(true), midiAgeCounter(0), samp_bb(NULL), samp_bbInLen(0), samp_temp(0), samp_prevSample(0), samp_bbIn(NULL), samp_bbOut(NULL), metroTick(NULL), metroTickLen(0), metroBuf(NULL), metroBufLen(0), metroFreq(0), metroPos(0), metroAmp(0.0f), metroVol(1.0f), previewVol(1.0f), totalProcessed(0), renderPoolThreads(0), renderPool(NULL), curOrders(NULL), curPat(NULL), tempIns(NULL), oscSize(1), oscReadPos(0), oscWritePos(0), tickMult(1), lastNBIns(0), lastNBOuts(0), lastNBSize(0), processTime(0), yrw801ROM(NULL), tg100ROM(NULL), 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)); memset(vibTable,0,64*sizeof(short)); memset(tremTable,0,128*sizeof(short)); memset(reversePitchTable,0,4096*sizeof(int)); memset(pitchTable,0,4096*sizeof(int)); memset(effectSlotMap,-1,4096*sizeof(short)); memset(sysDefs,0,DIV_MAX_CHIP_DEFS*sizeof(void*)); memset(walked,0,8192); memset(oscBuf,0,DIV_MAX_OUTPUTS*(sizeof(float*))); for (int i=0; i