diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e2d5aef2..149d234c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,7 @@ jobs: - { name: 'macOS x86_64', os: macos-latest, arch: x86_64 } - { name: 'macOS ARM', os: macos-latest, arch: arm64 } - { name: 'Linux x86_64', os: ubuntu-18.04, arch: x86_64 } - - { name: 'Linux ARM', os: ubuntu-18.04, arch: armhf } + #- { name: 'Linux ARM', os: ubuntu-18.04, arch: armhf } fail-fast: false name: ${{ matrix.config.name }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 63144c54..5ff5085b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -700,11 +700,11 @@ if (WIN32) if (NOT MSVC) list(APPEND DEPENDENCIES_LIBRARIES -static) endif() -endif() - -if (APPLE) +elseif (APPLE) find_library(COCOA Cocoa REQUIRED) list(APPEND DEPENDENCIES_LIBRARIES ${COCOA}) +else() + list(APPEND DEPENDENCIES_LIBRARIES dl) endif() if (NOT MSVC) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index afc43c49..31165511 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -76,6 +76,7 @@ additional guidelines: - I will run a test suite to make sure this is the case. - if something breaks, you might want to add a compatibility flag (this requires changing the format though). - do not use `#pragma once`. +- do not memcmp() structs. - on a switch block, **always** put `default` last and not in any other position. - I have fear of some C/C++ compilers ignoring the rest of cases upon hitting default. @@ -85,7 +86,7 @@ just put your demo song in `demos/`! be noted there are some guidelines: - avoid Nintendo song covers. - avoid big label song covers. -- avoid poor quality songs. +- low effort compositions/covers may not be accepted at all. # Finishing diff --git a/TODO.md b/TODO.md index e8d551ab..89a31eec 100644 --- a/TODO.md +++ b/TODO.md @@ -5,5 +5,4 @@ - (maybe) YM2612 CSM (no DualPCM) - port presets to new format - bug fixes -- (maybe) ExtCh FM macros? - (maybe) advanced linear arpeggio? (run arp+slide simultaneously) diff --git a/demos/Mars_Bar_On_Mars.fur b/demos/Mars_Bar_On_Mars.fur new file mode 100644 index 00000000..4f0f02e3 Binary files /dev/null and b/demos/Mars_Bar_On_Mars.fur differ diff --git a/demos/README.md b/demos/README.md index d07738e5..49d5cb37 100644 --- a/demos/README.md +++ b/demos/README.md @@ -6,10 +6,11 @@ these demo songs are not under the GPL. all rights are reserved to the original # submit demo songs! -contact me or send a pull request if you want your song to be added to this collection. be noted we have two rules: +contact me or send a pull request if you want your song to be added to this collection. be noted we have three rules: - Nintendo covers are frowned upon - big label music covers also are discouraged +- low effort compositions/covers may not be accepted at all. tildearrow also accepts demo songs in the .dmf format as well as the .fur format. diff --git a/demos/Rave_Dancetune_VSU-VUE.fur b/demos/Rave_Dancetune_VSU-VUE.fur new file mode 100644 index 00000000..6af04b61 Binary files /dev/null and b/demos/Rave_Dancetune_VSU-VUE.fur differ diff --git a/demos/ice-cap-nc30.fur b/demos/ice-cap-nc30.fur new file mode 100644 index 00000000..815d7323 Binary files /dev/null and b/demos/ice-cap-nc30.fur differ diff --git a/extern/SAASound/src/SAAFreq.cpp b/extern/SAASound/src/SAAFreq.cpp index 0058c35a..5d0dd952 100755 --- a/extern/SAASound/src/SAAFreq.cpp +++ b/extern/SAASound/src/SAAFreq.cpp @@ -12,18 +12,6 @@ #include "SAAFreq.h" #include "defns.h" -#ifdef SAAFREQ_FIXED_CLOCKRATE -// 'load in' the data for the static frequency lookup table -// precomputed for a fixed clockrate -// See: tools/freqdat.py -const unsigned long CSAAFreq::m_FreqTable[2048] = { -#include "SAAFreq.dat" -}; -#else -unsigned long CSAAFreq::m_FreqTable[2048]; -unsigned long CSAAFreq::m_nClockRate = 0; -#endif // SAAFREQ_FIXED_CLOCKRATE - const int INITIAL_LEVEL = 1; ////////////////////////////////////////////////////////////////////// diff --git a/extern/SAASound/src/SAAFreq.h b/extern/SAASound/src/SAAFreq.h index 47875462..716c085e 100755 --- a/extern/SAASound/src/SAAFreq.h +++ b/extern/SAASound/src/SAAFreq.h @@ -20,8 +20,8 @@ private: const static unsigned long m_FreqTable[2048]; #else // we'll calculate the frequency lookup table at runtime. - static unsigned long m_FreqTable[2048]; - static unsigned long m_nClockRate; + unsigned long m_FreqTable[2048]; + unsigned long m_nClockRate; #endif unsigned long m_nCounter; diff --git a/extern/SAASound/src/SAAImpl.cpp b/extern/SAASound/src/SAAImpl.cpp index 6602feee..55665b6d 100644 --- a/extern/SAASound/src/SAAImpl.cpp +++ b/extern/SAASound/src/SAAImpl.cpp @@ -26,7 +26,9 @@ m_uParamRate(0), m_nClockRate(EXTERNAL_CLK_HZ), m_nSampleRate(SAMPLE_RATE_HZ), m_nOversample(DEFAULT_OVERSAMPLE), -m_bHighpass(false) +m_bHighpass(false), +filterout_z1_left_mixed(0), +filterout_z1_right_mixed(0) { #ifdef USE_CONFIG_FILE m_Config.ReadConfig(); @@ -301,7 +303,6 @@ void scale_for_output(unsigned int left_input, unsigned int right_input, void CSAASoundInternal::GenerateMany(BYTE* pBuffer, unsigned long nSamples, DivDispatchOscBuffer** oscBuf) { unsigned int left_mixed, right_mixed; - static double filterout_z1_left_mixed = 0, filterout_z1_right_mixed = 0; #if defined(DEBUGSAA) || defined(USE_CONFIG_FILE) BYTE* pBufferStart = pBuffer; diff --git a/extern/SAASound/src/SAAImpl.h b/extern/SAASound/src/SAAImpl.h index f5f58d55..50de979d 100755 --- a/extern/SAASound/src/SAAImpl.h +++ b/extern/SAASound/src/SAAImpl.h @@ -36,6 +36,7 @@ private: unsigned int m_nSampleRate; unsigned int m_nOversample; bool m_bHighpass; + double filterout_z1_left_mixed, filterout_z1_right_mixed; #ifdef USE_CONFIG_FILE SAAConfig m_Config; #endif diff --git a/papers/format.md b/papers/format.md index 391029ea..09e58362 100644 --- a/papers/format.md +++ b/papers/format.md @@ -32,6 +32,10 @@ these fields are 0 in format versions prior to 100 (0.6pre1). the format versions are: +- 127: Furnace dev127 +- 126: Furnace dev126 +- 125: Furnace dev125 +- 124: Furnace dev124 - 123: Furnace dev123 - 122: Furnace dev122 - 121: Furnace dev121 @@ -437,7 +441,23 @@ clock=4000000 stereo=true ``` -# instrument +# instrument (>=127) + +Furnace dev127 and higher use the new instrument format. + +``` +size | description +-----|------------------------------------ + 4 | "INS2" block ID + 4 | size of this block + 2 | format version + 2 | instrument type + ??? | features... +``` + +see [newIns.md](newIns.md) for more information. + +# old instrument (<127) notes: diff --git a/papers/newIns.md b/papers/newIns.md index fcc5f966..dbc1e4f2 100644 --- a/papers/newIns.md +++ b/papers/newIns.md @@ -1,49 +1,516 @@ -# possible new Furnace instrument format +# new Furnace instrument format the main issue with Furnace instrument files is that they are too big, even if the instrument is nothing more than the FM setup... the aim of this new format is to greatly reduce the size of a resulting instrument. +# information + +this format is "featural", meaning that only used parameters are stored (depending on instrument types). +this is the biggest improvement over the previous format, which stored everything including unused parameters. + +features which are not recognized by Furnace will be ignored. + +instruments are not compressed using zlib, unlike Furnace songs. + +all numbers are little-endian. + +the following fields may be found in "size": +- `f` indicates a floating point number. +- `STR` is a UTF-8 zero-terminated string. +- `???` is an array of variable size. +- `S??` is an array of `STR`s. +- `1??` is an array of bytes. +- `2??` is an array of shorts. +- `4??` is an array of ints. + +the format may change across versions. a `(>=VER)` indicates this field is only present starting from format version `VER`, and `(renderSamples(); + disCont[i].dispatch->renderSamples(i); } } } @@ -2478,10 +2478,10 @@ void DivEngine::previewSampleNoLock(int sample, int note, int pStart, int pEnd) return; } blip_clear(samp_bb); - double rate=song.sample[sample]->rate; + double rate=song.sample[sample]->centerRate; if (note>=0) { rate=(pow(2.0,(double)(note)/12.0)*((double)song.sample[sample]->centerRate)*0.0625); - if (rate<=0) rate=song.sample[sample]->rate; + if (rate<=0) rate=song.sample[sample]->centerRate; } if (rate<100) rate=100; blip_set_rates(samp_bb,rate,got.rate); diff --git a/src/engine/engine.h b/src/engine/engine.h index 4a36b27d..ab028aca 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -47,8 +47,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev125" -#define DIV_ENGINE_VERSION 125 +#define DIV_VERSION "dev128" +#define DIV_ENGINE_VERSION 128 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 @@ -766,7 +766,7 @@ class DivEngine { // get instrument from file // if the returned vector is empty then there was an error. - std::vector instrumentFromFile(const char* path); + std::vector instrumentFromFile(const char* path, bool loadAssets=true); // load temporary instrument void loadTempIns(DivInstrument* which); diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index a5502040..e01a5710 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -208,7 +208,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { }*/ // Game Boy arp+soundLen screwery - ds.systemFlags[0].set("enoughAlready",true); + if (ds.system[0]==DIV_SYSTEM_GB) { + ds.systemFlags[0].set("enoughAlready",true); + } logI("reading module data..."); if (ds.version>0x0c) { @@ -966,6 +968,11 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ds.system[1]=DIV_SYSTEM_FDS; } + // SMS noise freq + if (ds.system[0]==DIV_SYSTEM_SMS) { + ds.systemFlags[0].set("noEasyNoise",true); + } + ds.systemName=getSongSystemLegacyName(ds,!getConfInt("noMultiSystem",0)); if (active) quitDispatch(); @@ -2348,142 +2355,24 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { // read samples for (int i=0; ireadSampleData(reader,ds.version)!=DIV_DATA_SUCCESS) { + lastError="invalid sample header/data!"; ds.unload(); + delete sample; delete[] file; return false; } - bool isNewSample=(strcmp(magic,"SMP2")==0); - reader.readI(); - DivSample* sample=new DivSample; - logD("reading sample %d at %x...",i,samplePtr[i]); - if (!isNewSample) logV("(old sample)"); - - sample->name=reader.readString(); - sample->samples=reader.readI(); - if (!isNewSample) { - sample->loopEnd=sample->samples; - } - sample->rate=reader.readI(); - - if (isNewSample) { - sample->centerRate=reader.readI(); - sample->depth=(DivSampleDepth)reader.readC(); - if (ds.version>=123) { - sample->loopMode=(DivSampleLoopMode)reader.readC(); - } else { - sample->loopMode=DIV_SAMPLE_LOOP_FORWARD; - reader.readC(); - } - - // reserved - reader.readC(); - reader.readC(); - - sample->loopStart=reader.readI(); - sample->loopEnd=reader.readI(); - sample->loop=(sample->loopStart>=0)&&(sample->loopEnd>=0); - - for (int i=0; i<4; i++) { - reader.readI(); - } - } else { - if (ds.version<58) { - vol=reader.readS(); - pitch=reader.readS(); - } else { - reader.readI(); - } - sample->depth=(DivSampleDepth)reader.readC(); - - // reserved - reader.readC(); - - // while version 32 stored this value, it was unused. - if (ds.version>=38) { - sample->centerRate=(unsigned short)reader.readS(); - } else { - reader.readS(); - } - - if (ds.version>=19) { - sample->loopStart=reader.readI(); - sample->loop=(sample->loopStart>=0)&&(sample->loopEnd>=0); - } else { - reader.readI(); - } - } - - if (ds.version>=58) { // modern sample - sample->init(sample->samples); - reader.read(sample->getCurBuf(),sample->getCurBufLen()); -#ifdef TA_BIG_ENDIAN - // convert 16-bit samples to big-endian - if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) { - unsigned char* sampleBuf=(unsigned char*)sample->getCurBuf(); - size_t sampleBufLen=sample->getCurBufLen(); - for (size_t pos=0; possamples; - short* data=new short[length]; - reader.read(data,2*length); - -#ifdef TA_BIG_ENDIAN - // convert 16-bit samples to big-endian - for (int pos=0; pos>8)|((unsigned short)data[pos]<<8); - } -#endif - - if (pitch!=5) { - logD("%d: scaling from %d...",i,pitch); - } - - // render data - if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) { - logW("%d: sample depth is wrong! (%d)",i,sample->depth); - sample->depth=DIV_SAMPLE_DEPTH_16BIT; - } - sample->samples=(double)sample->samples/samplePitches[pitch]; - sample->init(sample->samples); - - unsigned int k=0; - float mult=(float)(vol)/50.0f; - for (double j=0; j=sample->samples) { - break; - } - if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) { - float next=(float)(data[(unsigned int)j]-0x80)*mult; - sample->data8[k++]=fmin(fmax(next,-128),127); - } else { - float next=(float)data[(unsigned int)j]*mult; - sample->data16[k++]=fmin(fmax(next,-32768),32767); - } - } - - delete[] data; - } ds.sample.push_back(sample); } @@ -2631,6 +2520,16 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } } + // SN noise compat + if (ds.version<128) { + for (int i=0; itell()); - ins->putInsData(w); + logV("writing instrument %d...",i); + ins->putInsData2(w,false); } /// WAVETABLE @@ -4635,45 +4535,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { for (int i=0; itell()); - w->write("SMP2",4); - blockStartSeek=w->tell(); - w->writeI(0); - - w->writeString(sample->name,false); - w->writeI(sample->samples); - w->writeI(sample->rate); - w->writeI(sample->centerRate); - w->writeC(sample->depth); - w->writeC(sample->loopMode); - w->writeC(0); // reserved - w->writeC(0); - w->writeI(sample->loop?sample->loopStart:-1); - w->writeI(sample->loop?sample->loopEnd:-1); - - for (int i=0; i<4; i++) { - w->writeI(0xffffffff); - } - -#ifdef TA_BIG_ENDIAN - // store 16-bit samples as little-endian - if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) { - unsigned char* sampleBuf=(unsigned char*)sample->getCurBuf(); - size_t bufLen=sample->getCurBufLen(); - for (size_t i=0; iwriteC(sampleBuf[i+1]); - w->writeC(sampleBuf[i]); - } - } else { - w->write(sample->getCurBuf(),sample->getCurBufLen()); - } -#else - w->write(sample->getCurBuf(),sample->getCurBufLen()); -#endif - - blockEndSeek=w->tell(); - w->seek(blockStartSeek,SEEK_SET); - w->writeI(blockEndSeek-blockStartSeek-4); - w->seek(0,SEEK_END); + sample->putSampleData(w); } /// PATTERN diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 83361130..359a7ae3 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -147,7 +147,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St logD("instrument type is C64"); break; case 8: // Arcade - ins->type=DIV_INS_FM; + ins->type=DIV_INS_OPM; logD("instrument type is Arcade"); break; case 9: // Neo Geo @@ -187,6 +187,8 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St ins->type=DIV_INS_OPLL; } else if (sys==1) { ins->type=DIV_INS_OPL; + } else if (sys==8) { + ins->type=DIV_INS_OPM; } else { ins->type=DIV_INS_FM; } @@ -1341,7 +1343,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St // At this point we know any other line would be associated with patch params if (newPatch == NULL) { newPatch = new DivInstrument; - newPatch->type = DIV_INS_FM; + newPatch->type = DIV_INS_OPM; newPatch->fm.ops = 4; } @@ -1814,7 +1816,7 @@ void DivEngine::loadWOPN(SafeReader& reader, std::vector& ret, S } } -std::vector DivEngine::instrumentFromFile(const char* path) { +std::vector DivEngine::instrumentFromFile(const char* path, bool loadAssets) { std::vector ret; warnings=""; @@ -1880,10 +1882,19 @@ std::vector DivEngine::instrumentFromFile(const char* path) { unsigned char magic[16]; bool isFurnaceInstr=false; + bool isOldFurnaceIns=false; try { - reader.read(magic,16); - if (memcmp("-Furnace instr.-",magic,16)==0) { + reader.read(magic,4); + if (memcmp("FINS",magic,4)==0) { isFurnaceInstr=true; + logV("found a new Furnace ins"); + } else { + reader.read(&magic[4],12); + if (memcmp("-Furnace instr.-",magic,16)==0) { + logV("found an old Furnace ins"); + isFurnaceInstr=true; + isOldFurnaceIns=true; + } } } catch (EndOfFileException& e) { reader.seek(0,SEEK_SET); @@ -1892,17 +1903,25 @@ std::vector DivEngine::instrumentFromFile(const char* path) { if (isFurnaceInstr) { DivInstrument* ins=new DivInstrument; try { - short version=reader.readS(); - reader.readS(); // reserved + short version=0; + if (isOldFurnaceIns) { + version=reader.readS(); + reader.readS(); // reserved + } else { + version=reader.readS(); + reader.seek(0,SEEK_SET); + } if (version>DIV_ENGINE_VERSION) { warnings="this instrument is made with a more recent version of Furnace!"; } - unsigned int dataPtr=reader.readI(); - reader.seek(dataPtr,SEEK_SET); + if (isOldFurnaceIns) { + unsigned int dataPtr=reader.readI(); + reader.seek(dataPtr,SEEK_SET); + } - if (ins->readInsData(reader,version)!=DIV_DATA_SUCCESS) { + if (ins->readInsData(reader,version,loadAssets?(&song):NULL)!=DIV_DATA_SUCCESS) { lastError="invalid instrument header/data!"; delete ins; delete[] buf; @@ -1961,7 +1980,12 @@ std::vector DivEngine::instrumentFromFile(const char* path) { format=DIV_INSFORMAT_WOPL; } else if (extS==".wopn") { format=DIV_INSFORMAT_WOPN; - } + } else { + // unknown format + lastError="unknown instrument format"; + delete[] buf; + return ret; + } } switch (format) { diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index fe420bd5..dc991f6a 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -23,6 +23,1135 @@ #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) + ); +} + +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(volIsCutoff) && + _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) + ); +} + +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); +} + +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) + ); +} + +#undef _C + +#define FEATURE_BEGIN(x) \ + logV("- %s",x); \ + w->write(x,2); \ + size_t featStartSeek=w->tell(); \ + w->writeS(0); + +#define FEATURE_END \ + size_t featEndSeek=w->tell(); \ + w->seek(featStartSeek,SEEK_SET); \ + w->writeS(featEndSeek-featStartSeek-2); \ + w->seek(featEndSeek,SEEK_SET); + +void DivInstrument::writeFeatureNA(SafeWriter* w) { + FEATURE_BEGIN("NA"); + + w->writeString(name,false); + + FEATURE_END; +} + +void DivInstrument::writeFeatureFM(SafeWriter* w, bool fui) { + FEATURE_BEGIN("FM"); + + int opCount=4; + if (fui) { + if (type==DIV_INS_OPLL) { + opCount=2; + } else if (type==DIV_INS_OPL) { + opCount=(fm.ops==4)?4:2; + } + } + + w->writeC( + (fm.op[3].enable?128:0)| + (fm.op[2].enable?64:0)| + (fm.op[1].enable?32:0)| + (fm.op[0].enable?16:0)| + opCount + ); + + // base data + w->writeC(((fm.alg&7)<<4)|(fm.fb&7)); + w->writeC(((fm.fms2&7)<<5)|((fm.ams&3)<<3)|(fm.fms&7)); + w->writeC(((fm.ams2&3)<<6)|((fm.ops==4)?32:0)|(fm.opllPreset&31)); + + // operator data + for (int i=0; iwriteC((op.ksr?128:0)|((op.dt&7)<<4)|(op.mult&15)); + w->writeC((op.sus?128:0)|(op.tl&127)); + w->writeC(((op.rs&3)<<6)|(op.vib?32:0)|(op.ar&31)); + w->writeC((op.am?128:0)|((op.ksl&3)<<5)|(op.dr&31)); + w->writeC((op.egt?128:0)|((op.kvs&3)<<5)|(op.d2r&31)); + w->writeC(((op.sl&15)<<4)|(op.rr&15)); + w->writeC(((op.dvb&15)<<4)|(op.ssgEnv&15)); + w->writeC(((op.dam&7)<<5)|((op.dt2&3)<<3)|(op.ws&7)); + } + + FEATURE_END; +} + +void DivInstrument::writeMacro(SafeWriter* w, const DivInstrumentMacro& m, unsigned char macroCode) { + if (!m.len) return; + + // determine word size + int macroMin=0x7fffffff; + int macroMax=0x80000000; + for (int i=0; imacroMax) macroMax=m.val[i]; + } + + unsigned char wordSize=192; // 32-bit + + if (macroMin>=0 && macroMax<=255) { + wordSize=0; // 8-bit unsigned + } else if (macroMin>=-128 && macroMax<=127) { + wordSize=64; // 8-bit signed + } else if (macroMin>=-32768 && macroMax<=32767) { + wordSize=128; // 16-bit signed + } else { + wordSize=192; // 32-bit signed + } + + w->writeC(macroCode); + w->writeC(m.len); + w->writeC(m.loop); + w->writeC(m.rel); + w->writeC(m.mode); + w->writeC(m.open|wordSize); + w->writeC(m.delay); + w->writeC(m.speed); + + switch (wordSize) { + case 0: + for (int i=0; iwriteC((unsigned char)m.val[i]); + } + break; + case 64: + for (int i=0; iwriteC((signed char)m.val[i]); + } + break; + case 128: + for (int i=0; iwriteS((short)m.val[i]); + } + break; + default: // 192 + for (int i=0; iwriteI(m.val[i]); + } + break; + } +} + +void DivInstrument::writeFeatureMA(SafeWriter* w) { + FEATURE_BEGIN("MA"); + + // if you update the macro header, please update this value as well. + // it's the length. + w->writeS(8); + + // write macros + writeMacro(w,std.volMacro,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); + + // "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.volIsCutoff?0x20:0)| + (c64.toFilter?0x10:0)| + (c64.noiseOn?8:0)| + (c64.pulseOn?4:0)| + (c64.sawOn?2:0)| + (c64.triOn?1:0) + ); + + w->writeC( + (c64.oscSync?0x80:0)| + (c64.ringMod?0x40:0)| + (c64.noTest?0x20:0)| + (c64.filterIsAbs?0x10:0)| + (c64.ch3off?8:0)| + (c64.bp?4:0)| + (c64.hp?2:0)| + (c64.lp?1:0) + ); + + w->writeC(((c64.a&15)<<4)|(c64.d&15)); + w->writeC(((c64.s&15)<<4)|(c64.r&15)); + w->writeS(c64.duty); + w->writeS((unsigned short)((c64.cut&2047)|(c64.res<<12))); + + FEATURE_END; +} + +void DivInstrument::writeFeatureGB(SafeWriter* w) { + FEATURE_BEGIN("GB"); + + w->writeC(((gb.envLen&7)<<5)|(gb.envDir?16:0)|(gb.envVol&15)); + w->writeC(gb.soundLen); + + w->writeC( + (gb.alwaysInit?2:0)| + (gb.softEnv?1:0) + ); + + w->writeC(gb.hwSeqLen); + for (int i=0; iwriteC(gb.hwSeq[i].cmd); + w->writeS(gb.hwSeq[i].data); + } + + FEATURE_END; +} + +void DivInstrument::writeFeatureSM(SafeWriter* w) { + FEATURE_BEGIN("SM"); + + w->writeS(amiga.initSample); + w->writeC( + (amiga.useWave?4:0)| + (amiga.useSample?2:0)| + (amiga.useNoteMap?1:0) + ); + w->writeC(amiga.waveLen); + + if (amiga.useNoteMap) { + for (int note=0; note<120; note++) { + w->writeS(amiga.noteMap[note].freq); + w->writeS(amiga.noteMap[note].map); + } + } + + FEATURE_END; +} + +void DivInstrument::writeFeatureOx(SafeWriter* w, int ope) { + char opCode[3]; + opCode[0]='O'; + opCode[1]='1'+ope; + opCode[2]=0; + + FEATURE_BEGIN(opCode); + + // if you update the macro header, please update this value as well. + // it's the length. + w->writeS(8); + + // write macros + const DivInstrumentSTD::OpMacro& o=std.opMacros[ope]; + + writeMacro(w,o.amMacro,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); + + // "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); + + 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); + + FEATURE_END; +} + +void DivInstrument::writeFeatureFD(SafeWriter* w) { + FEATURE_BEGIN("FD"); + + w->writeI(fds.modSpeed); + w->writeI(fds.modDepth); + w->writeC(fds.initModTableWithFirstWave); + w->write(fds.modTable,32); + + FEATURE_END; +} + +void DivInstrument::writeFeatureWS(SafeWriter* w) { + FEATURE_BEGIN("WS"); + + w->writeI(ws.wave1); + w->writeI(ws.wave2); + w->writeC(ws.rateDivider); + w->writeC(ws.effect); + w->writeC(ws.enabled); + w->writeC(ws.global); + w->writeC(ws.speed); + w->writeC(ws.param1); + w->writeC(ws.param2); + w->writeC(ws.param3); + w->writeC(ws.param4); + + FEATURE_END; +} + +size_t DivInstrument::writeFeatureSL(SafeWriter* w, std::vector& list, const DivSong* song) { + bool sampleUsed[256]; + memset(sampleUsed,0,256*sizeof(bool)); + + if (amiga.initSample>=0 && amiga.initSample<(int)song->sample.size()) { + sampleUsed[amiga.initSample]=true; + } + + if (amiga.useNoteMap) { + for (int i=0; i<120; i++) { + if (amiga.noteMap[i].map>=0 && amiga.noteMap[i].map<(int)song->sample.size()) { + sampleUsed[amiga.noteMap[i].map]=true; + } + } + } + + for (size_t i=0; isample.size(); i++) { + if (sampleUsed[i]) { + list.push_back(i); + } + } + + if (list.empty()) return 0; + + FEATURE_BEGIN("SL"); + + w->writeC(list.size()); + + for (int i: list) { + w->writeC(i); + } + + size_t ret=w->tell(); + + // pointers (these will be filled later) + for (size_t i=0; iwriteI(0); + } + + FEATURE_END; + + return ret; +} + +size_t DivInstrument::writeFeatureWL(SafeWriter* w, std::vector& list, const DivSong* song) { + bool waveUsed[256]; + memset(waveUsed,0,256*sizeof(bool)); + + for (int i=0; i=0 && std.waveMacro.val[i]<(int)song->wave.size()) { + waveUsed[std.waveMacro.val[i]]=true; + } + } + + if (ws.enabled) { + if (ws.wave1>=0 && ws.wave1<(int)song->wave.size()) { + waveUsed[ws.wave1]=true; + } + if ((ws.effect&0x80) && ws.wave2>=0 && ws.wave2<(int)song->wave.size()) { + waveUsed[ws.wave2]=true; + } + } + + for (size_t i=0; iwave.size(); i++) { + if (waveUsed[i]) { + list.push_back(i); + } + } + + if (list.empty()) return 0; + + FEATURE_BEGIN("WL"); + + w->writeC(list.size()); + + for (int i: list) { + w->writeC(i); + } + + size_t ret=w->tell(); + + // pointers (these will be filled later) + for (size_t i=0; iwriteI(0); + } + + FEATURE_END; + + return ret; +} + +void DivInstrument::writeFeatureMP(SafeWriter* w) { + FEATURE_BEGIN("MP"); + + w->writeC(multipcm.ar); + w->writeC(multipcm.d1r); + w->writeC(multipcm.dl); + w->writeC(multipcm.d2r); + w->writeC(multipcm.rr); + w->writeC(multipcm.rc); + w->writeC(multipcm.lfo); + w->writeC(multipcm.vib); + w->writeC(multipcm.am); + + FEATURE_END; +} + +void DivInstrument::writeFeatureSU(SafeWriter* w) { + FEATURE_BEGIN("SU"); + + w->writeC(su.switchRoles); + + 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::putInsData2(SafeWriter* w, bool fui, const DivSong* song) { + size_t blockStartSeek=0; + size_t blockEndSeek=0; + size_t slSeek=0; + size_t wlSeek=0; + std::vector waveList; + std::vector sampleList; + + std::vector wavePtr; + std::vector samplePtr; + + if (fui) { + w->write("FINS",4); + } else { + w->write("INS2",4); + blockStartSeek=w->tell(); + w->writeI(0); + } + + w->writeS(DIV_ENGINE_VERSION); + w->writeC(type); + w->writeC(0); + + // write features + bool featureNA=false; + bool featureFM=false; + bool featureMA=false; + bool feature64=false; + bool featureGB=false; + bool featureSM=false; + bool featureOx[4]; + bool featureLD=false; + bool featureSN=false; + bool featureN1=false; + bool featureFD=false; + bool featureWS=false; + bool featureSL=false; + bool featureWL=false; + bool featureMP=false; + bool featureSU=false; + bool featureES=false; + bool featureX1=false; + + bool 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: + 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_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; + } + 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; + } + } + + // check ins name + if (!name.empty()) { + featureNA=true; + } + + // check macros + if (std.volMacro.len || + std.arpMacro.len || + std.dutyMacro.len || + std.waveMacro.len || + std.pitchMacro.len || + std.ex1Macro.len || + std.ex2Macro.len || + std.ex3Macro.len || + std.algMacro.len || + std.fbMacro.len || + std.fmsMacro.len || + std.amsMacro.len || + std.panLMacro.len || + std.panRMacro.len || + std.phaseResetMacro.len || + std.ex4Macro.len || + std.ex5Macro.len || + std.ex6Macro.len || + std.ex7Macro.len || + std.ex8Macro.len) { + featureMA=true; + } + + // check whether to write wavetable list + if (checkForWL && fui) { + if (std.waveMacro.len || ws.enabled) { + featureWL=true; + } + } + + if (featureFM || !fui) { + // check FM macros + int opCount=4; + bool storeExtendedAsWell=true; + if (fui) { + if (type==DIV_INS_OPLL) { + opCount=2; + } else if (type==DIV_INS_OPL) { + opCount=(fm.ops==4)?4:2; + } else if (type==DIV_INS_FM || type==DIV_INS_OPM) { + storeExtendedAsWell=false; + } + } + for (int i=0; iwrite("EN",2); + + if (wlSeek!=0 && !waveList.empty()) { + for (int i: waveList) { + if (i<0 || i>=(int)song->wave.size()) { + wavePtr.push_back(0); + continue; + } + DivWavetable* wave=song->wave[i]; + + wavePtr.push_back(w->tell()); + wave->putWaveData(w); + } + + w->seek(wlSeek,SEEK_SET); + for (unsigned int i: wavePtr) { + w->writeI(i); + } + w->seek(0,SEEK_END); + } + + if (slSeek!=0 && !sampleList.empty()) { + for (int i: sampleList) { + if (i<0 || i>=(int)song->sample.size()) { + samplePtr.push_back(0); + continue; + } + DivSample* sample=song->sample[i]; + + samplePtr.push_back(w->tell()); + sample->putSampleData(w); + } + + w->seek(slSeek,SEEK_SET); + for (unsigned int i: samplePtr) { + w->writeI(i); + } + w->seek(0,SEEK_END); + } + } + + if (!fui) { + w->write("EN",2); + } + + blockEndSeek=w->tell(); + if (!fui) { + w->seek(blockStartSeek,SEEK_SET); + w->writeI(blockEndSeek-blockStartSeek-4); + } + w->seek(0,SEEK_END); +} + void DivInstrument::putInsData(SafeWriter* w) { size_t blockStartSeek, blockEndSeek; @@ -663,21 +1792,747 @@ void DivInstrument::putInsData(SafeWriter* w) { w->seek(0,SEEK_END); } +#define READ_FEAT_BEGIN \ + unsigned short featLen=reader.readS(); \ + size_t endOfFeat=reader.tell()+featLen; + +#define READ_FEAT_END \ + if (reader.tell()>4)&7; + fm.fb=next&7; + + next=reader.readC(); + fm.fms2=(next>>5)&7; + fm.ams=(next>>3)&3; + fm.fms=next&7; + + next=reader.readC(); + fm.ams2=(next>>6)&3; + fm.ops=(next&32)?4:2; + fm.opllPreset=next&31; + + // read operators + for (int i=0; i>4)&7; + op.mult=next&15; + + next=reader.readC(); + op.sus=(next&128)?1:0; + op.tl=next&127; + + next=reader.readC(); + op.rs=(next>>6)&3; + op.vib=(next&32)?1:0; + op.ar=next&31; + + next=reader.readC(); + op.am=(next&128)?1:0; + op.ksl=(next>>5)&3; + op.dr=next&31; + + next=reader.readC(); + op.egt=(next&128)?1:0; + op.kvs=(next>>5)&3; + op.d2r=next&31; + + next=reader.readC(); + op.sl=(next>>4)&15; + op.rr=next&15; + + next=reader.readC(); + op.dvb=(next>>4)&15; + op.ssgEnv=next&15; + + next=reader.readC(); + op.dam=(next>>5)&7; + op.dt2=(next>>3)&3; + op.ws=next&7; + } + + READ_FEAT_END; +} + +void DivInstrument::readFeatureMA(SafeReader& reader) { + READ_FEAT_BEGIN; + + unsigned short macroHeaderLen=reader.readS(); + + DivInstrumentMacro* target=&std.volMacro; + + while (reader.tell()len=reader.readC(); + target->loop=reader.readC(); + target->rel=reader.readC(); + target->mode=reader.readC(); + + unsigned char wordSize=reader.readC(); + target->open=wordSize&7; + wordSize>>=6; + + target->delay=reader.readC(); + target->speed=reader.readC(); + + reader.seek(endOfMacroHeader,SEEK_SET); + + // read macro + switch (wordSize) { + case 0: + for (int i=0; ilen; i++) { + target->val[i]=(unsigned char)reader.readC(); + } + break; + case 1: + for (int i=0; ilen; i++) { + target->val[i]=(signed char)reader.readC(); + } + break; + case 2: + for (int i=0; ilen; i++) { + target->val[i]=reader.readS(); + } + break; + default: + for (int i=0; ilen; i++) { + target->val[i]=reader.readI(); + } + break; + } + } + + READ_FEAT_END; +} + +void DivInstrument::readFeature64(SafeReader& reader) { + READ_FEAT_BEGIN; + + unsigned char next=reader.readC(); + c64.dutyIsAbs=next&128; + c64.initFilter=next&64; + c64.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&2047; + c64.res=cr>>12; + + READ_FEAT_END; +} + +void DivInstrument::readFeatureGB(SafeReader& reader) { + 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(); + gb.alwaysInit=next&2; + gb.softEnv=next&1; + + gb.hwSeqLen=reader.readC(); + for (int i=0; ilen=reader.readC(); + target->loop=reader.readC(); + target->rel=reader.readC(); + target->mode=reader.readC(); + + unsigned char wordSize=reader.readC(); + target->open=wordSize&7; + wordSize>>=6; + + target->delay=reader.readC(); + target->speed=reader.readC(); + + reader.seek(endOfMacroHeader,SEEK_SET); + + // read macro + switch (wordSize) { + case 0: + for (int i=0; ilen; i++) { + target->val[i]=(unsigned char)reader.readC(); + } + break; + case 1: + for (int i=0; ilen; i++) { + target->val[i]=(signed char)reader.readC(); + } + break; + case 2: + for (int i=0; ilen; i++) { + target->val[i]=reader.readS(); + } + break; + default: + for (int i=0; ilen; i++) { + target->val[i]=reader.readI(); + } + break; + } + } + + READ_FEAT_END; +} + +void DivInstrument::readFeatureLD(SafeReader& reader) { + 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) { + 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; + 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(); + + + READ_FEAT_END; +} + +void DivInstrument::readFeatureN1(SafeReader& reader) { + 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(); + + READ_FEAT_END; +} + +void DivInstrument::readFeatureFD(SafeReader& reader) { + 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) { + READ_FEAT_BEGIN; + + ws.wave1=reader.readI(); + ws.wave2=reader.readI(); + ws.rateDivider=reader.readC(); + ws.effect=reader.readC(); + ws.enabled=reader.readC(); + ws.global=reader.readC(); + ws.speed=reader.readC(); + ws.param1=reader.readC(); + ws.param2=reader.readC(); + ws.param3=reader.readC(); + ws.param4=reader.readC(); + + READ_FEAT_END; +} + +void DivInstrument::readFeatureSL(SafeReader& reader, DivSong* song, short version) { + READ_FEAT_BEGIN; + + unsigned int samplePtr[256]; + unsigned char sampleIndex[256]; + unsigned char sampleRemap[256]; + memset(samplePtr,0,256*sizeof(unsigned int)); + memset(sampleIndex,0,256); + memset(sampleRemap,0,256); + + unsigned char sampleCount=reader.readC(); + + for (int i=0; isample.size()>=256) { + break; + } + DivSample* sample=new DivSample; + int sampleCount=(int)song->sample.size(); + + DivDataErrors result=sample->readSampleData(reader,version); + if (result==DIV_DATA_SUCCESS) { + song->sample.push_back(sample); + song->sampleLen=sampleCount+1; + sampleRemap[sampleIndex[i]]=sampleCount; + } else { + delete sample; + sampleRemap[sampleIndex[i]]=0; + } + } + + reader.seek(lastSeek,SEEK_CUR); + + // re-map samples + if (amiga.initSample>=0 && amiga.initSample<256) { + amiga.initSample=sampleRemap[amiga.initSample]; + } + + if (amiga.useNoteMap) { + for (int i=0; i<120; i++) { + if (amiga.noteMap[i].map>=0 && amiga.noteMap[i].map<256) { + amiga.noteMap[i].map=sampleRemap[amiga.noteMap[i].map]; + } + } + } + + READ_FEAT_END; +} + +void DivInstrument::readFeatureWL(SafeReader& reader, DivSong* song, short version) { + READ_FEAT_BEGIN; + + unsigned int wavePtr[256]; + unsigned char waveIndex[256]; + unsigned char waveRemap[256]; + memset(wavePtr,0,256*sizeof(unsigned int)); + memset(waveIndex,0,256); + memset(waveRemap,0,256); + + unsigned char waveCount=reader.readC(); + + for (int i=0; iwave.size()>=256) { + break; + } + DivWavetable* wave=new DivWavetable; + int waveCount=(int)song->wave.size(); + + DivDataErrors result=wave->readWaveData(reader,version); + if (result==DIV_DATA_SUCCESS) { + song->wave.push_back(wave); + song->waveLen=waveCount+1; + waveRemap[waveIndex[i]]=waveCount; + } else { + delete wave; + waveRemap[waveIndex[i]]=0; + } + } + + reader.seek(lastSeek,SEEK_CUR); + + // re-map wavetables + if (ws.enabled) { + if (ws.wave1>=0 && ws.wave1<256) ws.wave1=waveRemap[ws.wave1]; + if (ws.effect&0x80) { + if (ws.wave2>=0 && ws.wave2<256) ws.wave2=waveRemap[ws.wave2]; + } + } + if (n163.wave>=0 && n163.wave<256) n163.wave=waveRemap[n163.wave]; + for (int i=0; i=0 && std.waveMacro.val[i]<256) std.waveMacro.val[i]=waveRemap[std.waveMacro.val[i]]; + } + + READ_FEAT_END; +} + +void DivInstrument::readFeatureMP(SafeReader& reader) { + 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) { + READ_FEAT_BEGIN; + + su.switchRoles=reader.readC(); + + READ_FEAT_END; +} + +void DivInstrument::readFeatureES(SafeReader& reader) { + 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) { + READ_FEAT_BEGIN; + + x1_010.bankSlot=reader.readI(); + + READ_FEAT_END; +} + +DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song) { + unsigned char featCode[2]; + + 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()init(); - // write magic - w->write("-Furnace instr.-",16); + if (oldFormat) { + // write magic + w->write("-Furnace instr.-",16); - // write version - w->writeS(DIV_ENGINE_VERSION); + // write version + w->writeS(DIV_ENGINE_VERSION); - // reserved - w->writeS(0); + // reserved + w->writeS(0); - // pointer to data - w->writeI(32); + // pointer to data + w->writeI(32); - // currently reserved (TODO; wavetable and sample here) - w->writeS(0); - w->writeS(0); - w->writeI(0); + // currently reserved (TODO; wavetable and sample here) + w->writeS(0); + w->writeS(0); + w->writeI(0); - putInsData(w); + putInsData(w); + } else { + putInsData2(w,true,song); + } FILE* outFile=ps_fopen(path,"wb"); if (outFile==NULL) { diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 1a2e53e7..971c4bdb 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -24,6 +24,9 @@ #include "safeWriter.h" #include "dataErrors.h" #include "../ta-utils.h" +#include + +struct DivSong; // NOTICE! // before adding new instrument types to this struct, please ask me first. @@ -97,11 +100,21 @@ struct DivInstrumentFM { unsigned char alg, fb, fms, ams, fms2, ams2, ops, opllPreset; bool fixedDrums; unsigned short kickFreq, snareHatFreq, tomTopFreq; + + bool operator==(const DivInstrumentFM& other); + bool operator!=(const DivInstrumentFM& other) { + return !(*this==other); + } struct Operator { bool enable; unsigned char am, ar, dr, mult, rr, sl, tl, dt2, rs, dt, d2r, ssgEnv; unsigned char dam, dvb, egt, ksl, sus, vib, ws, ksr; // YMU759/OPL/OPZ unsigned char kvs; + + bool operator==(const Operator& other); + bool operator!=(const Operator& other) { + return !(*this==other); + } Operator(): enable(true), am(0), @@ -296,6 +309,12 @@ struct DivInstrumentGB { unsigned char cmd; unsigned short data; } hwSeq[256]; + + bool operator==(const DivInstrumentGB& other); + bool operator!=(const DivInstrumentGB& other) { + return !(*this==other); + } + DivInstrumentGB(): envVol(15), envDir(0), @@ -318,6 +337,11 @@ struct DivInstrumentC64 { unsigned short cut; bool hp, lp, bp, ch3off; + bool operator==(const DivInstrumentC64& other); + bool operator!=(const DivInstrumentC64& other) { + return !(*this==other); + } + DivInstrumentC64(): triOn(false), sawOn(true), @@ -369,6 +393,11 @@ struct DivInstrumentAmiga { unsigned char waveLen; SampleMap noteMap[120]; + bool operator==(const DivInstrumentAmiga& other); + bool operator!=(const DivInstrumentAmiga& other) { + return !(*this==other); + } + /** * get the sample at specified note. * @return the sample. @@ -424,6 +453,11 @@ struct DivInstrumentAmiga { struct DivInstrumentX1_010 { int bankSlot; + bool operator==(const DivInstrumentX1_010& other); + bool operator!=(const DivInstrumentX1_010& other) { + return !(*this==other); + } + DivInstrumentX1_010(): bankSlot(0) {} }; @@ -432,6 +466,11 @@ struct DivInstrumentN163 { int wave, wavePos, waveLen; unsigned char waveMode; + bool operator==(const DivInstrumentN163& other); + bool operator!=(const DivInstrumentN163& other) { + return !(*this==other); + } + DivInstrumentN163(): wave(-1), wavePos(0), @@ -444,6 +483,12 @@ struct DivInstrumentFDS { int modSpeed, modDepth; // this is here for compatibility. bool initModTableWithFirstWave; + + bool operator==(const DivInstrumentFDS& other); + bool operator!=(const DivInstrumentFDS& other) { + return !(*this==other); + } + DivInstrumentFDS(): modSpeed(0), modDepth(0), @@ -456,6 +501,11 @@ struct DivInstrumentMultiPCM { unsigned char ar, d1r, dl, d2r, rr, rc; unsigned char lfo, vib, am; + bool operator==(const DivInstrumentMultiPCM& other); + bool operator!=(const DivInstrumentMultiPCM& other) { + return !(*this==other); + } + DivInstrumentMultiPCM(): ar(15), d1r(15), dl(0), d2r(0), rr(15), rc(15), lfo(0), vib(0), am(0) { @@ -494,6 +544,12 @@ struct DivInstrumentWaveSynth { unsigned char effect; bool oneShot, enabled, global; unsigned char speed, param1, param2, param3, param4; + + bool operator==(const DivInstrumentWaveSynth& other); + bool operator!=(const DivInstrumentWaveSynth& other) { + return !(*this==other); + } + DivInstrumentWaveSynth(): wave1(0), wave2(0), @@ -511,6 +567,12 @@ struct DivInstrumentWaveSynth { struct DivInstrumentSoundUnit { bool switchRoles; + + bool operator==(const DivInstrumentSoundUnit& other); + bool operator!=(const DivInstrumentSoundUnit& other) { + return !(*this==other); + } + DivInstrumentSoundUnit(): switchRoles(false) {} }; @@ -546,6 +608,12 @@ struct DivInstrumentES5506 { }; Filter filter; Envelope envelope; + + bool operator==(const DivInstrumentES5506& other); + bool operator!=(const DivInstrumentES5506& other) { + return !(*this==other); + } + DivInstrumentES5506(): filter(Filter()), envelope(Envelope()) {} @@ -563,6 +631,12 @@ struct DivInstrumentSNES { GainMode gainMode; unsigned char gain; unsigned char a, d, s, r; + + bool operator==(const DivInstrumentSNES& other); + bool operator!=(const DivInstrumentSNES& other) { + return !(*this==other); + } + DivInstrumentSNES(): useEnv(true), sus(false), @@ -576,7 +650,6 @@ struct DivInstrumentSNES { struct DivInstrument { String name; - bool mode; DivInstrumentType type; DivInstrumentFM fm; DivInstrumentSTD std; @@ -591,6 +664,51 @@ struct DivInstrument { DivInstrumentSoundUnit su; DivInstrumentES5506 es5506; DivInstrumentSNES snes; + + /** + * these are internal functions. + */ + void writeMacro(SafeWriter* w, const DivInstrumentMacro& m, unsigned char macroCode); + void writeFeatureNA(SafeWriter* w); + void writeFeatureFM(SafeWriter* w, bool fui); + void writeFeatureMA(SafeWriter* w); + void writeFeature64(SafeWriter* w); + void writeFeatureGB(SafeWriter* w); + void writeFeatureSM(SafeWriter* w); + void writeFeatureOx(SafeWriter* w, int op); + void writeFeatureLD(SafeWriter* w); + void writeFeatureSN(SafeWriter* w); + void writeFeatureN1(SafeWriter* w); + void writeFeatureFD(SafeWriter* w); + void writeFeatureWS(SafeWriter* w); + size_t writeFeatureSL(SafeWriter* w, std::vector& list, const DivSong* song); + size_t writeFeatureWL(SafeWriter* w, std::vector& list, const DivSong* song); + void writeFeatureMP(SafeWriter* w); + void writeFeatureSU(SafeWriter* w); + void writeFeatureES(SafeWriter* w); + void writeFeatureX1(SafeWriter* w); + + void readFeatureNA(SafeReader& reader); + void readFeatureFM(SafeReader& reader); + void readFeatureMA(SafeReader& reader); + void readFeature64(SafeReader& reader); + void readFeatureGB(SafeReader& reader); + void readFeatureSM(SafeReader& reader); + void readFeatureOx(SafeReader& reader, int op); + void readFeatureLD(SafeReader& reader); + void readFeatureSN(SafeReader& reader); + void readFeatureN1(SafeReader& reader); + void readFeatureFD(SafeReader& reader); + void readFeatureWS(SafeReader& reader); + void readFeatureSL(SafeReader& reader, DivSong* song, short version); + void readFeatureWL(SafeReader& reader, DivSong* song, short version); + void readFeatureMP(SafeReader& reader); + void readFeatureSU(SafeReader& reader); + void readFeatureES(SafeReader& reader); + void readFeatureX1(SafeReader& reader); + + DivDataErrors readInsDataOld(SafeReader& reader, short version); + DivDataErrors readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song); /** * save the instrument to a SafeWriter. @@ -598,20 +716,28 @@ struct DivInstrument { */ void putInsData(SafeWriter* w); + /** + * save the instrument to a SafeWriter using new format. + * @param w the SafeWriter in question. + */ + void putInsData2(SafeWriter* w, bool fui=false, const DivSong* song=NULL); + /** * read instrument data in .fui format. * @param reader the reader. * @param version the format version. * @return a DivDataErrors. */ - DivDataErrors readInsData(SafeReader& reader, short version); + DivDataErrors readInsData(SafeReader& reader, short version, DivSong* song=NULL); /** * save this instrument to a file. * @param path file path. + * @param oldFormat whether to save in legacy Furnace ins format. + * @param song if new format, a DivSong to read wavetables and samples. * @return whether it was successful. */ - bool save(const char* path); + bool save(const char* path, bool oldFormat=false, DivSong* song=NULL); /** * save this instrument to a file in .dmp format. diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 8b3239f0..33dd3c17 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -152,11 +152,20 @@ size_t DivDispatch::getSampleMemCapacity(int index) { return 0; } +const char* DivDispatch::getSampleMemName(int index) { + return NULL; +} + size_t DivDispatch::getSampleMemUsage(int index) { return 0; } -void DivDispatch::renderSamples() { +bool DivDispatch::isSampleLoaded(int index, int sample) { + printf("you are calling.\n"); + return false; +} + +void DivDispatch::renderSamples(int sysID) { } diff --git a/src/engine/platform/es5506.cpp b/src/engine/platform/es5506.cpp index cd4da7ed..45547371 100644 --- a/src/engine/platform/es5506.cpp +++ b/src/engine/platform/es5506.cpp @@ -1191,13 +1191,25 @@ size_t DivPlatformES5506::getSampleMemUsage(int index) { return index == 0 ? sampleMemLen : 0; } -void DivPlatformES5506::renderSamples() { +bool DivPlatformES5506::isSampleLoaded(int index, int sample) { + if (index!=0) return false; + if (sample<0 || sample>255) return false; + return sampleLoaded[sample]; +} + +void DivPlatformES5506::renderSamples(int sysID) { memset(sampleMem,0,getSampleMemCapacity()); memset(sampleOffES5506,0,256*sizeof(unsigned int)); + memset(sampleLoaded,0,256*sizeof(bool)); size_t memPos=128; // add silent at begin and end of each bank for reverse playback for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOffES5506[i]=0; + continue; + } + unsigned int length=s->length16; // fit sample size to single bank size if (length>(4194304-128)) { @@ -1217,6 +1229,7 @@ void DivPlatformES5506::renderSamples() { memcpy(sampleMem+(memPos/sizeof(short)),s->data16,length); } sampleOffES5506[i]=memPos; + sampleLoaded[i]=true; memPos+=length; } sampleMemLen=memPos+256; diff --git a/src/engine/platform/es5506.h b/src/engine/platform/es5506.h index d7b88c8b..626f014b 100644 --- a/src/engine/platform/es5506.h +++ b/src/engine/platform/es5506.h @@ -249,6 +249,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf { signed short* sampleMem; // ES5506 uses 16 bit data bus for samples size_t sampleMemLen; unsigned int sampleOffES5506[256]; + bool sampleLoaded[256]; struct QueuedHostIntf { unsigned char state; unsigned char step; @@ -335,7 +336,8 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf { virtual const void* getSampleMem(int index = 0) override; virtual size_t getSampleMemCapacity(int index = 0) override; virtual size_t getSampleMemUsage(int index = 0) override; - virtual void renderSamples() override; + virtual bool isSampleLoaded(int index, int sample) override; + virtual void renderSamples(int sysID) override; virtual const char** getRegisterSheet() override; virtual int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) override; virtual void quit() override; diff --git a/src/engine/platform/msm6258.cpp b/src/engine/platform/msm6258.cpp index 17e88ae6..7111c84c 100644 --- a/src/engine/platform/msm6258.cpp +++ b/src/engine/platform/msm6258.cpp @@ -362,8 +362,15 @@ size_t DivPlatformMSM6258::getSampleMemUsage(int index) { return index == 0 ? adpcmMemLen : 0; } -void DivPlatformMSM6258::renderSamples() { +bool DivPlatformMSM6258::isSampleLoaded(int index, int sample) { + if (index!=0) return false; + if (sample<0 || sample>255) return false; + return sampleLoaded[sample]; +} + +void DivPlatformMSM6258::renderSamples(int sysID) { memset(adpcmMem,0,getSampleMemCapacity(0)); + memset(sampleLoaded,0,256*sizeof(bool)); // sample data size_t memPos=0; @@ -371,6 +378,8 @@ void DivPlatformMSM6258::renderSamples() { if (sampleCount>128) sampleCount=128; for (int i=0; isong.sample[i]; + if (!s->renderOn[0][sysID]) continue; + int paddedLen=s->lengthVOX; if (memPos>=getSampleMemCapacity(0)) { logW("out of ADPCM memory for sample %d!",i); @@ -381,6 +390,7 @@ void DivPlatformMSM6258::renderSamples() { logW("out of ADPCM memory for sample %d!",i); } else { memcpy(adpcmMem+memPos,s->dataVOX,paddedLen); + sampleLoaded[i]=true; } memPos+=paddedLen; } diff --git a/src/engine/platform/msm6258.h b/src/engine/platform/msm6258.h index 02ec7e14..9a5cd7d7 100644 --- a/src/engine/platform/msm6258.h +++ b/src/engine/platform/msm6258.h @@ -81,6 +81,7 @@ class DivPlatformMSM6258: public DivDispatch { unsigned char* adpcmMem; size_t adpcmMemLen; + bool sampleLoaded[256]; unsigned char sampleBank, msmPan, msmDivider, rateSel, msmClock, clockSel; signed char msmDividerCount, msmClockCount; short msmOut; @@ -113,7 +114,8 @@ class DivPlatformMSM6258: public DivDispatch { const void* getSampleMem(int index); size_t getSampleMemCapacity(int index); size_t getSampleMemUsage(int index); - void renderSamples(); + bool isSampleLoaded(int index, int sample); + void renderSamples(int chipID); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); diff --git a/src/engine/platform/msm6295.cpp b/src/engine/platform/msm6295.cpp index d46a9bb6..71b8d7cf 100644 --- a/src/engine/platform/msm6295.cpp +++ b/src/engine/platform/msm6295.cpp @@ -335,11 +335,18 @@ size_t DivPlatformMSM6295::getSampleMemUsage(int index) { return index == 0 ? adpcmMemLen : 0; } -void DivPlatformMSM6295::renderSamples() { +bool DivPlatformMSM6295::isSampleLoaded(int index, int sample) { + if (index!=0) return false; + if (sample<0 || sample>255) return false; + return sampleLoaded[sample]; +} + +void DivPlatformMSM6295::renderSamples(int sysID) { unsigned int sampleOffVOX[256]; memset(adpcmMem,0,getSampleMemCapacity(0)); memset(sampleOffVOX,0,256*sizeof(unsigned int)); + memset(sampleLoaded,0,256*sizeof(bool)); // sample data size_t memPos=128*8; @@ -347,6 +354,11 @@ void DivPlatformMSM6295::renderSamples() { if (sampleCount>128) sampleCount=128; for (int i=0; isong.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOffVOX[i]=0; + continue; + } + int paddedLen=s->lengthVOX; if (memPos>=getSampleMemCapacity(0)) { logW("out of ADPCM memory for sample %d!",i); @@ -357,6 +369,7 @@ void DivPlatformMSM6295::renderSamples() { logW("out of ADPCM memory for sample %d!",i); } else { memcpy(adpcmMem+memPos,s->dataVOX,paddedLen); + sampleLoaded[i]=true; } sampleOffVOX[i]=memPos; memPos+=paddedLen; diff --git a/src/engine/platform/msm6295.h b/src/engine/platform/msm6295.h index 40d35757..9d47824a 100644 --- a/src/engine/platform/msm6295.h +++ b/src/engine/platform/msm6295.h @@ -68,6 +68,7 @@ class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf { unsigned char* adpcmMem; size_t adpcmMemLen; + bool sampleLoaded[256]; unsigned char sampleBank; int delay, updateOsc; @@ -101,7 +102,8 @@ class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf { virtual const void* getSampleMem(int index) override; virtual size_t getSampleMemCapacity(int index) override; virtual size_t getSampleMemUsage(int index) override; - virtual void renderSamples() override; + virtual bool isSampleLoaded(int index, int sample) override; + virtual void renderSamples(int chipID) override; virtual int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) override; virtual void quit() override; diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 47d81573..c971d4bf 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -721,12 +721,24 @@ size_t DivPlatformNES::getSampleMemUsage(int index) { return index==0?dpcmMemLen:0; } -void DivPlatformNES::renderSamples() { - memset(dpcmMem,0,getSampleMemCapacity(0)); +bool DivPlatformNES::isSampleLoaded(int index, int sample) { + if (index!=0) return false; + if (sample<0 || sample>255) return false; + return sampleLoaded[sample]; +} + +void DivPlatformNES::renderSamples(int sysID) { + memset(dpcmMem,0,getSampleMemCapacity(0));\ + memset(sampleLoaded,0,256*sizeof(bool)); size_t memPos=0; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOffDPCM[i]=0; + continue; + } + unsigned int paddedLen=(s->lengthDPCM+63)&(~0x3f); logV("%d padded length: %d",i,paddedLen); if ((memPos&(~0x3fff))!=((memPos+paddedLen)&(~0x3fff))) { @@ -744,6 +756,7 @@ void DivPlatformNES::renderSamples() { logW("out of DPCM memory for sample %d!",i); } else { memcpy(dpcmMem+memPos,s->dataDPCM,MIN(s->lengthDPCM,paddedLen)); + sampleLoaded[i]=true; } sampleOffDPCM[i]=memPos; memPos+=paddedLen; diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index 16f52f20..4dbfcec3 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -68,6 +68,7 @@ class DivPlatformNES: public DivDispatch { int dacSample; unsigned char* dpcmMem; size_t dpcmMemLen; + bool sampleLoaded[256]; unsigned char dpcmBank; unsigned char sampleBank; unsigned char writeOscBuf; @@ -115,7 +116,8 @@ class DivPlatformNES: public DivDispatch { const void* getSampleMem(int index); size_t getSampleMemCapacity(int index); size_t getSampleMemUsage(int index); - void renderSamples(); + bool isSampleLoaded(int index, int sample); + void renderSamples(int chipID); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); ~DivPlatformNES(); diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index e7152bca..5353fa6c 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -1757,14 +1757,26 @@ size_t DivPlatformOPL::getSampleMemUsage(int index) { return (index==0 && adpcmChan>=0) ? adpcmBMemLen : 0; } -void DivPlatformOPL::renderSamples() { +bool DivPlatformOPL::isSampleLoaded(int index, int sample) { + if (index!=0) return false; + if (sample<0 || sample>255) return false; + return sampleLoaded[sample]; +} + +void DivPlatformOPL::renderSamples(int sysID) { if (adpcmChan<0) return; memset(adpcmBMem,0,getSampleMemCapacity(0)); memset(sampleOffB,0,256*sizeof(unsigned int)); + memset(sampleLoaded,0,256*sizeof(bool)); size_t memPos=0; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOffB[i]=0; + continue; + } + int paddedLen=(s->lengthB+255)&(~0xff); if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { memPos=(memPos+0xfffff)&0xf00000; @@ -1778,6 +1790,7 @@ void DivPlatformOPL::renderSamples() { logW("out of ADPCM memory for sample %d!",i); } else { memcpy(adpcmBMem+memPos,s->dataB,paddedLen); + sampleLoaded[i]=true; } sampleOffB[i]=memPos; memPos+=paddedLen; diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index 8fe8cb25..b8dee394 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -91,6 +91,7 @@ class DivPlatformOPL: public DivDispatch { size_t adpcmBMemLen; DivOPLAInterface iface; unsigned int sampleOffB[256]; + bool sampleLoaded[256]; ymfm::adpcm_b_engine* adpcmB; const unsigned char** slotsNonDrums; @@ -152,7 +153,8 @@ class DivPlatformOPL: public DivDispatch { const void* getSampleMem(int index); size_t getSampleMemCapacity(int index); size_t getSampleMemUsage(int index); - void renderSamples(); + bool isSampleLoaded(int index, int sample); + void renderSamples(int chipID); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); ~DivPlatformOPL(); diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index 264f3a7a..a8b51b10 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -247,7 +247,7 @@ void DivPlatformOPLL::tick(bool sysTick) { if (chan[i].freq>65535) chan[i].freq=65535; int freqt=toFreq(chan[i].freq); chan[i].freqL=freqt&0xff; - if (i>=6 && properDrums) { + if (i>=6 && properDrums && (i<9 || !noTopHatFreq)) { immWrite(0x10+drumSlot[i],freqt&0xff); immWrite(0x20+drumSlot[i],freqt>>8); } else if (i<6 || !drums) { @@ -963,6 +963,7 @@ void DivPlatformOPLL::setFlags(const DivConfig& flags) { for (int i=0; i<11; i++) { oscBuf[i]->rate=rate/2; } + noTopHatFreq=flags.getBool("noTopHatFreq",false); } int DivPlatformOPLL::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 390d79a9..8dd8743d 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -82,7 +82,7 @@ class DivPlatformOPLL: public DivDispatch { bool useYMFM; bool drums; - bool properDrums, properDrumsSys; + bool properDrums, properDrumsSys, noTopHatFreq; bool vrc7; unsigned char patchSet; diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index b40fa92d..e8a308f1 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -252,6 +252,11 @@ void DivPlatformPCE::tick(bool sysTick) { chan[i].freqChanged=false; } } + if (updateLFO) { + rWrite(0x08,lfoSpeed); + rWrite(0x09,lfoMode); + updateLFO=false; + } } int DivPlatformPCE::dispatch(DivCommand c) { @@ -389,13 +394,11 @@ int DivPlatformPCE::dispatch(DivCommand c) { } else { lfoMode=c.value; } - rWrite(0x08,lfoSpeed); - rWrite(0x09,lfoMode); + updateLFO=true; break; case DIV_CMD_PCE_LFO_SPEED: lfoSpeed=255-c.value; - rWrite(0x08,lfoSpeed); - rWrite(0x09,lfoMode); + updateLFO=true; break; case DIV_CMD_NOTE_PORTA: { int destFreq=NOTE_PERIODIC(c.value2); @@ -525,8 +528,7 @@ void DivPlatformPCE::reset() { rWrite(0,0); rWrite(0x01,0xff); // set LFO - rWrite(0x08,lfoSpeed); - rWrite(0x09,lfoMode); + updateLFO=true; // set per-channel initial panning for (int i=0; i<6; i++) { chWrite(i,0x05,isMuted[i]?0:chan[i].pan); @@ -588,6 +590,7 @@ int DivPlatformPCE::init(DivEngine* p, int channels, int sugRate, const DivConfi parent=p; dumpWrites=false; skipRegisterWrites=false; + updateLFO=false; for (int i=0; i<6; i++) { isMuted[i]=false; oscBuf[i]=new DivDispatchOscBuffer; diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index a7878310..5645586b 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -76,6 +76,7 @@ class DivPlatformPCE: public DivDispatch { DivDispatchOscBuffer* oscBuf[6]; bool isMuted[6]; bool antiClickEnabled; + bool updateLFO; struct QueuedWrite { unsigned char addr; unsigned char val; diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index 31525528..fce9f783 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -644,13 +644,25 @@ size_t DivPlatformQSound::getSampleMemUsage(int index) { return index == 0 ? sampleMemLen : 0; } +bool DivPlatformQSound::isSampleLoaded(int index, int sample) { + if (index!=0) return false; + if (sample<0 || sample>255) return false; + return sampleLoaded[sample]; +} + // TODO: ADPCM... come on... -void DivPlatformQSound::renderSamples() { +void DivPlatformQSound::renderSamples(int sysID) { memset(sampleMem,0,getSampleMemCapacity()); + memset(sampleLoaded,0,256*sizeof(bool)); size_t memPos=0; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; + if (!s->renderOn[0][sysID]) { + offPCM[i]=0; + continue; + } + int length=s->length8; if (length>65536-16) { length=65536-16; @@ -671,6 +683,7 @@ void DivPlatformQSound::renderSamples() { for (int i=0; idata8[i]; } + sampleLoaded[i]=true; } offPCM[i]=memPos^0x8000; memPos+=length+16; diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index 89488627..1f911a83 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -69,6 +69,7 @@ class DivPlatformQSound: public DivDispatch { unsigned char* sampleMem; size_t sampleMemLen; + bool sampleLoaded[256]; struct qsound_chip chip; unsigned short regPool[512]; @@ -103,7 +104,8 @@ class DivPlatformQSound: public DivDispatch { const void* getSampleMem(int index = 0); size_t getSampleMemCapacity(int index = 0); size_t getSampleMemUsage(int index = 0); - void renderSamples(); + bool isSampleLoaded(int index, int sample); + void renderSamples(int chipID); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); }; diff --git a/src/engine/platform/rf5c68.cpp b/src/engine/platform/rf5c68.cpp index d0786896..e33cfbf3 100644 --- a/src/engine/platform/rf5c68.cpp +++ b/src/engine/platform/rf5c68.cpp @@ -385,13 +385,25 @@ size_t DivPlatformRF5C68::getSampleMemUsage(int index) { return index == 0 ? sampleMemLen : 0; } -void DivPlatformRF5C68::renderSamples() { +bool DivPlatformRF5C68::isSampleLoaded(int index, int sample) { + if (index!=0) return false; + if (sample<0 || sample>255) return false; + return sampleLoaded[sample]; +} + +void DivPlatformRF5C68::renderSamples(int sysID) { memset(sampleMem,0,getSampleMemCapacity()); memset(sampleOffRFC,0,256*sizeof(unsigned int)); + memset(sampleLoaded,0,256*sizeof(bool)); size_t memPos=0; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOffRFC[i]=0; + continue; + } + int length=s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT); int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-31,length); if (actualLength>0) { @@ -412,6 +424,7 @@ void DivPlatformRF5C68::renderSamples() { } // align memPos to 256-byte boundary memPos=(memPos+0xff)&~0xff; + sampleLoaded[i]=true; } sampleMemLen=memPos; } diff --git a/src/engine/platform/rf5c68.h b/src/engine/platform/rf5c68.h index 4c64c2d6..5af36c03 100644 --- a/src/engine/platform/rf5c68.h +++ b/src/engine/platform/rf5c68.h @@ -67,6 +67,7 @@ class DivPlatformRF5C68: public DivDispatch { int chipType; unsigned char curChan; unsigned int sampleOffRFC[256]; + bool sampleLoaded[256]; unsigned char* sampleMem; size_t sampleMemLen; @@ -99,7 +100,8 @@ class DivPlatformRF5C68: public DivDispatch { const void* getSampleMem(int index = 0); size_t getSampleMemCapacity(int index = 0); size_t getSampleMemUsage(int index = 0); - void renderSamples(); + bool isSampleLoaded(int index, int sample); + void renderSamples(int chipID); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); private: diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index acbecf68..7f48bca4 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -71,10 +71,17 @@ void DivPlatformSAA1099::acquire_saaSound(short* bufL, short* bufR, size_t start writes.pop(); } saa_saaSound->GenerateMany((unsigned char*)saaBuf[0],len,oscBuf); +#ifdef TA_BIG_ENDIAN + for (size_t i=0; i>8)); + bufR[i+start]=(short)((((unsigned short)saaBuf[0][i<<1])<<8)|(((unsigned short)saaBuf[0][i<<1])>>8)); + } +#else for (size_t i=0; isong.sampleLen; i++) { diff --git a/src/engine/platform/segapcm.h b/src/engine/platform/segapcm.h index 222f4bd9..2ed15eae 100644 --- a/src/engine/platform/segapcm.h +++ b/src/engine/platform/segapcm.h @@ -110,7 +110,7 @@ class DivPlatformSegaPCM: public DivDispatch { void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyInsChange(int ins); - void renderSamples(); + void renderSamples(int chipID); void setFlags(const DivConfig& flags); bool isStereo(); void poke(unsigned int addr, unsigned short val); diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index 60cb13c5..9f65a953 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -119,10 +119,29 @@ void DivPlatformSMS::acquire(short* bufL, short* bufR, size_t start, size_t len) } } +double DivPlatformSMS::NOTE_SN(int ch, int note) { + double CHIP_DIVIDER=toneDivider; + if (ch==3) CHIP_DIVIDER=noiseDivider; + if (parent->song.linearPitch==2 || !easyNoise) { + return NOTE_PERIODIC(note); + } + if (note>easyThreshold) { + return MAX(0,easyStartingPeriod-(note-easyThreshold)); + } + return NOTE_PERIODIC(note); +} + +int DivPlatformSMS::snCalcFreq(int ch) { + if (parent->song.linearPitch==2 && easyNoise && chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2>(easyThreshold<<7)) { + int ret=(((easyStartingPeriod<<7)+0x40)-(chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2-(easyThreshold<<7)))>>7; + if (ret<0) ret=0; + return ret; + } + return parent->calcFreq(chan[ch].baseFreq,chan[ch].pitch,true,0,chan[ch].pitch2,chipClock,ch==3?noiseDivider:toneDivider); +} + void DivPlatformSMS::tick(bool sysTick) { for (int i=0; i<4; i++) { - double CHIP_DIVIDER=toneDivider; - if (i==3) CHIP_DIVIDER=noiseDivider; chan[i].std.next(); if (chan[i].std.vol.had) { chan[i].outVol=VOL_SCALE_LOG_BROKEN(chan[i].std.vol.val,chan[i].vol,15); @@ -137,7 +156,7 @@ void DivPlatformSMS::tick(bool sysTick) { // TODO: add compatibility flag. this is horrible. int areYouSerious=parent->calcArp(chan[i].note,chan[i].std.arp.val); while (areYouSerious>0x60) areYouSerious-=12; - chan[i].baseFreq=NOTE_PERIODIC(areYouSerious); + chan[i].baseFreq=NOTE_SN(i,areYouSerious); chan[i].actualNote=areYouSerious; chan[i].freqChanged=true; } @@ -177,7 +196,7 @@ void DivPlatformSMS::tick(bool sysTick) { } for (int i=0; i<3; i++) { if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,toneDivider); + chan[i].freq=snCalcFreq(i); if (chan[i].freq>1023) chan[i].freq=1023; if (parent->song.snNoLowPeriods) { if (chan[i].freq<8) chan[i].freq=1; @@ -196,7 +215,8 @@ void DivPlatformSMS::tick(bool sysTick) { } } if (chan[3].freqChanged || updateSNMode) { - chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true,0,chan[3].pitch2,chipClock,noiseDivider); + chan[3].freq=snCalcFreq(3); + //parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true,0,chan[3].pitch2,chipClock,noiseDivider); if (chan[3].freq>1023) chan[3].freq=1023; if (parent->song.snNoLowPeriods) { if (chan[3].actualNote>0x5d) chan[3].freq=0x01; @@ -244,12 +264,10 @@ void DivPlatformSMS::tick(bool sysTick) { } int DivPlatformSMS::dispatch(DivCommand c) { - double CHIP_DIVIDER=toneDivider; - if (c.chan==3) CHIP_DIVIDER=noiseDivider; switch (c.cmd) { case DIV_CMD_NOTE_ON: if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + chan[c.chan].baseFreq=NOTE_SN(c.chan,c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; chan[c.chan].actualNote=c.value; @@ -300,7 +318,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_PERIODIC(c.value2); + int destFreq=NOTE_SN(c.chan,c.value2); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -340,7 +358,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].baseFreq=NOTE_SN(c.chan,c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; chan[c.chan].actualNote=c.value; @@ -349,7 +367,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { 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 (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_SN(c.chan,chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: @@ -446,27 +464,42 @@ void DivPlatformSMS::setFlags(const DivConfig& flags) { switch (flags.getInt("clockSel",0)) { case 1: chipClock=COLOR_PAL*4.0/5.0; + easyThreshold=84; + easyStartingPeriod=13; break; case 2: chipClock=4000000; + easyThreshold=86; + easyStartingPeriod=13; break; case 3: chipClock=COLOR_NTSC/2.0; + easyThreshold=72; + easyStartingPeriod=13; break; case 4: chipClock=3000000; + easyThreshold=81; + easyStartingPeriod=13; break; case 5: chipClock=2000000; + easyThreshold=74; + easyStartingPeriod=13; break; case 6: chipClock=COLOR_NTSC/8.0; + easyThreshold=48; + easyStartingPeriod=13; break; default: chipClock=COLOR_NTSC; + easyThreshold=84; + easyStartingPeriod=13; break; } resetPhase=!flags.getBool("noPhaseReset",false); + easyNoise=!flags.getBool("noEasyNoise",false); divider=16; toneDivider=64.0; noiseDivider=64.0; diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index e636b9d8..ce3ee5e7 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -65,11 +65,14 @@ class DivPlatformSMS: public DivDispatch { int divider=16; double toneDivider=64.0; double noiseDivider=64.0; + int easyThreshold; + int easyStartingPeriod; bool updateSNMode; bool resetPhase; bool isRealSN; bool stereo; bool nuked; + bool easyNoise; sn76496_base_device* sn; ympsg_t sn_nuked; struct QueuedWrite { @@ -82,6 +85,9 @@ class DivPlatformSMS: public DivDispatch { friend void putDispatchChip(void*,int); friend void putDispatchChan(void*,int,int); + double NOTE_SN(int ch, int note); + int snCalcFreq(int ch); + void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len); void acquire_mame(short* bufL, short* bufR, size_t start, size_t len); public: diff --git a/src/engine/platform/snes.cpp b/src/engine/platform/snes.cpp index f7790e42..2dd64de3 100644 --- a/src/engine/platform/snes.cpp +++ b/src/engine/platform/snes.cpp @@ -797,14 +797,26 @@ size_t DivPlatformSNES::getSampleMemUsage(int index) { return index == 0 ? sampleMemLen : 0; } -void DivPlatformSNES::renderSamples() { +bool DivPlatformSNES::isSampleLoaded(int index, int sample) { + if (index!=0) return false; + if (sample<0 || sample>255) return false; + return sampleLoaded[sample]; +} + +void DivPlatformSNES::renderSamples(int sysID) { memset(copyOfSampleMem,0,getSampleMemCapacity()); memset(sampleOff,0,256*sizeof(unsigned int)); + memset(sampleLoaded,0,256*sizeof(bool)); // skip past sample table and wavetable buffer size_t memPos=sampleTableBase+8*4+8*9*16; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOff[i]=0; + continue; + } + int length=s->lengthBRR; int actualLength=MIN((int)(getSampleMemCapacity()-memPos)/9*9,length); if (actualLength>0) { @@ -822,6 +834,7 @@ void DivPlatformSNES::renderSamples() { logW("out of BRR memory for sample %d!",i); break; } + sampleLoaded[i]=true; } sampleMemLen=memPos; memcpy(sampleMem,copyOfSampleMem,65536); diff --git a/src/engine/platform/snes.h b/src/engine/platform/snes.h index a21db150..ed8bfbf3 100644 --- a/src/engine/platform/snes.h +++ b/src/engine/platform/snes.h @@ -109,6 +109,7 @@ class DivPlatformSNES: public DivDispatch { signed char copyOfSampleMem[65536]; size_t sampleMemLen; unsigned int sampleOff[256]; + bool sampleLoaded[256]; unsigned char regPool[0x80]; SPC_DSP dsp; friend void putDispatchChan(void*,int,int); @@ -136,7 +137,8 @@ class DivPlatformSNES: public DivDispatch { const void* getSampleMem(int index = 0); size_t getSampleMemCapacity(int index = 0); size_t getSampleMemUsage(int index = 0); - void renderSamples(); + bool isSampleLoaded(int index, int sample); + void renderSamples(int chipID); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); private: diff --git a/src/engine/platform/sound/su.cpp b/src/engine/platform/sound/su.cpp index a8720545..4d98892c 100644 --- a/src/engine/platform/sound/su.cpp +++ b/src/engine/platform/sound/su.cpp @@ -391,7 +391,18 @@ void SoundUnit::Reset() { memset(chan,0,sizeof(SUChannel)*8); } +#ifdef TA_BIG_ENDIAN +const unsigned char suBERemap[32]={ + 0x01, 0x00, 0x02, 0x03, 0x05, 0x04, 0x07, 0x06, 0x08, 0x09, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, + 0x11, 0x10, 0x12, 0x13, 0x15, 0x14, 0x16, 0x17, 0x19, 0x18, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f, 0x1e +}; +#endif + void SoundUnit::Write(unsigned char addr, unsigned char data) { +#ifdef TA_BIG_ENDIAN + // remap + addr=(addr&0xe0)|(suBERemap[addr&0x1f]); +#endif ((unsigned char*)chan)[addr]=data; } diff --git a/src/engine/platform/sound/ymz280b.cpp b/src/engine/platform/sound/ymz280b.cpp index 74fafe67..4ab0e87e 100644 --- a/src/engine/platform/sound/ymz280b.cpp +++ b/src/engine/platform/sound/ymz280b.cpp @@ -286,6 +286,8 @@ int ymz280b_device::generate_pcm8(struct YMZ280BVoice *voice, s16 *buffer, int s ***********************************************************************************************/ +// according to this core, it should be little-endian. +// but it's big-endian in VGMPlay... int ymz280b_device::generate_pcm16(struct YMZ280BVoice *voice, s16 *buffer, int samples) { u32 position = voice->position; @@ -298,7 +300,7 @@ int ymz280b_device::generate_pcm16(struct YMZ280BVoice *voice, s16 *buffer, int while (samples) { /* fetch the current value */ - val = (s16)((m_ext_mem[position / 2 + 1] << 8) + m_ext_mem[position / 2 + 0]); + val = (s16)((m_ext_mem[position / 2 + 0] << 8) + m_ext_mem[position / 2 + 1]); /* output to the buffer, scaling by the volume */ *buffer++ = val; @@ -321,7 +323,7 @@ int ymz280b_device::generate_pcm16(struct YMZ280BVoice *voice, s16 *buffer, int while (samples) { /* fetch the current value */ - val = (s16)((m_ext_mem[position / 2 + 1] << 8) + m_ext_mem[position / 2 + 0]); + val = (s16)((m_ext_mem[position / 2 + 0] << 8) + m_ext_mem[position / 2 + 1]); /* output to the buffer, scaling by the volume */ *buffer++ = val; diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index c00847fc..931225b4 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -524,7 +524,7 @@ void DivPlatformSoundUnit::setFlags(const DivConfig& flags) { sampleMemSize=flags.getInt("sampleMemSize",0); su->Init(sampleMemSize?65536:8192,flags.getBool("pdm",false)); - renderSamples(); + renderSamples(sysIDCache); } void DivPlatformSoundUnit::poke(unsigned int addr, unsigned short val) { @@ -547,14 +547,26 @@ size_t DivPlatformSoundUnit::getSampleMemUsage(int index) { return (index==0)?sampleMemLen:0; } -void DivPlatformSoundUnit::renderSamples() { +bool DivPlatformSoundUnit::isSampleLoaded(int index, int sample) { + if (index!=0) return false; + if (sample<0 || sample>255) return false; + return sampleLoaded[sample]; +} + +void DivPlatformSoundUnit::renderSamples(int sysID) { memset(su->pcm,0,getSampleMemCapacity(0)); memset(sampleOffSU,0,256*sizeof(unsigned int)); + memset(sampleLoaded,0,256*sizeof(bool)); size_t memPos=0; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; if (s->data8==NULL) continue; + if (!s->renderOn[0][sysID]) { + sampleOffSU[i]=0; + continue; + } + int paddedLen=s->length8; if (memPos>=getSampleMemCapacity(0)) { logW("out of PCM memory for sample %d!",i); @@ -565,12 +577,13 @@ void DivPlatformSoundUnit::renderSamples() { logW("out of PCM memory for sample %d!",i); } else { memcpy(su->pcm+memPos,s->data8,paddedLen); + sampleLoaded[i]=true; } sampleOffSU[i]=memPos; memPos+=paddedLen; } sampleMemLen=memPos; - + sysIDCache=sysID; } int DivPlatformSoundUnit::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { @@ -582,6 +595,7 @@ int DivPlatformSoundUnit::init(DivEngine* p, int channels, int sugRate, const Di oscBuf[i]=new DivDispatchOscBuffer; } su=new SoundUnit(); + sysIDCache=0; setFlags(flags); reset(); return 8; diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h index 004c04c5..bacdbc01 100644 --- a/src/engine/platform/su.h +++ b/src/engine/platform/su.h @@ -102,8 +102,9 @@ class DivPlatformSoundUnit: public DivDispatch { unsigned char initIlCtrl, initIlSize, initFil1; signed char echoVol, initEchoVol; unsigned int sampleOffSU[256]; + bool sampleLoaded[256]; - int cycles, curChan, delay; + int cycles, curChan, delay, sysIDCache; short tempL; short tempR; unsigned char sampleBank, lfoMode, lfoSpeed; @@ -138,7 +139,8 @@ class DivPlatformSoundUnit: public DivDispatch { const void* getSampleMem(int index); size_t getSampleMemCapacity(int index); size_t getSampleMemUsage(int index); - void renderSamples(); + bool isSampleLoaded(int index, int sample); + void renderSamples(int chipID); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); ~DivPlatformSoundUnit(); diff --git a/src/engine/platform/t6w28.cpp b/src/engine/platform/t6w28.cpp index 0f4dbf82..c6317e11 100644 --- a/src/engine/platform/t6w28.cpp +++ b/src/engine/platform/t6w28.cpp @@ -81,10 +81,29 @@ void DivPlatformT6W28::writeOutVol(int ch) { } } +double DivPlatformT6W28::NOTE_SN(int ch, int note) { + double CHIP_DIVIDER=16; + if (ch==3) CHIP_DIVIDER=15; + if (parent->song.linearPitch==2 || !easyNoise) { + return NOTE_PERIODIC(note); + } + if (note>107) { + return MAX(0,13-(note-107)); + } + return NOTE_PERIODIC(note); +} + +int DivPlatformT6W28::snCalcFreq(int ch) { + if (parent->song.linearPitch==2 && easyNoise && chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2>(107<<7)) { + int ret=(((13<<7)+0x40)-(chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2-(107<<7)))>>7; + if (ret<0) ret=0; + return ret; + } + return parent->calcFreq(chan[ch].baseFreq,chan[ch].pitch,true,0,chan[ch].pitch2,chipClock,ch==3?15:16); +} + void DivPlatformT6W28::tick(bool sysTick) { for (int i=0; i<4; i++) { - double CHIP_DIVIDER=16; - if (i==3) CHIP_DIVIDER=15; chan[i].std.next(); if (chan[i].std.vol.had) { chan[i].outVol=VOL_SCALE_LOG_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15); @@ -92,7 +111,7 @@ void DivPlatformT6W28::tick(bool sysTick) { if (chan[i].std.arp.had) { if (!chan[i].inPorta) { int noiseSeek=parent->calcArp(chan[i].note,chan[i].std.arp.val); - chan[i].baseFreq=NOTE_PERIODIC(noiseSeek); + chan[i].baseFreq=NOTE_SN(i,noiseSeek); } chan[i].freqChanged=true; } @@ -124,7 +143,7 @@ void DivPlatformT6W28::tick(bool sysTick) { rWrite(1,0xe0+chan[i].duty); } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); + chan[i].freq=snCalcFreq(i); if (chan[i].freq>1023) chan[i].freq=1023; if (i==3) { rWrite(1,0x80|(2<<5)|(chan[3].freq&15)); @@ -141,13 +160,11 @@ void DivPlatformT6W28::tick(bool sysTick) { } int DivPlatformT6W28::dispatch(DivCommand c) { - double CHIP_DIVIDER=16; - if (c.chan==3) CHIP_DIVIDER=15; switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_PCE); if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + chan[c.chan].baseFreq=NOTE_SN(c.chan,c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; } @@ -197,7 +214,7 @@ int DivPlatformT6W28::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_PERIODIC(c.value2); + int destFreq=NOTE_SN(c.chan,c.value2); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -231,15 +248,15 @@ int DivPlatformT6W28::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].baseFreq=NOTE_SN(c.chan,c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; 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_PCE)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_T6W28)); } - if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_SN(c.chan,chan[c.chan].note); chan[c.chan].inPorta=c.value; break; case DIV_CMD_GET_VOLMAX: @@ -330,6 +347,7 @@ void DivPlatformT6W28::setFlags(const DivConfig& flags) { for (int i=0; i<4; i++) { oscBuf[i]->rate=rate; } + easyNoise=!flags.getBool("noEasyNoise",false); if (t6w!=NULL) { delete t6w; diff --git a/src/engine/platform/t6w28.h b/src/engine/platform/t6w28.h index 5b5f1fc8..2cd66f9f 100644 --- a/src/engine/platform/t6w28.h +++ b/src/engine/platform/t6w28.h @@ -60,6 +60,7 @@ class DivPlatformT6W28: public DivDispatch { DivDispatchOscBuffer* oscBuf[4]; bool isMuted[4]; bool antiClickEnabled; + bool easyNoise; struct QueuedWrite { unsigned char addr; unsigned char val; @@ -75,6 +76,10 @@ class DivPlatformT6W28: public DivDispatch { unsigned char regPool[128]; friend void putDispatchChip(void*,int); friend void putDispatchChan(void*,int,int); + + double NOTE_SN(int ch, int note); + int snCalcFreq(int ch); + void writeOutVol(int ch); public: void acquire(short* bufL, short* bufR, size_t start, size_t len); diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 6f220242..683b36a6 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -949,13 +949,25 @@ size_t DivPlatformX1_010::getSampleMemUsage(int index) { return index >= 0 ? sampleMemLen : 0; } -void DivPlatformX1_010::renderSamples() { +bool DivPlatformX1_010::isSampleLoaded(int index, int sample) { + if (index!=0) return false; + if (sample<0 || sample>255) return false; + return sampleLoaded[sample]; +} + +void DivPlatformX1_010::renderSamples(int sysID) { memset(sampleMem,0,getSampleMemCapacity()); memset(sampleOffX1,0,256*sizeof(unsigned int)); + memset(sampleLoaded,0,256*sizeof(bool)); size_t memPos=0; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOffX1[i]=0; + continue; + } + int paddedLen=(s->length8+4095)&(~0xfff); if (isBanked) { // fit sample bank size to 128KB for Seta 2 external bankswitching logic (not emulated yet!) @@ -975,6 +987,7 @@ void DivPlatformX1_010::renderSamples() { logW("out of X1-010 memory for sample %d!",i); } else { memcpy(sampleMem+memPos,s->data8,paddedLen); + sampleLoaded[i]=true; } sampleOffX1[i]=memPos; memPos+=paddedLen; diff --git a/src/engine/platform/x1_010.h b/src/engine/platform/x1_010.h index ff5557bb..d588d944 100644 --- a/src/engine/platform/x1_010.h +++ b/src/engine/platform/x1_010.h @@ -116,6 +116,7 @@ class DivPlatformX1_010: public DivDispatch, public vgsound_emu_mem_intf { bool isBanked=false; unsigned int bankSlot[8]; unsigned int sampleOffX1[256]; + bool sampleLoaded[256]; unsigned char regPool[0x2000]; double NoteX1_010(int ch, int note); @@ -146,7 +147,8 @@ class DivPlatformX1_010: public DivDispatch, public vgsound_emu_mem_intf { const void* getSampleMem(int index = 0); size_t getSampleMemCapacity(int index = 0); size_t getSampleMemUsage(int index = 0); - void renderSamples(); + bool isSampleLoaded(int index, int sample); + void renderSamples(int chipID); const char** getRegisterSheet(); void setBanked(bool banked); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index a58bad8d..63c84ad5 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -1336,13 +1336,25 @@ size_t DivPlatformYM2608::getSampleMemUsage(int index) { return index == 0 ? adpcmBMemLen : 0; } -void DivPlatformYM2608::renderSamples() { +bool DivPlatformYM2608::isSampleLoaded(int index, int sample) { + if (index!=0) return false; + if (sample<0 || sample>255) return false; + return sampleLoaded[sample]; +} + +void DivPlatformYM2608::renderSamples(int sysID) { memset(adpcmBMem,0,getSampleMemCapacity(0)); memset(sampleOffB,0,256*sizeof(unsigned int)); + memset(sampleLoaded,0,256*sizeof(bool)); size_t memPos=0; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOffB[i]=0; + continue; + } + int paddedLen=(s->lengthB+255)&(~0xff); if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { memPos=(memPos+0xfffff)&0xf00000; @@ -1356,6 +1368,7 @@ void DivPlatformYM2608::renderSamples() { logW("out of ADPCM memory for sample %d!",i); } else { memcpy(adpcmBMem+memPos,s->dataB,paddedLen); + sampleLoaded[i]=true; } sampleOffB[i]=memPos; memPos+=paddedLen; diff --git a/src/engine/platform/ym2608.h b/src/engine/platform/ym2608.h index f4c101c2..ef63fe63 100644 --- a/src/engine/platform/ym2608.h +++ b/src/engine/platform/ym2608.h @@ -100,6 +100,7 @@ class DivPlatformYM2608: public DivPlatformOPN { size_t adpcmBMemLen; DivYM2608Interface iface; unsigned int sampleOffB[256]; + bool sampleLoaded[256]; DivPlatformAY8910* ay; unsigned char sampleBank; @@ -137,7 +138,8 @@ class DivPlatformYM2608: public DivPlatformOPN { const void* getSampleMem(int index); size_t getSampleMemCapacity(int index); size_t getSampleMemUsage(int index); - void renderSamples(); + bool isSampleLoaded(int index, int sample); + void renderSamples(int chipID); void setFlags(const DivConfig& flags); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); diff --git a/src/engine/platform/ym2610shared.h b/src/engine/platform/ym2610shared.h index 4b69920c..065eb5f3 100644 --- a/src/engine/platform/ym2610shared.h +++ b/src/engine/platform/ym2610shared.h @@ -142,6 +142,8 @@ template class DivPlatformYM2610Base: public DivPlatformOPN { unsigned char sampleBank; bool extMode, noExtMacros; + + bool sampleLoaded[2][256]; unsigned char writeADPCMAOff, writeADPCMAOn; int globalADPCMAVolume; @@ -204,18 +206,34 @@ template class DivPlatformYM2610Base: public DivPlatformOPN { return index == 0 ? 16777216 : index == 1 ? 16777216 : 0; } + const char* getSampleMemName(int index=0) { + return index == 0 ? "ADPCM-A" : index == 1 ? "ADPCM-B" : NULL; + } + size_t getSampleMemUsage(int index) { return index == 0 ? adpcmAMemLen : index == 1 ? adpcmBMemLen : 0; } - void renderSamples() { + bool isSampleLoaded(int index, int sample) { + if (index<0 || index>1) return false; + if (sample<0 || sample>255) return false; + return sampleLoaded[index][sample]; + } + + void renderSamples(int sysID) { memset(adpcmAMem,0,getSampleMemCapacity(0)); memset(sampleOffA,0,256*sizeof(unsigned int)); memset(sampleOffB,0,256*sizeof(unsigned int)); + memset(sampleLoaded,0,256*2*sizeof(bool)); size_t memPos=0; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOffA[i]=0; + continue; + } + int paddedLen=(s->lengthA+255)&(~0xff); if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { memPos=(memPos+0xfffff)&0xf00000; @@ -229,6 +247,7 @@ template class DivPlatformYM2610Base: public DivPlatformOPN { logW("out of ADPCM-A memory for sample %d!",i); } else { memcpy(adpcmAMem+memPos,s->dataA,paddedLen); + sampleLoaded[0][i]=true; } sampleOffA[i]=memPos; memPos+=paddedLen; @@ -240,6 +259,11 @@ template class DivPlatformYM2610Base: public DivPlatformOPN { memPos=0; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; + if (!s->renderOn[1][sysID]) { + sampleOffB[i]=0; + continue; + } + int paddedLen=(s->lengthB+255)&(~0xff); if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { memPos=(memPos+0xfffff)&0xf00000; @@ -253,6 +277,7 @@ template class DivPlatformYM2610Base: public DivPlatformOPN { logW("out of ADPCM-B memory for sample %d!",i); } else { memcpy(adpcmBMem+memPos,s->dataB,paddedLen); + sampleLoaded[1][i]=true; } sampleOffB[i]=memPos; memPos+=paddedLen; diff --git a/src/engine/platform/ymz280b.cpp b/src/engine/platform/ymz280b.cpp index b2797cd6..8a903ef0 100644 --- a/src/engine/platform/ymz280b.cpp +++ b/src/engine/platform/ymz280b.cpp @@ -420,18 +420,40 @@ size_t DivPlatformYMZ280B::getSampleMemUsage(int index) { return index == 0 ? sampleMemLen : 0; } -void DivPlatformYMZ280B::renderSamples() { +bool DivPlatformYMZ280B::isSampleLoaded(int index, int sample) { + if (index!=0) return false; + if (sample<0 || sample>255) return false; + return sampleLoaded[sample]; +} + +void DivPlatformYMZ280B::renderSamples(int sysID) { memset(sampleMem,0,getSampleMemCapacity()); memset(sampleOff,0,256*sizeof(unsigned int)); + memset(sampleLoaded,0,256*sizeof(bool)); size_t memPos=0; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; + if (!s->renderOn[0][sysID]) { + sampleOff[i]=0; + continue; + } + int length=s->getCurBufLen(); unsigned char* src=(unsigned char*)s->getCurBuf(); int actualLength=MIN((int)(getSampleMemCapacity()-memPos),length); if (actualLength>0) { +#ifdef TA_BIG_ENDIAN memcpy(&sampleMem[memPos],src,actualLength); +#else + if (s->depth==DIV_SAMPLE_DEPTH_16BIT) { + for (int i=0; iwrite("SMP2",4); + blockStartSeek=w->tell(); + w->writeI(0); + + w->writeString(name,false); + w->writeI(samples); + w->writeI(rate); + w->writeI(centerRate); + w->writeC(depth); + w->writeC(loopMode); + w->writeC(0); // reserved + w->writeC(0); + w->writeI(loop?loopStart:-1); + w->writeI(loop?loopEnd:-1); + + for (int i=0; i<4; i++) { + w->writeI(0xffffffff); + } + +#ifdef TA_BIG_ENDIAN + // store 16-bit samples as little-endian + if (depth==DIV_SAMPLE_DEPTH_16BIT) { + unsigned char* sampleBuf=(unsigned char*)getCurBuf(); + size_t bufLen=getCurBufLen(); + for (size_t i=0; iwriteC(sampleBuf[i+1]); + w->writeC(sampleBuf[i]); + } + } else { + w->write(getCurBuf(),getCurBufLen()); + } +#else + w->write(getCurBuf(),getCurBufLen()); +#endif + + blockEndSeek=w->tell(); + w->seek(blockStartSeek,SEEK_SET); + w->writeI(blockEndSeek-blockStartSeek-4); + w->seek(0,SEEK_END); +} + +// Delek why +static double samplePitchesSD[11]={ + 0.1666666666, 0.2, 0.25, 0.333333333, 0.5, + 1, + 2, 3, 4, 5, 6 +}; + +DivDataErrors DivSample::readSampleData(SafeReader& reader, short version) { + int vol=0; + int pitch=0; + char magic[4]; + + reader.read(magic,4); + if (memcmp(magic,"SMPL",4)!=0 && memcmp(magic,"SMP2",4)!=0) { + logV("header is invalid: %c%c%c%c",magic[0],magic[1],magic[2],magic[3]); + return DIV_DATA_INVALID_HEADER; + } + bool isNewSample=(memcmp(magic,"SMP2",4)==0); + reader.readI(); + if (!isNewSample) logV("(old sample)"); + + name=reader.readString(); + samples=reader.readI(); + if (!isNewSample) { + loopEnd=samples; + } + rate=reader.readI(); + + if (isNewSample) { + centerRate=reader.readI(); + depth=(DivSampleDepth)reader.readC(); + if (version>=123) { + loopMode=(DivSampleLoopMode)reader.readC(); + } else { + loopMode=DIV_SAMPLE_LOOP_FORWARD; + reader.readC(); + } + + // reserved + reader.readC(); + reader.readC(); + + loopStart=reader.readI(); + loopEnd=reader.readI(); + loop=(loopStart>=0)&&(loopEnd>=0); + + for (int i=0; i<4; i++) { + reader.readI(); + } + } else { + if (version<58) { + vol=reader.readS(); + pitch=reader.readS(); + } else { + reader.readI(); + } + depth=(DivSampleDepth)reader.readC(); + + // reserved + reader.readC(); + + // while version 32 stored this value, it was unused. + if (version>=38) { + centerRate=(unsigned short)reader.readS(); + } else { + reader.readS(); + } + + if (version>=19) { + loopStart=reader.readI(); + loop=(loopStart>=0)&&(loopEnd>=0); + } else { + reader.readI(); + } + } + + if (version>=58) { // modern sample + init(samples); + reader.read(getCurBuf(),getCurBufLen()); +#ifdef TA_BIG_ENDIAN + // convert 16-bit samples to big-endian + if (depth==DIV_SAMPLE_DEPTH_16BIT) { + unsigned char* sampleBuf=(unsigned char*)getCurBuf(); + size_t sampleBufLen=getCurBufLen(); + for (size_t pos=0; pos>8)|((unsigned short)data[pos]<<8); + } +#endif + + if (pitch!=5) { + logD("scaling from %d...",pitch); + } + + // render data + if (depth!=DIV_SAMPLE_DEPTH_8BIT && depth!=DIV_SAMPLE_DEPTH_16BIT) { + logW("sample depth is wrong! (%d)",depth); + depth=DIV_SAMPLE_DEPTH_16BIT; + } + samples=(double)samples/samplePitchesSD[pitch]; + init(samples); + + unsigned int k=0; + float mult=(float)(vol)/50.0f; + for (double j=0; j=samples) { + break; + } + if (depth==DIV_SAMPLE_DEPTH_8BIT) { + float next=(float)(data[(unsigned int)j]-0x80)*mult; + data8[k++]=fmin(fmax(next,-128),127); + } else { + float next=(float)data[(unsigned int)j]*mult; + data16[k++]=fmin(fmax(next,-32768),32767); + } + } + + delete[] data; + } + + return DIV_DATA_SUCCESS; +} + bool DivSample::isLoopable() { return loop && ((loopStart>=0 && loopStartloopStart && loopEnd<=(int)samples)); } diff --git a/src/engine/sample.h b/src/engine/sample.h index 4afac673..6b62a841 100644 --- a/src/engine/sample.h +++ b/src/engine/sample.h @@ -21,6 +21,8 @@ #define _SAMPLE_H #include "../ta-utils.h" +#include "safeWriter.h" +#include "dataErrors.h" #include enum DivSampleLoopMode: unsigned char { @@ -112,6 +114,8 @@ struct DivSample { // - 2: Pingpong loop DivSampleLoopMode loopMode; + bool renderOn[4][32]; + // these are the new data structures. signed char* data8; // 8 short* data16; // 16 @@ -131,6 +135,20 @@ struct DivSample { std::deque undoHist; std::deque redoHist; + /** + * put sample data. + * @param w a SafeWriter. + */ + void putSampleData(SafeWriter* w); + + /** + * read sample data. + * @param reader the reader. + * @param version the format version. + * @return a DivDataErrors. + */ + DivDataErrors readSampleData(SafeReader& reader, short version); + /** * check if sample is loopable. * @return whether it is loopable. @@ -310,7 +328,14 @@ struct DivSample { lengthB(0), lengthBRR(0), lengthVOX(0), - samples(0) {} + samples(0) { + for (int i=0; i<32; i++) { + renderOn[0][i]=true; + renderOn[1][i]=true; + renderOn[2][i]=true; + renderOn[3][i]=true; + } + } ~DivSample(); }; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index b04a2eae..ac7dbaf0 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1711,8 +1711,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_T6W28]=new DivSysDef( - // 0x0a = wild guess. it may as well be 0x83 - "T6W28", NULL, 0xbf, 0x0a, 4, false, true, 0x160, false, 0, + "T6W28", NULL, 0xbf, 0, 4, false, true, 0x160, false, 0, "an SN76489 derivative used in Neo Geo Pocket, has independent stereo volume and noise channel frequency.", {"Square 1", "Square 2", "Square 3", "Noise"}, {"S1", "S2", "S3", "NO"}, diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 3cc1cc5b..4d921219 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -1800,21 +1800,6 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p size_t sampleMemLen=writeZ280[i]->getSampleMemUsage(); unsigned char* sampleMem=new unsigned char[sampleMemLen]; memcpy(sampleMem,writeZ280[i]->getSampleMem(),sampleMemLen); - // TODO: please fix this later - /* - for (int i=0; idepth==DIV_SAMPLE_DEPTH_16BIT) { - unsigned int pos=s->offYMZ280B; - for (unsigned int j=0; jsamples; j++) { - unsigned char lo=sampleMem[pos+j*2]; - unsigned char hi=sampleMem[pos+j*2+1]; - sampleMem[pos+j*2]=hi; - sampleMem[pos+j*2+1]=lo; - } - } - } - */ w->writeC(0x67); w->writeC(0x66); w->writeC(0x86); diff --git a/src/engine/wavetable.cpp b/src/engine/wavetable.cpp index 9de8544f..e068f06a 100644 --- a/src/engine/wavetable.cpp +++ b/src/engine/wavetable.cpp @@ -48,6 +48,7 @@ DivDataErrors DivWavetable::readWaveData(SafeReader& reader, short version) { char magic[4]; reader.read(magic,4); if (memcmp(magic,"WAVE",4)!=0) { + logV("header is invalid: %c%c%c%c",magic[0],magic[1],magic[2],magic[3]); return DIV_DATA_INVALID_HEADER; } reader.readI(); // reserved @@ -57,7 +58,10 @@ DivDataErrors DivWavetable::readWaveData(SafeReader& reader, short version) { min=reader.readI(); max=reader.readI(); - if (len>256 || min!=0 || max>255) return DIV_DATA_INVALID_DATA; + if (len>256 || min!=0 || max>255) { + logV("invalid len/min/max: %d %d %d",len,min,max); + return DIV_DATA_INVALID_DATA; + } for (int i=0; igetTotalChannelCount(); for (int i=0; igetOscBuffer(i); + while (buf==NULL) { + if (--tryAgain<0) break; + buf=e->getOscBuffer(tryAgain); + } if (buf!=NULL && e->curSubSong->chanShow[i]) { // 30ms should be enough int displaySize=(float)(buf->rate)*0.03f; diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 20f66dd5..face7424 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -132,6 +132,9 @@ void FurnaceGUI::drawInsList(bool asChild) { if (ImGui::MenuItem("instrument")) { doAction(GUI_ACTION_INS_LIST_SAVE); } + if (ImGui::MenuItem("instrument (legacy .fui)")) { + doAction(GUI_ACTION_INS_LIST_SAVE_OLD); + } if (ImGui::MenuItem("instrument (.dmp)")) { doAction(GUI_ACTION_INS_LIST_SAVE_DMP); } @@ -151,6 +154,9 @@ void FurnaceGUI::drawInsList(bool asChild) { } } else { if (ImGui::BeginPopupContextItem("InsSaveFormats",ImGuiMouseButton_Right)) { + if (ImGui::MenuItem("save in legacy format...")) { + doAction(GUI_ACTION_INS_LIST_SAVE_OLD); + } if (ImGui::MenuItem("save as .dmp...")) { doAction(GUI_ACTION_INS_LIST_SAVE_DMP); } @@ -434,6 +440,9 @@ void FurnaceGUI::drawInsList(bool asChild) { if (ImGui::MenuItem("save")) { doAction(GUI_ACTION_INS_LIST_SAVE); } + if (ImGui::MenuItem("save (legacy .fui)")) { + doAction(GUI_ACTION_INS_LIST_SAVE_OLD); + } if (ImGui::MenuItem("save (.dmp)")) { doAction(GUI_ACTION_INS_LIST_SAVE_DMP); } diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 3c0a9d3c..143ec23a 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -387,6 +387,33 @@ void FurnaceGUI::drawDebug() { ImGui::Text("System Managed Scale: %d",sysManagedScale); ImGui::TreePop(); } + if (ImGui::TreeNode("Visualizer Debug")) { + if (ImGui::BeginTable("visX",3,ImGuiTableFlags_Borders)) { + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::Text("channel"); + ImGui::TableNextColumn(); + ImGui::Text("patChanX"); + ImGui::TableNextColumn(); + ImGui::Text("patChanSlideY"); + + for (int i=0; igetTotalChannelCount(); i++) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%d",i); + ImGui::TableNextColumn(); + ImGui::Text("%f",patChanX[i]); + ImGui::TableNextColumn(); + ImGui::Text("%f",patChanSlideY[i]); + } + + ImGui::EndTable(); + } + + ImGui::Text("particle count: %d",(int)particles.size()); + + ImGui::TreePop(); + } if (ImGui::TreeNode("Playground")) { if (pgSys<0 || pgSys>=e->song.systemLen) pgSys=0; if (ImGui::BeginCombo("Chip",fmt::sprintf("%d. %s",pgSys+1,e->getSystemName(e->song.system[pgSys])).c_str())) { diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 941d037c..14fb4466 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -599,6 +599,9 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_INS_LIST_SAVE: if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE); break; + case GUI_ACTION_INS_LIST_SAVE_OLD: + if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE_OLD); + break; case GUI_ACTION_INS_LIST_SAVE_DMP: if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE_DMP); break; diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index fe72351a..a7582ff3 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -204,13 +204,15 @@ void FurnaceGUI::drawMobileControls() { ImGui::SameLine(); ImGui::Button("Export Audio"); ImGui::SameLine(); - ImGui::Button("Export VGM"); + if (ImGui::Button("Export VGM")) { + openFileDialog(GUI_FILE_EXPORT_VGM); + } ImGui::Button("CmdStream"); ImGui::Separator(); - ImGui::Text("Song info here..."); + drawSongInfo(true); break; } case GUI_SCENE_CHANNELS: diff --git a/src/gui/fileDialog.cpp b/src/gui/fileDialog.cpp index c5103019..9b13fff0 100644 --- a/src/gui/fileDialog.cpp +++ b/src/gui/fileDialog.cpp @@ -80,6 +80,14 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector filter, c if (opened) return false; saving=false; curPath=path; + + // strip excess directory separators + while (!curPath.empty()) { + if (curPath[curPath.size()-1]!=DIR_SEPARATOR) break; + curPath.erase(curPath.size()-1); + } + curPath+=DIR_SEPARATOR; + logD("opening load file dialog with curPath %s",curPath.c_str()); if (sysDialog) { #ifdef USE_NFD @@ -145,6 +153,14 @@ bool FurnaceGUIFileDialog::openSave(String header, std::vector filter, c if (opened) return false; saving=true; curPath=path; + + // strip excess directory separators + while (!curPath.empty()) { + if (curPath[curPath.size()-1]!=DIR_SEPARATOR) break; + curPath.erase(curPath.size()-1); + } + curPath+=DIR_SEPARATOR; + logD("opening save file dialog with curPath %s",curPath.c_str()); if (sysDialog) { #ifdef USE_NFD diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 93c33368..ebd7dafa 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1474,8 +1474,12 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { workingDirIns, dpiScale, [this](const char* path) { - std::vector instruments=e->instrumentFromFile(path); + int sampleCountBefore=e->song.sampleLen; + std::vector instruments=e->instrumentFromFile(path,false); if (!instruments.empty()) { + if (e->song.sampleLen!=sampleCountBefore) { + e->renderSamplesP(); + } if (curFileDialog==GUI_FILE_INS_OPEN_REPLACE) { if (prevIns==-3) { prevIns=curIns; @@ -1506,6 +1510,16 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { dpiScale ); break; + case GUI_FILE_INS_SAVE_OLD: + if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); + hasOpened=fileDialog->openSave( + "Save Instrument", + {"Furnace instrument", "*.fui"}, + "Furnace instrument{.fui}", + workingDirIns, + dpiScale + ); + break; case GUI_FILE_INS_SAVE_DMP: if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); hasOpened=fileDialog->openSave( @@ -3059,10 +3073,14 @@ bool FurnaceGUI::loop() { break; case SDL_DROPFILE: if (ev.drop.file!=NULL) { + int sampleCountBefore=e->song.sampleLen; std::vector instruments=e->instrumentFromFile(ev.drop.file); DivWavetable* droppedWave=NULL; DivSample* droppedSample=NULL;; if (!instruments.empty()) { + if (e->song.sampleLen!=sampleCountBefore) { + e->renderSamplesP(); + } if (!e->getWarnings().empty()) { showWarning(e->getWarnings(),GUI_WARN_GENERIC); } @@ -3746,11 +3764,17 @@ bool FurnaceGUI::loop() { drawSampleEdit(); drawPiano(); break; + case GUI_SCENE_CHIPS: + sysManagerOpen=true; + curWindow=GUI_WINDOW_SYS_MANAGER; + drawSysManager(); + break; default: patternOpen=true; curWindow=GUI_WINDOW_PATTERN; drawPattern(); drawPiano(); + drawMobileOrderSel(); break; } @@ -3855,6 +3879,7 @@ bool FurnaceGUI::loop() { case GUI_FILE_INS_OPEN: case GUI_FILE_INS_OPEN_REPLACE: case GUI_FILE_INS_SAVE: + case GUI_FILE_INS_SAVE_OLD: case GUI_FILE_INS_SAVE_DMP: workingDirIns=fileDialog->getPath()+DIR_SEPARATOR_STR; break; @@ -3950,6 +3975,9 @@ bool FurnaceGUI::loop() { if (curFileDialog==GUI_FILE_INS_SAVE) { checkExtension(".fui"); } + if (curFileDialog==GUI_FILE_INS_SAVE_OLD) { + checkExtension(".fui"); + } if (curFileDialog==GUI_FILE_INS_SAVE_DMP) { checkExtension(".dmp"); } @@ -4041,7 +4069,12 @@ bool FurnaceGUI::loop() { break; case GUI_FILE_INS_SAVE: if (curIns>=0 && curIns<(int)e->song.ins.size()) { - e->song.ins[curIns]->save(copyOfName.c_str()); + e->song.ins[curIns]->save(copyOfName.c_str(),false,&e->song); + } + break; + case GUI_FILE_INS_SAVE_OLD: + if (curIns>=0 && curIns<(int)e->song.ins.size()) { + e->song.ins[curIns]->save(copyOfName.c_str(),true); } break; case GUI_FILE_INS_SAVE_DMP: @@ -4141,6 +4174,7 @@ bool FurnaceGUI::loop() { bool ask=false; bool warn=false; String warns="there were some warnings/errors while loading instruments:\n"; + int sampleCountBefore=e->song.sampleLen; for (String i: fileDialog->getFileName()) { std::vector insTemp=e->instrumentFromFile(i.c_str()); if (insTemp.empty()) { @@ -4155,6 +4189,9 @@ bool FurnaceGUI::loop() { instruments.push_back(j); } } + if (e->song.sampleLen!=sampleCountBefore) { + e->renderSamplesP(); + } if (warn) { if (instruments.empty()) { if (fileDialog->getFileName().size()>1) { @@ -4184,8 +4221,12 @@ bool FurnaceGUI::loop() { break; } case GUI_FILE_INS_OPEN_REPLACE: { + int sampleCountBefore=e->song.sampleLen; std::vector instruments=e->instrumentFromFile(copyOfName.c_str()); if (!instruments.empty()) { + if (e->song.sampleLen!=sampleCountBefore) { + e->renderSamplesP(); + } if (!e->getWarnings().empty()) { showWarning(e->getWarnings(),GUI_WARN_GENERIC); } @@ -5801,6 +5842,7 @@ FurnaceGUI::FurnaceGUI(): oldOrdersLen(0), sampleZoom(1.0), prevSampleZoom(1.0), + minSampleZoom(1.0), samplePos(0), resizeSize(1024), silenceSize(1024), @@ -5809,6 +5851,7 @@ FurnaceGUI::FurnaceGUI(): amplifyVol(100.0), sampleSelStart(-1), sampleSelEnd(-1), + sampleInfo(true), sampleDragActive(false), sampleDragMode(false), sampleDrag16(false), diff --git a/src/gui/gui.h b/src/gui/gui.h index 29ece540..23da5071 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -227,6 +227,13 @@ enum FurnaceGUIColors { GUI_COLOR_SAMPLE_SEL, GUI_COLOR_SAMPLE_SEL_POINT, GUI_COLOR_SAMPLE_NEEDLE, + GUI_COLOR_SAMPLE_NEEDLE_PLAYING, + GUI_COLOR_SAMPLE_LOOP_POINT, + GUI_COLOR_SAMPLE_TIME_BG, + GUI_COLOR_SAMPLE_TIME_FG, + GUI_COLOR_SAMPLE_CHIP_DISABLED, + GUI_COLOR_SAMPLE_CHIP_ENABLED, + GUI_COLOR_SAMPLE_CHIP_WARNING, GUI_COLOR_PAT_MANAGER_NULL, GUI_COLOR_PAT_MANAGER_USED, @@ -313,6 +320,7 @@ enum FurnaceGUIFileDialogs { GUI_FILE_INS_OPEN, GUI_FILE_INS_OPEN_REPLACE, GUI_FILE_INS_SAVE, + GUI_FILE_INS_SAVE_OLD, GUI_FILE_INS_SAVE_DMP, GUI_FILE_WAVE_OPEN, GUI_FILE_WAVE_OPEN_REPLACE, @@ -507,6 +515,7 @@ enum FurnaceGUIActions { GUI_ACTION_INS_LIST_OPEN, GUI_ACTION_INS_LIST_OPEN_REPLACE, GUI_ACTION_INS_LIST_SAVE, + GUI_ACTION_INS_LIST_SAVE_OLD, GUI_ACTION_INS_LIST_SAVE_DMP, GUI_ACTION_INS_LIST_MOVE_UP, GUI_ACTION_INS_LIST_MOVE_DOWN, @@ -913,7 +922,6 @@ struct FurnaceGUISysDefChip { struct FurnaceGUISysDef { const char* name; String definition; - FurnaceGUISysDef(const char* n, std::initializer_list def); FurnaceGUISysDef(const char* n, std::initializer_list def); }; @@ -1073,6 +1081,7 @@ class FurnaceGUI { int displayInsTypeListMakeInsSample; float mobileMenuPos, autoButtonSize; const int* curSysSection; + DivInstrumentFM opllPreview; String pendingRawSample; int pendingRawSampleDepth, pendingRawSampleChannels; @@ -1581,12 +1590,14 @@ class FurnaceGUI { // sample editor specific double sampleZoom; double prevSampleZoom; + double minSampleZoom; int samplePos; int resizeSize, silenceSize; double resampleTarget; int resampleStrat; float amplifyVol; int sampleSelStart, sampleSelEnd; + bool sampleInfo; bool sampleDragActive, sampleDragMode, sampleDrag16, sampleZoomAuto; void* sampleDragTarget; ImVec2 sampleDragStart; @@ -1719,8 +1730,9 @@ class FurnaceGUI { void popToggleColors(); void drawMobileControls(); + void drawMobileOrderSel(); void drawEditControls(); - void drawSongInfo(); + void drawSongInfo(bool asChild=false); void drawOrders(); void drawPattern(); void drawInsList(bool asChild=false); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index dfc27b3b..e4638f79 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -590,6 +590,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("INS_LIST_OPEN", "Open", 0), D("INS_LIST_OPEN_REPLACE", "Open (replace current)", 0), D("INS_LIST_SAVE", "Save", 0), + D("INS_LIST_SAVE_OLD", "Save (legacy .fui)", 0), D("INS_LIST_SAVE_DMP", "Save (.dmp)", 0), D("INS_LIST_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP), D("INS_LIST_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN), @@ -859,6 +860,13 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_SAMPLE_SEL,"",ImVec4(0.26f,0.59f,0.98f,0.25f)), D(GUI_COLOR_SAMPLE_SEL_POINT,"",ImVec4(0.06f,0.53f,0.98f,0.5f)), D(GUI_COLOR_SAMPLE_NEEDLE,"",ImVec4(1.0f,0.8f,0.0f,1.0f)), + D(GUI_COLOR_SAMPLE_NEEDLE_PLAYING,"",ImVec4(0.2f,1.0f,0.0f,1.0f)), + D(GUI_COLOR_SAMPLE_LOOP_POINT,"",ImVec4(1.0f,0.0f,0.0f,1.0f)), + D(GUI_COLOR_SAMPLE_TIME_BG,"",ImVec4(0.1f,0.11f,0.12f,1.0f)), + D(GUI_COLOR_SAMPLE_TIME_FG,"",ImVec4(0.4f,0.4f,0.4f,1.0f)), + D(GUI_COLOR_SAMPLE_CHIP_DISABLED,"",ImVec4(0.6f,0.6f,0.6f,1.0f)), + D(GUI_COLOR_SAMPLE_CHIP_ENABLED,"",ImVec4(0.3f,1.0f,0.3f,1.0f)), + D(GUI_COLOR_SAMPLE_CHIP_WARNING,"",ImVec4(1.0f,0.75f,0.3f,1.0f)), D(GUI_COLOR_PAT_MANAGER_NULL,"",ImVec4(0.15f,0.15f,0.15f,1.0f)), D(GUI_COLOR_PAT_MANAGER_USED,"",ImVec4(0.15f,1.0f,0.15f,1.0f)), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index efee89a3..a7e45a7c 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -28,12 +28,16 @@ #include #include "plot_nolerp.h" +extern "C" { +#include "../../extern/Nuked-OPLL/opll.h" +} + const char* ssgEnvTypes[8]={ "Down Down Down", "Down.", "Down Up Down Up", "Down UP", "Up Up Up", "Up.", "Up Down Up Down", "Up DOWN" }; const char* fmParamNames[3][32]={ - {"Algorithm", "Feedback", "LFO > Freq", "LFO > Amp", "Attack", "Decay", "Decay 2", "Release", "Sustain", "Level", "EnvScale", "Multiplier", "Detune", "Detune 2", "SSG-EG", "AM", "AM Depth", "Vibrato Depth", "Sustained", "Sustained", "Level Scaling", "Sustain", "Vibrato", "Waveform", "Key Scale Rate", "OP2 Half Sine", "OP1 Half Sine", "EnvShift", "Reverb", "Fine", "LFO2 > Freq", "LFO2 > Amp"}, + {"Algorithm", "Feedback", "LFO > Freq", "LFO > Amp", "Attack", "Decay", "Decay 2", "Release", "Sustain", "Level", "EnvScale", "Multiplier", "Detune", "Detune 2", "SSG-EG", "AM", "AM Depth", "Vibrato Depth", "Sustained", "Sustained", "Level Scaling", "Sustain", "Vibrato", "Waveform", "Scale Rate", "OP2 Half Sine", "OP1 Half Sine", "EnvShift", "Reverb", "Fine", "LFO2 > Freq", "LFO2 > Amp"}, {"ALG", "FB", "FMS/PMS", "AMS", "AR", "DR", "SR", "RR", "SL", "TL", "KS", "MULT", "DT", "DT2", "SSG-EG", "AM", "AMD", "FMD", "EGT", "EGT", "KSL", "SUS", "VIB", "WS", "KSR", "DC", "DM", "EGS", "REV", "Fine", "FMS/PMS2", "AMS2"}, {"ALG", "FB", "FMS/PMS", "AMS", "AR", "DR", "D2R", "RR", "SL", "TL", "RS", "MULT", "DT", "DT2", "SSG-EG", "AM", "DAM", "DVB", "EGT", "EGS", "KSL", "SUS", "VIB", "WS", "KSR", "DC", "DM", "EGS", "REV", "Fine", "FMS/PMS2", "AMS2"} }; @@ -333,6 +337,16 @@ const char* snesGainModes[5]={ "Increase (bent line)" }; +const int detuneMap[2][8]={ + {-3, -2, -1, 0, 1, 2, 3, 4}, + { 7, 6, 5, 0, 1, 2, 3, 4} +}; + +const int detuneUnmap[2][11]={ + {0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0}, + {0, 0, 0, 3, 4, 5, 6, 7, 2, 1, 0} +}; + // do not change these! // anything other than a checkbox will look ugly! // @@ -2153,6 +2167,9 @@ void FurnaceGUI::drawInsEdit() { doAction(GUI_ACTION_INS_LIST_SAVE); } if (ImGui::BeginPopupContextItem("InsSaveFormats",ImGuiMouseButton_Right)) { + if (ImGui::MenuItem("save in legacy format...")) { + doAction(GUI_ACTION_INS_LIST_SAVE_OLD); + } if (ImGui::MenuItem("save as .dmp...")) { doAction(GUI_ACTION_INS_LIST_SAVE_DMP); } @@ -2433,368 +2450,666 @@ void FurnaceGUI::drawInsEdit() { if (ins->fm.opllPreset==16) { ImGui::Text("this volume slider only works in compatibility (non-drums) system."); } + + // update OPLL preset preview + if (ins->fm.opllPreset>0 && ins->fm.opllPreset<16) { + const opll_patch_t* patchROM=OPLL_GetPatchROM(opll_type_ym2413); + + const opll_patch_t* patch=&patchROM[ins->fm.opllPreset-1]; + + opllPreview.alg=0; + opllPreview.fb=patch->fb; + opllPreview.fms=patch->dm; + opllPreview.ams=patch->dc; + + opllPreview.op[0].tl=patch->tl; + opllPreview.op[1].tl=ins->fm.op[1].tl; + + for (int i=0; i<2; i++) { + opllPreview.op[i].am=patch->am[i]; + opllPreview.op[i].vib=patch->vib[i]; + opllPreview.op[i].ssgEnv=patch->et[i]?8:0; + opllPreview.op[i].ksr=patch->ksr[i]; + opllPreview.op[i].mult=patch->multi[i]; + opllPreview.op[i].ar=patch->ar[i]; + opllPreview.op[i].dr=patch->dr[i]; + opllPreview.op[i].sl=patch->sl[i]; + opllPreview.op[i].rr=patch->rr[i]; + } + } } - if (willDisplayOps) { - if (settings.fmLayout==0) { - int numCols=15; - if (ins->type==DIV_INS_OPL ||ins->type==DIV_INS_OPL_DRUMS) numCols=13; - if (ins->type==DIV_INS_OPLL) numCols=12; - if (ins->type==DIV_INS_OPZ) numCols=19; - if (ImGui::BeginTable("FMOperators",numCols,ImGuiTableFlags_SizingStretchProp|ImGuiTableFlags_BordersH|ImGuiTableFlags_BordersOuterV)) { - // configure columns - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); // op name - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.05f); // ar - ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.05f); // dr - ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch,0.05f); // sl - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { - ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthStretch,0.05f); // d2r - } - ImGui::TableSetupColumn("c5",ImGuiTableColumnFlags_WidthStretch,0.05f); // rr - ImGui::TableSetupColumn("c6",ImGuiTableColumnFlags_WidthFixed); // -separator- - ImGui::TableSetupColumn("c7",ImGuiTableColumnFlags_WidthStretch,0.05f); // tl - ImGui::TableSetupColumn("c8",ImGuiTableColumnFlags_WidthStretch,0.05f); // rs/ksl - if (ins->type==DIV_INS_OPZ) { - ImGui::TableSetupColumn("c8z0",ImGuiTableColumnFlags_WidthStretch,0.05f); // egs - ImGui::TableSetupColumn("c8z1",ImGuiTableColumnFlags_WidthStretch,0.05f); // rev - } - ImGui::TableSetupColumn("c9",ImGuiTableColumnFlags_WidthStretch,0.05f); // mult - if (ins->type==DIV_INS_OPZ) { - ImGui::TableSetupColumn("c9z",ImGuiTableColumnFlags_WidthStretch,0.05f); // fine - } + DivInstrumentFM& fmOrigin=(ins->type==DIV_INS_OPLL && ins->fm.opllPreset>0 && ins->fm.opllPreset<16)?opllPreview:ins->fm; - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { - ImGui::TableSetupColumn("c10",ImGuiTableColumnFlags_WidthStretch,0.05f); // dt - } - if (ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { - ImGui::TableSetupColumn("c11",ImGuiTableColumnFlags_WidthStretch,0.05f); // dt2 - } - ImGui::TableSetupColumn("c15",ImGuiTableColumnFlags_WidthFixed); // am + ImGui::BeginDisabled(!willDisplayOps); + if (settings.fmLayout==0) { + int numCols=15; + if (ins->type==DIV_INS_OPL ||ins->type==DIV_INS_OPL_DRUMS) numCols=13; + if (ins->type==DIV_INS_OPLL) numCols=12; + if (ins->type==DIV_INS_OPZ) numCols=19; + if (ImGui::BeginTable("FMOperators",numCols,ImGuiTableFlags_SizingStretchProp|ImGuiTableFlags_BordersH|ImGuiTableFlags_BordersOuterV)) { + // configure columns + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); // op name + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.05f); // ar + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.05f); // dr + ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch,0.05f); // sl + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { + ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthStretch,0.05f); // d2r + } + ImGui::TableSetupColumn("c5",ImGuiTableColumnFlags_WidthStretch,0.05f); // rr + ImGui::TableSetupColumn("c6",ImGuiTableColumnFlags_WidthFixed); // -separator- + ImGui::TableSetupColumn("c7",ImGuiTableColumnFlags_WidthStretch,0.05f); // tl + ImGui::TableSetupColumn("c8",ImGuiTableColumnFlags_WidthStretch,0.05f); // rs/ksl + if (ins->type==DIV_INS_OPZ) { + ImGui::TableSetupColumn("c8z0",ImGuiTableColumnFlags_WidthStretch,0.05f); // egs + ImGui::TableSetupColumn("c8z1",ImGuiTableColumnFlags_WidthStretch,0.05f); // rev + } + ImGui::TableSetupColumn("c9",ImGuiTableColumnFlags_WidthStretch,0.05f); // mult - ImGui::TableSetupColumn("c12",ImGuiTableColumnFlags_WidthFixed); // -separator- - if (ins->type!=DIV_INS_OPLL && ins->type!=DIV_INS_OPM) { - ImGui::TableSetupColumn("c13",ImGuiTableColumnFlags_WidthStretch,0.2f); // ssg/waveform - } - ImGui::TableSetupColumn("c14",ImGuiTableColumnFlags_WidthStretch,0.3f); // env + if (ins->type==DIV_INS_OPZ) { + ImGui::TableSetupColumn("c9z",ImGuiTableColumnFlags_WidthStretch,0.05f); // fine + } - // header - ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { + ImGui::TableSetupColumn("c10",ImGuiTableColumnFlags_WidthStretch,0.05f); // dt + } + if (ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { + ImGui::TableSetupColumn("c11",ImGuiTableColumnFlags_WidthStretch,0.05f); // dt2 + } + ImGui::TableSetupColumn("c15",ImGuiTableColumnFlags_WidthFixed); // am + + ImGui::TableSetupColumn("c12",ImGuiTableColumnFlags_WidthFixed); // -separator- + if (ins->type!=DIV_INS_OPLL && ins->type!=DIV_INS_OPM) { + ImGui::TableSetupColumn("c13",ImGuiTableColumnFlags_WidthStretch,0.2f); // ssg/waveform + } + ImGui::TableSetupColumn("c14",ImGuiTableColumnFlags_WidthStretch,0.3f); // env + + // header + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + + ImGui::TableNextColumn(); + CENTER_TEXT(FM_SHORT_NAME(FM_AR)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_AR)); + TOOLTIP_TEXT(FM_NAME(FM_AR)); + ImGui::TableNextColumn(); + CENTER_TEXT(FM_SHORT_NAME(FM_DR)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_DR)); + TOOLTIP_TEXT(FM_NAME(FM_DR)); + if (settings.susPosition==0) { + ImGui::TableNextColumn(); + CENTER_TEXT(FM_SHORT_NAME(FM_SL)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_SL)); + TOOLTIP_TEXT(FM_NAME(FM_SL)); + } + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { + ImGui::TableNextColumn(); + CENTER_TEXT(FM_SHORT_NAME(FM_D2R)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_D2R)); + TOOLTIP_TEXT(FM_NAME(FM_D2R)); + } + ImGui::TableNextColumn(); + CENTER_TEXT(FM_SHORT_NAME(FM_RR)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_RR)); + TOOLTIP_TEXT(FM_NAME(FM_RR)); + if (settings.susPosition==1) { + ImGui::TableNextColumn(); + CENTER_TEXT(FM_SHORT_NAME(FM_SL)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_SL)); + TOOLTIP_TEXT(FM_NAME(FM_SL)); + } + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + CENTER_TEXT(FM_SHORT_NAME(FM_TL)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_TL)); + TOOLTIP_TEXT(FM_NAME(FM_TL)); + ImGui::TableNextColumn(); + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { + CENTER_TEXT(FM_SHORT_NAME(FM_RS)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_RS)); + TOOLTIP_TEXT(FM_NAME(FM_RS)); + } else { + CENTER_TEXT(FM_SHORT_NAME(FM_KSL)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_KSL)); + TOOLTIP_TEXT(FM_NAME(FM_KSL)); + } + if (ins->type==DIV_INS_OPZ) { + ImGui::TableNextColumn(); + CENTER_TEXT(FM_SHORT_NAME(FM_EGSHIFT)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_EGSHIFT)); + TOOLTIP_TEXT(FM_NAME(FM_EGSHIFT)); + ImGui::TableNextColumn(); + CENTER_TEXT(FM_SHORT_NAME(FM_REV)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_REV)); + TOOLTIP_TEXT(FM_NAME(FM_REV)); + } + ImGui::TableNextColumn(); + CENTER_TEXT(FM_SHORT_NAME(FM_MULT)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_MULT)); + TOOLTIP_TEXT(FM_NAME(FM_MULT)); + if (ins->type==DIV_INS_OPZ) { + ImGui::TableNextColumn(); + CENTER_TEXT(FM_SHORT_NAME(FM_FINE)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_FINE)); + TOOLTIP_TEXT(FM_NAME(FM_FINE)); + } + ImGui::TableNextColumn(); + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { + CENTER_TEXT(FM_SHORT_NAME(FM_DT)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_DT)); + TOOLTIP_TEXT(FM_NAME(FM_DT)); + ImGui::TableNextColumn(); + } + if (ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { + CENTER_TEXT(FM_SHORT_NAME(FM_DT2)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_DT2)); + TOOLTIP_TEXT(FM_NAME(FM_DT2)); + ImGui::TableNextColumn(); + } + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM) { + CENTER_TEXT(FM_SHORT_NAME(FM_AM)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_AM)); + TOOLTIP_TEXT(FM_NAME(FM_AM)); + } else { + CENTER_TEXT("Other"); + ImGui::TextUnformatted("Other"); + } + ImGui::TableNextColumn(); + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPZ) { + ImGui::TableNextColumn(); + CENTER_TEXT(FM_NAME(FM_WS)); + ImGui::TextUnformatted(FM_NAME(FM_WS)); + } else if (ins->type!=DIV_INS_OPLL && ins->type!=DIV_INS_OPM) { + ImGui::TableNextColumn(); + CENTER_TEXT(FM_NAME(FM_SSG)); + ImGui::TextUnformatted(FM_NAME(FM_SSG)); + } + ImGui::TableNextColumn(); + CENTER_TEXT("Envelope"); + ImGui::TextUnformatted("Envelope"); + + float sliderHeight=32.0f*dpiScale; + + for (int i=0; itype!=DIV_INS_OPL_DRUMS)?opOrder[i]:i]; + ImGui::TableNextRow(); ImGui::TableNextColumn(); - ImGui::TableNextColumn(); - CENTER_TEXT(FM_SHORT_NAME(FM_AR)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_AR)); - TOOLTIP_TEXT(FM_NAME(FM_AR)); - ImGui::TableNextColumn(); - CENTER_TEXT(FM_SHORT_NAME(FM_DR)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_DR)); - TOOLTIP_TEXT(FM_NAME(FM_DR)); - if (settings.susPosition==0) { - ImGui::TableNextColumn(); - CENTER_TEXT(FM_SHORT_NAME(FM_SL)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_SL)); - TOOLTIP_TEXT(FM_NAME(FM_SL)); - } - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { - ImGui::TableNextColumn(); - CENTER_TEXT(FM_SHORT_NAME(FM_D2R)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_D2R)); - TOOLTIP_TEXT(FM_NAME(FM_D2R)); - } - ImGui::TableNextColumn(); - CENTER_TEXT(FM_SHORT_NAME(FM_RR)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_RR)); - TOOLTIP_TEXT(FM_NAME(FM_RR)); - if (settings.susPosition==1) { - ImGui::TableNextColumn(); - CENTER_TEXT(FM_SHORT_NAME(FM_SL)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_SL)); - TOOLTIP_TEXT(FM_NAME(FM_SL)); - } - ImGui::TableNextColumn(); - ImGui::TableNextColumn(); - CENTER_TEXT(FM_SHORT_NAME(FM_TL)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_TL)); - TOOLTIP_TEXT(FM_NAME(FM_TL)); - ImGui::TableNextColumn(); - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { - CENTER_TEXT(FM_SHORT_NAME(FM_RS)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_RS)); - TOOLTIP_TEXT(FM_NAME(FM_RS)); - } else { - CENTER_TEXT(FM_SHORT_NAME(FM_KSL)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_KSL)); - TOOLTIP_TEXT(FM_NAME(FM_KSL)); - } - if (ins->type==DIV_INS_OPZ) { - ImGui::TableNextColumn(); - CENTER_TEXT(FM_SHORT_NAME(FM_EGSHIFT)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_EGSHIFT)); - TOOLTIP_TEXT(FM_NAME(FM_EGSHIFT)); - ImGui::TableNextColumn(); - CENTER_TEXT(FM_SHORT_NAME(FM_REV)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_REV)); - TOOLTIP_TEXT(FM_NAME(FM_REV)); - } - ImGui::TableNextColumn(); - CENTER_TEXT(FM_SHORT_NAME(FM_MULT)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_MULT)); - TOOLTIP_TEXT(FM_NAME(FM_MULT)); - if (ins->type==DIV_INS_OPZ) { - ImGui::TableNextColumn(); - CENTER_TEXT(FM_SHORT_NAME(FM_FINE)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_FINE)); - TOOLTIP_TEXT(FM_NAME(FM_FINE)); - } - ImGui::TableNextColumn(); - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { - CENTER_TEXT(FM_SHORT_NAME(FM_DT)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_DT)); - TOOLTIP_TEXT(FM_NAME(FM_DT)); - ImGui::TableNextColumn(); - } - if (ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { - CENTER_TEXT(FM_SHORT_NAME(FM_DT2)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_DT2)); - TOOLTIP_TEXT(FM_NAME(FM_DT2)); - ImGui::TableNextColumn(); - } - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM) { - CENTER_TEXT(FM_SHORT_NAME(FM_AM)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_AM)); - TOOLTIP_TEXT(FM_NAME(FM_AM)); - } else { - CENTER_TEXT("Other"); - ImGui::TextUnformatted("Other"); - } - ImGui::TableNextColumn(); - if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPZ) { - ImGui::TableNextColumn(); - CENTER_TEXT(FM_NAME(FM_WS)); - ImGui::TextUnformatted(FM_NAME(FM_WS)); - } else if (ins->type!=DIV_INS_OPLL && ins->type!=DIV_INS_OPM) { - ImGui::TableNextColumn(); - CENTER_TEXT(FM_NAME(FM_SSG)); - ImGui::TextUnformatted(FM_NAME(FM_SSG)); - } - ImGui::TableNextColumn(); - CENTER_TEXT("Envelope"); - ImGui::TextUnformatted("Envelope"); - - float sliderHeight=32.0f*dpiScale; - - for (int i=0; ifm.op[(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]:i]; - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - - // push colors - if (settings.separateFMColors) { - bool mod=true; - if (ins->type==DIV_INS_OPL_DRUMS) { - mod=false; - } else if (opCount==4) { - if (ins->type==DIV_INS_OPL) { - if (opIsOutputOPL[ins->fm.alg&3][i]) mod=false; - } else { - if (opIsOutput[ins->fm.alg&7][i]) mod=false; - } - } else { - if (i==1 || (ins->type==DIV_INS_OPL && (ins->fm.alg&1))) mod=false; - } - if (mod) { - pushAccentColors( - uiColors[GUI_COLOR_FM_PRIMARY_MOD], - uiColors[GUI_COLOR_FM_SECONDARY_MOD], - uiColors[GUI_COLOR_FM_BORDER_MOD], - uiColors[GUI_COLOR_FM_BORDER_SHADOW_MOD] - ); - } else { - pushAccentColors( - uiColors[GUI_COLOR_FM_PRIMARY_CAR], - uiColors[GUI_COLOR_FM_SECONDARY_CAR], - uiColors[GUI_COLOR_FM_BORDER_CAR], - uiColors[GUI_COLOR_FM_BORDER_SHADOW_CAR] - ); - } - } - - if (i==0) sliderHeight=(ImGui::GetContentRegionAvail().y/opCount)-ImGui::GetStyle().ItemSpacing.y; - - ImGui::PushID(fmt::sprintf("op%d",i).c_str()); - String opNameLabel; + // push colors + if (settings.separateFMColors) { + bool mod=true; if (ins->type==DIV_INS_OPL_DRUMS) { - opNameLabel=fmt::sprintf("%s",oplDrumNames[i]); - } else if (ins->type==DIV_INS_OPL && ins->fm.opllPreset==16) { - if (i==1) { - opNameLabel="Kick"; + mod=false; + } else if (opCount==4) { + if (ins->type==DIV_INS_OPL) { + if (opIsOutputOPL[fmOrigin.alg&3][i]) mod=false; } else { - opNameLabel="Env"; + if (opIsOutput[fmOrigin.alg&7][i]) mod=false; } } else { - opNameLabel=fmt::sprintf("OP%d",i+1); + if (i==1 || (ins->type==DIV_INS_OPL && (fmOrigin.alg&1))) mod=false; } - if (opsAreMutable) { - pushToggleColors(op.enable); - if (ImGui::Button(opNameLabel.c_str())) { - op.enable=!op.enable; - PARAMETER; - } - popToggleColors(); + if (mod) { + pushAccentColors( + uiColors[GUI_COLOR_FM_PRIMARY_MOD], + uiColors[GUI_COLOR_FM_SECONDARY_MOD], + uiColors[GUI_COLOR_FM_BORDER_MOD], + uiColors[GUI_COLOR_FM_BORDER_SHADOW_MOD] + ); } else { - ImGui::TextUnformatted(opNameLabel.c_str()); + pushAccentColors( + uiColors[GUI_COLOR_FM_PRIMARY_CAR], + uiColors[GUI_COLOR_FM_SECONDARY_CAR], + uiColors[GUI_COLOR_FM_BORDER_CAR], + uiColors[GUI_COLOR_FM_BORDER_SHADOW_CAR] + ); } - - // drag point - OP_DRAG_POINT; + } - int maxTl=127; - if (ins->type==DIV_INS_OPLL) { - if (i==1) { - maxTl=15; - } else { - maxTl=63; - } + if (i==0) sliderHeight=(ImGui::GetContentRegionAvail().y/opCount)-ImGui::GetStyle().ItemSpacing.y; + + ImGui::PushID(fmt::sprintf("op%d",i).c_str()); + String opNameLabel; + if (ins->type==DIV_INS_OPL_DRUMS) { + opNameLabel=fmt::sprintf("%s",oplDrumNames[i]); + } else if (ins->type==DIV_INS_OPL && fmOrigin.opllPreset==16) { + if (i==1) { + opNameLabel="Kick"; + } else { + opNameLabel="Env"; } - if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { + } else { + opNameLabel=fmt::sprintf("OP%d",i+1); + } + if (opsAreMutable) { + pushToggleColors(op.enable); + if (ImGui::Button(opNameLabel.c_str())) { + op.enable=!op.enable; + PARAMETER; + } + popToggleColors(); + } else { + ImGui::TextUnformatted(opNameLabel.c_str()); + } + + // drag point + OP_DRAG_POINT; + + int maxTl=127; + if (ins->type==DIV_INS_OPLL) { + if (i==1) { + maxTl=15; + } else { maxTl=63; } - int maxArDr=(ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM)?31:15; - bool ssgOn=op.ssgEnv&8; - bool ksrOn=op.ksr; - bool vibOn=op.vib; - bool susOn=op.sus; - unsigned char ssgEnv=op.ssgEnv&7; + } + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { + maxTl=63; + } + int maxArDr=(ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM)?31:15; + bool ssgOn=op.ssgEnv&8; + bool ksrOn=op.ksr; + bool vibOn=op.vib; + bool susOn=op.sus; + unsigned char ssgEnv=op.ssgEnv&7; + + ImGui::TableNextColumn(); + op.ar&=maxArDr; + CENTER_VSLIDER; + P(CWVSliderScalar("##AR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.ar,&maxArDr,&_ZERO)); + + ImGui::TableNextColumn(); + op.dr&=maxArDr; + CENTER_VSLIDER; + P(CWVSliderScalar("##DR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.dr,&maxArDr,&_ZERO)); + + if (settings.susPosition==0) { + ImGui::TableNextColumn(); + op.sl&=15; + CENTER_VSLIDER; + P(CWVSliderScalar("##SL",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO)); + } + + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { + ImGui::TableNextColumn(); + op.d2r&=31; + CENTER_VSLIDER; + P(CWVSliderScalar("##D2R",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.d2r,&_THIRTY_ONE,&_ZERO)); + } + + ImGui::TableNextColumn(); + op.rr&=15; + CENTER_VSLIDER; + P(CWVSliderScalar("##RR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.rr,&_FIFTEEN,&_ZERO)); + + if (settings.susPosition==1) { + ImGui::TableNextColumn(); + op.sl&=15; + CENTER_VSLIDER; + P(CWVSliderScalar("##SL",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO)); + } + + ImGui::TableNextColumn(); + ImGui::Dummy(ImVec2(4.0f*dpiScale,2.0f*dpiScale)); + + ImGui::TableNextColumn(); + op.tl&=maxTl; + CENTER_VSLIDER; + P(CWVSliderScalar("##TL",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.tl,&maxTl,&_ZERO)); + + ImGui::TableNextColumn(); + CENTER_VSLIDER; + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { + P(CWVSliderScalar("##RS",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE)); + } else { + P(CWVSliderScalar("##KSL",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); + } + + if (ins->type==DIV_INS_OPZ) { + ImGui::TableNextColumn(); + CENTER_VSLIDER; + P(CWVSliderScalar("##EGS",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); ImGui::TableNextColumn(); - op.ar&=maxArDr; CENTER_VSLIDER; + P(CWVSliderScalar("##REV",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.dam,&_ZERO,&_SEVEN)); + } + + ImGui::TableNextColumn(); + CENTER_VSLIDER; + P(CWVSliderScalar("##MULT",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); + + if (ins->type==DIV_INS_OPZ) { + ImGui::TableNextColumn(); + CENTER_VSLIDER; + P(CWVSliderScalar("##FINE",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.dvb,&_ZERO,&_FIFTEEN)); + } + + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { + int detune=detuneMap[settings.unsignedDetune?1:0][op.dt&7]; + ImGui::TableNextColumn(); + CENTER_VSLIDER; + if (CWVSliderInt("##DT",ImVec2(20.0f*dpiScale,sliderHeight),&detune,settings.unsignedDetune?0:-3,settings.unsignedDetune?7:4)) { PARAMETER + if (detune<-3) detune=-3; + if (detune>7) detune=7; + op.dt=detuneUnmap[settings.unsignedDetune?1:0][detune+3]; + } + + if (ins->type!=DIV_INS_FM) { + ImGui::TableNextColumn(); + CENTER_VSLIDER; + P(CWVSliderScalar("##DT2",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE)); rightClickable + } + + ImGui::TableNextColumn(); + bool amOn=op.am; + if (ins->type==DIV_INS_OPZ) { + bool egtOn=op.egt; + if (egtOn) { + ImGui::SetCursorPosY(ImGui::GetCursorPosY()+0.5*(sliderHeight-ImGui::GetFrameHeight()*4.0-ImGui::GetStyle().ItemSpacing.y*3.0)); + } else { + ImGui::SetCursorPosY(ImGui::GetCursorPosY()+0.5*(sliderHeight-ImGui::GetFrameHeight()*2.0-ImGui::GetStyle().ItemSpacing.y*1.0)); + } + if (ImGui::Checkbox("AM",&amOn)) { PARAMETER + op.am=amOn; + } + if (ImGui::Checkbox("Fixed",&egtOn)) { PARAMETER + op.egt=egtOn; + } + if (egtOn) { + int block=op.dt; + int freqNum=(op.mult<<4)|(op.dvb&15); + if (ImGui::InputInt("Block",&block,1,1)) { + if (block<0) block=0; + if (block>7) block=7; + op.dt=block; + } + if (ImGui::InputInt("FreqNum",&freqNum,1,16)) { + if (freqNum<0) freqNum=0; + if (freqNum>255) freqNum=255; + op.mult=freqNum>>4; + op.dvb=freqNum&15; + } + } + } else { + ImGui::SetCursorPosY(ImGui::GetCursorPosY()+0.5*(sliderHeight-ImGui::GetFrameHeight())); + if (ImGui::Checkbox("##AM",&amOn)) { PARAMETER + op.am=amOn; + } + } + + if (ins->type!=DIV_INS_OPL && ins->type!=DIV_INS_OPL_DRUMS && ins->type!=DIV_INS_OPZ && ins->type!=DIV_INS_OPM) { + ImGui::TableNextColumn(); + ImGui::Dummy(ImVec2(4.0f*dpiScale,2.0f*dpiScale)); + ImGui::TableNextColumn(); + ImGui::BeginDisabled(!ssgOn); + drawSSGEnv(op.ssgEnv&7,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight-ImGui::GetFrameHeightWithSpacing())); + ImGui::EndDisabled(); + if (ImGui::Checkbox("##SSGOn",&ssgOn)) { PARAMETER + op.ssgEnv=(op.ssgEnv&7)|(ssgOn<<3); + } + + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (CWSliderScalar("##SSG",ImGuiDataType_U8,&ssgEnv,&_ZERO,&_SEVEN,ssgEnvTypes[ssgEnv])) { PARAMETER + op.ssgEnv=(op.ssgEnv&8)|(ssgEnv&7); + } + } + } else if (ins->type!=DIV_INS_OPM) { + ImGui::TableNextColumn(); + bool amOn=op.am; + ImGui::SetCursorPosY(ImGui::GetCursorPosY()+0.5*(sliderHeight-ImGui::GetFrameHeight()*4.0-ImGui::GetStyle().ItemSpacing.y*3.0)); + if (ImGui::Checkbox(FM_NAME(FM_AM),&amOn)) { PARAMETER + op.am=amOn; + } + if (ImGui::Checkbox(FM_NAME(FM_VIB),&vibOn)) { PARAMETER + op.vib=vibOn; + } + if (ImGui::Checkbox(FM_NAME(FM_KSR),&ksrOn)) { PARAMETER + op.ksr=ksrOn; + } + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { + if (ImGui::Checkbox(FM_NAME(FM_SUS),&susOn)) { PARAMETER + op.sus=susOn; + } + } else if (ins->type==DIV_INS_OPLL) { + if (ImGui::Checkbox(FM_NAME(FM_EGS),&ssgOn)) { PARAMETER + op.ssgEnv=(op.ssgEnv&7)|(ssgOn<<3); + } + } + } + + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPZ) { + ImGui::TableNextColumn(); + ImGui::Dummy(ImVec2(4.0f*dpiScale,2.0f*dpiScale)); + ImGui::TableNextColumn(); + + drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight-ImGui::GetFrameHeightWithSpacing())); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable + if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) { + ImGui::SetTooltip("OPL2/3 only (last 4 waveforms are OPL3 only)"); + } + } else if (ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPM) { + ImGui::TableNextColumn(); + ImGui::Dummy(ImVec2(4.0f*dpiScale,2.0f*dpiScale)); + } + + ImGui::TableNextColumn(); + drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,fmOrigin.alg,maxTl,maxArDr,15,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight),ins->type); + + if (settings.separateFMColors) { + popAccentColors(); + } + + ImGui::PopID(); + } + + ImGui::EndTable(); + } + } else if (settings.fmLayout>=4 && settings.fmLayout<=6) { // alternate + int columns=2; + switch (settings.fmLayout) { + case 4: // 2x2 + columns=2; + break; + case 5: // 1x4 + columns=1; + break; + case 6: // 4x1 + columns=opCount; + break; + } + char tempID[1024]; + ImVec2 oldPadding=ImGui::GetStyle().CellPadding; + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(8.0f*dpiScale,4.0f*dpiScale)); + if (ImGui::BeginTable("AltFMOperators",columns,ImGuiTableFlags_SizingStretchSame|ImGuiTableFlags_BordersInner)) { + for (int i=0; itype!=DIV_INS_OPL_DRUMS)?opOrder[i]:i]; + if ((settings.fmLayout!=6 && ((i+1)&1)) || i==0 || settings.fmLayout==5) ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::PushID(fmt::sprintf("op%d",i).c_str()); + + // push colors + if (settings.separateFMColors) { + bool mod=true; + if (ins->type==DIV_INS_OPL_DRUMS) { + mod=false; + } else if (opCount==4) { + if (ins->type==DIV_INS_OPL) { + if (opIsOutputOPL[fmOrigin.alg&3][i]) mod=false; + } else { + if (opIsOutput[fmOrigin.alg&7][i]) mod=false; + } + } else { + if (i==1 || (ins->type==DIV_INS_OPL && (fmOrigin.alg&1))) mod=false; + } + if (mod) { + pushAccentColors( + uiColors[GUI_COLOR_FM_PRIMARY_MOD], + uiColors[GUI_COLOR_FM_SECONDARY_MOD], + uiColors[GUI_COLOR_FM_BORDER_MOD], + uiColors[GUI_COLOR_FM_BORDER_SHADOW_MOD] + ); + } else { + pushAccentColors( + uiColors[GUI_COLOR_FM_PRIMARY_CAR], + uiColors[GUI_COLOR_FM_SECONDARY_CAR], + uiColors[GUI_COLOR_FM_BORDER_CAR], + uiColors[GUI_COLOR_FM_BORDER_SHADOW_CAR] + ); + } + } + + ImGui::Dummy(ImVec2(dpiScale,dpiScale)); + if (ins->type==DIV_INS_OPL_DRUMS) { + snprintf(tempID,1024,"%s",oplDrumNames[i]); + } else if (ins->type==DIV_INS_OPL && fmOrigin.opllPreset==16) { + if (i==1) { + snprintf(tempID,1024,"Envelope 2 (kick only)"); + } else { + snprintf(tempID,1024,"Envelope"); + } + } else { + snprintf(tempID,1024,"Operator %d",i+1); + } + float nextCursorPosX=ImGui::GetCursorPosX()+0.5*(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize(tempID).x-(opsAreMutable?(ImGui::GetStyle().FramePadding.x*2.0f):0.0f)); + OP_DRAG_POINT; + ImGui::SameLine(); + ImGui::SetCursorPosX(nextCursorPosX); + if (opsAreMutable) { + pushToggleColors(op.enable); + if (ImGui::Button(tempID)) { + op.enable=!op.enable; + PARAMETER; + } + popToggleColors(); + } else { + ImGui::TextUnformatted(tempID); + } + + float sliderHeight=200.0f*dpiScale; + float waveWidth=140.0*dpiScale; + float waveHeight=sliderHeight-ImGui::GetFrameHeightWithSpacing()*((ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPL)?5.0f:4.5f); + + int maxTl=127; + if (ins->type==DIV_INS_OPLL) { + if (i==1) { + maxTl=15; + } else { + maxTl=63; + } + } + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { + maxTl=63; + } + int maxArDr=(ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM)?31:15; + + bool ssgOn=op.ssgEnv&8; + bool ksrOn=op.ksr; + bool vibOn=op.vib; + bool egtOn=op.egt; + bool susOn=op.sus; // yawn + unsigned char ssgEnv=op.ssgEnv&7; + + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,oldPadding); + if (ImGui::BeginTable("opParams",4,ImGuiTableFlags_BordersInnerV)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,waveWidth); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthFixed); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + float textY=ImGui::GetCursorPosY(); + CENTER_TEXT_20(FM_SHORT_NAME(FM_AR)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_AR)); + TOOLTIP_TEXT(FM_NAME(FM_AR)); + ImGui::TableNextColumn(); + if (ins->type==DIV_INS_FM) { + ImGui::Text("SSG-EG"); + } else { + ImGui::Text("Waveform"); + } + ImGui::TableNextColumn(); + ImGui::Text("Envelope"); + ImGui::TableNextColumn(); + CENTER_TEXT(FM_SHORT_NAME(FM_TL)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_TL)); + TOOLTIP_TEXT(FM_NAME(FM_TL)); + + // A/D/S/R + ImGui::TableNextColumn(); + + op.ar&=maxArDr; P(CWVSliderScalar("##AR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.ar,&maxArDr,&_ZERO)); - ImGui::TableNextColumn(); + ImGui::SameLine(); op.dr&=maxArDr; - CENTER_VSLIDER; + float textX_DR=ImGui::GetCursorPosX(); P(CWVSliderScalar("##DR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.dr,&maxArDr,&_ZERO)); + float textX_SL=0.0f; if (settings.susPosition==0) { - ImGui::TableNextColumn(); + ImGui::SameLine(); op.sl&=15; - CENTER_VSLIDER; + textX_SL=ImGui::GetCursorPosX(); P(CWVSliderScalar("##SL",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO)); } + float textX_D2R=0.0f; if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { - ImGui::TableNextColumn(); + ImGui::SameLine(); op.d2r&=31; - CENTER_VSLIDER; + textX_D2R=ImGui::GetCursorPosX(); P(CWVSliderScalar("##D2R",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.d2r,&_THIRTY_ONE,&_ZERO)); } - ImGui::TableNextColumn(); + ImGui::SameLine(); op.rr&=15; - CENTER_VSLIDER; + float textX_RR=ImGui::GetCursorPosX(); P(CWVSliderScalar("##RR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.rr,&_FIFTEEN,&_ZERO)); if (settings.susPosition==1) { - ImGui::TableNextColumn(); + ImGui::SameLine(); op.sl&=15; - CENTER_VSLIDER; + textX_SL=ImGui::GetCursorPosX(); P(CWVSliderScalar("##SL",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO)); } - ImGui::TableNextColumn(); - ImGui::Dummy(ImVec2(4.0f*dpiScale,2.0f*dpiScale)); + ImVec2 prevCurPos=ImGui::GetCursorPos(); + + // labels + ImGui::SetCursorPos(ImVec2(textX_DR,textY)); + CENTER_TEXT_20(FM_SHORT_NAME(FM_DR)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_DR)); + TOOLTIP_TEXT(FM_NAME(FM_DR)); - ImGui::TableNextColumn(); - op.tl&=maxTl; - CENTER_VSLIDER; - P(CWVSliderScalar("##TL",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.tl,&maxTl,&_ZERO)); + ImGui::SetCursorPos(ImVec2(textX_SL,textY)); + CENTER_TEXT_20(FM_SHORT_NAME(FM_SL)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_SL)); + TOOLTIP_TEXT(FM_NAME(FM_SL)); - ImGui::TableNextColumn(); - CENTER_VSLIDER; - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { - P(CWVSliderScalar("##RS",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE)); - } else { - P(CWVSliderScalar("##KSL",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); - } - - if (ins->type==DIV_INS_OPZ) { - ImGui::TableNextColumn(); - CENTER_VSLIDER; - P(CWVSliderScalar("##EGS",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); - - ImGui::TableNextColumn(); - CENTER_VSLIDER; - P(CWVSliderScalar("##REV",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.dam,&_ZERO,&_SEVEN)); - } - - ImGui::TableNextColumn(); - CENTER_VSLIDER; - P(CWVSliderScalar("##MULT",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); - - if (ins->type==DIV_INS_OPZ) { - ImGui::TableNextColumn(); - CENTER_VSLIDER; - P(CWVSliderScalar("##FINE",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.dvb,&_ZERO,&_FIFTEEN)); - } + ImGui::SetCursorPos(ImVec2(textX_RR,textY)); + CENTER_TEXT_20(FM_SHORT_NAME(FM_RR)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_RR)); + TOOLTIP_TEXT(FM_NAME(FM_RR)); if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { - int detune=(op.dt&7)-(settings.unsignedDetune?0:3); - ImGui::TableNextColumn(); - CENTER_VSLIDER; - if (CWVSliderInt("##DT",ImVec2(20.0f*dpiScale,sliderHeight),&detune,settings.unsignedDetune?0:-3,settings.unsignedDetune?7:4)) { PARAMETER - op.dt=detune+(settings.unsignedDetune?0:3); - } + ImGui::SetCursorPos(ImVec2(textX_D2R,textY)); + CENTER_TEXT_20(FM_SHORT_NAME(FM_D2R)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_D2R)); + TOOLTIP_TEXT(FM_NAME(FM_D2R)); + } - if (ins->type!=DIV_INS_FM) { - ImGui::TableNextColumn(); - CENTER_VSLIDER; - P(CWVSliderScalar("##DT2",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE)); rightClickable - } - - ImGui::TableNextColumn(); - bool amOn=op.am; - if (ins->type==DIV_INS_OPZ) { - bool egtOn=op.egt; - if (egtOn) { - ImGui::SetCursorPosY(ImGui::GetCursorPosY()+0.5*(sliderHeight-ImGui::GetFrameHeight()*4.0-ImGui::GetStyle().ItemSpacing.y*3.0)); - } else { - ImGui::SetCursorPosY(ImGui::GetCursorPosY()+0.5*(sliderHeight-ImGui::GetFrameHeight()*2.0-ImGui::GetStyle().ItemSpacing.y*1.0)); - } - if (ImGui::Checkbox("AM",&amOn)) { PARAMETER - op.am=amOn; - } - if (ImGui::Checkbox("Fixed",&egtOn)) { PARAMETER - op.egt=egtOn; - } - if (egtOn) { - int block=op.dt; - int freqNum=(op.mult<<4)|(op.dvb&15); - if (ImGui::InputInt("Block",&block,1,1)) { - if (block<0) block=0; - if (block>7) block=7; - op.dt=block; - } - if (ImGui::InputInt("FreqNum",&freqNum,1,16)) { - if (freqNum<0) freqNum=0; - if (freqNum>255) freqNum=255; - op.mult=freqNum>>4; - op.dvb=freqNum&15; - } - } - } else { - ImGui::SetCursorPosY(ImGui::GetCursorPosY()+0.5*(sliderHeight-ImGui::GetFrameHeight())); - if (ImGui::Checkbox("##AM",&amOn)) { PARAMETER - op.am=amOn; - } - } - - if (ins->type!=DIV_INS_OPL && ins->type!=DIV_INS_OPL_DRUMS && ins->type!=DIV_INS_OPZ && ins->type!=DIV_INS_OPM) { - ImGui::TableNextColumn(); - ImGui::Dummy(ImVec2(4.0f*dpiScale,2.0f*dpiScale)); - ImGui::TableNextColumn(); + ImGui::SetCursorPos(prevCurPos); + + ImGui::TableNextColumn(); + switch (ins->type) { + case DIV_INS_FM: { + // SSG ImGui::BeginDisabled(!ssgOn); - drawSSGEnv(op.ssgEnv&7,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight-ImGui::GetFrameHeightWithSpacing())); + drawSSGEnv(op.ssgEnv&7,ImVec2(waveWidth,waveHeight)); ImGui::EndDisabled(); if (ImGui::Checkbox("##SSGOn",&ssgOn)) { PARAMETER op.ssgEnv=(op.ssgEnv&7)|(ssgOn<<3); @@ -2805,806 +3120,531 @@ void FurnaceGUI::drawInsEdit() { if (CWSliderScalar("##SSG",ImGuiDataType_U8,&ssgEnv,&_ZERO,&_SEVEN,ssgEnvTypes[ssgEnv])) { PARAMETER op.ssgEnv=(op.ssgEnv&8)|(ssgEnv&7); } - } - } else if (ins->type!=DIV_INS_OPM) { - ImGui::TableNextColumn(); - bool amOn=op.am; - ImGui::SetCursorPosY(ImGui::GetCursorPosY()+0.5*(sliderHeight-ImGui::GetFrameHeight()*4.0-ImGui::GetStyle().ItemSpacing.y*3.0)); - if (ImGui::Checkbox(FM_NAME(FM_AM),&amOn)) { PARAMETER - op.am=amOn; - } - if (ImGui::Checkbox(FM_NAME(FM_VIB),&vibOn)) { PARAMETER - op.vib=vibOn; - } - if (ImGui::Checkbox(FM_NAME(FM_KSR),&ksrOn)) { PARAMETER - op.ksr=ksrOn; - } - if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { - if (ImGui::Checkbox(FM_NAME(FM_SUS),&susOn)) { PARAMETER - op.sus=susOn; - } - } else if (ins->type==DIV_INS_OPLL) { - if (ImGui::Checkbox(FM_NAME(FM_EGS),&ssgOn)) { PARAMETER - op.ssgEnv=(op.ssgEnv&7)|(ssgOn<<3); - } - } - } - - if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPZ) { - ImGui::TableNextColumn(); - ImGui::Dummy(ImVec2(4.0f*dpiScale,2.0f*dpiScale)); - ImGui::TableNextColumn(); - - drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight-ImGui::GetFrameHeightWithSpacing())); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable - if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) { - ImGui::SetTooltip("OPL2/3 only (last 4 waveforms are OPL3 only)"); - } - } else if (ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPM) { - ImGui::TableNextColumn(); - ImGui::Dummy(ImVec2(4.0f*dpiScale,2.0f*dpiScale)); - } - - ImGui::TableNextColumn(); - drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,15,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight),ins->type); - - if (settings.separateFMColors) { - popAccentColors(); - } - - ImGui::PopID(); - } - - ImGui::EndTable(); - } - } else if (settings.fmLayout>=4 && settings.fmLayout<=6) { // alternate - int columns=2; - switch (settings.fmLayout) { - case 4: // 2x2 - columns=2; - break; - case 5: // 1x4 - columns=1; - break; - case 6: // 4x1 - columns=opCount; - break; - } - char tempID[1024]; - ImVec2 oldPadding=ImGui::GetStyle().CellPadding; - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(8.0f*dpiScale,4.0f*dpiScale)); - if (ImGui::BeginTable("AltFMOperators",columns,ImGuiTableFlags_SizingStretchSame|ImGuiTableFlags_BordersInner)) { - for (int i=0; ifm.op[(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]:i]; - if ((settings.fmLayout!=6 && ((i+1)&1)) || i==0 || settings.fmLayout==5) ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::PushID(fmt::sprintf("op%d",i).c_str()); - - // push colors - if (settings.separateFMColors) { - bool mod=true; - if (ins->type==DIV_INS_OPL_DRUMS) { - mod=false; - } else if (opCount==4) { - if (ins->type==DIV_INS_OPL) { - if (opIsOutputOPL[ins->fm.alg&3][i]) mod=false; - } else { - if (opIsOutput[ins->fm.alg&7][i]) mod=false; - } - } else { - if (i==1 || (ins->type==DIV_INS_OPL && (ins->fm.alg&1))) mod=false; - } - if (mod) { - pushAccentColors( - uiColors[GUI_COLOR_FM_PRIMARY_MOD], - uiColors[GUI_COLOR_FM_SECONDARY_MOD], - uiColors[GUI_COLOR_FM_BORDER_MOD], - uiColors[GUI_COLOR_FM_BORDER_SHADOW_MOD] - ); - } else { - pushAccentColors( - uiColors[GUI_COLOR_FM_PRIMARY_CAR], - uiColors[GUI_COLOR_FM_SECONDARY_CAR], - uiColors[GUI_COLOR_FM_BORDER_CAR], - uiColors[GUI_COLOR_FM_BORDER_SHADOW_CAR] - ); - } - } - - ImGui::Dummy(ImVec2(dpiScale,dpiScale)); - if (ins->type==DIV_INS_OPL_DRUMS) { - snprintf(tempID,1024,"%s",oplDrumNames[i]); - } else if (ins->type==DIV_INS_OPL && ins->fm.opllPreset==16) { - if (i==1) { - snprintf(tempID,1024,"Envelope 2 (kick only)"); - } else { - snprintf(tempID,1024,"Envelope"); - } - } else { - snprintf(tempID,1024,"Operator %d",i+1); - } - float nextCursorPosX=ImGui::GetCursorPosX()+0.5*(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize(tempID).x-(opsAreMutable?(ImGui::GetStyle().FramePadding.x*2.0f):0.0f)); - OP_DRAG_POINT; - ImGui::SameLine(); - ImGui::SetCursorPosX(nextCursorPosX); - if (opsAreMutable) { - pushToggleColors(op.enable); - if (ImGui::Button(tempID)) { - op.enable=!op.enable; - PARAMETER; - } - popToggleColors(); - } else { - ImGui::TextUnformatted(tempID); - } - - float sliderHeight=200.0f*dpiScale; - float waveWidth=140.0*dpiScale; - float waveHeight=sliderHeight-ImGui::GetFrameHeightWithSpacing()*((ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPL)?5.0f:4.5f); - - int maxTl=127; - if (ins->type==DIV_INS_OPLL) { - if (i==1) { - maxTl=15; - } else { - maxTl=63; - } - } - if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { - maxTl=63; - } - int maxArDr=(ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM)?31:15; - - bool ssgOn=op.ssgEnv&8; - bool ksrOn=op.ksr; - bool vibOn=op.vib; - bool egtOn=op.egt; - bool susOn=op.sus; // yawn - unsigned char ssgEnv=op.ssgEnv&7; - - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,oldPadding); - if (ImGui::BeginTable("opParams",4,ImGuiTableFlags_BordersInnerV)) { - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,waveWidth); - ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthFixed); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - float textY=ImGui::GetCursorPosY(); - CENTER_TEXT_20(FM_SHORT_NAME(FM_AR)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_AR)); - TOOLTIP_TEXT(FM_NAME(FM_AR)); - ImGui::TableNextColumn(); - if (ins->type==DIV_INS_FM) { - ImGui::Text("SSG-EG"); - } else { - ImGui::Text("Waveform"); - } - ImGui::TableNextColumn(); - ImGui::Text("Envelope"); - ImGui::TableNextColumn(); - CENTER_TEXT(FM_SHORT_NAME(FM_TL)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_TL)); - TOOLTIP_TEXT(FM_NAME(FM_TL)); - - // A/D/S/R - ImGui::TableNextColumn(); - - op.ar&=maxArDr; - P(CWVSliderScalar("##AR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.ar,&maxArDr,&_ZERO)); - - ImGui::SameLine(); - op.dr&=maxArDr; - float textX_DR=ImGui::GetCursorPosX(); - P(CWVSliderScalar("##DR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.dr,&maxArDr,&_ZERO)); - - float textX_SL=0.0f; - if (settings.susPosition==0) { - ImGui::SameLine(); - op.sl&=15; - textX_SL=ImGui::GetCursorPosX(); - P(CWVSliderScalar("##SL",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO)); - } - - float textX_D2R=0.0f; - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { - ImGui::SameLine(); - op.d2r&=31; - textX_D2R=ImGui::GetCursorPosX(); - P(CWVSliderScalar("##D2R",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.d2r,&_THIRTY_ONE,&_ZERO)); - } - - ImGui::SameLine(); - op.rr&=15; - float textX_RR=ImGui::GetCursorPosX(); - P(CWVSliderScalar("##RR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.rr,&_FIFTEEN,&_ZERO)); - - if (settings.susPosition==1) { - ImGui::SameLine(); - op.sl&=15; - textX_SL=ImGui::GetCursorPosX(); - P(CWVSliderScalar("##SL",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO)); - } - - ImVec2 prevCurPos=ImGui::GetCursorPos(); - - // labels - ImGui::SetCursorPos(ImVec2(textX_DR,textY)); - CENTER_TEXT_20(FM_SHORT_NAME(FM_DR)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_DR)); - TOOLTIP_TEXT(FM_NAME(FM_DR)); - - ImGui::SetCursorPos(ImVec2(textX_SL,textY)); - CENTER_TEXT_20(FM_SHORT_NAME(FM_SL)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_SL)); - TOOLTIP_TEXT(FM_NAME(FM_SL)); - - ImGui::SetCursorPos(ImVec2(textX_RR,textY)); - CENTER_TEXT_20(FM_SHORT_NAME(FM_RR)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_RR)); - TOOLTIP_TEXT(FM_NAME(FM_RR)); - - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { - ImGui::SetCursorPos(ImVec2(textX_D2R,textY)); - CENTER_TEXT_20(FM_SHORT_NAME(FM_D2R)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_D2R)); - TOOLTIP_TEXT(FM_NAME(FM_D2R)); - } - - ImGui::SetCursorPos(prevCurPos); - - ImGui::TableNextColumn(); - switch (ins->type) { - case DIV_INS_FM: { - // SSG - ImGui::BeginDisabled(!ssgOn); - drawSSGEnv(op.ssgEnv&7,ImVec2(waveWidth,waveHeight)); - ImGui::EndDisabled(); - if (ImGui::Checkbox("##SSGOn",&ssgOn)) { PARAMETER - op.ssgEnv=(op.ssgEnv&7)|(ssgOn<<3); - } - - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (CWSliderScalar("##SSG",ImGuiDataType_U8,&ssgEnv,&_ZERO,&_SEVEN,ssgEnvTypes[ssgEnv])) { PARAMETER - op.ssgEnv=(op.ssgEnv&8)|(ssgEnv&7); - } - - // params - ImGui::Separator(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT)); - P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable - - int detune=(op.dt&7)-(settings.unsignedDetune?0:3); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT)); - if (CWSliderInt("##DT",&detune,settings.unsignedDetune?0:-3,settings.unsignedDetune?7:4,tempID)) { PARAMETER - op.dt=detune+(settings.unsignedDetune?0:3); - } rightClickable - - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_RS)); - P(CWSliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE,tempID)); rightClickable - - break; - } - case DIV_INS_OPM: { - drawWaveform(0,true,ImVec2(waveWidth,waveHeight)); - - // params - ImGui::Separator(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT)); - P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable - - int detune=(op.dt&7)-(settings.unsignedDetune?0:3); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT)); - if (CWSliderInt("##DT",&detune,settings.unsignedDetune?0:-3,settings.unsignedDetune?7:4,tempID)) { PARAMETER - op.dt=detune+(settings.unsignedDetune?0:3); - } rightClickable - - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT2)); - P(CWSliderScalar("##DT2",ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE,tempID)); rightClickable - - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_RS)); - P(CWSliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE,tempID)); rightClickable - break; - } - case DIV_INS_OPLL: - // waveform - drawWaveform(i==0?(ins->fm.ams&1):(ins->fm.fms&1),ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight)); - - // params - ImGui::Separator(); - if (ImGui::BeginTable("FMParamsInner",2)) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - bool amOn=op.am; - if (ImGui::Checkbox(FM_NAME(FM_AM),&amOn)) { PARAMETER - op.am=amOn; - } - ImGui::TableNextColumn(); - if (ImGui::Checkbox(FM_NAME(FM_KSR),&ksrOn)) { PARAMETER - op.ksr=ksrOn; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ImGui::Checkbox(FM_NAME(FM_VIB),&vibOn)) { PARAMETER - op.vib=vibOn; - } - ImGui::TableNextColumn(); - if (ImGui::Checkbox(FM_NAME(FM_EGS),&ssgOn)) { PARAMETER - op.ssgEnv=(op.ssgEnv&7)|(ssgOn<<3); - } - - ImGui::EndTable(); - } - - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT)); - P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable - - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_KSL)); - P(CWSliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE,tempID)); rightClickable - - break; - case DIV_INS_OPL: - case DIV_INS_OPL_DRUMS: - // waveform - drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight)); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable - if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) { - ImGui::SetTooltip("OPL2/3 only (last 4 waveforms are OPL3 only)"); - } - - // params - ImGui::Separator(); - if (ImGui::BeginTable("FMParamsInner",2)) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - bool amOn=op.am; - if (ImGui::Checkbox(FM_NAME(FM_AM),&amOn)) { PARAMETER - op.am=amOn; - } - ImGui::TableNextColumn(); - if (ImGui::Checkbox(FM_NAME(FM_KSR),&ksrOn)) { PARAMETER - op.ksr=ksrOn; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ImGui::Checkbox(FM_NAME(FM_VIB),&vibOn)) { PARAMETER - op.vib=vibOn; - } - ImGui::TableNextColumn(); - if (ImGui::Checkbox(FM_NAME(FM_SUS),&susOn)) { PARAMETER - op.sus=susOn; - } - - ImGui::EndTable(); - } - - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT)); - P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable - - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_KSL)); - P(CWSliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE,tempID)); rightClickable - - break; - case DIV_INS_OPZ: { - // waveform - drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight)); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable - if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) { - ImGui::SetTooltip("OPL2/3 only (last 4 waveforms are OPL3 only)"); - } - - // params - ImGui::Separator(); - if (egtOn) { - int block=op.dt; - int freqNum=(op.mult<<4)|(op.dvb&15); - ImGui::Text("Block"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - ImVec2 cursorAlign=ImGui::GetCursorPos(); - if (ImGui::InputInt("##Block",&block,1,1)) { - if (block<0) block=0; - if (block>7) block=7; - op.dt=block; - } - - ImGui::Text("Freq"); - ImGui::SameLine(); - ImGui::SetCursorPos(ImVec2(cursorAlign.x,ImGui::GetCursorPosY())); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##FreqNum",&freqNum,1,16)) { - if (freqNum<0) freqNum=0; - if (freqNum>255) freqNum=255; - op.mult=freqNum>>4; - op.dvb=freqNum&15; - } - } else { - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT)); - P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable - - int detune=(op.dt&7)-(settings.unsignedDetune?0:3); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT)); - if (CWSliderInt("##DT",&detune,settings.unsignedDetune?0:-3,settings.unsignedDetune?7:4,tempID)) { PARAMETER - op.dt=detune+(settings.unsignedDetune?0:3); - } rightClickable - } - - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT2)); - P(CWSliderScalar("##DT2",ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE,tempID)); rightClickable - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Only on YM2151 (OPM)"); - } - - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_RS)); - P(CWSliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE,tempID)); rightClickable - break; - } - default: - break; - } - - ImGui::TableNextColumn(); - float envHeight=sliderHeight;//-ImGui::GetStyle().ItemSpacing.y*2.0f; - if (ins->type==DIV_INS_OPZ) { - envHeight-=ImGui::GetFrameHeightWithSpacing()*2.0f; - } - drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,15,ImVec2(ImGui::GetContentRegionAvail().x,envHeight),ins->type); - - if (ins->type==DIV_INS_OPZ) { + + // params ImGui::Separator(); - if (ImGui::BeginTable("FMParamsInnerOPZ",2)) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (!egtOn) { - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_FINE)); - P(CWSliderScalar("##FINE",ImGuiDataType_U8,&op.dvb,&_ZERO,&_FIFTEEN,tempID)); rightClickable - } + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT)); + P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable + int detune=detuneMap[settings.unsignedDetune?1:0][op.dt&7]; + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT)); + if (CWSliderInt("##DT",&detune,settings.unsignedDetune?0:-3,settings.unsignedDetune?7:4,tempID)) { PARAMETER + if (detune<-3) detune=-3; + if (detune>7) detune=7; + op.dt=detuneUnmap[settings.unsignedDetune?1:0][detune+3]; + } rightClickable + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_RS)); + P(CWSliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE,tempID)); rightClickable + + break; + } + case DIV_INS_OPM: { + drawWaveform(0,true,ImVec2(waveWidth,waveHeight)); + + // params + ImGui::Separator(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT)); + P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable + + int detune=detuneMap[settings.unsignedDetune?1:0][op.dt&7]; + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT)); + if (CWSliderInt("##DT",&detune,settings.unsignedDetune?0:-3,settings.unsignedDetune?7:4,tempID)) { PARAMETER + if (detune<-3) detune=-3; + if (detune>7) detune=7; + op.dt=detuneUnmap[settings.unsignedDetune?1:0][detune+3]; + } rightClickable + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT2)); + P(CWSliderScalar("##DT2",ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE,tempID)); rightClickable + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_RS)); + P(CWSliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE,tempID)); rightClickable + break; + } + case DIV_INS_OPLL: + // waveform + drawWaveform(i==0?(fmOrigin.ams&1):(fmOrigin.fms&1),ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight)); + + // params + ImGui::Separator(); + if (ImGui::BeginTable("FMParamsInner",2)) { + ImGui::TableNextRow(); ImGui::TableNextColumn(); bool amOn=op.am; if (ImGui::Checkbox(FM_NAME(FM_AM),&amOn)) { PARAMETER op.am=amOn; } - ImGui::SameLine(); - if (ImGui::Checkbox("Fixed",&egtOn)) { PARAMETER - op.egt=egtOn; + ImGui::TableNextColumn(); + if (ImGui::Checkbox(FM_NAME(FM_KSR),&ksrOn)) { PARAMETER + op.ksr=ksrOn; } ImGui::TableNextRow(); ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_EGSHIFT)); - P(CWSliderScalar("##EGShift",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE,tempID)); rightClickable - + if (ImGui::Checkbox(FM_NAME(FM_VIB),&vibOn)) { PARAMETER + op.vib=vibOn; + } ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_REV)); - P(CWSliderScalar("##REV",ImGuiDataType_U8,&op.dam,&_ZERO,&_SEVEN,tempID)); rightClickable - - ImGui::TableNextColumn(); - - + if (ImGui::Checkbox(FM_NAME(FM_EGS),&ssgOn)) { PARAMETER + op.ssgEnv=(op.ssgEnv&7)|(ssgOn<<3); + } + ImGui::EndTable(); } - } - ImGui::TableNextColumn(); - op.tl&=maxTl; - P(CWVSliderScalar("##TL",ImVec2(ImGui::GetFrameHeight(),sliderHeight-((ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM)?(ImGui::GetFrameHeightWithSpacing()+ImGui::CalcTextSize(FM_SHORT_NAME(FM_AM)).y+ImGui::GetStyle().ItemSpacing.y):0.0f)),ImGuiDataType_U8,&op.tl,&maxTl,&_ZERO)); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT)); + P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM) { - CENTER_TEXT(FM_SHORT_NAME(FM_AM)); - ImGui::TextUnformatted(FM_SHORT_NAME(FM_AM)); - TOOLTIP_TEXT(FM_NAME(FM_AM)); - bool amOn=op.am; - if (ImGui::Checkbox("##AM",&amOn)) { PARAMETER - op.am=amOn; + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_KSL)); + P(CWSliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE,tempID)); rightClickable + + break; + case DIV_INS_OPL: + case DIV_INS_OPL_DRUMS: + // waveform + drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight)); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable + if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) { + ImGui::SetTooltip("OPL2/3 only (last 4 waveforms are OPL3 only)"); } - } - ImGui::EndTable(); - } - ImGui::PopStyleVar(); - - if (settings.separateFMColors) { - popAccentColors(); - } - - ImGui::PopID(); - } - ImGui::EndTable(); - } - ImGui::PopStyleVar(); - } else { // classic - int columns=2; - switch (settings.fmLayout) { - case 1: // 2x2 - columns=2; - break; - case 2: // 1x4 - columns=1; - break; - case 3: // 4x1 - columns=opCount; - break; - } - if (ImGui::BeginTable("FMOperators",columns,ImGuiTableFlags_SizingStretchSame)) { - for (int i=0; ifm.op[(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]:i]; - if ((settings.fmLayout!=3 && ((i+1)&1)) || i==0 || settings.fmLayout==2) ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Separator(); - ImGui::PushID(fmt::sprintf("op%d",i).c_str()); - - // push colors - if (settings.separateFMColors) { - bool mod=true; - if (ins->type==DIV_INS_OPL_DRUMS) { - mod=false; - } else if (opCount==4) { - if (ins->type==DIV_INS_OPL) { - if (opIsOutputOPL[ins->fm.alg&3][i]) mod=false; - } else { - if (opIsOutput[ins->fm.alg&7][i]) mod=false; - } - } else { - if (i==1 || (ins->type==DIV_INS_OPL && (ins->fm.alg&1))) mod=false; - } - if (mod) { - pushAccentColors( - uiColors[GUI_COLOR_FM_PRIMARY_MOD], - uiColors[GUI_COLOR_FM_SECONDARY_MOD], - uiColors[GUI_COLOR_FM_BORDER_MOD], - uiColors[GUI_COLOR_FM_BORDER_SHADOW_MOD] - ); - } else { - pushAccentColors( - uiColors[GUI_COLOR_FM_PRIMARY_CAR], - uiColors[GUI_COLOR_FM_SECONDARY_CAR], - uiColors[GUI_COLOR_FM_BORDER_CAR], - uiColors[GUI_COLOR_FM_BORDER_SHADOW_CAR] - ); - } - } - - ImGui::Dummy(ImVec2(dpiScale,dpiScale)); - String opNameLabel; - OP_DRAG_POINT; - ImGui::SameLine(); - if (ins->type==DIV_INS_OPL_DRUMS) { - opNameLabel=fmt::sprintf("%s",oplDrumNames[i]); - } else if (ins->type==DIV_INS_OPL && ins->fm.opllPreset==16) { - if (i==1) { - opNameLabel="Envelope 2 (kick only)"; - } else { - opNameLabel="Envelope"; - } - } else { - opNameLabel=fmt::sprintf("OP%d",i+1); - } - if (opsAreMutable) { - pushToggleColors(op.enable); - if (ImGui::Button(opNameLabel.c_str())) { - op.enable=!op.enable; - PARAMETER; - } - popToggleColors(); - } else { - ImGui::TextUnformatted(opNameLabel.c_str()); - } - - ImGui::SameLine(); - - bool amOn=op.am; - if (ImGui::Checkbox(FM_NAME(FM_AM),&amOn)) { PARAMETER - op.am=amOn; - } - - int maxTl=127; - if (ins->type==DIV_INS_OPLL) { - if (i==1) { - maxTl=15; - } else { - maxTl=63; - } - } - if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { - maxTl=63; - } - int maxArDr=(ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM)?31:15; - - bool ssgOn=op.ssgEnv&8; - bool ksrOn=op.ksr; - bool vibOn=op.vib; - bool susOn=op.sus; // don't you make fun of this one - unsigned char ssgEnv=op.ssgEnv&7; - if (ins->type!=DIV_INS_OPL && ins->type!=DIV_INS_OPL_DRUMS && ins->type!=DIV_INS_OPZ && ins->type!=DIV_INS_OPM) { - ImGui::SameLine(); - if (ImGui::Checkbox((ins->type==DIV_INS_OPLL)?FM_NAME(FM_EGS):"SSG On",&ssgOn)) { PARAMETER - op.ssgEnv=(op.ssgEnv&7)|(ssgOn<<3); - } - } - - if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { - ImGui::SameLine(); - if (ImGui::Checkbox(FM_NAME(FM_SUS),&susOn)) { PARAMETER - op.sus=susOn; - } - } - - if (ins->type==DIV_INS_OPZ) { - ImGui::SameLine(); - bool fixedOn=op.egt; - if (ImGui::Checkbox("Fixed",&fixedOn)) { PARAMETER - op.egt=fixedOn; - } - } - - //52.0 controls vert scaling; default 96 - drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,15,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale),ins->type); - //P(CWSliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_ZERO,&_THIRTY_ONE)); rightClickable - if (ImGui::BeginTable("opParams",2,ImGuiTableFlags_SizingStretchProp)) { - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0); \ - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,0.0); \ - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - op.ar&=maxArDr; - P(CWSliderScalar("##AR",ImGuiDataType_U8,&op.ar,&maxArDr,&_ZERO)); rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_AR)); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - op.dr&=maxArDr; - P(CWSliderScalar("##DR",ImGuiDataType_U8,&op.dr,&maxArDr,&_ZERO)); rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_DR)); - - if (settings.susPosition==0) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar("##SL",ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO)); rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_SL)); - } - - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar("##D2R",ImGuiDataType_U8,&op.d2r,&_THIRTY_ONE,&_ZERO)); rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_D2R)); - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar("##RR",ImGuiDataType_U8,&op.rr,&_FIFTEEN,&_ZERO)); rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_RR)); - - if (settings.susPosition==1) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar("##SL",ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO)); rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_SL)); - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - op.tl&=maxTl; - P(CWSliderScalar("##TL",ImGuiDataType_U8,&op.tl,&maxTl,&_ZERO)); rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_TL)); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Separator(); - ImGui::TableNextColumn(); - ImGui::Separator(); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { - P(CWSliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE)); rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_RS)); - } else { - P(CWSliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_KSL)); - } - - if (ins->type==DIV_INS_OPZ) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar(FM_NAME(FM_EGSHIFT),ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_EGSHIFT)); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar(FM_NAME(FM_REV),ImGuiDataType_U8,&op.dam,&_ZERO,&_SEVEN)); rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_REV)); - } - - if (ins->type==DIV_INS_OPZ) { - if (op.egt) { - int block=op.dt; - int freqNum=(op.mult<<4)|(op.dvb&15); + // params + ImGui::Separator(); + if (ImGui::BeginTable("FMParamsInner",2)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + bool amOn=op.am; + if (ImGui::Checkbox(FM_NAME(FM_AM),&amOn)) { PARAMETER + op.am=amOn; + } + ImGui::TableNextColumn(); + if (ImGui::Checkbox(FM_NAME(FM_KSR),&ksrOn)) { PARAMETER + op.ksr=ksrOn; + } ImGui::TableNextRow(); ImGui::TableNextColumn(); + if (ImGui::Checkbox(FM_NAME(FM_VIB),&vibOn)) { PARAMETER + op.vib=vibOn; + } + ImGui::TableNextColumn(); + if (ImGui::Checkbox(FM_NAME(FM_SUS),&susOn)) { PARAMETER + op.sus=susOn; + } + + ImGui::EndTable(); + } + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT)); + P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_KSL)); + P(CWSliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE,tempID)); rightClickable + + break; + case DIV_INS_OPZ: { + // waveform + drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight)); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable + if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) { + ImGui::SetTooltip("OPL2/3 only (last 4 waveforms are OPL3 only)"); + } + + // params + ImGui::Separator(); + if (egtOn) { + int block=op.dt; + int freqNum=(op.mult<<4)|(op.dvb&15); + ImGui::Text("Block"); + ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (CWSliderInt(FM_NAME(FM_MULT),&block,0,7)) { PARAMETER + ImVec2 cursorAlign=ImGui::GetCursorPos(); + if (ImGui::InputInt("##Block",&block,1,1)) { if (block<0) block=0; if (block>7) block=7; op.dt=block; - } rightClickable - ImGui::TableNextColumn(); - ImGui::Text("Block"); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); + } + + ImGui::Text("Freq"); + ImGui::SameLine(); + ImGui::SetCursorPos(ImVec2(cursorAlign.x,ImGui::GetCursorPosY())); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (CWSliderInt(FM_NAME(FM_FINE),&freqNum,0,255)) { PARAMETER + if (ImGui::InputInt("##FreqNum",&freqNum,1,16)) { if (freqNum<0) freqNum=0; if (freqNum>255) freqNum=255; op.mult=freqNum>>4; op.dvb=freqNum&15; - } rightClickable - ImGui::TableNextColumn(); - ImGui::Text("FreqNum"); + } } else { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_MULT)); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT)); + P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable - ImGui::TableNextRow(); - ImGui::TableNextColumn(); + int detune=detuneMap[settings.unsignedDetune?1:0][op.dt&7]; ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar(FM_NAME(FM_FINE),ImGuiDataType_U8,&op.dvb,&_ZERO,&_FIFTEEN)); rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_FINE)); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT)); + if (CWSliderInt("##DT",&detune,settings.unsignedDetune?0:-3,settings.unsignedDetune?7:4,tempID)) { PARAMETER + if (detune<-3) detune=-3; + if (detune>7) detune=7; + op.dt=detuneUnmap[settings.unsignedDetune?1:0][detune+3]; + } rightClickable } + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT2)); + P(CWSliderScalar("##DT2",ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE,tempID)); rightClickable + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Only on YM2151 (OPM)"); + } + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_RS)); + P(CWSliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE,tempID)); rightClickable + break; + } + default: + break; + } + + ImGui::TableNextColumn(); + float envHeight=sliderHeight;//-ImGui::GetStyle().ItemSpacing.y*2.0f; + if (ins->type==DIV_INS_OPZ) { + envHeight-=ImGui::GetFrameHeightWithSpacing()*2.0f; + } + drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,fmOrigin.alg,maxTl,maxArDr,15,ImVec2(ImGui::GetContentRegionAvail().x,envHeight),ins->type); + + if (ins->type==DIV_INS_OPZ) { + ImGui::Separator(); + if (ImGui::BeginTable("FMParamsInnerOPZ",2)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (!egtOn) { + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_FINE)); + P(CWSliderScalar("##FINE",ImGuiDataType_U8,&op.dvb,&_ZERO,&_FIFTEEN,tempID)); rightClickable + } + + ImGui::TableNextColumn(); + bool amOn=op.am; + if (ImGui::Checkbox(FM_NAME(FM_AM),&amOn)) { PARAMETER + op.am=amOn; + } + ImGui::SameLine(); + if (ImGui::Checkbox("Fixed",&egtOn)) { PARAMETER + op.egt=egtOn; + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_EGSHIFT)); + P(CWSliderScalar("##EGShift",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE,tempID)); rightClickable + + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_REV)); + P(CWSliderScalar("##REV",ImGuiDataType_U8,&op.dam,&_ZERO,&_SEVEN,tempID)); rightClickable + + ImGui::TableNextColumn(); + + + ImGui::EndTable(); + } + } + + ImGui::TableNextColumn(); + op.tl&=maxTl; + P(CWVSliderScalar("##TL",ImVec2(ImGui::GetFrameHeight(),sliderHeight-((ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM)?(ImGui::GetFrameHeightWithSpacing()+ImGui::CalcTextSize(FM_SHORT_NAME(FM_AM)).y+ImGui::GetStyle().ItemSpacing.y):0.0f)),ImGuiDataType_U8,&op.tl,&maxTl,&_ZERO)); + + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM) { + CENTER_TEXT(FM_SHORT_NAME(FM_AM)); + ImGui::TextUnformatted(FM_SHORT_NAME(FM_AM)); + TOOLTIP_TEXT(FM_NAME(FM_AM)); + bool amOn=op.am; + if (ImGui::Checkbox("##AM",&amOn)) { PARAMETER + op.am=amOn; + } + } + + ImGui::EndTable(); + } + ImGui::PopStyleVar(); + + if (settings.separateFMColors) { + popAccentColors(); + } + + ImGui::PopID(); + } + ImGui::EndTable(); + } + ImGui::PopStyleVar(); + } else { // classic + int columns=2; + switch (settings.fmLayout) { + case 1: // 2x2 + columns=2; + break; + case 2: // 1x4 + columns=1; + break; + case 3: // 4x1 + columns=opCount; + break; + } + if (ImGui::BeginTable("FMOperators",columns,ImGuiTableFlags_SizingStretchSame)) { + for (int i=0; itype!=DIV_INS_OPL_DRUMS)?opOrder[i]:i]; + if ((settings.fmLayout!=3 && ((i+1)&1)) || i==0 || settings.fmLayout==2) ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Separator(); + ImGui::PushID(fmt::sprintf("op%d",i).c_str()); + + // push colors + if (settings.separateFMColors) { + bool mod=true; + if (ins->type==DIV_INS_OPL_DRUMS) { + mod=false; + } else if (opCount==4) { + if (ins->type==DIV_INS_OPL) { + if (opIsOutputOPL[fmOrigin.alg&3][i]) mod=false; + } else { + if (opIsOutput[fmOrigin.alg&7][i]) mod=false; + } + } else { + if (i==1 || (ins->type==DIV_INS_OPL && (fmOrigin.alg&1))) mod=false; + } + if (mod) { + pushAccentColors( + uiColors[GUI_COLOR_FM_PRIMARY_MOD], + uiColors[GUI_COLOR_FM_SECONDARY_MOD], + uiColors[GUI_COLOR_FM_BORDER_MOD], + uiColors[GUI_COLOR_FM_BORDER_SHADOW_MOD] + ); + } else { + pushAccentColors( + uiColors[GUI_COLOR_FM_PRIMARY_CAR], + uiColors[GUI_COLOR_FM_SECONDARY_CAR], + uiColors[GUI_COLOR_FM_BORDER_CAR], + uiColors[GUI_COLOR_FM_BORDER_SHADOW_CAR] + ); + } + } + + ImGui::Dummy(ImVec2(dpiScale,dpiScale)); + String opNameLabel; + OP_DRAG_POINT; + ImGui::SameLine(); + if (ins->type==DIV_INS_OPL_DRUMS) { + opNameLabel=fmt::sprintf("%s",oplDrumNames[i]); + } else if (ins->type==DIV_INS_OPL && fmOrigin.opllPreset==16) { + if (i==1) { + opNameLabel="Envelope 2 (kick only)"; + } else { + opNameLabel="Envelope"; + } + } else { + opNameLabel=fmt::sprintf("OP%d",i+1); + } + if (opsAreMutable) { + pushToggleColors(op.enable); + if (ImGui::Button(opNameLabel.c_str())) { + op.enable=!op.enable; + PARAMETER; + } + popToggleColors(); + } else { + ImGui::TextUnformatted(opNameLabel.c_str()); + } + + ImGui::SameLine(); + + bool amOn=op.am; + if (ImGui::Checkbox(FM_NAME(FM_AM),&amOn)) { PARAMETER + op.am=amOn; + } + + int maxTl=127; + if (ins->type==DIV_INS_OPLL) { + if (i==1) { + maxTl=15; + } else { + maxTl=63; + } + } + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { + maxTl=63; + } + int maxArDr=(ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM)?31:15; + + bool ssgOn=op.ssgEnv&8; + bool ksrOn=op.ksr; + bool vibOn=op.vib; + bool susOn=op.sus; // don't you make fun of this one + unsigned char ssgEnv=op.ssgEnv&7; + if (ins->type!=DIV_INS_OPL && ins->type!=DIV_INS_OPL_DRUMS && ins->type!=DIV_INS_OPZ && ins->type!=DIV_INS_OPM) { + ImGui::SameLine(); + if (ImGui::Checkbox((ins->type==DIV_INS_OPLL)?FM_NAME(FM_EGS):"SSG On",&ssgOn)) { PARAMETER + op.ssgEnv=(op.ssgEnv&7)|(ssgOn<<3); + } + } + + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { + ImGui::SameLine(); + if (ImGui::Checkbox(FM_NAME(FM_SUS),&susOn)) { PARAMETER + op.sus=susOn; + } + } + + if (ins->type==DIV_INS_OPZ) { + ImGui::SameLine(); + bool fixedOn=op.egt; + if (ImGui::Checkbox("Fixed",&fixedOn)) { PARAMETER + op.egt=fixedOn; + } + } + + //52.0 controls vert scaling; default 96 + drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,fmOrigin.alg,maxTl,maxArDr,15,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale),ins->type); + //P(CWSliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_ZERO,&_THIRTY_ONE)); rightClickable + if (ImGui::BeginTable("opParams",2,ImGuiTableFlags_SizingStretchProp)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0); \ + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,0.0); \ + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + op.ar&=maxArDr; + P(CWSliderScalar("##AR",ImGuiDataType_U8,&op.ar,&maxArDr,&_ZERO)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_AR)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + op.dr&=maxArDr; + P(CWSliderScalar("##DR",ImGuiDataType_U8,&op.dr,&maxArDr,&_ZERO)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_DR)); + + if (settings.susPosition==0) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar("##SL",ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_SL)); + } + + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar("##D2R",ImGuiDataType_U8,&op.d2r,&_THIRTY_ONE,&_ZERO)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_D2R)); + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar("##RR",ImGuiDataType_U8,&op.rr,&_FIFTEEN,&_ZERO)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_RR)); + + if (settings.susPosition==1) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar("##SL",ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_SL)); + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + op.tl&=maxTl; + P(CWSliderScalar("##TL",ImGuiDataType_U8,&op.tl,&maxTl,&_ZERO)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_TL)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Separator(); + ImGui::TableNextColumn(); + ImGui::Separator(); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { + P(CWSliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_RS)); + } else { + P(CWSliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_KSL)); + } + + if (ins->type==DIV_INS_OPZ) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar(FM_NAME(FM_EGSHIFT),ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_EGSHIFT)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar(FM_NAME(FM_REV),ImGuiDataType_U8,&op.dam,&_ZERO,&_SEVEN)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_REV)); + } + + if (ins->type==DIV_INS_OPZ) { + if (op.egt) { + int block=op.dt; + int freqNum=(op.mult<<4)|(op.dvb&15); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (CWSliderInt(FM_NAME(FM_MULT),&block,0,7)) { PARAMETER + if (block<0) block=0; + if (block>7) block=7; + op.dt=block; + } rightClickable + ImGui::TableNextColumn(); + ImGui::Text("Block"); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (CWSliderInt(FM_NAME(FM_FINE),&freqNum,0,255)) { PARAMETER + if (freqNum<0) freqNum=0; + if (freqNum>255) freqNum=255; + op.mult=freqNum>>4; + op.dvb=freqNum&15; + } rightClickable + ImGui::TableNextColumn(); + ImGui::Text("FreqNum"); } else { ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -3612,77 +3652,94 @@ void FurnaceGUI::drawInsEdit() { P(CWSliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); rightClickable ImGui::TableNextColumn(); ImGui::Text("%s",FM_NAME(FM_MULT)); - } - - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { - if (!(ins->type==DIV_INS_OPZ && op.egt)) { - int detune=(op.dt&7)-(settings.unsignedDetune?0:3); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (CWSliderInt("##DT",&detune,settings.unsignedDetune?0:-3,settings.unsignedDetune?7:4)) { PARAMETER - op.dt=detune+(settings.unsignedDetune?0:3); - } rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_DT)); - } - if (ins->type!=DIV_INS_FM) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar("##DT2",ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE)); rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_DT2)); - } - - if (ins->type==DIV_INS_FM) { // OPN only - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (CWSliderScalar("##SSG",ImGuiDataType_U8,&ssgEnv,&_ZERO,&_SEVEN,ssgEnvTypes[ssgEnv])) { PARAMETER - op.ssgEnv=(op.ssgEnv&8)|(ssgEnv&7); - } rightClickable - ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_SSG)); - } - } - - if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPZ) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable - if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) { - ImGui::SetTooltip("OPL2/3 only (last 4 waveforms are OPL3 only)"); - } + P(CWSliderScalar(FM_NAME(FM_FINE),ImGuiDataType_U8,&op.dvb,&_ZERO,&_FIFTEEN)); rightClickable ImGui::TableNextColumn(); - ImGui::Text("%s",FM_NAME(FM_WS)); + ImGui::Text("%s",FM_NAME(FM_FINE)); } - - ImGui::EndTable(); + } else { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar(FM_NAME(FM_MULT),ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_MULT)); } - - if (ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { - if (ImGui::Checkbox(FM_NAME(FM_VIB),&vibOn)) { PARAMETER - op.vib=vibOn; + + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { + if (!(ins->type==DIV_INS_OPZ && op.egt)) { + int detune=detuneMap[settings.unsignedDetune?1:0][op.dt&7]; + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (CWSliderInt("##DT",&detune,settings.unsignedDetune?0:-3,settings.unsignedDetune?7:4)) { PARAMETER + if (detune<-3) detune=-3; + if (detune>7) detune=7; + op.dt=detuneUnmap[settings.unsignedDetune?1:0][detune+3]; + } rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_DT)); } - ImGui::SameLine(); - if (ImGui::Checkbox(FM_NAME(FM_KSR),&ksrOn)) { PARAMETER - op.ksr=ksrOn; + + if (ins->type!=DIV_INS_FM) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar("##DT2",ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE)); rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_DT2)); + } + + if (ins->type==DIV_INS_FM) { // OPN only + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (CWSliderScalar("##SSG",ImGuiDataType_U8,&ssgEnv,&_ZERO,&_SEVEN,ssgEnvTypes[ssgEnv])) { PARAMETER + op.ssgEnv=(op.ssgEnv&8)|(ssgEnv&7); + } rightClickable + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_SSG)); } } - if (settings.separateFMColors) { - popAccentColors(); + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPZ) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable + if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) { + ImGui::SetTooltip("OPL2/3 only (last 4 waveforms are OPL3 only)"); + } + ImGui::TableNextColumn(); + ImGui::Text("%s",FM_NAME(FM_WS)); } - - ImGui::PopID(); + + ImGui::EndTable(); } - ImGui::EndTable(); + + if (ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) { + if (ImGui::Checkbox(FM_NAME(FM_VIB),&vibOn)) { PARAMETER + op.vib=vibOn; + } + ImGui::SameLine(); + if (ImGui::Checkbox(FM_NAME(FM_KSR),&ksrOn)) { PARAMETER + op.ksr=ksrOn; + } + } + + if (settings.separateFMColors) { + popAccentColors(); + } + + ImGui::PopID(); } + ImGui::EndTable(); } } + ImGui::EndDisabled(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("FM Macros")) { diff --git a/src/gui/orders.cpp b/src/gui/orders.cpp index 5f822078..73fcb914 100644 --- a/src/gui/orders.cpp +++ b/src/gui/orders.cpp @@ -18,8 +18,45 @@ */ #include "gui.h" +#include #include "IconsFontAwesome4.h" -#include +#include "imgui_internal.h" + +void FurnaceGUI::drawMobileOrderSel() { + if (!portrait) return; + + ImGui::SetNextWindowPos(ImVec2(0.0f,mobileMenuPos*-0.65*canvasH)); + ImGui::SetNextWindowSize(ImVec2(canvasW,0.12*canvasW)); + if (ImGui::Begin("OrderSel",NULL,globalWinFlags)) { + ImDrawList* dl=ImGui::GetWindowDrawList(); + ImGuiWindow* window=ImGui::GetCurrentWindow(); + ImGuiStyle& style=ImGui::GetStyle(); + + ImVec2 size=ImGui::GetContentRegionAvail(); + + ImVec2 minArea=window->DC.CursorPos; + ImVec2 maxArea=ImVec2( + minArea.x+size.x, + minArea.y+size.y + ); + ImRect rect=ImRect(minArea,maxArea); + ImGui::ItemSize(size,style.FramePadding.y); + ImU32 col=ImGui::GetColorU32(ImGuiCol_Text); + if (ImGui::ItemAdd(rect,ImGui::GetID("OrderSelW"))) { + String text=fmt::sprintf("%.2X",curOrder); + + ImVec2 pos=ImLerp(minArea,maxArea,ImVec2(0.5,0.0)); + ImGui::PushFont(bigFont); + ImVec2 textSize=ImGui::CalcTextSize(text.c_str()); + ImGui::PopFont(); + + pos.x-=textSize.x*0.5*(size.y/textSize.y); + + dl->AddText(bigFont,size.y,pos,col,text.c_str()); + } + } + ImGui::End(); +} void FurnaceGUI::drawOrders() { static char selID[4096]; diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index dbfee302..483d66b5 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -133,14 +133,14 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int for (int j=0; j