diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 293c4cd0..13ef95c7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -187,6 +187,8 @@ jobs: export USE_WAE=ON export CMAKE_EXTRA_ARGS=() if [ '${{ matrix.config.compiler }}' == 'msvc' ]; then + # 1. Go to hell + export USE_WAE=OFF CMAKE_EXTRA_ARGS+=('-DCMAKE_GENERATOR_PLATFORM=${{ steps.windows-identify.outputs.msvc-target }}') # Force static linking diff --git a/CMakeLists.txt b/CMakeLists.txt index e529f49e..c121ec34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -401,6 +401,7 @@ src/gui/debugWindow.cpp src/gui/doAction.cpp src/gui/editing.cpp src/gui/editControls.cpp +src/gui/effectList.cpp src/gui/insEdit.cpp src/gui/log.cpp src/gui/mixer.cpp diff --git a/README.md b/README.md index f8ea1aec..1eac106d 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ this is a multi-system chiptune tracker. check out the [Releases](https://github.com/tildearrow/furnace/releases) page. available for Windows, macOS and Linux (AppImage). +[see here](https://nightly.link/tildearrow/furnace/workflows/build/master) for unstable developer builds. + ## features - supports the following systems: @@ -64,6 +66,8 @@ some people have provided packages for Unix/Unix-like distributions. here's a li [![Build furnace](https://github.com/tildearrow/furnace/actions/workflows/build.yml/badge.svg)](https://github.com/tildearrow/furnace/actions/workflows/build.yml) +if you can't download these artifacts (because GitHub requires you to be logged in), [go here](https://nightly.link/tildearrow/furnace/workflows/build/master) instead. + **NOTE: do not download the project's source as a .zip or .tar.gz as these do not include the project's submodules which are necessary to proceed with building. please instead use Git as shown below.** ## dependencies diff --git a/TODO.md b/TODO.md index 2077bbae..5db47ec0 100644 --- a/TODO.md +++ b/TODO.md @@ -9,7 +9,10 @@ # to-do for 0.6pre1 - panning macro + - QSound? - pitch macro + - relative mode + - test - piano/input pad - note input via piano - input pad @@ -26,21 +29,17 @@ - ability to customize startup system - store system presets in new file - Game Boy envelope macro/sequence -- Game Boy envelope view - option to display chip names instead of "multi-system" on title bar - rewrite the system name detection function anyway - add nightly.link - scroll instrument/wave/sample list when selecting item -- "absorb" mode for instrument input - when this happens, current instrument is set to the input value - unified data view -- separate "transpose note" and "transpose value" - see next point - volume commands should work on Game Boy - macro editor menu - refactor sysDef.cpp - add another FM editor layout - try to find out why does VSlider not accept keyboard input - finish lock layout -- note input latch! and separate edit masks - if macros have release, note off should release them - add "don't scroll on cursor movement" option - add ability to select entire row when clicking on row number @@ -51,3 +50,4 @@ - settings: OK/Cancel buttons should be always visible - Apply button in settings - better FM chip names (number and codename) +- find and replace diff --git a/papers/format.md b/papers/format.md index 2f4037a0..7775e9ea 100644 --- a/papers/format.md +++ b/papers/format.md @@ -274,6 +274,19 @@ size | description # instrument +notes: + +- the entire instrument is stored, regardless of instrument type. +- the macro range varies depending on the instrument type. +- "macro open" indicates whether the macro is collapsed or not in the instrument editor. +- FM operator order is: + - 1/3/2/4 (internal order) for OPN, OPM, OPZ and OPL 4-op + - 1/2/?/? (? = unused) for OPL 2-op and OPLL +- meaning of extended macros varies depending on instrument type. +- meaning of panning macros varies depending on instrument type: + - for hard-panned chips (e.g. FM and Game Boy): left panning is 2-bit panning macro (left/right) + - otherwise both left and right panning macros are used + ``` size | description -----|------------------------------------ @@ -282,17 +295,39 @@ size | description 2 | format version (see header) 1 | instrument type | - 0: standard - | - 1: FM + | - 1: FM (OPM/OPN) | - 2: Game Boy | - 3: C64 | - 4: Amiga/sample + | - 5: PC Engine + | - 6: AY-3-8910 + | - 7: AY8930 + | - 8: TIA + | - 9: SAA1099 + | - 10: VIC + | - 11: PET + | - 12: VRC6 + | - 13: OPLL + | - 14: OPL + | - 15: FDS + | - 16: Virtual Boy + | - 17: Namco 163 + | - 18: SCC + | - 19: OPZ + | - 20: POKEY + | - 21: PC Speaker + | - 22: WonderSwan + | - 23: Lynx + | - 24: VERA + | - 25: X1-010 + | - 26: VRC6 (saw) 1 | reserved STR | instrument name --- | **FM instrument data** - 1 | alg + 1 | alg (SUS on OPLL) 1 | feedback - 1 | fms - 1 | ams + 1 | fms (DC on OPLL) + 1 | ams (DM on OPLL) 1 | operator count | - this is either 2 or 4, and is ignored on non-OPL systems. | - always read 4 ops regardless of this value. @@ -314,10 +349,12 @@ size | description 1 | dt 1 | d2r 1 | ssgEnv - 1 | dam - 1 | dvb - 1 | egt - 1 | ksl + | - bit 4: on (EG-S on OPLL) + | - bit 0-3: envelope type + 1 | dam (for YMU759 compat; REV on OPZ) + 1 | dvb (for YMU759 compat; FINE on OPZ) + 1 | egt (for YMU759 compat; FixedFreq on OPZ) + 1 | ksl (EGShift on OPZ) 1 | sus 1 | vib 1 | ws @@ -690,15 +727,18 @@ size | description | - 10: A# | - 11: B | - 12: C (of next octave) + | - this is actually a leftover of the .dmf format. | - 100: note off | - 100: note release | - 100: macro release | - octave | - this is an signed char stored in a short. | - therefore octave value 255 is actually octave -1. + | - yep, another leftover of the .dmf format... | - instrument | - volume - | - effect and effect data... + | - effect and effect data (× effect columns) + | - for note/octave, if both values are 0 then it means empty. | - for instrument, volume, effect and effect data, a value of -1 means empty. STR | pattern name (>=51) ``` diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 37e36a2b..d3f916bb 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -146,6 +146,15 @@ enum DivDispatchCmds { DIV_CMD_N163_GLOBAL_WAVE_LOADLEN, DIV_CMD_N163_GLOBAL_WAVE_LOADMODE, + DIV_CMD_ES5506_FILTER_MODE, + DIV_CMD_ES5506_FILTER_K1, + DIV_CMD_ES5506_FILTER_K2, + DIV_CMD_ES5506_ENVELOPE_COUNT, + DIV_CMD_ES5506_ENVELOPE_LVRAMP, + DIV_CMD_ES5506_ENVELOPE_RVRAMP, + DIV_CMD_ES5506_ENVELOPE_K1RAMP, + DIV_CMD_ES5506_ENVELOPE_K2RAMP, + DIV_ALWAYS_SET_VOLUME, DIV_CMD_MAX @@ -245,8 +254,9 @@ class DivDispatch { /** * ticks this dispatch. + * @param sysTick whether the engine has ticked (if not then this may be a sub-tick used in low-latency mode). */ - virtual void tick(); + virtual void tick(bool sysTick=true); /** * get the state of a channel. diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 290e154d..78d5f03a 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -41,7 +41,7 @@ void process(void* u, float** in, float** out, int inChans, int outChans, unsign ((DivEngine*)u)->nextBuf(in,out,inChans,outChans,size); } -const char* DivEngine::getEffectDesc(unsigned char effect, int chan) { +const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNull) { switch (effect) { case 0x00: return "00xy: Arpeggio"; @@ -116,14 +116,13 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan) { default: if ((effect&0xf0)==0x90) { return "9xxx: Set sample offset*256"; - } - else if (chan>=0 && chan=0 && changetEffectName(effect); if (ret!=NULL) return ret; } break; } - return "Invalid effect"; + return notNull?"Invalid effect":NULL; } void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) { @@ -397,7 +396,7 @@ void DivEngine::runExportThread() { if (getChannelType(i)==5) { i++; while (true) { - if (++i>=chans) break; + if (i>=chans) break; if (getChannelType(i)!=5) break; } i--; @@ -635,10 +634,10 @@ void DivEngine::renderSamples() { break; } if (memPos+length>=16777216) { - memcpy(es5506Mem+memPos,s->data16,16777216-memPos); + memcpy(es5506Mem+(memPos/sizeof(short)),s->data16,16777216-memPos); logW("out of ES5506 memory for sample %d!",i); } else { - memcpy(es5506Mem+memPos,s->data16,length); + memcpy(es5506Mem+(memPos/sizeof(short)),s->data16,length); } s->offES5506=memPos; memPos+=length; @@ -770,8 +769,20 @@ String DivEngine::getWarnings() { return warnings; } -DivInstrument* DivEngine::getIns(int index) { - if (index<0 || index>=song.insLen) return &song.nullIns; +DivInstrument* DivEngine::getIns(int index, DivInstrumentType fallbackType) { + if (index<0 || index>=song.insLen) { + switch (fallbackType) { + case DIV_INS_OPLL: + return &song.nullInsOPLL; + break; + case DIV_INS_OPL: + return &song.nullInsOPL; + break; + default: + break; + } + return &song.nullIns; + } return song.ins[index]; } @@ -884,6 +895,7 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) { } if (!preserveDrift) { ticks=1; + subticks=1; } skipping=false; cmdStream.clear(); @@ -1338,8 +1350,19 @@ int DivEngine::addInstrument(int refChan) { BUSY_BEGIN; DivInstrument* ins=new DivInstrument; int insCount=(int)song.ins.size(); + DivInstrumentType prefType=getPreferInsType(refChan); + switch (prefType) { + case DIV_INS_OPLL: + *ins=song.nullInsOPLL; + break; + case DIV_INS_OPL: + *ins=song.nullInsOPL; + break; + default: + break; + } ins->name=fmt::sprintf("Instrument %d",insCount); - ins->type=getPreferInsType(refChan); + ins->type=prefType; saveLock.lock(); song.ins.push_back(ins); song.insLen=insCount+1; @@ -2014,7 +2037,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { } do { - if ((ins==-1 || getPreferInsType(finalChan)==getIns(ins)->type) && chan[finalChan].midiNote==-1) { + if ((ins<0 || ins>=song.insLen || getChannelType(finalChan)==4 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) { chan[finalChan].midiNote=note; pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); break; @@ -2040,6 +2063,20 @@ void DivEngine::autoNoteOff(int ch, int note, int vol) { } } +void DivEngine::autoNoteOffAll() { + if (!playing) { + reset(); + freelance=true; + playing=true; + } + for (int i=0; i2.0f) metroVol=2.0f; + if (lowLatency) logI("using low latency mode."); + switch (audioEngine) { case DIV_AUDIO_JACK: #ifndef HAVE_JACK diff --git a/src/engine/engine.h b/src/engine/engine.h index 18c9459b..6c8fd8ed 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -19,6 +19,7 @@ #ifndef _ENGINE_H #define _ENGINE_H +#include "instrument.h" #include "song.h" #include "dispatch.h" #include "dataErrors.h" @@ -42,8 +43,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev83" -#define DIV_ENGINE_VERSION 83 +#define DIV_VERSION "dev84" +#define DIV_ENGINE_VERSION 84 // for imports #define DIV_VERSION_MOD 0xff01 @@ -273,7 +274,7 @@ class DivEngine { void nextRow(); void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool isSecond); // returns true if end of song. - bool nextTick(bool noAccum=false); + bool nextTick(bool noAccum=false, bool inhibitLowLat=false); bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal); bool perSystemPostEffect(int ch, unsigned char effect, unsigned char effectVal); void recalcChans(); @@ -289,6 +290,7 @@ class DivEngine { void loadVGI(SafeReader& reader, std::vector& ret, String& stripPath); void loadS3I(SafeReader& reader, std::vector& ret, String& stripPath); void loadSBI(SafeReader& reader, std::vector& ret, String& stripPath); + void loadBNK(SafeReader& reader, std::vector& ret, String& stripPath); void loadOPM(SafeReader& reader, std::vector& ret, String& stripPath); void loadFF(SafeReader& reader, std::vector& ret, String& stripPath); @@ -310,7 +312,7 @@ class DivEngine { void runExportThread(); void nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size); - DivInstrument* getIns(int index); + DivInstrument* getIns(int index, DivInstrumentType fallbackType=DIV_INS_FM); DivWavetable* getWave(int index); DivSample* getSample(int index); // start fresh @@ -403,7 +405,7 @@ class DivEngine { int getTotalChannelCount(); // get effect description - const char* getEffectDesc(unsigned char effect, int chan); + const char* getEffectDesc(unsigned char effect, int chan, bool notNull=false); // get channel type // - 0: FM @@ -573,6 +575,7 @@ class DivEngine { void autoNoteOn(int chan, int ins, int note, int vol=-1); void autoNoteOff(int chan, int note, int vol=-1); + void autoNoteOffAll(); // go to order void setOrder(unsigned char order); diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 0b11b552..3e91f976 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -990,6 +990,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { ds.ignoreDACModeOutsideIntendedChannel=true; ds.e1e2AlsoTakePriority=false; } + if (ds.version<84) { + ds.newSegaPCM=false; + } ds.isDMF=false; reader.readS(); // reserved @@ -1334,7 +1337,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { reader.readC(); reader.readC(); } - for (int i=0; i<23; i++) { + if (ds.version>=84) { + ds.newSegaPCM=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<22; i++) { reader.readC(); } } @@ -2287,7 +2295,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeC(song.sharedExtStat); w->writeC(song.ignoreDACModeOutsideIntendedChannel); w->writeC(song.e1e2AlsoTakePriority); - for (int i=0; i<23; i++) { + w->writeC(song.newSegaPCM); + for (int i=0; i<22; i++) { w->writeC(0); } diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index af0c06d8..f9eaab38 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -30,10 +30,66 @@ enum DivInsFormats { DIV_INSFORMAT_BTI, DIV_INSFORMAT_S3I, DIV_INSFORMAT_SBI, + DIV_INSFORMAT_BNK, DIV_INSFORMAT_OPM, DIV_INSFORMAT_FF, }; +// Patch data structures + +// SBI and some other OPL containers +struct sbi_t { + uint8_t Mcharacteristics, + Ccharacteristics, + Mscaling_output, + Cscaling_output, + Meg_AD, + Ceg_AD, + Meg_SR, + Ceg_SR, + Mwave, + Cwave, + FeedConnect; +}; + +// Adlib Visual Composer BNK +struct bnkop_t { + uint8_t ksl, + multiple, + feedback, // op1 only + attack, + sustain, + eg, + decay, + releaseRate, + totalLevel, + am, + vib, + ksr, + con; // op1 only +}; +struct bnktimbre_t { + uint8_t mode, + percVoice; + bnkop_t op[2]; + uint8_t wave0, + wave1; +}; + +auto readSbiOpData = [](sbi_t& sbi, SafeReader& reader) { + sbi.Mcharacteristics = reader.readC(); + sbi.Ccharacteristics = reader.readC(); + sbi.Mscaling_output = reader.readC(); + sbi.Cscaling_output = reader.readC(); + sbi.Meg_AD = reader.readC(); + sbi.Ceg_AD = reader.readC(); + sbi.Meg_SR = reader.readC(); + sbi.Ceg_SR = reader.readC(); + sbi.Mwave = reader.readC(); + sbi.Cwave = reader.readC(); + sbi.FeedConnect = reader.readC(); +}; + void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, String& stripPath) { DivInstrument* ins=new DivInstrument; // this is a ridiculous mess @@ -45,7 +101,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St logD(".dmp version %d",version); } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE("premature end of file"); delete ins; return; } @@ -104,7 +160,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE("premature end of file"); delete ins; return; } @@ -296,7 +352,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE("premature end of file"); delete ins; return; } @@ -331,7 +387,7 @@ void DivEngine::loadTFI(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE("premature end of file"); delete ins; return; } @@ -373,7 +429,7 @@ void DivEngine::loadVGI(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE("premature end of file"); delete ins; return; } @@ -390,71 +446,70 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St if (s3i_type >= 2) { ins->type = DIV_INS_OPL; + if (s3i_type > 2 && s3i_type <= 7) { + ins->fm.opllPreset = (uint8_t)(1<<4); // Flag as Drum preset. + } // skip internal filename - we'll use the long name description reader.seek(12, SEEK_CUR); // skip reserved bytes reader.seek(3, SEEK_CUR); - // 12-byte opl value - uint8_t s3i_Mcharacteristics = reader.readC(); - uint8_t s3i_Ccharacteristics = reader.readC(); - uint8_t s3i_Mscaling_output = reader.readC(); - uint8_t s3i_Cscaling_output = reader.readC(); - uint8_t s3i_Meg_AD = reader.readC(); - uint8_t s3i_Ceg_AD = reader.readC(); - uint8_t s3i_Meg_SR = reader.readC(); - uint8_t s3i_Ceg_SR = reader.readC(); - uint8_t s3i_Mwave = reader.readC(); - uint8_t s3i_Cwave = reader.readC(); - uint8_t s3i_FeedConnect = reader.readC(); + // 12-byte opl value - identical to SBI format + sbi_t s3i; + + readSbiOpData(s3i, reader); DivInstrumentFM::Operator& opM = ins->fm.op[0]; DivInstrumentFM::Operator& opC = ins->fm.op[1]; ins->fm.ops = 2; - opM.mult = s3i_Mcharacteristics & 0xF; - opM.ksr = ((s3i_Mcharacteristics >> 4) & 0x1); - opM.sus = ((s3i_Mcharacteristics >> 5) & 0x1); - opM.vib = ((s3i_Mcharacteristics >> 6) & 0x1); - opM.am = ((s3i_Mcharacteristics >> 7) & 0x1); - opM.tl = s3i_Mscaling_output & 0x3F; - opM.ksl = ((s3i_Mscaling_output >> 6) & 0x3); - opM.ar = ((s3i_Meg_AD >> 4) & 0xF); - opM.dr = (s3i_Meg_AD & 0xF); - opM.rr = (s3i_Meg_SR & 0xF); - opM.sl = ((s3i_Meg_SR >> 4) & 0xF); - opM.ws = s3i_Mwave; + opM.mult = s3i.Mcharacteristics & 0xF; + opM.ksr = ((s3i.Mcharacteristics >> 4) & 0x1); + opM.sus = ((s3i.Mcharacteristics >> 5) & 0x1); + opM.vib = ((s3i.Mcharacteristics >> 6) & 0x1); + opM.am = ((s3i.Mcharacteristics >> 7) & 0x1); + opM.tl = s3i.Mscaling_output & 0x3F; + opM.ksl = ((s3i.Mscaling_output >> 6) & 0x3); + opM.ar = ((s3i.Meg_AD >> 4) & 0xF); + opM.dr = (s3i.Meg_AD & 0xF); + opM.rr = (s3i.Meg_SR & 0xF); + opM.sl = ((s3i.Meg_SR >> 4) & 0xF); + opM.ws = s3i.Mwave; - ins->fm.alg = (s3i_FeedConnect & 0x1); - ins->fm.fb = ((s3i_FeedConnect >> 1) & 0x7); + ins->fm.alg = (s3i.FeedConnect & 0x1); + ins->fm.fb = ((s3i.FeedConnect >> 1) & 0x7); - opC.mult = s3i_Ccharacteristics & 0xF; - opC.ksr = ((s3i_Ccharacteristics >> 4) & 0x1); - opC.sus = ((s3i_Ccharacteristics >> 5) & 0x1); - opC.vib = ((s3i_Ccharacteristics >> 6) & 0x1); - opC.am = ((s3i_Ccharacteristics >> 7) & 0x1); - opC.tl = s3i_Cscaling_output & 0x3F; - opC.ksl = ((s3i_Cscaling_output >> 6) & 0x3); - opC.ar = ((s3i_Ceg_AD >> 4) & 0xF); - opC.dr = (s3i_Ceg_AD & 0xF); - opC.rr = (s3i_Ceg_SR & 0xF); - opC.sl = ((s3i_Ceg_SR >> 4) & 0xF); - opC.ws = s3i_Cwave; + opC.mult = s3i.Ccharacteristics & 0xF; + opC.ksr = ((s3i.Ccharacteristics >> 4) & 0x1); + opC.sus = ((s3i.Ccharacteristics >> 5) & 0x1); + opC.vib = ((s3i.Ccharacteristics >> 6) & 0x1); + opC.am = ((s3i.Ccharacteristics >> 7) & 0x1); + opC.tl = s3i.Cscaling_output & 0x3F; + opC.ksl = ((s3i.Cscaling_output >> 6) & 0x3); + opC.ar = ((s3i.Ceg_AD >> 4) & 0xF); + opC.dr = (s3i.Ceg_AD & 0xF); + opC.rr = (s3i.Ceg_SR & 0xF); + opC.sl = ((s3i.Ceg_SR >> 4) & 0xF); + opC.ws = s3i.Cwave; // Skip more stuff we don't need reader.seek(21, SEEK_CUR); } else { + lastError = "S3I PCM samples currently not supported."; logE("S3I PCM samples currently not supported."); } ins->name = reader.readString(28); + ins->name = (ins->name.length() == 0) ? stripPath : ins->name; + int s3i_signature = reader.readI(); if (s3i_signature != 0x49524353) { + addWarning("S3I signature invalid."); logW("S3I signature invalid."); }; } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE("premature end of file!"); + logE("premature end of file"); delete ins; return; } @@ -470,77 +525,69 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St int sbi_header = reader.readI(); // SBI header determines format - bool is_2op = (sbi_header == 0x1A494253); // SBI\x1A + bool is_2op = (sbi_header == 0x1A494253 || sbi_header == 0x1A504F32); // SBI\x1A or 2OP\x1A bool is_4op = (sbi_header == 0x1A504F34); // 4OP\x1A bool is_6op = (sbi_header == 0x1A504F36); // 6OP\x1A - Freq Monster 801-specific // 32-byte null terminated instrument name - ins->name = reader.readString(32); + String patchName = reader.readString(32); + patchName = (patchName.length() == 0) ? stripPath : patchName; - // 2op SBI - uint8_t sbi_Mcharacteristics = reader.readC(); - uint8_t sbi_Ccharacteristics = reader.readC(); - uint8_t sbi_Mscaling_output = reader.readC(); - uint8_t sbi_Cscaling_output = reader.readC(); - uint8_t sbi_Meg_AD = reader.readC(); - uint8_t sbi_Ceg_AD = reader.readC(); - uint8_t sbi_Meg_SR = reader.readC(); - uint8_t sbi_Ceg_SR = reader.readC(); - uint8_t sbi_Mwave = reader.readC(); - uint8_t sbi_Cwave = reader.readC(); - uint8_t sbi_FeedConnect = reader.readC(); + auto writeOp = [](sbi_t& sbi, DivInstrumentFM::Operator& opM, DivInstrumentFM::Operator& opC) { + opM.mult = sbi.Mcharacteristics & 0xF; + opM.ksr = ((sbi.Mcharacteristics >> 4) & 0x1); + opM.sus = ((sbi.Mcharacteristics >> 5) & 0x1); + opM.vib = ((sbi.Mcharacteristics >> 6) & 0x1); + opM.am = ((sbi.Mcharacteristics >> 7) & 0x1); + opM.tl = sbi.Mscaling_output & 0x3F; + opM.ksl = ((sbi.Mscaling_output >> 6) & 0x3); + opM.ar = ((sbi.Meg_AD >> 4) & 0xF); + opM.dr = (sbi.Meg_AD & 0xF); + opM.rr = (sbi.Meg_SR & 0xF); + opM.sl = ((sbi.Meg_SR >> 4) & 0xF); + opM.ws = sbi.Mwave; - // 4op SBI - uint8_t sbi_M4characteristics; - uint8_t sbi_C4characteristics; - uint8_t sbi_M4scaling_output; - uint8_t sbi_C4scaling_output; - uint8_t sbi_M4eg_AD; - uint8_t sbi_C4eg_AD; - uint8_t sbi_M4eg_SR; - uint8_t sbi_C4eg_SR; - uint8_t sbi_M4wave; - uint8_t sbi_C4wave; - uint8_t sbi_4opConnect; + opC.mult = sbi.Ccharacteristics & 0xF; + opC.ksr = ((sbi.Ccharacteristics >> 4) & 0x1); + opC.sus = ((sbi.Ccharacteristics >> 5) & 0x1); + opC.vib = ((sbi.Ccharacteristics >> 6) & 0x1); + opC.am = ((sbi.Ccharacteristics >> 7) & 0x1); + opC.tl = sbi.Cscaling_output & 0x3F; + opC.ksl = ((sbi.Cscaling_output >> 6) & 0x3); + opC.ar = ((sbi.Ceg_AD >> 4) & 0xF); + opC.dr = (sbi.Ceg_AD & 0xF); + opC.rr = (sbi.Ceg_SR & 0xF); + opC.sl = ((sbi.Ceg_SR >> 4) & 0xF); + opC.ws = sbi.Cwave; + }; + + sbi_t sbi_op12; // 2op (+6op portion) + sbi_t sbi_op34; // 4op + readSbiOpData(sbi_op12, reader); + if (is_2op) { DivInstrumentFM::Operator& opM = ins->fm.op[0]; DivInstrumentFM::Operator& opC = ins->fm.op[1]; ins->fm.ops = 2; - opM.mult = sbi_Mcharacteristics & 0xF; - opM.ksr = ((sbi_Mcharacteristics >> 4) & 0x1); - opM.sus = ((sbi_Mcharacteristics >> 5) & 0x1); - opM.vib = ((sbi_Mcharacteristics >> 6) & 0x1); - opM.am = ((sbi_Mcharacteristics >> 7) & 0x1); - opM.tl = sbi_Mscaling_output & 0x3F; - opM.ksl = ((sbi_Mscaling_output >> 6) & 0x3); - opM.ar = ((sbi_Meg_AD >> 4) & 0xF); - opM.dr = (sbi_Meg_AD & 0xF); - opM.rr = (sbi_Meg_SR & 0xF); - opM.sl = ((sbi_Meg_SR >> 4) & 0xF); - opM.ws = sbi_Mwave; + ins->name = patchName; + writeOp(sbi_op12, opM, opC); + ins->fm.alg = (sbi_op12.FeedConnect & 0x1); + ins->fm.fb = ((sbi_op12.FeedConnect >> 1) & 0x7); - ins->fm.alg = (sbi_FeedConnect & 0x1); - ins->fm.fb = ((sbi_FeedConnect >> 1) & 0x7); - - opC.mult = sbi_Ccharacteristics & 0xF; - opC.ksr = ((sbi_Ccharacteristics >> 4) & 0x1); - opC.sus = ((sbi_Ccharacteristics >> 5) & 0x1); - opC.vib = ((sbi_Ccharacteristics >> 6) & 0x1); - opC.am = ((sbi_Ccharacteristics >> 7) & 0x1); - opC.tl = sbi_Cscaling_output & 0x3F; - opC.ksl = ((sbi_Cscaling_output >> 6) & 0x3); - opC.ar = ((sbi_Ceg_AD >> 4) & 0xF); - opC.dr = (sbi_Ceg_AD & 0xF); - opC.rr = (sbi_Ceg_SR & 0xF); - opC.sl = ((sbi_Ceg_SR >> 4) & 0xF); - opC.ws = sbi_Cwave; + // SBTimbre extensions + uint8_t perc_voc = reader.readC(); + if (perc_voc >= 6) { + ins->fm.opllPreset = (uint8_t)(1 << 4); + } // Ignore rest of file - rest is 'reserved padding'. - reader.seek(0, SEEK_END); - } + reader.seek(4, SEEK_CUR); + ret.push_back(ins); - if (is_4op || is_6op) { + } else if (is_4op || is_6op) { + readSbiOpData(sbi_op34, reader); + // Operator placement is different so need to place in correct registers. // Note: 6op is an unofficial extension of 4op SBIs by Darron Broad (Freq Monster 801). // We'll only use the 4op portion here for pure OPL3. @@ -549,87 +596,144 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St DivInstrumentFM::Operator& opM4 = ins->fm.op[1]; DivInstrumentFM::Operator& opC4 = ins->fm.op[3]; ins->fm.ops = 4; + ins->name = patchName; + ins->fm.alg = (sbi_op12.FeedConnect & 0x1) | ((sbi_op34.FeedConnect & 0x1) << 1); + ins->fm.fb = ((sbi_op34.FeedConnect >> 1) & 0x7); + writeOp(sbi_op12, opM, opC); + writeOp(sbi_op34, opM4, opC4); - sbi_M4characteristics = reader.readC(); - sbi_C4characteristics = reader.readC(); - sbi_M4scaling_output = reader.readC(); - sbi_C4scaling_output = reader.readC(); - sbi_M4eg_AD = reader.readC(); - sbi_C4eg_AD = reader.readC(); - sbi_M4eg_SR = reader.readC(); - sbi_C4eg_SR = reader.readC(); - sbi_M4wave = reader.readC(); - sbi_C4wave = reader.readC(); - sbi_4opConnect = reader.readC(); - - ins->fm.alg = (sbi_FeedConnect & 0x1) | ((sbi_4opConnect & 0x1) << 1); - ins->fm.fb = ((sbi_FeedConnect >> 1) & 0x7); + if (is_6op) { + // Freq Monster 801 6op SBIs use a 4+2op layout + // Save the 4op portion before reading the 2op part + ins->name = fmt::format("{0} (4op)", ins->name); + ret.push_back(ins); - opM.mult = sbi_Mcharacteristics & 0xF; - opM.ksr = ((sbi_Mcharacteristics >> 4) & 0x1); - opM.sus = ((sbi_Mcharacteristics >> 5) & 0x1); - opM.vib = ((sbi_Mcharacteristics >> 6) & 0x1); - opM.am = ((sbi_Mcharacteristics >> 7) & 0x1); - opM.tl = sbi_Mscaling_output & 0x3F; - opM.ksl = ((sbi_Mscaling_output >> 6) & 0x3); - opM.ar = ((sbi_Meg_AD >> 4) & 0xF); - opM.dr = (sbi_Meg_AD & 0xF); - opM.rr = (sbi_Meg_SR & 0xF); - opM.sl = ((sbi_Meg_SR >> 4) & 0xF); - opM.ws = sbi_Mwave; + readSbiOpData(sbi_op12, reader); - opC.mult = sbi_Ccharacteristics & 0xF; - opC.ksr = ((sbi_Ccharacteristics >> 4) & 0x1); - opC.sus = ((sbi_Ccharacteristics >> 5) & 0x1); - opC.vib = ((sbi_Ccharacteristics >> 6) & 0x1); - opC.am = ((sbi_Ccharacteristics >> 7) & 0x1); - opC.tl = sbi_Cscaling_output & 0x3F; - opC.ksl = ((sbi_Cscaling_output >> 6) & 0x3); - opC.ar = ((sbi_Ceg_AD >> 4) & 0xF); - opC.dr = (sbi_Ceg_AD & 0xF); - opC.rr = (sbi_Ceg_SR & 0xF); - opC.sl = ((sbi_Ceg_SR >> 4) & 0xF); - opC.ws = sbi_Cwave; - - opM4.mult = sbi_M4characteristics & 0xF; - opM4.ksr = ((sbi_M4characteristics >> 4) & 0x1); - opM4.sus = ((sbi_M4characteristics >> 5) & 0x1); - opM4.vib = ((sbi_M4characteristics >> 6) & 0x1); - opM4.am = ((sbi_M4characteristics >> 7) & 0x1); - opM4.tl = sbi_M4scaling_output & 0x3F; - opM4.ksl = ((sbi_M4scaling_output >> 6) & 0x3); - opM4.ar = ((sbi_M4eg_AD >> 4) & 0xF); - opM4.dr = (sbi_M4eg_AD & 0xF); - opM4.rr = (sbi_M4eg_SR & 0xF); - opM4.sl = ((sbi_M4eg_SR >> 4) & 0xF); - opM4.ws = sbi_M4wave; - - opC4.mult = sbi_C4characteristics & 0xF; - opC4.ksr = ((sbi_C4characteristics >> 4) & 0x1); - opC4.sus = ((sbi_C4characteristics >> 5) & 0x1); - opC4.vib = ((sbi_C4characteristics >> 6) & 0x1); - opC4.am = ((sbi_C4characteristics >> 7) & 0x1); - opC4.tl = sbi_C4scaling_output & 0x3F; - opC4.ksl = ((sbi_C4scaling_output >> 6) & 0x3); - opC4.ar = ((sbi_C4eg_AD >> 4) & 0xF); - opC4.dr = (sbi_C4eg_AD & 0xF); - opC4.rr = (sbi_C4eg_SR & 0xF); - opC4.sl = ((sbi_C4eg_SR >> 4) & 0xF); - opC4.ws = sbi_C4wave; + ins = new DivInstrument; + DivInstrumentFM::Operator& opM6 = ins->fm.op[0]; + DivInstrumentFM::Operator& opC6 = ins->fm.op[1]; + ins->type = DIV_INS_OPL; + ins->fm.ops = 2; + ins->name = fmt::format("{0} (2op)", patchName); + writeOp(sbi_op12, opM6, opC6); + ins->fm.alg = (sbi_op12.FeedConnect & 0x1); + ins->fm.fb = ((sbi_op12.FeedConnect >> 1) & 0x7); + } // Ignore rest of file once we've read in all we need. // Note: Freq Monster 801 adds a ton of other additional fields irrelevant to chip registers. + // If instrument transpose is ever supported, we can read it in maybe? reader.seek(0, SEEK_END); + ret.push_back(ins); } } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE("premature end of file!"); + logE("premature end of file"); delete ins; - return; + } +} +void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, String& stripPath) { + std::vector insList; + std::vector instNames; + reader.seek(0, SEEK_SET); + + // First distinguish between GEMS BNK and Adlib BNK + uint64_t header = reader.readL(); + bool is_adlib = ((header>>8) == 0x2d42494c444100L); + bool is_failed = false; + int readCount = 0; + + if (is_adlib) { + try { + reader.seek(0x0c, SEEK_SET); + uint32_t name_offset = reader.readI(); + reader.seek(0x10, SEEK_SET); + uint32_t data_offset = reader.readI(); + + // Seek to BNK patch names + reader.seek(name_offset, SEEK_SET); + while (reader.tell() < data_offset) { + reader.seek(3, SEEK_CUR); + instNames.push_back(new String(reader.readString(9))); + ++readCount; + } + + // Seek to BNK data + reader.seek(data_offset, SEEK_SET); + + // Read until EOF + for (int i = 0; i < readCount; ++i) { + bnktimbre_t timbre; + insList.push_back(new DivInstrument); + auto& ins = insList[i]; + + ins->type = DIV_INS_OPL; + + timbre.mode = reader.readC(); + timbre.percVoice = reader.readC(); + if (timbre.mode == 1) { + ins->fm.opllPreset = (uint8_t)(1<<4); + } + ins->fm.op[0].ksl = reader.readC(); + ins->fm.op[0].mult = reader.readC(); + ins->fm.fb = reader.readC(); + ins->fm.op[0].ar = reader.readC(); + ins->fm.op[0].sl = reader.readC(); + ins->fm.op[0].sus = (reader.readC() != 0) ? 1 : 0; + ins->fm.op[0].dr = reader.readC(); + ins->fm.op[0].rr = reader.readC(); + ins->fm.op[0].tl = reader.readC(); + ins->fm.op[0].am = reader.readC(); + ins->fm.op[0].vib = reader.readC(); + ins->fm.op[0].ksr = reader.readC(); + ins->fm.alg = (reader.readC() == 0) ? 1 : 0; + + ins->fm.op[1].ksl = reader.readC(); + ins->fm.op[1].mult = reader.readC(); + reader.readC(); // skip + ins->fm.op[1].ar = reader.readC(); + ins->fm.op[1].sl = reader.readC(); + ins->fm.op[1].sus = (reader.readC() != 0) ? 1 : 0; + ins->fm.op[1].dr = reader.readC(); + ins->fm.op[1].rr = reader.readC(); + ins->fm.op[1].tl = reader.readC(); + ins->fm.op[1].am = reader.readC(); + ins->fm.op[1].vib = reader.readC(); + ins->fm.op[1].ksr = reader.readC(); + reader.readC(); // skip + + ins->fm.op[0].ws = reader.readC(); + ins->fm.op[1].ws = reader.readC(); + ins->name = instNames[i]->length() > 0 ? (*instNames[i]) : fmt::format("{0}[{1}]", stripPath, i); + } + reader.seek(0, SEEK_END); + + } catch (EndOfFileException& e) { + lastError = "premature end of file"; + logE("premature end of file"); + for (int i = readCount; i >= 0; --i) { + delete insList[i]; + } + is_failed = true; + } + + } else { + // assume GEMS BNK for now. + lastError = "GEMS BNK currently not supported."; + logE("GEMS BNK currently not supported."); } - ret.push_back(ins); + if (!is_failed) { + for (int i = 0; i < readCount; ++i) { + ret.push_back(insList[i]); + } + } + + for (auto& name : instNames) { + delete name; + } } void DivEngine::loadFF(SafeReader& reader, std::vector& ret, String& stripPath) { @@ -693,7 +797,7 @@ void DivEngine::loadFF(SafeReader& reader, std::vector& ret, Str } } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE("premature end of file!\n"); + logE("premature end of file"); for (int i = readCount; i >= 0; --i) { delete insList[i]; } @@ -714,7 +818,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE("premature end of file"); return; } } @@ -812,7 +916,7 @@ std::vector DivEngine::instrumentFromFile(const char* path) { } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE("premature end of file"); delete ins; delete[] buf; return ret; @@ -843,6 +947,8 @@ std::vector DivEngine::instrumentFromFile(const char* path) { format=DIV_INSFORMAT_S3I; } else if (extS==String(".sbi")) { format=DIV_INSFORMAT_SBI; + } else if (extS==String(".bnk")) { + format=DIV_INSFORMAT_BNK; } else if (extS==String(".opm")) { format=DIV_INSFORMAT_OPM; } else if (extS==String(".ff")) { @@ -873,6 +979,9 @@ std::vector DivEngine::instrumentFromFile(const char* path) { case DIV_INSFORMAT_SBI: loadSBI(reader,ret,stripPath); break; + case DIV_INSFORMAT_BNK: + loadBNK(reader, ret, stripPath); + break; case DIV_INSFORMAT_FF: loadFF(reader,ret,stripPath); break; diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index cfd338c1..f5e6378c 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -486,6 +486,27 @@ void DivInstrument::putInsData(SafeWriter* w) { w->writeC(ws.param2); w->writeC(ws.param3); w->writeC(ws.param4); + + // other macro modes + w->writeC(std.volMacro.mode); + w->writeC(std.dutyMacro.mode); + w->writeC(std.waveMacro.mode); + w->writeC(std.pitchMacro.mode); + w->writeC(std.ex1Macro.mode); + w->writeC(std.ex2Macro.mode); + w->writeC(std.ex3Macro.mode); + w->writeC(std.algMacro.mode); + w->writeC(std.fbMacro.mode); + w->writeC(std.fmsMacro.mode); + w->writeC(std.amsMacro.mode); + w->writeC(std.panLMacro.mode); + w->writeC(std.panRMacro.mode); + w->writeC(std.phaseResetMacro.mode); + w->writeC(std.ex4Macro.mode); + w->writeC(std.ex5Macro.mode); + w->writeC(std.ex6Macro.mode); + w->writeC(std.ex7Macro.mode); + w->writeC(std.ex8Macro.mode); } DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { @@ -962,6 +983,29 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { ws.param4=reader.readC(); } + // other macro modes + if (version>=84) { + std.volMacro.mode=reader.readC(); + std.dutyMacro.mode=reader.readC(); + std.waveMacro.mode=reader.readC(); + std.pitchMacro.mode=reader.readC(); + std.ex1Macro.mode=reader.readC(); + std.ex2Macro.mode=reader.readC(); + std.ex3Macro.mode=reader.readC(); + std.algMacro.mode=reader.readC(); + std.fbMacro.mode=reader.readC(); + std.fmsMacro.mode=reader.readC(); + std.amsMacro.mode=reader.readC(); + std.panLMacro.mode=reader.readC(); + std.panRMacro.mode=reader.readC(); + std.phaseResetMacro.mode=reader.readC(); + std.ex4Macro.mode=reader.readC(); + std.ex5Macro.mode=reader.readC(); + std.ex6Macro.mode=reader.readC(); + std.ex7Macro.mode=reader.readC(); + std.ex8Macro.mode=reader.readC(); + } + return DIV_DATA_SUCCESS; } diff --git a/src/engine/instrument.h b/src/engine/instrument.h index ee832e7f..e19b3203 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -302,6 +302,7 @@ struct DivInstrumentAmiga { }; short initSample; + bool reversed; bool useNoteMap; bool useWave; unsigned char waveLen; @@ -309,6 +310,7 @@ struct DivInstrumentAmiga { DivInstrumentAmiga(): initSample(0), + reversed(false), useNoteMap(false), useWave(false), waveLen(31) {} diff --git a/src/engine/macroInt.cpp b/src/engine/macroInt.cpp index ae6caf5d..00fee080 100644 --- a/src/engine/macroInt.cpp +++ b/src/engine/macroInt.cpp @@ -21,14 +21,19 @@ #include "instrument.h" #include "engine.h" -void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released) { +void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tick) { + if (!tick) { + had=false; + return; + } if (finished) { finished=false; } - if (had!=has) { + if (actualHad!=has) { finished=true; } - had=has; + actualHad=has; + had=actualHad; if (has) { val=source.val[pos++]; if (source.rel>=0 && pos>source.rel && !released) { @@ -52,17 +57,18 @@ void DivMacroInt::next() { if (ins==NULL) return; // run macros // TODO: potentially get rid of list to avoid allocations - if (--subTick<=0) { + subTick--; + for (size_t i=0; idoMacro(*macroSource[i],released,subTick==0); + } + } + if (subTick<=0) { if (e==NULL) { subTick=1; } else { subTick=e->tickMult; } - for (size_t i=0; idoMacro(*macroSource[i],released); - } - } } } @@ -85,6 +91,7 @@ void DivMacroInt::init(DivInstrument* which) { if (macroList[i]!=NULL) macroList[i]->init(); } macroListLen=0; + subTick=1; released=false; diff --git a/src/engine/macroInt.h b/src/engine/macroInt.h index 886ecbe2..f1cd29a7 100644 --- a/src/engine/macroInt.h +++ b/src/engine/macroInt.h @@ -27,17 +27,17 @@ class DivEngine; struct DivMacroStruct { int pos; int val; - bool has, had, finished, will; + bool has, had, actualHad, finished, will; unsigned int mode; - void doMacro(DivInstrumentMacro& source, bool released); + void doMacro(DivInstrumentMacro& source, bool released, bool tick); void init() { pos=mode=0; - has=had=will=false; + has=had=actualHad=will=false; // TODO: test whether this breaks anything? val=0; } void prepare(DivInstrumentMacro& source) { - has=had=will=true; + has=had=actualHad=will=true; mode=source.mode; } DivMacroStruct(): @@ -45,6 +45,7 @@ struct DivMacroStruct { val(0), has(false), had(false), + actualHad(false), finished(false), will(false), mode(0) {} @@ -128,7 +129,7 @@ class DivMacroInt { e(NULL), ins(NULL), macroListLen(0), - subTick(0), + subTick(1), released(false), vol(), arp(), diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index b860f680..343838e7 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -22,7 +22,7 @@ void DivDispatch::acquire(short* bufL, short* bufR, size_t start, size_t len) { } -void DivDispatch::tick() { +void DivDispatch::tick(bool sysTick) { } void* DivDispatch::getChanState(int chan) { diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 678f9afa..55d99af0 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -108,7 +108,9 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le } else { DivSample* s=parent->getSample(chan[i].sample); if (s->samples>0) { - writeAudDat(s->data8[chan[i].audPos++]); + if (chan[i].audPossamples) { + writeAudDat(s->data8[chan[i].audPos++]); + } if (((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && chan[i].audPos>=s->loopEnd) || (chan[i].audPos>=s->samples) || (chan[i].audPos>=131071)) { if (s->isLoopable()) { chan[i].audPos=s->loopStart; @@ -155,7 +157,7 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le } } -void DivPlatformAmiga::tick() { +void DivPlatformAmiga::tick(bool sysTick) { for (int i=0; i<4; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { @@ -204,7 +206,7 @@ void DivPlatformAmiga::tick() { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - //DivInstrument* ins=parent->getIns(chan[i].ins); + //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].freq<0) chan[i].freq=0; @@ -222,7 +224,7 @@ void DivPlatformAmiga::tick() { int DivPlatformAmiga::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); double off=1.0; if (ins->amiga.useWave) { chan[c.chan].useWave=true; @@ -312,7 +314,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { chan[c.chan].ws.changeWave1(chan[c.chan].wave); break; case DIV_CMD_NOTE_PORTA: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); chan[c.chan].sample=ins->amiga.initSample; double off=1.0; if (!chan[c.chan].useWave && chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { @@ -362,7 +364,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { } case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/amiga.h b/src/engine/platform/amiga.h index 8fbe8fec..07c44ee7 100644 --- a/src/engine/platform/amiga.h +++ b/src/engine/platform/amiga.h @@ -88,7 +88,7 @@ class DivPlatformAmiga: public DivDispatch { void* getChanState(int chan); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index a7ab53ec..6be50b6e 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -219,7 +219,7 @@ inline int hScale(int note) { return ((note/12)<<4)+(noteMap[note%12]); } -void DivPlatformArcade::tick() { +void DivPlatformArcade::tick(bool sysTick) { for (int i=0; i<8; i++) { chan[i].std.next(); @@ -264,6 +264,20 @@ void DivPlatformArcade::tick() { rWrite(0x1b,chan[i].std.wave.val&3); } + if (chan[i].std.panL.had) { + chan[i].chVolL=(chan[i].std.panL.val&2)>>1; + chan[i].chVolR=chan[i].std.panL.val&1; + if (isMuted[i]) { + rWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); + } else { + rWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|((chan[i].chVolL&1)<<6)|((chan[i].chVolR&1)<<7)); + } + } + + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].keyOn=true; @@ -432,7 +446,7 @@ void DivPlatformArcade::muteChannel(int ch, bool mute) { int DivPlatformArcade::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); if (chan[c.chan].insChanged) { chan[c.chan].state=ins->fm; diff --git a/src/engine/platform/arcade.h b/src/engine/platform/arcade.h index a6ec82c9..41af49e3 100644 --- a/src/engine/platform/arcade.h +++ b/src/engine/platform/arcade.h @@ -107,7 +107,7 @@ class DivPlatformArcade: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyInsChange(int ins); void setFlags(unsigned int flags); diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index e8a61b9a..4ba38483 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -172,7 +172,7 @@ void DivPlatformAY8910::updateOutSel(bool immediate) { } } -void DivPlatformAY8910::tick() { +void DivPlatformAY8910::tick(bool sysTick) { // PSG for (int i=0; i<3; i++) { chan[i].std.next(); @@ -215,6 +215,9 @@ void DivPlatformAY8910::tick() { rWrite(0x08+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2)); } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { oldWrites[0x08+i]=-1; @@ -291,7 +294,7 @@ void DivPlatformAY8910::tick() { int DivPlatformAY8910::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AY); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; @@ -454,7 +457,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AY)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index b1a3ea12..a78f21bc 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -90,7 +90,7 @@ class DivPlatformAY8910: public DivDispatch { void flushWrites(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void setFlags(unsigned int flags); bool isStereo(); diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 9f684a04..640b495b 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -187,7 +187,7 @@ const unsigned char regMode[3]={ 0x0d, 0x14, 0x15 }; -void DivPlatformAY8930::tick() { +void DivPlatformAY8930::tick(bool sysTick) { // PSG for (int i=0; i<3; i++) { chan[i].std.next(); @@ -226,6 +226,9 @@ void DivPlatformAY8930::tick() { rWrite(0x08+i,(chan[i].outVol&31)|((chan[i].psgMode&4)<<3)); } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { oldWrites[0x08+i]=-1; @@ -315,7 +318,7 @@ void DivPlatformAY8930::tick() { int DivPlatformAY8930::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AY8930); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; @@ -479,7 +482,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AY8930)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index 6bd51bdc..adbe0be8 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -78,7 +78,7 @@ class DivPlatformAY8930: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void setFlags(unsigned int flags); bool isStereo(); diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index 4a80bf41..6783bc33 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -81,7 +81,7 @@ void DivPlatformBubSysWSG::updateWave(int ch) { } } -void DivPlatformBubSysWSG::tick() { +void DivPlatformBubSysWSG::tick(bool sysTick) { for (int i=0; i<2; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { @@ -110,13 +110,16 @@ void DivPlatformBubSysWSG::tick() { if (!chan[i].keyOff) chan[i].keyOn=true; } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].active) { if (chan[i].ws.tick()) { updateWave(i); } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - //DivInstrument* ins=parent->getIns(chan[i].ins); + //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SCC); chan[i].freq=0x1000-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>4095) chan[i].freq=4095; @@ -139,7 +142,7 @@ void DivPlatformBubSysWSG::tick() { int DivPlatformBubSysWSG::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SCC); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; @@ -225,7 +228,7 @@ int DivPlatformBubSysWSG::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_SCC)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/bubsyswsg.h b/src/engine/platform/bubsyswsg.h index 6c0e2261..2efcc4d9 100644 --- a/src/engine/platform/bubsyswsg.h +++ b/src/engine/platform/bubsyswsg.h @@ -67,7 +67,7 @@ class DivPlatformBubSysWSG: public DivDispatch { int getRegisterPoolDepth(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index db5ab46a..604dcfe3 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -122,11 +122,11 @@ void DivPlatformC64::updateFilter() { rWrite(0x18,(filtControl<<4)|vol); } -void DivPlatformC64::tick() { +void DivPlatformC64::tick(bool sysTick) { for (int i=0; i<3; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { - DivInstrument* ins=parent->getIns(chan[i].ins); + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64); if (ins->c64.volIsCutoff) { if (ins->c64.filterIsAbs) { filtCut=MIN(2047,chan[i].std.vol.val); @@ -157,7 +157,7 @@ void DivPlatformC64::tick() { } } if (chan[i].std.duty.had) { - DivInstrument* ins=parent->getIns(chan[i].ins); + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64); if (ins->c64.dutyIsAbs) { chan[i].duty=chan[i].std.duty.val; } else { @@ -166,12 +166,14 @@ void DivPlatformC64::tick() { rWrite(i*7+2,chan[i].duty&0xff); rWrite(i*7+3,chan[i].duty>>8); } - if (chan[i].testWhen>0) { - if (--chan[i].testWhen<1) { - if (!chan[i].resetMask) { - rWrite(i*7+5,0); - rWrite(i*7+6,0); - rWrite(i*7+4,(chan[i].wave<<4)|8|(chan[i].ring<<2)|(chan[i].sync<<1)); + if (sysTick) { + if (chan[i].testWhen>0) { + if (--chan[i].testWhen<1) { + if (!chan[i].resetMask && !chan[i].inPorta) { + rWrite(i*7+5,0); + rWrite(i*7+6,0); + rWrite(i*7+4,(chan[i].wave<<4)|8|(chan[i].ring<<2)|(chan[i].sync<<1)); + } } } } @@ -179,6 +181,9 @@ void DivPlatformC64::tick() { chan[i].wave=chan[i].std.wave.val; rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active)); } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.ex1.had) { filtControl=chan[i].std.ex1.val&15; updateFilter(); @@ -218,7 +223,7 @@ void DivPlatformC64::tick() { int DivPlatformC64::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_C64); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].freqChanged=true; @@ -339,8 +344,8 @@ int DivPlatformC64::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) { - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta || !chan[c.chan].inPorta) { + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_C64)); chan[c.chan].keyOn=true; } } @@ -378,7 +383,7 @@ int DivPlatformC64::dispatch(DivCommand c) { break; case DIV_CMD_C64_FILTER_RESET: if (c.value&15) { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_C64); if (ins->c64.initFilter) { filtCut=ins->c64.cut; updateFilter(); @@ -388,7 +393,7 @@ int DivPlatformC64::dispatch(DivCommand c) { break; case DIV_CMD_C64_DUTY_RESET: if (c.value&15) { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_C64); chan[c.chan].duty=ins->c64.duty; rWrite(c.chan*7+2,chan[c.chan].duty&0xff); rWrite(c.chan*7+3,chan[c.chan].duty>>8); diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index e288940e..f77e24b9 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -83,7 +83,7 @@ class DivPlatformC64: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void setFlags(unsigned int flags); void notifyInsChange(int ins); diff --git a/src/engine/platform/dummy.cpp b/src/engine/platform/dummy.cpp index 308b8372..e751e3b6 100644 --- a/src/engine/platform/dummy.cpp +++ b/src/engine/platform/dummy.cpp @@ -38,10 +38,12 @@ void DivPlatformDummy::muteChannel(int ch, bool mute) { isMuted[ch]=mute; } -void DivPlatformDummy::tick() { +void DivPlatformDummy::tick(bool sysTick) { for (unsigned char i=0; i #include -#define CHIP_FREQBASE (16*2048) +#define CHIP_FREQBASE (16*2048*(chanMax+1)) #define NOTE_ES5506(c,note) (chan[c].pcm.freqOffs*NOTE_FREQUENCY(note)) #define rWrite(a,...) {if(!skipRegisterWrites) {hostIntf32.emplace(4,(a),__VA_ARGS__); }} @@ -106,14 +106,40 @@ const char** DivPlatformES5506::getRegisterSheet() { const char* DivPlatformES5506::getEffectName(unsigned char effect) { switch (effect) { case 0x10: - return "10xx: Set echo feedback level (00 to FF)"; + return "10xx: Change waveform"; break; case 0x11: - return "11xx: Set channel echo level (00 to FF)"; + return "11xx: Set filter mode (00 to 03)"; + break; + case 0x20: + return "20xx: Set envelope count (000 to 0FF)"; + break; + case 0x21: + return "21xx: Set envelope count (100 to 1FF)"; + break; + case 0x22: + return "22xx: Set envelope left volume ramp (signed)"; + break; + case 0x23: + return "23xx: Set envelope right volume ramp (signed)"; + break; + case 0x24: + return "24xx: Set envelope k1 ramp (signed)"; + break; + case 0x25: + return "25xx: Set envelope k1 ramp (signed, slower)"; + break; + case 0x26: + return "26xx: Set envelope k2 ramp (signed)"; + break; + case 0x27: + return "27xx: Set envelope k2 ramp (signed, slower)"; break; default: - if ((effect & 0xf0) == 0x30) { - return "3xxx: Set echo delay buffer length (000 to AA5)"; + if ((effect&0xf0)==0x30) { + return "3xxx: Set filter K1"; + } else if ((effect&0xf0)==0x40) { + return "4xxx: Set filter K2"; } } return NULL; @@ -190,9 +216,9 @@ void DivPlatformES5506::e(bool state) irqTrigger=false; if ((irqv&0x80)==0) { unsigned char ch=irqv&0x1f; - if (chan[ch].isReversed) { // Reversed loop - pageWriteMask(0x00|ch,0x5f,0x00,0x48,0x78); - chan[ch].isReversed=false; + if (chan[ch].isReverseLoop) { // Reversed loop + pageWriteMask(0x00|ch,0x5f,0x00,(chan[ch].pcm.reversed?0x0040:0x0000)|0x08,0x78); + chan[ch].isReverseLoop=false; } } } @@ -204,7 +230,7 @@ void DivPlatformES5506::irqb(bool state) { irqTrigger=true; } -void DivPlatformES5506::tick() { +void DivPlatformES5506::tick(bool sysTick) { for (int i=0; i<=chanMax; i++) { chan[i].std.next(); DivInstrument* ins=parent->getIns(chan[i].ins); @@ -252,6 +278,104 @@ void DivPlatformES5506::tick() { chan[i].keyOn=true; } } + // filter macros + if (chan[i].std.duty.had) { + if (chan[i].filter.mode!=DivInstrumentES5506::Filter::FilterMode(chan[i].std.duty.val&3)) { + chan[i].filter.mode=DivInstrumentES5506::Filter::FilterMode(chan[i].std.duty.val&3); + chan[i].filterChanged.mode=1; + } + } + if (chan[i].std.ex1.had) { + switch (chan[i].std.ex1.mode) { + case 0: // relative + if (chan[i].k1Offs!=chan[i].std.ex1.val) { + chan[i].k1Offs=chan[i].std.ex1.val; + chan[i].filterChanged.k1=1; + } + case 1: // absolute + if (chan[i].filter.k1!=(chan[i].std.ex1.val&0xffff)) { + chan[i].filter.k1=chan[i].std.ex1.val&0xffff; + chan[i].filterChanged.k1=1; + } + break; + case 2: { // delta + signed int next_k1=MAX(0,MIN(65535,chan[i].filter.k1+chan[i].std.ex1.val)); + if (chan[i].filter.k1!=next_k1) { + chan[i].filter.k1=next_k1; + chan[i].filterChanged.k1=1; + } + break; + } + default: + break; + } + } + if (chan[i].std.ex2.had) { + switch (chan[i].std.ex2.mode) { + case 0: // relative + if (chan[i].k2Offs!=chan[i].std.ex1.val) { + chan[i].k2Offs=chan[i].std.ex1.val; + chan[i].filterChanged.k2=1; + } + case 1: // absolute + if (chan[i].filter.k2!=(chan[i].std.ex2.val&0xffff)) { + chan[i].filter.k2=chan[i].std.ex2.val&0xffff; + chan[i].filterChanged.k2=1; + } + break; + case 2: { // delta + signed int next_k2=MAX(0,MIN(65535,chan[i].filter.k2+chan[i].std.ex2.val)); + if (chan[i].filter.k2!=next_k2) { + chan[i].filter.k2=next_k2; + chan[i].filterChanged.k2=1; + } + break; + } + default: + break; + } + } + // envelope macros + if (chan[i].std.ex3.had) { + if (chan[i].envelope.ecount!=(chan[i].std.ex3.val&0x1ff)) { + chan[i].envelope.ecount=chan[i].std.ex3.val&0x1ff; + chan[i].envChanged.ecount=1; + } + } + if (chan[i].std.ex4.had) { + if (chan[i].envelope.lVRamp!=chan[i].std.ex4.val) { + chan[i].envelope.lVRamp=chan[i].std.ex4.val; + chan[i].envChanged.lVRamp=1; + } + } + if (chan[i].std.ex5.had) { + if (chan[i].envelope.rVRamp!=chan[i].std.ex5.val) { + chan[i].envelope.rVRamp=chan[i].std.ex5.val; + chan[i].envChanged.rVRamp=1; + } + } + if (chan[i].std.ex6.had) { + if (chan[i].envelope.k1Ramp!=chan[i].std.ex6.val) { + chan[i].envelope.k1Ramp=chan[i].std.ex6.val; + chan[i].envChanged.k1Ramp=1; + } + } + if (chan[i].std.ex7.had) { + if (chan[i].envelope.k2Ramp!=chan[i].std.ex7.val) { + chan[i].envelope.k2Ramp=chan[i].std.ex7.val; + chan[i].envChanged.k2Ramp=1; + } + } + if (chan[i].std.ex8.had) { + if (chan[i].envelope.k1Slow!=(chan[i].std.ex8.val&1)) { + chan[i].envelope.k1Slow=chan[i].std.ex8.val&1; + chan[i].envChanged.k1Ramp=1; + } + if (chan[i].envelope.k2Slow!=(chan[i].std.ex8.val&2)) { + chan[i].envelope.k2Slow=chan[i].std.ex8.val&2; + chan[i].envChanged.k2Ramp=1; + } + } // update registers if (chan[i].volChanged) { if (!isMuted[i]) { // calculate volume (16 bit) @@ -267,72 +391,96 @@ void DivPlatformES5506::tick() { } chan[i].volChanged=false; } - if (chan[i].filterChanged) { - pageWriteMask(0x00|i,0x5f,0x00,(chan[i].filter.mode<<8),0x0300); + if (chan[i].filterChanged.changed) { if (!chan[i].keyOn) { - pageWrite(0x00|i,0x07,chan[i].filter.k2); - pageWrite(0x00|i,0x09,chan[i].filter.k1); + if (chan[i].filterChanged.mode) { + pageWriteMask(0x00|i,0x5f,0x00,(chan[i].filter.mode<<8),0x0300); + } + if (chan[i].filterChanged.k2) { + if (chan[i].std.ex2.mode==0) { // Relative + pageWrite(0x00|i,0x07,MAX(0,MIN(65535,chan[i].filter.k2+chan[i].k2Offs))); + } else { + pageWrite(0x00|i,0x07,chan[i].filter.k2); + } + } + if (chan[i].filterChanged.k1) { + if (chan[i].std.ex1.mode==0) { // Relative + pageWrite(0x00|i,0x09,MAX(0,MIN(65535,chan[i].filter.k1+chan[i].k1Offs))); + } else { + pageWrite(0x00|i,0x09,chan[i].filter.k1); + } + } } - chan[i].filterChanged=false; + chan[i].filterChanged.changed=0; } - if (chan[i].envChanged) { + if (chan[i].envChanged.changed) { if (!chan[i].keyOn) { - pageWrite(0x00|i,0x06,chan[i].envelope.ecount); + if (chan[i].envChanged.lVRamp) { + pageWrite(0x00|i,0x03,((unsigned char)chan[i].envelope.lVRamp)<<8); + } + if (chan[i].envChanged.rVRamp) { + pageWrite(0x00|i,0x05,((unsigned char)chan[i].envelope.rVRamp)<<8); + } + if (chan[i].envChanged.ecount) { + pageWrite(0x00|i,0x06,chan[i].envelope.ecount); + } + if (chan[i].envChanged.k2Ramp) { + pageWrite(0x00|i,0x08,(((unsigned char)chan[i].envelope.k2Ramp)<<8)|(chan[i].envelope.k2Slow?1:0)); + } + if (chan[i].envChanged.k1Ramp) { + pageWrite(0x00|i,0x0a,(((unsigned char)chan[i].envelope.k1Ramp)<<8)|(chan[i].envelope.k1Slow?1:0)); + } } - chan[i].envChanged=false; - } - if (chan[i].rampChanged) { - if (!chan[i].keyOn) { - pageWrite(0x00|i,0x03,((unsigned char)chan[i].envelope.lVRamp)<<8); - pageWrite(0x00|i,0x05,((unsigned char)chan[i].envelope.rVRamp)<<8); - pageWrite(0x00|i,0x0a,(((unsigned char)chan[i].envelope.k1Ramp)<<8)|(chan[i].envelope.k1Slow?1:0)); - pageWrite(0x00|i,0x08,(((unsigned char)chan[i].envelope.k2Ramp)<<8)|(chan[i].envelope.k2Slow?1:0)); - } - chan[i].rampChanged=false; + chan[i].envChanged.changed=0; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq*(chanMax+1),chan[i].pitch,false)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>0x1ffff) chan[i].freq=0x1ffff; if (chan[i].keyOn) { if (chan[i].pcm.index>=0) { pageWriteMask(0x00|i,0x5f,0x00,0x0303); // Wipeout CR pageWrite(0x00|i,0x06,0); // Clear ECOUNT - pageWrite(0x20|i,0x03,chan[i].pcm.base); // Set ACCUM to start address - pageWrite(0x00|i,0x09,0xffff); // Set K1 and K2 to 0xffff - pageWrite(0x00|i,0x07,0xffff,~0,(chanMax+1)*4*2); // needs to 4 sample period delay + pageWrite(0x20|i,0x03,chan[i].pcm.reversed?chan[i].pcm.end:chan[i].pcm.start); // Set ACCUM to start address + pageWrite(0x00|i,0x07,0xffff); // Set K1 and K2 to 0xffff + pageWrite(0x00|i,0x09,0xffff); pageWrite(0x00|i,0x01,chan[i].freq); - if (chan[i].pcm.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) { - pageWrite(0x20|i,0x01,chan[i].pcm.loopStart); - } - pageWrite(0x20|i,0x02,chan[i].pcm.loopEnd); + pageWrite(0x20|i,0x01,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT)?chan[i].pcm.start:chan[i].pcm.loopStart); + pageWrite(0x20|i,0x02,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT)?chan[i].pcm.end:chan[i].pcm.loopEnd); // initialize envelope pageWrite(0x00|i,0x03,((unsigned char)chan[i].envelope.lVRamp)<<8); pageWrite(0x00|i,0x05,((unsigned char)chan[i].envelope.rVRamp)<<8); pageWrite(0x00|i,0x0a,(((unsigned char)chan[i].envelope.k1Ramp)<<8)|(chan[i].envelope.k1Slow?1:0)); - pageWrite(0x00|i,0x08,(((unsigned char)chan[i].envelope.k2Ramp)<<8)|(chan[i].envelope.k2Slow?1:0)); + pageWrite(0x00|i,0x08,(((unsigned char)chan[i].envelope.k2Ramp)<<8)|(chan[i].envelope.k2Slow?1:0),~0,(chanMax+1)*4*2); // needs to 4 sample period delay // initialize filter pageWriteMask(0x00|i,0x5f,0x00,(chan[i].pcm.bank<<14)|(chan[i].filter.mode<<8),0xc300); - pageWrite(0x00|i,0x09,chan[i].filter.k1); - pageWrite(0x00|i,0x07,chan[i].filter.k2); + if ((chan[i].std.ex2.mode==0) && (chan[i].std.ex2.had)) { + pageWrite(0x00|i,0x07,MAX(0,MIN(65535,chan[i].filter.k2+chan[i].k2Offs))); + } else { + pageWrite(0x00|i,0x07,chan[i].filter.k2); + } + if ((chan[i].std.ex1.mode==0) && (chan[i].std.ex1.had)) { + pageWrite(0x00|i,0x09,MAX(0,MIN(65535,chan[i].filter.k1+chan[i].k1Offs))); + } else { + pageWrite(0x00|i,0x09,chan[i].filter.k1); + } pageWrite(0x00|i,0x02,chan[i].resLVol); pageWrite(0x00|i,0x04,chan[i].resRVol); - unsigned int loopFlag=0x0000; - chan[i].isReversed=false; + unsigned int loopFlag=chan[i].pcm.reversed?0x0040:0x0000; + chan[i].isReverseLoop=false; switch (chan[i].pcm.loopMode) { case DIV_SAMPLE_LOOPMODE_ONESHOT: // One shot (no loop) default: - loopFlag=0x0000; break; case DIV_SAMPLE_LOOPMODE_FOWARD: // Foward loop - loopFlag=0x0008; + loopFlag|=0x0008; break; case DIV_SAMPLE_LOOPMODE_BACKWARD: // Backward loop: IRQ enable - loopFlag=0x0038; - chan[i].isReversed=true; + loopFlag|=0x0038; + chan[i].isReverseLoop=true; break; case DIV_SAMPLE_LOOPMODE_PINGPONG: // Pingpong loop: Hardware support - loopFlag=0x0018; + loopFlag|=0x0018; break; } // Run sample @@ -341,7 +489,7 @@ void DivPlatformES5506::tick() { } } if (chan[i].keyOff) { - pageWriteMask(0x00|i,0x5f,0x00,0x0003); // Wipeout CR + pageWriteMask(0x00|i,0x5f,0x00,0x0303); // Wipeout CR } else if (chan[i].active) { pageWrite(0x00|i,0x01,chan[i].freq); } @@ -356,30 +504,38 @@ int DivPlatformES5506::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins); - chan[c.chan].sample=ins->amiga.useNoteMap?ins->amiga.noteMap[c.value].ind:ins->amiga.initSample; - double off=1.0; - if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { - chan[c.chan].pcm.index=chan[c.chan].sample; - DivSample* s=parent->getSample(chan[c.chan].sample); - if (s->centerRate<1) { - off=1.0; + if (chan[c.chan].insChanged) { + chan[c.chan].sample=ins->amiga.useNoteMap?ins->amiga.noteMap[c.value].ind:ins->amiga.initSample; + double off=1.0; + if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { + chan[c.chan].pcm.index=chan[c.chan].sample; + DivSample* s=parent->getSample(chan[c.chan].sample); + if (s->centerRate<1) { + off=1.0; + } else { + off=ins->amiga.useNoteMap?((double)ins->amiga.noteMap[c.value].freq/((double)s->centerRate*pow(2.0,((double)c.value-48.0)/12.0))):((double)s->centerRate/8363.0); + } + const unsigned int start=s->offES5506<<10; + const unsigned int length=s->samples-1; + const unsigned int end=start+(length<<11); + chan[c.chan].pcm.loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT; + chan[c.chan].pcm.freqOffs=off; + chan[c.chan].pcm.reversed=ins->amiga.reversed; + chan[c.chan].pcm.bank=(s->offES5506>>22)&3; + chan[c.chan].pcm.start=start; + chan[c.chan].pcm.end=end; + chan[c.chan].pcm.length=length; + chan[c.chan].pcm.loopStart=(start+(s->loopStart<<11))&0xfffff800; + chan[c.chan].pcm.loopEnd=(start+((s->loopEnd-1)<<11))&0xffffff80; + chan[c.chan].filter=ins->es5506.filter; + chan[c.chan].envelope=ins->es5506.envelope; } else { - off=ins->amiga.useNoteMap?((double)ins->amiga.noteMap[c.value].freq/((double)s->centerRate*pow(2.0,((double)c.value-48.0)/12.0))):((double)s->centerRate/8363.0); + chan[c.chan].sample=-1; + chan[c.chan].pcm.index=-1; + chan[c.chan].filter=DivInstrumentES5506::Filter(); + chan[c.chan].envelope=DivInstrumentES5506::Envelope(); } - unsigned int base=s->offES5506<<10; - chan[c.chan].pcm.loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT; - chan[c.chan].pcm.freqOffs=off; - chan[c.chan].pcm.bank=(s->offES5506>>22)&3; - chan[c.chan].pcm.base=base; - chan[c.chan].pcm.loopStart=(base+(s->loopStart<<11))&0xfffff800; - chan[c.chan].pcm.loopEnd=((base+(s->loopEnd<<11))-0x800)&0xffffff80; - chan[c.chan].filter=ins->es5506.filter; - chan[c.chan].envelope=ins->es5506.envelope; - } else { - chan[c.chan].sample=-1; - chan[c.chan].pcm.index=-1; - chan[c.chan].filter=DivInstrumentES5506::Filter(); - chan[c.chan].envelope=DivInstrumentES5506::Envelope(); + chan[c.chan].insChanged=false; } if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_ES5506(c.chan,c.value); @@ -466,6 +622,45 @@ int DivPlatformES5506::dispatch(DivCommand c) { chan[c.chan].pitch=c.value; chan[c.chan].freqChanged=true; break; + case DIV_CMD_WAVE: + // reserved for useWave + break; + // Filter commands + case DIV_CMD_ES5506_FILTER_MODE: + chan[c.chan].filter.mode=DivInstrumentES5506::Filter::FilterMode(c.value&3); + chan[c.chan].filterChanged.mode=1; + break; + case DIV_CMD_ES5506_FILTER_K1: + chan[c.chan].filter.k1=(chan[c.chan].filter.k1&0xf)|((c.value&0xfff)<<4); + chan[c.chan].filterChanged.k1=1; + break; + case DIV_CMD_ES5506_FILTER_K2: + chan[c.chan].filter.k2=(chan[c.chan].filter.k2&0xf)|((c.value&0xfff)<<4); + chan[c.chan].filterChanged.k2=1; + break; + // Envelope commands + case DIV_CMD_ES5506_ENVELOPE_COUNT: + chan[c.chan].envelope.ecount=c.value&0x1ff; + chan[c.chan].envChanged.ecount=1; + break; + case DIV_CMD_ES5506_ENVELOPE_LVRAMP: + chan[c.chan].envelope.lVRamp=(signed char)(c.value&0xff); + chan[c.chan].envChanged.lVRamp=1; + break; + case DIV_CMD_ES5506_ENVELOPE_RVRAMP: + chan[c.chan].envelope.rVRamp=(signed char)(c.value&0xff); + chan[c.chan].envChanged.rVRamp=1; + break; + case DIV_CMD_ES5506_ENVELOPE_K1RAMP: + chan[c.chan].envelope.k1Ramp=(signed char)(c.value&0xff); + chan[c.chan].envelope.k1Slow=c.value2&1; + chan[c.chan].envChanged.k1Ramp=1; + break; + case DIV_CMD_ES5506_ENVELOPE_K2RAMP: + chan[c.chan].envelope.k2Ramp=(signed char)(c.value&0xff); + chan[c.chan].envelope.k2Slow=c.value2&1; + chan[c.chan].envChanged.k2Ramp=1; + break; case DIV_CMD_NOTE_PORTA: { int destFreq=NOTE_ES5506(c.chan,c.value2); bool return2=false; @@ -501,6 +696,16 @@ int DivPlatformES5506::dispatch(DivCommand c) { } chan[c.chan].inPorta=c.value; break; + case DIV_CMD_SAMPLE_POS: { + if (chan[c.chan].useWave) break; + if (chan[c.chan].active) { + unsigned int pos=chan[c.chan].pcm.reversed?(chan[c.chan].pcm.length-c.value):c.value; + if ((chan[c.chan].pcm.reversed && pos>0) || ((!chan[c.chan].pcm.reversed) && pos255) chan[i].baseFreq=255; - if (chan[i].baseFreq<0) chan[i].baseFreq=0; - } else { - if (!chan[i].inPorta) { - if (chan[i].std.arp.mode) { - chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val); - } else { - chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val); - } + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val); } } chan[i].freqChanged=true; @@ -155,6 +145,9 @@ void DivPlatformFDS::tick() { //if (!chan[i].keyOff) chan[i].keyOn=true; } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].active) { if (ws.tick()) { updateWave(); @@ -205,7 +198,7 @@ void DivPlatformFDS::tick() { int DivPlatformFDS::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FDS); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].freqChanged=true; @@ -365,7 +358,7 @@ int DivPlatformFDS::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_FDS)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/fds.h b/src/engine/platform/fds.h index d8d588a2..6d206f3e 100644 --- a/src/engine/platform/fds.h +++ b/src/engine/platform/fds.h @@ -77,7 +77,7 @@ class DivPlatformFDS: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void setFlags(unsigned int flags); diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 8791e6d4..b9668d58 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -146,7 +146,7 @@ static unsigned char noiseTable[256]={ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -void DivPlatformGB::tick() { +void DivPlatformGB::tick(bool sysTick) { for (int i=0; i<4; i++) { chan[i].std.next(); if (chan[i].std.arp.had) { @@ -176,7 +176,7 @@ void DivPlatformGB::tick() { } if (chan[i].std.duty.had) { chan[i].duty=chan[i].std.duty.val; - DivInstrument* ins=parent->getIns(chan[i].ins); + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_GB); if (i!=2) { rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63))); } else { @@ -192,6 +192,14 @@ void DivPlatformGB::tick() { if (!chan[i].keyOff) chan[i].keyOn=true; } } + if (chan[i].std.panL.had) { + lastPan&=~(0x11<getIns(chan[i].ins); + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_GB); if (i==3) { // noise int ntPos=chan[i].baseFreq; if (ntPos<0) ntPos=0; @@ -261,7 +269,7 @@ void DivPlatformGB::muteChannel(int ch, bool mute) { int DivPlatformGB::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_GB); if (c.value!=DIV_NOTE_NULL) { if (c.chan==3) { // noise chan[c.chan].baseFreq=c.value; @@ -298,7 +306,7 @@ int DivPlatformGB::dispatch(DivCommand c) { chan[c.chan].ins=c.value; chan[c.chan].insChanged=true; if (c.chan!=2) { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_GB); chan[c.chan].vol=ins->gb.envVol; if (parent->song.gbInsAffectsEnvelope) { rWrite(16+c.chan*5+2,((chan[c.chan].vol<<4))|(ins->gb.envLen&7)|((ins->gb.envDir&1)<<3)); @@ -352,7 +360,7 @@ int DivPlatformGB::dispatch(DivCommand c) { chan[c.chan].duty=c.value; if (c.chan!=2) { chan[c.chan].freqChanged=true; - rWrite(16+c.chan*5+1,((chan[c.chan].duty&3)<<6)|(63-(parent->getIns(chan[c.chan].ins)->gb.soundLen&63))); + rWrite(16+c.chan*5+1,((chan[c.chan].duty&3)<<6)|(63-(parent->getIns(chan[c.chan].ins,DIV_INS_GB)->gb.soundLen&63))); } break; case DIV_CMD_PANNING: { @@ -371,7 +379,7 @@ int DivPlatformGB::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_GB)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/gb.h b/src/engine/platform/gb.h index 5a345e45..df7070ec 100644 --- a/src/engine/platform/gb.h +++ b/src/engine/platform/gb.h @@ -70,7 +70,7 @@ class DivPlatformGB: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); void notifyInsChange(int ins); diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 97a6384d..b02730da 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -222,7 +222,7 @@ void DivPlatformGenesis::acquire(short* bufL, short* bufR, size_t start, size_t } } -void DivPlatformGenesis::tick() { +void DivPlatformGenesis::tick(bool sysTick) { for (int i=0; i<6; i++) { if (i==2 && extMode) continue; chan[i].std.next(); @@ -260,6 +260,15 @@ void DivPlatformGenesis::tick() { } } + if (chan[i].std.panL.had) { + chan[i].pan=chan[i].std.panL.val&3; + rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + } + + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].keyOn=true; @@ -477,7 +486,7 @@ void DivPlatformGenesis::muteChannel(int ch, bool mute) { int DivPlatformGenesis::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); if (c.chan==5) { if (ins->type==DIV_INS_AMIGA) { dacMode=1; diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index 51d98d80..34db5a25 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -109,7 +109,7 @@ class DivPlatformGenesis: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); void setYMFM(bool use); diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index e8ddbdc7..f0369370 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -37,7 +37,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { int ordch=orderedOps[ch]; switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (opChan[ch].insChanged) { chan[2].state.alg=ins->fm.alg; @@ -266,7 +266,7 @@ static int opChanOffsH[4]={ 0xad, 0xae, 0xac, 0xa6 }; -void DivPlatformGenesisExt::tick() { +void DivPlatformGenesisExt::tick(bool sysTick) { if (extMode) { bool writeSomething=false; unsigned char writeMask=2; @@ -283,7 +283,7 @@ void DivPlatformGenesisExt::tick() { } } - DivPlatformGenesis::tick(); + DivPlatformGenesis::tick(sysTick); bool writeNoteOn=false; unsigned char writeMask=2; diff --git a/src/engine/platform/genesisext.h b/src/engine/platform/genesisext.h index e3320e19..fc9eed89 100644 --- a/src/engine/platform/genesisext.h +++ b/src/engine/platform/genesisext.h @@ -41,7 +41,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis { void* getChanState(int chan); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); bool keyOffAffectsPorta(int ch); diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 6120233a..4c688103 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -145,7 +145,7 @@ void DivPlatformLynx::acquire(short* bufL, short* bufR, size_t start, size_t len mikey->sampleAudio( bufL + start, bufR + start, len ); } -void DivPlatformLynx::tick() { +void DivPlatformLynx::tick(bool sysTick) { for (int i=0; i<4; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { @@ -171,6 +171,24 @@ void DivPlatformLynx::tick() { } } + if (chan[i].std.panL.had) { + chan[i].pan&=0x0f; + chan[i].pan|=(chan[i].std.panL.val&15)<<4; + } + + if (chan[i].std.panR.had) { + chan[i].pan&=0xf0; + chan[i].pan|=chan[i].std.panR.val&15; + } + + if (chan[i].std.panL.had || chan[i].std.panR.had) { + WRITE_ATTEN(i,chan[i].pan); + } + + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].freqChanged) { if (chan[i].lfsr >= 0) { WRITE_LFSR(i, (chan[i].lfsr&0xff)); @@ -184,8 +202,8 @@ void DivPlatformLynx::tick() { } WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7)); WRITE_BACKUP( i, chan[i].fd.backup ); - } - else if (chan[i].std.duty.had) { + chan[i].freqChanged=false; + } else if (chan[i].std.duty.had) { chan[i].duty = chan[i].std.duty.val; WRITE_FEEDBACK(i, chan[i].duty.feedback); WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7)); @@ -206,7 +224,7 @@ int DivPlatformLynx::dispatch(DivCommand c) { } chan[c.chan].active=true; WRITE_VOLUME(c.chan,(isMuted[c.chan]?0:(chan[c.chan].vol&127))); - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY)); break; case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; @@ -223,7 +241,7 @@ int DivPlatformLynx::dispatch(DivCommand c) { break; case DIV_CMD_INSTRUMENT: chan[c.chan].ins=c.value; - //chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + //chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY)); break; case DIV_CMD_VOLUME: if (chan[c.chan].vol!=c.value) { @@ -279,7 +297,7 @@ int DivPlatformLynx::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/lynx.h b/src/engine/platform/lynx.h index 536a874a..61f6e67d 100644 --- a/src/engine/platform/lynx.h +++ b/src/engine/platform/lynx.h @@ -80,7 +80,7 @@ class DivPlatformLynx: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 31f1e254..8a8c3e8a 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -96,7 +96,7 @@ void DivPlatformMMC5::acquire(short* bufL, short* bufR, size_t start, size_t len } } -void DivPlatformMMC5::tick() { +void DivPlatformMMC5::tick(bool sysTick) { for (int i=0; i<2; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { @@ -124,6 +124,9 @@ void DivPlatformMMC5::tick() { chan[i].duty=chan[i].std.duty.val; rWrite(0x5000+i*4,0x30|chan[i].outVol|((chan[i].duty&3)<<6)); } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].freqChanged=true; @@ -175,7 +178,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: if (c.chan==2) { // PCM - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD); if (ins->type==DIV_INS_AMIGA) { dacSample=ins->amiga.initSample; if (dacSample<0 || dacSample>=parent->song.sampleLen) { @@ -225,7 +228,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); rWrite(0x5000+c.chan*4,0x30|chan[c.chan].vol|((chan[c.chan].duty&3)<<6)); break; case DIV_CMD_NOTE_OFF: @@ -303,7 +306,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/mmc5.h b/src/engine/platform/mmc5.h index 6b364d5a..02ca06e8 100644 --- a/src/engine/platform/mmc5.h +++ b/src/engine/platform/mmc5.h @@ -71,7 +71,7 @@ class DivPlatformMMC5: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); float getPostAmp(); diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index a66157d9..a00d4d05 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -214,7 +214,7 @@ void DivPlatformN163::updateWaveCh(int ch) { } } -void DivPlatformN163::tick() { +void DivPlatformN163::tick(bool sysTick) { for (int i=0; i<=chanMax; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { @@ -261,6 +261,9 @@ void DivPlatformN163::tick() { } } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.ex1.had) { if (chan[i].waveLen!=(chan[i].std.ex1.val&0xfc)) { chan[i].waveLen=chan[i].std.ex1.val&0xfc; @@ -371,7 +374,7 @@ void DivPlatformN163::tick() { int DivPlatformN163::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_N163); if (chan[c.chan].insChanged) { chan[c.chan].wave=ins->n163.wave; chan[c.chan].ws.changeWave1(chan[c.chan].wave); @@ -543,7 +546,7 @@ int DivPlatformN163::dispatch(DivCommand c) { case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) { - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_N163)); chan[c.chan].keyOn=true; } } diff --git a/src/engine/platform/n163.h b/src/engine/platform/n163.h index 2f68d6c9..38ebeace 100644 --- a/src/engine/platform/n163.h +++ b/src/engine/platform/n163.h @@ -93,7 +93,7 @@ class DivPlatformN163: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void setFlags(unsigned int flags); void notifyWaveChange(int wave); diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 3208df61..475c35be 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -141,7 +141,7 @@ static unsigned char noiseTable[253]={ 15 }; -void DivPlatformNES::tick() { +void DivPlatformNES::tick(bool sysTick) { for (int i=0; i<4; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { @@ -196,6 +196,9 @@ void DivPlatformNES::tick() { chan[i].freqChanged=true; } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].sweepChanged) { chan[i].sweepChanged=false; if (i==0) { @@ -269,7 +272,7 @@ int DivPlatformNES::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: if (c.chan==4) { // PCM - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD); if (ins->type==DIV_INS_AMIGA) { dacSample=ins->amiga.initSample; if (dacSample<0 || dacSample>=parent->song.sampleLen) { @@ -323,7 +326,7 @@ int DivPlatformNES::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); if (c.chan==2) { rWrite(0x4000+c.chan*4,0xff); } else { @@ -426,7 +429,7 @@ int DivPlatformNES::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index 1dbb4411..2cf2a8a1 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -72,7 +72,7 @@ class DivPlatformNES: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); float getPostAmp(); diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index add3815c..ef9adecf 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -228,7 +228,7 @@ void DivPlatformOPL::acquire(short* bufL, short* bufR, size_t start, size_t len) //} } -void DivPlatformOPL::tick() { +void DivPlatformOPL::tick(bool sysTick) { for (int i=0; i>1); + } + + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].keyOn=true; @@ -282,7 +290,7 @@ void DivPlatformOPL::tick() { chan[i].state.fb=chan[i].std.fb.val; } - if (chan[i].std.alg.had || chan[i].std.fb.had) { + if (chan[i].std.alg.had || chan[i].std.fb.had || (oplType==3 && chan[i].std.panL.had)) { if (isMuted[i]) { rWrite(chanMap[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&1)|(chan[i].state.fb<<1)); if (ops==4) { @@ -527,7 +535,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { } switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPL); if (chan[c.chan].insChanged) { chan[c.chan].state=ins->fm; diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index 44d80fda..76979a54 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -104,7 +104,7 @@ class DivPlatformOPL: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); void setYMFM(bool use); diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index e6579e84..a02bc8de 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -111,7 +111,7 @@ void DivPlatformOPLL::acquire(short* bufL, short* bufR, size_t start, size_t len acquire_nuked(bufL,bufR,start,len); } -void DivPlatformOPLL::tick() { +void DivPlatformOPLL::tick(bool sysTick) { for (int i=0; i<11; i++) { chan[i].std.next(); @@ -145,6 +145,10 @@ void DivPlatformOPLL::tick() { } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].keyOn=true; @@ -361,7 +365,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { if (c.chan>=9 && !properDrums) return 0; - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPLL); if (chan[c.chan].insChanged) { chan[c.chan].state=ins->fm; } diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 4a7a4c97..04f4f13a 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -100,7 +100,7 @@ class DivPlatformOPLL: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void setYMFM(bool use); bool keyOffAffectsArp(int ch); diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 3acdd495..7762b391 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -146,7 +146,7 @@ static unsigned char noiseFreq[12]={ 4,13,15,18,21,23,25,27,29,31,0,2 }; -void DivPlatformPCE::tick() { +void DivPlatformPCE::tick(bool sysTick) { for (int i=0; i<6; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { @@ -196,13 +196,27 @@ void DivPlatformPCE::tick() { if (!chan[i].keyOff) chan[i].keyOn=true; } } + if (chan[i].std.panL.had) { + chan[i].pan&=0x0f; + chan[i].pan|=(chan[i].std.panL.val&15)<<4; + } + if (chan[i].std.panR.had) { + chan[i].pan&=0xf0; + chan[i].pan|=chan[i].std.panR.val&15; + } + if (chan[i].std.panL.had || chan[i].std.panR.had) { + chWrite(i,0x05,isMuted[i]?0:chan[i].pan); + } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].active) { if (chan[i].ws.tick() || (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1)) { updateWave(i); } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - //DivInstrument* ins=parent->getIns(chan[i].ins); + //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (chan[i].furnaceDac) { double off=1.0; @@ -237,7 +251,7 @@ void DivPlatformPCE::tick() { int DivPlatformPCE::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_PCE); if (ins->type==DIV_INS_AMIGA) { chan[c.chan].pcm=true; } else if (chan[c.chan].furnaceDac) { @@ -415,7 +429,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_PCE)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index 2e8614ba..57a4b033 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -87,7 +87,7 @@ class DivPlatformPCE: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index ffee3c03..b34d16f2 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -164,7 +164,7 @@ void DivPlatformPCSpeaker::acquire(short* bufL, short* bufR, size_t start, size_ } } -void DivPlatformPCSpeaker::tick() { +void DivPlatformPCSpeaker::tick(bool sysTick) { for (int i=0; i<1; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { @@ -186,6 +186,9 @@ void DivPlatformPCSpeaker::tick() { chan[i].freqChanged=true; } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; if (chan[i].freq<0) chan[i].freq=0; @@ -214,7 +217,7 @@ int DivPlatformPCSpeaker::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER)); break; case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; @@ -279,7 +282,7 @@ int DivPlatformPCSpeaker::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/pcspkr.h b/src/engine/platform/pcspkr.h index 17dedf24..68e6bf1a 100644 --- a/src/engine/platform/pcspkr.h +++ b/src/engine/platform/pcspkr.h @@ -77,7 +77,7 @@ class DivPlatformPCSpeaker: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void setFlags(unsigned int flags); diff --git a/src/engine/platform/pet.cpp b/src/engine/platform/pet.cpp index 104e2843..0e83e9d2 100644 --- a/src/engine/platform/pet.cpp +++ b/src/engine/platform/pet.cpp @@ -85,7 +85,7 @@ void DivPlatformPET::writeOutVol() { } } -void DivPlatformPET::tick() { +void DivPlatformPET::tick(bool sysTick) { chan.std.next(); if (chan.std.vol.had) { chan.outVol=chan.std.vol.val&chan.vol; @@ -112,6 +112,9 @@ void DivPlatformPET::tick() { rWrite(10,chan.wave); } } + if (chan.std.pitch.had) { + chan.freqChanged=true; + } if (chan.freqChanged || chan.keyOn || chan.keyOff) { chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true)+chan.std.pitch.val; if (chan.freq>257) chan.freq=257; @@ -135,7 +138,7 @@ void DivPlatformPET::tick() { int DivPlatformPET::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan.ins); + DivInstrument* ins=parent->getIns(chan.ins,DIV_INS_PET); if (c.value!=DIV_NOTE_NULL) { chan.baseFreq=NOTE_PERIODIC(c.value); chan.freqChanged=true; @@ -210,7 +213,7 @@ int DivPlatformPET::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan.active && c.value2) { - if (parent->song.resetMacroOnPorta) chan.std.init(parent->getIns(chan.ins)); + if (parent->song.resetMacroOnPorta) chan.std.init(parent->getIns(chan.ins,DIV_INS_PET)); } chan.inPorta=c.value; break; diff --git a/src/engine/platform/pet.h b/src/engine/platform/pet.h index 3b6af48d..7647a87f 100644 --- a/src/engine/platform/pet.h +++ b/src/engine/platform/pet.h @@ -65,7 +65,7 @@ class DivPlatformPET: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyInsDeletion(void* ins); bool isStereo(); diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index bfdaad3e..f54e82d9 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -274,7 +274,7 @@ void DivPlatformQSound::acquire(short* bufL, short* bufR, size_t start, size_t l } } -void DivPlatformQSound::tick() { +void DivPlatformQSound::tick(bool sysTick) { for (int i=0; i<16; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { @@ -326,8 +326,11 @@ void DivPlatformQSound::tick() { chan[i].freqChanged=true; } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - //DivInstrument* ins=parent->getIns(chan[i].ins); + //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; if (chan[i].freq>0xffff) chan[i].freq=0xffff; if (chan[i].keyOn) { @@ -360,7 +363,7 @@ void DivPlatformQSound::tick() { int DivPlatformQSound::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); chan[c.chan].sample=ins->amiga.initSample; double off=1.0; if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { @@ -484,7 +487,7 @@ int DivPlatformQSound::dispatch(DivCommand c) { } case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index 16420055..62b1d647 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -74,7 +74,7 @@ class DivPlatformQSound: public DivDispatch { int getRegisterPoolDepth(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index 20f1005e..f21c7d5b 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -132,7 +132,7 @@ inline unsigned char applyPan(unsigned char vol, unsigned char pan) { return ((vol*(pan>>4))/15)|(((vol*(pan&15))/15)<<4); } -void DivPlatformSAA1099::tick() { +void DivPlatformSAA1099::tick(bool sysTick) { for (int i=0; i<6; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { @@ -166,6 +166,29 @@ void DivPlatformSAA1099::tick() { if (chan[i].std.wave.had) { chan[i].psgMode=chan[i].std.wave.val&3; } + if (chan[i].std.panL.had) { + chan[i].pan&=0x0f; + chan[i].pan|=(chan[i].std.panL.val&15)<<4; + } + + if (chan[i].std.panR.had) { + chan[i].pan&=0xf0; + chan[i].pan|=chan[i].std.panR.val&15; + } + if (chan[i].std.panL.had || chan[i].std.panR.had) { + if (isMuted[i]) { + rWrite(i,0); + } else { + if (chan[i].std.vol.had) { + if (chan[i].active) rWrite(i,applyPan(chan[i].outVol&15,chan[i].pan)); + } else { + if (chan[i].active) rWrite(i,applyPan(chan[i].vol&15,chan[i].pan)); + } + } + } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.ex1.had) { saaEnv[i/3]=chan[i].std.ex1.val; rWrite(0x18+(i/3),saaEnv[i/3]); @@ -226,7 +249,7 @@ void DivPlatformSAA1099::tick() { int DivPlatformSAA1099::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SAA1099); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; @@ -333,7 +356,7 @@ int DivPlatformSAA1099::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_SAA1099)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/saa.h b/src/engine/platform/saa.h index 8df44f61..0542ff51 100644 --- a/src/engine/platform/saa.h +++ b/src/engine/platform/saa.h @@ -90,7 +90,7 @@ class DivPlatformSAA1099: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void setCore(DivSAACores core); void setFlags(unsigned int flags); diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index d5bb86da..e4fe0243 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -76,13 +76,14 @@ void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t } } -void DivPlatformSegaPCM::tick() { +void DivPlatformSegaPCM::tick(bool sysTick) { for (int i=0; i<16; i++) { chan[i].std.next(); - if (chan[i].std.vol.had) { + // TODO: fix + /*if (chan[i].std.vol.had) { chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127; - } + }*/ if (chan[i].std.arp.had) { if (!chan[i].inPorta) { @@ -99,6 +100,24 @@ void DivPlatformSegaPCM::tick() { chan[i].freqChanged=true; } } + + if (chan[i].std.panL.had) { + chan[i].chVolL=chan[i].std.panL.val&127; + if (dumpWrites) { + addWrite(0x10002+(i<<3),chan[i].chVolL); + } + } + + if (chan[i].std.panR.had) { + chan[i].chVolR=chan[i].std.panR.val&127; + if (dumpWrites) { + addWrite(0x10003+(i<<3),chan[i].chVolR); + } + } + + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } /*if (chan[i].keyOn || chan[i].keyOff) { chan[i].keyOff=false; }*/ @@ -130,7 +149,7 @@ void DivPlatformSegaPCM::muteChannel(int ch, bool mute) { int DivPlatformSegaPCM::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); if (skipRegisterWrites) break; if (ins->type==DIV_INS_AMIGA) { chan[c.chan].pcm.sample=ins->amiga.initSample; @@ -139,14 +158,17 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { if (dumpWrites) { addWrite(0x10086+(c.chan<<3),3); } + chan[c.chan].std.init(NULL); break; } chan[c.chan].pcm.pos=0; if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].note=c.value; chan[c.chan].baseFreq=(c.value<<6); chan[c.chan].freqChanged=true; } chan[c.chan].furnacePCM=true; + chan[c.chan].std.init(ins); if (dumpWrites) { // Sega PCM writes DivSample* s=parent->getSample(chan[c.chan].pcm.sample); addWrite(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3)); @@ -163,6 +185,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { } } } else { + chan[c.chan].std.init(NULL); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].note=c.value; } @@ -204,6 +227,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { chan[c.chan].keyOff=true; chan[c.chan].keyOn=false; chan[c.chan].active=false; + chan[c.chan].std.init(NULL); break; case DIV_CMD_NOTE_OFF_ENV: chan[c.chan].keyOff=true; diff --git a/src/engine/platform/segapcm.h b/src/engine/platform/segapcm.h index b3c84cd3..4b05c160 100644 --- a/src/engine/platform/segapcm.h +++ b/src/engine/platform/segapcm.h @@ -78,7 +78,7 @@ class DivPlatformSegaPCM: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyInsChange(int ins); void setFlags(unsigned int flags); diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index a0f8abdd..af339f6e 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -23,8 +23,6 @@ #define rWrite(v) {if (!skipRegisterWrites) {sn->write(v); if (dumpWrites) {addWrite(0x200,v);}}} -#define CHIP_DIVIDER 64 - const char* regCheatSheetSN[]={ "DATA", "0", NULL @@ -53,8 +51,10 @@ int DivPlatformSMS::acquireOne() { return v; } -void DivPlatformSMS::tick() { +void DivPlatformSMS::tick(bool sysTick) { for (int i=0; i<4; i++) { + int CHIP_DIVIDER=64; + if (i==3 && isRealSN) CHIP_DIVIDER=60; chan[i].std.next(); if (chan[i].std.vol.had) { chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15)); @@ -98,6 +98,9 @@ void DivPlatformSMS::tick() { } } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } } for (int i=0; i<3; i++) { if (chan[i].freqChanged) { @@ -116,8 +119,7 @@ void DivPlatformSMS::tick() { } } if (chan[3].freqChanged || updateSNMode) { - // seems arbitrary huh? - chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch-1-(isRealSN?127:0),true)+chan[3].std.pitch.val; + chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true)+chan[3].std.pitch.val; if (chan[3].freq>1023) chan[3].freq=1023; if (chan[3].actualNote>0x5d) chan[3].freq=0x01; if (snNoiseMode&2) { // take period from channel 3 @@ -161,6 +163,8 @@ void DivPlatformSMS::tick() { } int DivPlatformSMS::dispatch(DivCommand c) { + int CHIP_DIVIDER=64; + if (c.chan==3 && isRealSN) CHIP_DIVIDER=60; switch (c.cmd) { case DIV_CMD_NOTE_ON: if (c.value!=DIV_NOTE_NULL) { @@ -171,7 +175,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { } chan[c.chan].active=true; rWrite(0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15)))); - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); break; case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; @@ -184,7 +188,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { break; case DIV_CMD_INSTRUMENT: chan[c.chan].ins=c.value; - //chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + //chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); break; case DIV_CMD_VOLUME: if (chan[c.chan].vol!=c.value) { @@ -240,7 +244,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index b49a5e27..0aeb9e81 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -63,7 +63,7 @@ class DivPlatformSMS: public DivDispatch { void* getChanState(int chan); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); bool keyOffAffectsPorta(int ch); diff --git a/src/engine/platform/sound/es550x/es550x.hpp b/src/engine/platform/sound/es550x/es550x.hpp index e3652c60..c92c0db2 100644 --- a/src/engine/platform/sound/es550x/es550x.hpp +++ b/src/engine/platform/sound/es550x/es550x.hpp @@ -21,6 +21,7 @@ namespace es550x typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; + typedef unsigned long long u64; typedef signed char s8; typedef signed short s16; typedef signed int s32; @@ -250,12 +251,14 @@ protected: : m_integer(integer) , m_fraction(fraction) , m_total_bits(integer + fraction) + , m_accum_mask(u32(std::min(~0, u64(u64(1) << u64(integer + fraction)) - 1))) , m_transwave(transwave) {} const u8 m_integer; const u8 m_fraction; const u8 m_total_bits; + const u32 m_accum_mask; const bool m_transwave; void reset(); diff --git a/src/engine/platform/sound/es550x/es550x_alu.cpp b/src/engine/platform/sound/es550x/es550x_alu.cpp index f37bb37e..55ab4021 100644 --- a/src/engine/platform/sound/es550x/es550x_alu.cpp +++ b/src/engine/platform/sound/es550x/es550x_alu.cpp @@ -29,15 +29,14 @@ bool es550x_shared_core::es550x_alu_t::busy() bool es550x_shared_core::es550x_alu_t::tick() { if (m_cr.dir) - { - m_accum = bitfield(m_accum - m_fc, 0, m_total_bits); - return ((!m_cr.lei) && (m_accum < m_start)) ? true : false; - } + m_accum -= m_fc; else - { - m_accum = bitfield(m_accum + m_fc, 0, m_total_bits); - return ((!m_cr.lei) && (m_accum > m_end)) ? true : false; - } + m_accum += m_fc; + + m_accum &= m_accum_mask; + return ((!m_cr.lei) + && ((( m_cr.dir) && (m_accum < m_start)) + || ((!m_cr.dir) && (m_accum > m_end)))) ? true : false; } void es550x_shared_core::es550x_alu_t::loop_exec() diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index e6e248f8..0cd2c111 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -141,13 +141,13 @@ void DivPlatformSwan::writeOutVol(int ch) { } } -void DivPlatformSwan::tick() { +void DivPlatformSwan::tick(bool sysTick) { unsigned char sndCtrl=(pcm?0x20:0)|(sweep?0x40:0)|((noise>0)?0x80:0); for (int i=0; i<4; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { int env=chan[i].std.vol.val; - if(parent->getIns(chan[i].ins)->type==DIV_INS_AMIGA) { + if(parent->getIns(chan[i].ins,DIV_INS_SWAN)->type==DIV_INS_AMIGA) { env=MIN(env/4,15); } calcAndWriteOutVol(i,env); @@ -173,6 +173,20 @@ void DivPlatformSwan::tick() { chan[i].ws.changeWave1(chan[i].wave); } } + if (chan[i].std.panL.had) { + chan[i].pan&=0x0f; + chan[i].pan|=(chan[i].std.panL.val&15)<<4; + } + if (chan[i].std.panR.had) { + chan[i].pan&=0xf0; + chan[i].pan|=chan[i].std.panR.val&15; + } + if (chan[i].std.panL.had || chan[i].std.panR.had) { + calcAndWriteOutVol(i,chan[i].std.vol.will?chan[i].std.vol.val:15); + } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].active) { sndCtrl|=(1<getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SWAN); if (c.chan==1) { if (ins->type==DIV_INS_AMIGA) { pcm=true; @@ -401,7 +415,7 @@ int DivPlatformSwan::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_SWAN)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/swan.h b/src/engine/platform/swan.h index 610884b0..0f1643c5 100644 --- a/src/engine/platform/swan.h +++ b/src/engine/platform/swan.h @@ -77,7 +77,7 @@ class DivPlatformSwan: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyWaveChange(int wave); void notifyInsDeletion(void* ins); diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index 474cd00e..cc5151f0 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -84,7 +84,7 @@ unsigned char DivPlatformTIA::dealWithFreq(unsigned char shape, int base, int pi return 0; } -void DivPlatformTIA::tick() { +void DivPlatformTIA::tick(bool sysTick) { for (int i=0; i<2; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { @@ -116,6 +116,9 @@ void DivPlatformTIA::tick() { rWrite(0x15+i,chan[i].shape); chan[i].freqChanged=true; } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { if (chan[i].insChanged) { if (!chan[i].std.wave.will) { @@ -151,7 +154,7 @@ void DivPlatformTIA::tick() { int DivPlatformTIA::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_TIA); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=c.value<<8; chan[c.chan].freqChanged=true; @@ -245,7 +248,7 @@ int DivPlatformTIA::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_TIA)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/tia.h b/src/engine/platform/tia.h index ea149ec3..3ec3c7f3 100644 --- a/src/engine/platform/tia.h +++ b/src/engine/platform/tia.h @@ -51,7 +51,7 @@ class DivPlatformTIA: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void setFlags(unsigned int flags); bool isStereo(); diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 34b975dd..71233581 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -183,7 +183,7 @@ inline int hScale(int note) { return ((note/12)<<4)+(noteMap[note%12]); } -void DivPlatformTX81Z::tick() { +void DivPlatformTX81Z::tick(bool sysTick) { for (int i=0; i<8; i++) { chan[i].std.next(); @@ -228,6 +228,10 @@ void DivPlatformTX81Z::tick() { rWrite(0x1b,chan[i].std.wave.val&3); } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].keyOn=true; @@ -420,7 +424,7 @@ void DivPlatformTX81Z::muteChannel(int ch, bool mute) { int DivPlatformTX81Z::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPZ); if (chan[c.chan].insChanged) { chan[c.chan].state=ins->fm; diff --git a/src/engine/platform/tx81z.h b/src/engine/platform/tx81z.h index 363d5c23..c61b4bfe 100644 --- a/src/engine/platform/tx81z.h +++ b/src/engine/platform/tx81z.h @@ -102,7 +102,7 @@ class DivPlatformTX81Z: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyInsChange(int ins); void setFlags(unsigned int flags); diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 4604438e..c1b578d2 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -114,8 +114,8 @@ void DivPlatformVERA::acquire(short* bufL, short* bufR, size_t start, size_t len psg_render(psg,buf[0],buf[1],curLen); pcm_render(pcm,buf[2],buf[3],curLen); for (int i=0; i>1))/2); - bufR[pos]=(short)(((int)buf[1][i]+(buf[3][i]>>1))/2); + bufL[pos]=(short)(((int)buf[0][i]+buf[2][i])/2); + bufR[pos]=(short)(((int)buf[1][i]+buf[3][i])/2); pos++; } len-=curLen; @@ -156,7 +156,7 @@ int DivPlatformVERA::calcNoteFreq(int ch, int note) { } } -void DivPlatformVERA::tick() { +void DivPlatformVERA::tick(bool sysTick) { for (int i=0; i<16; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { @@ -184,6 +184,15 @@ void DivPlatformVERA::tick() { if (chan[i].std.wave.had) { rWriteHi(i,3,chan[i].std.wave.val); } + if (i<16) { + if (chan[i].std.panL.had) { + chan[i].pan=chan[i].std.panL.val&3; + rWriteHi(i,2,isMuted[i]?0:chan[i].pan); + } + } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].freqChanged) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8)+chan[i].std.pitch.val; if (chan[i].freq>65535) chan[i].freq=65535; @@ -228,7 +237,7 @@ int DivPlatformVERA::dispatch(DivCommand c) { if(c.chan<16) { rWriteLo(c.chan,2,chan[c.chan].vol) } else { - chan[16].pcm.sample=parent->getIns(chan[16].ins)->amiga.initSample; + chan[16].pcm.sample=parent->getIns(chan[16].ins,DIV_INS_VERA)->amiga.initSample; if (chan[16].pcm.sample<0 || chan[16].pcm.sample>=parent->song.sampleLen) { chan[16].pcm.sample=-1; } @@ -250,7 +259,7 @@ int DivPlatformVERA::dispatch(DivCommand c) { chan[c.chan].note=c.value; } chan[c.chan].active=true; - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VERA)); break; case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; @@ -318,7 +327,7 @@ int DivPlatformVERA::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VERA)); } chan[c.chan].inPorta=c.value; break; @@ -374,7 +383,7 @@ void DivPlatformVERA::muteChannel(int ch, bool mute) { } float DivPlatformVERA::getPostAmp() { - return 4.0f; + return 8.0f; } bool DivPlatformVERA::isStereo() { diff --git a/src/engine/platform/vera.h b/src/engine/platform/vera.h index 2b47f99a..ec6dcde1 100644 --- a/src/engine/platform/vera.h +++ b/src/engine/platform/vera.h @@ -63,7 +63,7 @@ class DivPlatformVERA: public DivDispatch { unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyInsDeletion(void* ins); float getPostAmp(); diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp index b3062a63..aa787672 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -91,7 +91,7 @@ void DivPlatformVIC20::writeOutVol(int ch) { } } -void DivPlatformVIC20::tick() { +void DivPlatformVIC20::tick(bool sysTick) { for (int i=0; i<4; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { @@ -119,6 +119,9 @@ void DivPlatformVIC20::tick() { chan[i].keyOn=true; } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (i<3) { @@ -155,7 +158,7 @@ void DivPlatformVIC20::tick() { int DivPlatformVIC20::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_VIC); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; @@ -229,7 +232,7 @@ int DivPlatformVIC20::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VIC)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/vic20.h b/src/engine/platform/vic20.h index 1c4d384b..7948c5ef 100644 --- a/src/engine/platform/vic20.h +++ b/src/engine/platform/vic20.h @@ -66,7 +66,7 @@ class DivPlatformVIC20: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void setFlags(unsigned int flags); void notifyInsDeletion(void* ins); diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 3aa208b7..6362c0cd 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -135,7 +135,7 @@ void DivPlatformVRC6::acquire(short* bufL, short* bufR, size_t start, size_t len } } -void DivPlatformVRC6::tick() { +void DivPlatformVRC6::tick(bool sysTick) { for (int i=0; i<3; i++) { // 16 for pulse; 14 for saw int CHIP_DIVIDER=(i==2)?14:16; @@ -178,6 +178,9 @@ void DivPlatformVRC6::tick() { chWrite(i,0,(chan[i].outVol&0xf)|((chan[i].duty&7)<<4)); } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { if (i==2) { // sawtooth chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; @@ -217,7 +220,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: if (c.chan!=2) { // pulse wave - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_VRC6); if (ins->type==DIV_INS_AMIGA) { chan[c.chan].pcm=true; } else if (chan[c.chan].furnaceDac) { @@ -281,7 +284,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VRC6)); if (!isMuted[c.chan]) { if (c.chan==2) { // sawtooth chWrite(c.chan,0,chan[c.chan].vol); @@ -377,7 +380,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VRC6)); } chan[c.chan].inPorta=c.value; break; @@ -431,9 +434,9 @@ void DivPlatformVRC6::reset() { chan[i]=DivPlatformVRC6::Channel(); chan[i].std.setEngine(parent); } - // a poll may be necessary to decide the default - chan[2].vol=30; - chan[2].outVol=30; + // HELP + chan[2].vol=63; + chan[2].outVol=63; if (dumpWrites) { addWrite(0xffffffff,0); } diff --git a/src/engine/platform/vrc6.h b/src/engine/platform/vrc6.h index 05cd8941..d28265a4 100644 --- a/src/engine/platform/vrc6.h +++ b/src/engine/platform/vrc6.h @@ -82,7 +82,7 @@ class DivPlatformVRC6: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void setFlags(unsigned int flags); diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 1bbd2a80..f1498709 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -336,7 +336,7 @@ void DivPlatformX1_010::updateEnvelope(int ch) { } } -void DivPlatformX1_010::tick() { +void DivPlatformX1_010::tick(bool sysTick) { for (int i=0; i<16; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { @@ -372,6 +372,19 @@ void DivPlatformX1_010::tick() { } } } + if (chan[i].std.panL.had) { + chan[i].pan&=0x0f; + chan[i].pan|=(chan[i].std.panL.val&15)<<4; + chan[i].envChanged=true; + } + if (chan[i].std.panR.had) { + chan[i].pan&=0xf0; + chan[i].pan|=chan[i].std.panR.val&15; + chan[i].envChanged=true; + } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.ex1.had) { bool nextEnable=(chan[i].std.ex1.val&1); if (nextEnable!=(chan[i].env.flag.envEnable)) { @@ -512,7 +525,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { chWrite(c.chan,0,0); // reset previous note - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_X1_010); if ((ins->type==DIV_INS_AMIGA) || chan[c.chan].pcm) { if (ins->type==DIV_INS_AMIGA) { chan[c.chan].furnacePCM=true; @@ -690,7 +703,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_X1_010)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/x1_010.h b/src/engine/platform/x1_010.h index 2bfb78cc..bee54647 100644 --- a/src/engine/platform/x1_010.h +++ b/src/engine/platform/x1_010.h @@ -126,7 +126,7 @@ class DivPlatformX1_010: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 02340fce..27d27241 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -365,9 +365,9 @@ void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t l } } -void DivPlatformYM2610::tick() { +void DivPlatformYM2610::tick(bool sysTick) { // PSG - ay->tick(); + ay->tick(sysTick); ay->flushWrites(); for (DivRegWrite& i: ay->getRegisterWrites()) { immWrite(i.addr&15,i.val); @@ -408,6 +408,15 @@ void DivPlatformYM2610::tick() { } } + if (chan[i].std.panL.had) { + chan[i].pan=chan[i].std.panL.val&3; + rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + } + + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].keyOn=true; @@ -626,7 +635,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { if (c.chan>12) { // ADPCM-B - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); if (ins->type==DIV_INS_AMIGA) { chan[c.chan].furnacePCM=true; } else { @@ -709,7 +718,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { immWrite(0x100,0x00|(1<<(c.chan-7))); break; } - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); chan[c.chan].std.init(ins); if (c.chan<4) { if (!chan[c.chan].std.vol.will) { @@ -972,7 +981,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { case DIV_CMD_PRE_PORTA: if (c.chan>3) { if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_FM)); } } chan[c.chan].inPorta=c.value; diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index 2fd525b0..e75e24d7 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -116,7 +116,7 @@ class DivPlatformYM2610: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 04ec10ab..01f4c7cc 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -429,9 +429,9 @@ void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t } } -void DivPlatformYM2610B::tick() { +void DivPlatformYM2610B::tick(bool sysTick) { // PSG - ay->tick(); + ay->tick(sysTick); ay->flushWrites(); for (DivRegWrite& i: ay->getRegisterWrites()) { immWrite(i.addr&15,i.val); @@ -472,6 +472,15 @@ void DivPlatformYM2610B::tick() { } } + if (chan[i].std.panL.had) { + chan[i].pan=chan[i].std.panL.val&3; + rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + } + + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].keyOn=true; @@ -689,7 +698,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { if (c.chan>14) { // ADPCM-B - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); if (ins->type==DIV_INS_AMIGA) { chan[c.chan].furnacePCM=true; } else { @@ -772,7 +781,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { immWrite(0x100,0x00|(1<<(c.chan-9))); break; } - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); chan[c.chan].std.init(ins); if (c.chan<6) { if (!chan[c.chan].std.vol.will) { @@ -1035,7 +1044,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { case DIV_CMD_PRE_PORTA: if (c.chan>5) { if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_FM)); } } chan[c.chan].inPorta=c.value; diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index d6b616c5..6d46ecb2 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -107,7 +107,7 @@ class DivPlatformYM2610B: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 03e9f869..74794a7d 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -36,7 +36,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { int ordch=orderedOps[ch]; switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch]; @@ -78,7 +78,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { break; case DIV_CMD_VOLUME: { opChan[ch].vol=c.value; - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch]; if (isOpMuted[ch]) { @@ -104,7 +104,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { } else { opChan[ch].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1); } - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (parent->song.sharedExtStat) { for (int i=0; i<4; i++) { if (ch==i) continue; @@ -159,14 +159,14 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { } case DIV_CMD_FM_MULT: { // TODO unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4)); break; } case DIV_CMD_FM_TL: { // TODO unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (isOutput[ins->fm.alg][c.value]) { rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127)); } else { @@ -175,7 +175,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { break; } case DIV_CMD_FM_AR: { - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (c.value<0) { for (int i=0; i<4; i++) { DivInstrumentFM::Operator op=ins->fm.op[i]; @@ -212,7 +212,7 @@ static int opChanOffsH[4]={ 0xad, 0xae, 0xac, 0xa6 }; -void DivPlatformYM2610BExt::tick() { +void DivPlatformYM2610BExt::tick(bool sysTick) { if (extMode) { bool writeSomething=false; unsigned char writeMask=2; @@ -229,7 +229,7 @@ void DivPlatformYM2610BExt::tick() { } } - DivPlatformYM2610B::tick(); + DivPlatformYM2610B::tick(sysTick); bool writeNoteOn=false; unsigned char writeMask=2; @@ -268,7 +268,7 @@ void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) { isOpMuted[ch-2]=mute; int ordch=orderedOps[ch-2]; - DivInstrument* ins=parent->getIns(opChan[ch-2].ins); + DivInstrument* ins=parent->getIns(opChan[ch-2].ins,DIV_INS_FM); unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch]; if (isOpMuted[ch-2]) { diff --git a/src/engine/platform/ym2610bext.h b/src/engine/platform/ym2610bext.h index 25ca5919..e908916e 100644 --- a/src/engine/platform/ym2610bext.h +++ b/src/engine/platform/ym2610bext.h @@ -41,7 +41,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B { void* getChanState(int chan); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void notifyInsChange(int ins); diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 40294733..df9b4351 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -36,7 +36,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { int ordch=orderedOps[ch]; switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); unsigned short baseAddr=chanOffs[1]|opOffs[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch]; @@ -78,7 +78,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { break; case DIV_CMD_VOLUME: { opChan[ch].vol=c.value; - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); unsigned short baseAddr=chanOffs[1]|opOffs[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch]; if (isOpMuted[ch]) { @@ -104,7 +104,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { } else { opChan[ch].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1); } - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (parent->song.sharedExtStat) { for (int i=0; i<4; i++) { if (ch==i) continue; @@ -159,14 +159,14 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { } case DIV_CMD_FM_MULT: { // TODO unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4)); break; } case DIV_CMD_FM_TL: { // TODO unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (isOutput[ins->fm.alg][c.value]) { rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127)); } else { @@ -175,7 +175,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { break; } case DIV_CMD_FM_AR: { - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (c.value<0) { for (int i=0; i<4; i++) { DivInstrumentFM::Operator op=ins->fm.op[i]; @@ -212,7 +212,7 @@ static int opChanOffsH[4]={ 0xad, 0xae, 0xac, 0xa6 }; -void DivPlatformYM2610Ext::tick() { +void DivPlatformYM2610Ext::tick(bool sysTick) { if (extMode) { bool writeSomething=false; unsigned char writeMask=2; @@ -229,7 +229,7 @@ void DivPlatformYM2610Ext::tick() { } } - DivPlatformYM2610::tick(); + DivPlatformYM2610::tick(sysTick); bool writeNoteOn=false; unsigned char writeMask=2; @@ -268,7 +268,7 @@ void DivPlatformYM2610Ext::muteChannel(int ch, bool mute) { isOpMuted[ch-1]=mute; int ordch=orderedOps[ch-1]; - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); unsigned short baseAddr=chanOffs[1]|opOffs[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch]; if (isOpMuted[ch]) { diff --git a/src/engine/platform/ym2610ext.h b/src/engine/platform/ym2610ext.h index 37c9e328..9fa44d0b 100644 --- a/src/engine/platform/ym2610ext.h +++ b/src/engine/platform/ym2610ext.h @@ -41,7 +41,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 { void* getChanState(int chan); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void notifyInsChange(int ins); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 9c70a0bd..0f7779cf 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -40,7 +40,7 @@ const char* notes[12]={ }; // update this when adding new commands. -const char* cmdName[DIV_CMD_MAX]={ +const char* cmdName[]={ "NOTE_ON", "NOTE_OFF", "NOTE_OFF_ENV", @@ -144,15 +144,27 @@ const char* cmdName[DIV_CMD_MAX]={ "N163_WAVE_LOAD", "N163_WAVE_LOADPOS", "N163_WAVE_LOADLEN", + "N163_WAVE_LOADMODE", "N163_CHANNEL_LIMIT", "N163_GLOBAL_WAVE_LOAD", "N163_GLOBAL_WAVE_LOADPOS", "N163_GLOBAL_WAVE_LOADLEN", "N163_GLOBAL_WAVE_LOADMODE", + "ES5506_FILTER_MODE", + "ES5506_FILTER_K1", + "ES5506_FILTER_K2", + "ES5506_ENVELOPE_COUNT", + "ES5506_ENVELOPE_LVRAMP", + "ES5506_ENVELOPE_RVRAMP", + "ES5506_ENVELOPE_K1RAMP", + "ES5506_ENVELOPE_K2RAMP", + "ALWAYS_SET_VOLUME" }; +static_assert((sizeof(cmdName)/sizeof(void*))==DIV_CMD_MAX,"update cmdName!"); + const char* formatNote(unsigned char note, unsigned char octave) { static char ret[4]; if (note==100) { @@ -513,19 +525,38 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe break; case DIV_SYSTEM_ES5506: switch (effect) { - case 0x10: // echo feedback - dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_FEEDBACK,ch,effectVal)); + case 0x10: // select waveform + dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); break; - case 0x11: // echo level - dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_LEVEL,ch,effectVal)); + case 0x11: // filter mode + dispatchCmd(DivCommand(DIV_CMD_ES5506_FILTER_MODE,ch,effectVal&3)); + break; + case 0x20: + case 0x21: // envelope ECOUNT + dispatchCmd(DivCommand(DIV_CMD_ES5506_ENVELOPE_COUNT,ch,((effect&0x01)<<8)|effectVal)); + break; + case 0x22: // envelope LVRAMP + dispatchCmd(DivCommand(DIV_CMD_ES5506_ENVELOPE_LVRAMP,ch,effectVal)); + break; + case 0x23: // envelope RVRAMP + dispatchCmd(DivCommand(DIV_CMD_ES5506_ENVELOPE_RVRAMP,ch,effectVal)); + break; + case 0x24: + case 0x25: // envelope K1RAMP + dispatchCmd(DivCommand(DIV_CMD_ES5506_ENVELOPE_K1RAMP,ch,effectVal,effect&0x01)); + break; + case 0x26: + case 0x27: // envelope K2RAMP + dispatchCmd(DivCommand(DIV_CMD_ES5506_ENVELOPE_K2RAMP,ch,effectVal,effect&0x01)); break; default: if ((effect&0xf0)==0x30) { - dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_DELAY,ch,((effect & 0x0f) << 8) | effectVal)); + dispatchCmd(DivCommand(DIV_CMD_ES5506_FILTER_K1,ch,((effect&0x0f)<<8)|effectVal)); + } else if ((effect&0xf0)==0x40) { + dispatchCmd(DivCommand(DIV_CMD_ES5506_FILTER_K2,ch,((effect&0x0f)<<8)|effectVal)); } else { return false; } - break; } break; default: @@ -1487,19 +1518,21 @@ void DivEngine::nextRow() { firstTick=true; } -bool DivEngine::nextTick(bool noAccum) { +bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { bool ret=false; if (divider<10) divider=10; - if (lowLatency) { + if (lowLatency && !skipping && !inhibitLowLat) { tickMult=1000/divider; if (tickMult<1) tickMult=1; + } else { + tickMult=1; } cycles=got.rate*pow(2,MASTER_CLOCK_PREC)/(divider*tickMult); - clockDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)divider); - if (clockDrift>=divider) { - clockDrift-=divider; + clockDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)(divider*tickMult)); + if (clockDrift>=(divider*tickMult)) { + clockDrift-=(divider*tickMult); cycles++; } @@ -1656,7 +1689,7 @@ bool DivEngine::nextTick(bool noAccum) { firstTick=false; // system tick - for (int i=0; itick(); + for (int i=0; itick(subticks==tickMult); if (!freelance) { if (stepPlay!=1) { @@ -1672,7 +1705,7 @@ bool DivEngine::nextTick(bool noAccum) { } } - if (consoleMode) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,song.ordersLen,curRow,song.patLen,cmdsPerSecond); + if (consoleMode && subticks<=1) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,song.ordersLen,curRow,song.patLen,cmdsPerSecond); } if (haltOn==DIV_HALT_TICK) halted=true; diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index 4be92b70..c971f997 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -595,8 +595,8 @@ bool DivSample::resampleSinc(double r) { result+=s[j]*t2[7-j]; result+=s[8+j]*t1[j]; } - if (result<-32768) result=-32768; - if (result>32767) result=32767; + if (result<-128) result=-128; + if (result>127) result=127; if (i>=8) { data8[i-8]=result; } diff --git a/src/engine/song.h b/src/engine/song.h index 9f223d00..03ff0296 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -324,6 +324,7 @@ struct DivSong { bool sharedExtStat; bool ignoreDACModeOutsideIntendedChannel; bool e1e2AlsoTakePriority; + bool newSegaPCM; DivOrders orders; std::vector ins; @@ -334,7 +335,7 @@ struct DivSong { bool chanShow[DIV_MAX_CHANS]; bool chanCollapse[DIV_MAX_CHANS]; - DivInstrument nullIns; + DivInstrument nullIns, nullInsOPLL, nullInsOPL; DivWavetable nullWave; DivSample nullSample; @@ -405,7 +406,8 @@ struct DivSong { gbInsAffectsEnvelope(true), sharedExtStat(true), ignoreDACModeOutsideIntendedChannel(false), - e1e2AlsoTakePriority(false) { + e1e2AlsoTakePriority(false), + newSegaPCM(true) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; @@ -418,6 +420,23 @@ struct DivSong { } system[0]=DIV_SYSTEM_YM2612; system[1]=DIV_SYSTEM_SMS; + + nullInsOPLL.fm.opllPreset=7; + nullInsOPLL.fm.op[1].tl=0; + nullInsOPLL.name="This is a bug! Report!"; + + nullInsOPL.fm.alg=0; + nullInsOPL.fm.fb=7; + nullInsOPL.fm.op[0].dr=2; + nullInsOPL.fm.op[0].rr=7; + nullInsOPL.fm.op[0].tl=22; + nullInsOPL.fm.op[0].ksl=1; + nullInsOPL.fm.op[0].mult=3; + nullInsOPL.fm.op[1].tl=0; + nullInsOPL.fm.op[1].dr=3; + nullInsOPL.fm.op[1].rr=12; + nullInsOPL.fm.op[1].mult=1; + nullInsOPL.name="This is a bug! Report!"; } }; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index f6fd36ef..7ec13fc3 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1133,7 +1133,7 @@ const DivInstrumentType chanPrefType[48][32]={ {DIV_INS_VIC, DIV_INS_VIC, DIV_INS_VIC, DIV_INS_VIC}, // VIC-20 {DIV_INS_PET}, // PET {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // SNES/N163/RF5C68 - {DIV_INS_VRC6, DIV_INS_VRC6, DIV_INS_VRC6}, // VRC6 + {DIV_INS_VRC6, DIV_INS_VRC6, DIV_INS_VRC6_SAW}, // VRC6 {DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL}, // OPLL/VRC7 {DIV_INS_FDS}, // FDS {DIV_INS_STD, DIV_INS_STD, DIV_INS_AMIGA}, // MMC5 diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 781435e2..af57325a 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -752,11 +752,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { bool writeDACSamples=false; bool writeNESSamples=false; bool writePCESamples=false; - bool writeADPCM=false; - bool writeSegaPCM=false; - bool writeX1010=false; - bool writeQSound=false; - bool writeES5506=false; + unsigned char writeADPCM=0; + unsigned char writeSegaPCM=0; + unsigned char writeX1010=0; + unsigned char writeQSound=0; + unsigned char writeES5506=0; for (int i=0; ichipClock; willExport[i]=true; - writeX1010=true; + writeX1010=1; } else if (!(hasX1&0x40000000)) { isSecond[i]=true; willExport[i]=true; + writeX1010=2; hasX1|=0x40000000; howManyChips++; } @@ -861,10 +863,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { if (!hasOPNB) { hasOPNB=disCont[i].dispatch->chipClock; willExport[i]=true; - writeADPCM=true; + writeADPCM=1; } else if (!(hasOPNB&0x40000000)) { isSecond[i]=true; willExport[i]=true; + writeADPCM=2; hasOPNB|=0x40000000; howManyChips++; } @@ -966,10 +969,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { // not be able to handle the 64kb sample bank trick hasQSound=disCont[i].dispatch->chipClock; willExport[i]=true; - writeQSound=true; + writeQSound=1; } else if (!(hasQSound&0x40000000)) { isSecond[i]=true; willExport[i]=false; + writeQSound=2; addWarning("dual QSound is not supported by the VGM format"); } break; @@ -993,11 +997,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { // VGM identifies ES5506 if highest bit sets, otherwise ES5505 hasES5505=0x80000000|disCont[i].dispatch->chipClock; willExport[i]=true; - writeES5506=true; + writeES5506=1; } else if (!(hasES5505&0x40000000)) { isSecond[i]=true; willExport[i]=false; hasES5505|=0xc0000000; + writeES5506=2; howManyChips++; } break; @@ -1253,7 +1258,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { } } - if (writeSegaPCM) { + if (writeSegaPCM>0) { unsigned char* pcmMem=new unsigned char[16777216]; size_t memPos=0; @@ -1285,70 +1290,82 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { if (memPos>=16777216) break; } - w->writeC(0x67); - w->writeC(0x66); - w->writeC(0x80); - w->writeI(memPos+8); - w->writeI(memPos); - w->writeI(0); - w->write(pcmMem,memPos); + for (unsigned char i=0; iwriteC(0x67); + w->writeC(0x66); + w->writeC(0x80); + w->writeI((memPos+8)|(i*0x80000000)); + w->writeI(memPos); + w->writeI(0); + w->write(pcmMem,memPos); + } delete[] pcmMem; } - if (writeADPCM && adpcmAMemLen>0) { - w->writeC(0x67); - w->writeC(0x66); - w->writeC(0x82); - w->writeI(adpcmAMemLen+8); - w->writeI(adpcmAMemLen); - w->writeI(0); - w->write(adpcmAMem,adpcmAMemLen); + if (adpcmAMemLen>0) { + for (unsigned char i=0; iwriteC(0x67); + w->writeC(0x66); + w->writeC(0x82); + w->writeI((adpcmAMemLen+8)|(i*0x80000000)); + w->writeI(adpcmAMemLen); + w->writeI(0); + w->write(adpcmAMem,adpcmAMemLen); + } } - if (writeADPCM && adpcmBMemLen>0) { - w->writeC(0x67); - w->writeC(0x66); - w->writeC(0x83); - w->writeI(adpcmBMemLen+8); - w->writeI(adpcmBMemLen); - w->writeI(0); - w->write(adpcmBMem,adpcmBMemLen); + if (adpcmBMemLen>0) { + for (unsigned char i=0; iwriteC(0x67); + w->writeC(0x66); + w->writeC(0x83); + w->writeI((adpcmBMemLen+8)|(i*0x80000000)); + w->writeI(adpcmBMemLen); + w->writeI(0); + w->write(adpcmBMem,adpcmBMemLen); + } } - if (writeQSound && qsoundMemLen>0) { + if (qsoundMemLen>0) { // always write a whole bank unsigned int blockSize=(qsoundMemLen+0xffff)&(~0xffff); if (blockSize > 0x1000000) { blockSize = 0x1000000; } - w->writeC(0x67); - w->writeC(0x66); - w->writeC(0x8F); - w->writeI(blockSize+8); - w->writeI(0x1000000); - w->writeI(0); - w->write(qsoundMem,blockSize); + for (unsigned char i=0; iwriteC(0x67); + w->writeC(0x66); + w->writeC(0x8F); + w->writeI((blockSize+8)|(i*0x80000000)); + w->writeI(0x1000000); + w->writeI(0); + w->write(qsoundMem,blockSize); + } } - if (writeX1010 && x1_010MemLen>0) { - w->writeC(0x67); - w->writeC(0x66); - w->writeC(0x91); - w->writeI(x1_010MemLen+8); - w->writeI(x1_010MemLen); - w->writeI(0); - w->write(x1_010Mem,x1_010MemLen); + if (x1_010MemLen>0) { + for (unsigned char i=0; iwriteC(0x67); + w->writeC(0x66); + w->writeC(0x91); + w->writeI((x1_010MemLen+8)|(i*0x80000000)); + w->writeI(x1_010MemLen); + w->writeI(0); + w->write(x1_010Mem,x1_010MemLen); + } } - if (writeES5506 && es5506MemLen>0) { - w->writeC(0x67); - w->writeC(0x66); - w->writeC(0x8F); - w->writeI(es5506MemLen+8); - w->writeI(es5506MemLen); - w->writeI(0); - w->write(es5506Mem,es5506MemLen); + if (es5506MemLen>0) { + for (unsigned char i=0; iwriteC(0x67); + w->writeC(0x66); + w->writeC(0x8F); + w->writeI((es5506MemLen+8)|(i*0x80000000)); + w->writeI(es5506MemLen); + w->writeI(0); + w->write(es5506Mem,es5506MemLen); + } } // initialize streams @@ -1447,7 +1464,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { writeLoop=true; } } - if (nextTick() || !playing) { + if (nextTick(false,true) || !playing) { done=true; if (!loop) { for (int i=0; i0) return false; + bool updated=first; first=false; + subDivCounter=e->tickMult; if (!state.enabled) return updated; if (width<1) return false; @@ -167,6 +170,7 @@ void DivWaveSynth::init(DivInstrument* which, int w, int h, bool insChanged) { pos=0; stage=0; divCounter=1+state.rateDivider; + subDivCounter=0; first=true; changeWave1(state.wave1); diff --git a/src/engine/waveSynth.h b/src/engine/waveSynth.h index ecd6f8a8..f5c89e3a 100644 --- a/src/engine/waveSynth.h +++ b/src/engine/waveSynth.h @@ -28,7 +28,7 @@ class DivEngine; class DivWaveSynth { DivEngine* e; DivInstrumentWaveSynth state; - int pos, stage, divCounter, width, height; + int pos, stage, divCounter, width, height, subDivCounter; bool first, activeChangedB; unsigned char wave1[256]; unsigned char wave2[256]; @@ -78,6 +78,7 @@ class DivWaveSynth { divCounter(0), width(32), height(31), + subDivCounter(0), first(false), activeChangedB(false) { memset(wave1,0,256); diff --git a/src/gui/about.cpp b/src/gui/about.cpp index c15f3268..ce543f5f 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -214,6 +214,8 @@ void FurnaceGUI::drawAbout() { while (aboutHue>1) aboutHue--; while (aboutSin>=2400) aboutSin-=2400; if (aboutScroll>(42*aboutCount+scrH)) aboutScroll=-20; + + WAKE_UP; } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_ABOUT; ImGui::End(); diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index 8a45741f..d2035ff7 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -166,6 +166,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("behavior changed in 0.6"); } + ImGui::Checkbox("New SegaPCM features (macros and better panning)",&e->song.newSegaPCM); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.6"); + } } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS; ImGui::End(); diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index cdc3157b..1776caa6 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -72,130 +72,136 @@ void FurnaceGUI::drawInsList() { ImGui::Indent(); } - for (int i=0; i<(int)e->song.ins.size(); i++) { - DivInstrument* ins=e->song.ins[i]; - String name; - switch (ins->type) { - case DIV_INS_FM: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FM]); - name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_STD: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_STD]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_GB: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GB]); - name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_C64: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_C64]); - name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_AMIGA: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AMIGA]); - name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_PCE: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PCE]); - name=fmt::sprintf(ICON_FA_ID_BADGE " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_AY: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_AY8930: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY8930]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_TIA: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_TIA]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_SAA1099: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SAA1099]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_VIC: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VIC]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_PET: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PET]); - name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_VRC6: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_VRC6_SAW: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6_SAW]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_OPLL: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPLL]); - name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_OPL: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL]); - name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_FDS: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FDS]); - name=fmt::sprintf(ICON_FA_FLOPPY_O " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_VBOY: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VBOY]); - name=fmt::sprintf(ICON_FA_BINOCULARS " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_N163: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_N163]); - name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_SCC: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SCC]); - name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_OPZ: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPZ]); - name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_POKEY: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEY]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_BEEPER: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_BEEPER]); - name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_SWAN: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SWAN]); - name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_MIKEY: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MIKEY]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_VERA: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VERA]); - name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_X1_010: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_X1_010]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_ES5506: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_ES5506]); - name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i); - break; - default: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]); - name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i); - break; + for (int i=-1; i<(int)e->song.ins.size(); i++) { + String name=ICON_FA_CIRCLE_O " - None -"; + const char* insType="Bug!"; + if (i>=0) { + DivInstrument* ins=e->song.ins[i]; + insType=(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type]; + switch (ins->type) { + case DIV_INS_FM: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FM]); + name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_STD: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_STD]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_GB: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GB]); + name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_C64: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_C64]); + name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_AMIGA: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AMIGA]); + name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_PCE: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PCE]); + name=fmt::sprintf(ICON_FA_ID_BADGE " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_AY: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_AY8930: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY8930]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_TIA: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_TIA]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_SAA1099: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SAA1099]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_VIC: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VIC]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_PET: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PET]); + name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_VRC6: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_VRC6_SAW: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6_SAW]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_OPLL: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPLL]); + name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_OPL: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL]); + name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_FDS: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FDS]); + name=fmt::sprintf(ICON_FA_FLOPPY_O " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_VBOY: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VBOY]); + name=fmt::sprintf(ICON_FA_BINOCULARS " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_N163: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_N163]); + name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_SCC: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SCC]); + name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_OPZ: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPZ]); + name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_POKEY: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEY]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_BEEPER: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_BEEPER]); + name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_SWAN: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SWAN]); + name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_MIKEY: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MIKEY]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_VERA: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VERA]); + name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_X1_010: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_X1_010]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_ES5506: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_ES5506]); + name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i); + break; + default: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]); + name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i); + break; + } + } else { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]); } ImGui::TableNextRow(); ImGui::TableNextColumn(); - if (ImGui::Selectable(name.c_str(),curIns==i)) { + if (ImGui::Selectable(name.c_str(),(i==-1)?(curIns<0 || curIns>=e->song.insLen):(curIns==i))) { curIns=i; } if (settings.insFocusesPattern && patternOpen && ImGui::IsItemActivated()) { @@ -203,8 +209,8 @@ void FurnaceGUI::drawInsList() { curIns=i; } ImGui::PopStyleColor(); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("%s",(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type]); + if (ImGui::IsItemHovered() && i>=0) { + ImGui::SetTooltip("%s",insType); if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { insEditOpen=true; nextWindow=GUI_WINDOW_INS_EDIT; diff --git a/src/gui/debug.cpp b/src/gui/debug.cpp index f1c974e8..eb7558b6 100644 --- a/src/gui/debug.cpp +++ b/src/gui/debug.cpp @@ -39,6 +39,7 @@ #include "../engine/platform/x1_010.h" #include "../engine/platform/n163.h" #include "../engine/platform/vrc6.h" +#include "../engine/platform/es5506.h" #include "../engine/platform/dummy.h" #define GENESIS_DEBUG \ @@ -333,6 +334,66 @@ void putDispatchChan(void* data, int chanNum, int type) { ImGui::TextColored(ch->furnaceDac?colorOn:colorOff,">> FurnaceDAC"); break; } + case DIV_SYSTEM_ES5506: { + DivPlatformES5506::Channel* ch=(DivPlatformES5506::Channel*)data; + ImGui::Text("> ES5506"); + ImGui::Text("* freq: %.4x",ch->freq); + ImGui::Text(" - base: %d",ch->baseFreq); + ImGui::Text(" - pitch: %d",ch->pitch); + ImGui::Text("- note: %d",ch->note); + ImGui::Text("- wave: %d",ch->wave); + ImGui::Text("- sample: %d",ch->sample); + ImGui::Text("- ins: %d",ch->ins); + ImGui::Text("* PCM:"); + ImGui::Text(" - index: %d",ch->pcm.index); + ImGui::Text(" - freqOffs: %.6f",ch->pcm.freqOffs); + ImGui::Text(" - bank: %.2x",ch->pcm.bank); + ImGui::Text(" - start: %.8x",ch->pcm.start); + ImGui::Text(" - end: %.8x",ch->pcm.end); + ImGui::Text(" - length: %.8x",ch->pcm.length); + ImGui::Text(" - loopStart: %.8x",ch->pcm.loopStart); + ImGui::Text(" - loopEnd: %.8x",ch->pcm.loopEnd); + ImGui::Text(" - loopMode: %d",ch->pcm.loopMode); + ImGui::Text("* Filter:"); + ImGui::Text(" - Mode: %d",ch->filter.mode); + ImGui::Text(" - K1: %.4x",ch->filter.k1); + ImGui::Text(" - K2: %.4x",ch->filter.k2); + ImGui::Text("* Envelope:"); + ImGui::Text(" - EnvCount: %.3x",ch->envelope.ecount); + ImGui::Text(" - LVRamp: %d",ch->envelope.lVRamp); + ImGui::Text(" - RVRamp: %d",ch->envelope.rVRamp); + ImGui::Text(" - K1Ramp: %d",ch->envelope.k1Ramp); + ImGui::Text(" - K2Ramp: %d",ch->envelope.k2Ramp); + ImGui::Text("- vol: %.2x",ch->vol); + ImGui::Text("- LVol: %.2x",ch->lVol); + ImGui::Text("- RVol: %.2x",ch->rVol); + ImGui::Text("- outVol: %.2x",ch->outVol); + ImGui::Text("- outLVol: %.2x",ch->outLVol); + ImGui::Text("- outRVol: %.2x",ch->outRVol); + ImGui::Text("- ResLVol: %.2x",ch->resLVol); + ImGui::Text("- ResRVol: %.2x",ch->resRVol); + ImGui::TextColored(ch->active?colorOn:colorOff,">> Active"); + ImGui::TextColored(ch->insChanged?colorOn:colorOff,">> InsChanged"); + ImGui::TextColored(ch->freqChanged?colorOn:colorOff,">> FreqChanged"); + ImGui::TextColored(ch->volChanged?colorOn:colorOff,">> VolChanged"); + ImGui::TextColored(ch->filterChanged.mode?colorOn:colorOff,">> FilterModeChanged"); + ImGui::TextColored(ch->filterChanged.k1?colorOn:colorOff,">> FilterK1Changed"); + ImGui::TextColored(ch->filterChanged.k2?colorOn:colorOff,">> FilterK2Changed"); + ImGui::TextColored(ch->envChanged.ecount?colorOn:colorOff,">> EnvECountChanged"); + ImGui::TextColored(ch->envChanged.lVRamp?colorOn:colorOff,">> EnvLVRampChanged"); + ImGui::TextColored(ch->envChanged.rVRamp?colorOn:colorOff,">> EnvRVRampChanged"); + ImGui::TextColored(ch->envChanged.k1Ramp?colorOn:colorOff,">> EnvK1RampChanged"); + ImGui::TextColored(ch->envChanged.k2Ramp?colorOn:colorOff,">> EnvK2RampChanged"); + ImGui::TextColored(ch->keyOn?colorOn:colorOff,">> KeyOn"); + ImGui::TextColored(ch->keyOff?colorOn:colorOff,">> KeyOff"); + ImGui::TextColored(ch->inPorta?colorOn:colorOff,">> InPorta"); + ImGui::TextColored(ch->useWave?colorOn:colorOff,">> UseWave"); + ImGui::TextColored(ch->isReverseLoop?colorOn:colorOff,">> IsReverseLoop"); + ImGui::TextColored(ch->pcm.reversed?colorOn:colorOff,">> PCMReversed"); + ImGui::TextColored(ch->envelope.k1Slow?colorOn:colorOff,">> EnvK1Slow"); + ImGui::TextColored(ch->envelope.k2Slow?colorOn:colorOff,">> EnvK2Slow"); + break; + } default: ImGui::Text("Unknown system! Help!"); break; diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 31830681..06184c0b 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -226,6 +226,26 @@ void FurnaceGUI::drawDebug() { } ImGui::TreePop(); } + if (ImGui::TreeNode("ADSR Test Area")) { + static int tl, ar, dr, d2r, sl, rr, sus, egt, algOrGlobalSus, instType; + static float maxArDr, maxTl; + ImGui::Text("This window was done out of frustration"); + drawFMEnv(tl,ar,dr,d2r,rr,sl,sus,egt,algOrGlobalSus,maxTl,maxArDr,ImVec2(200.0f*dpiScale,100.0f*dpiScale),instType); + + ImGui::InputInt("tl",&tl); + ImGui::InputInt("ar",&ar); + ImGui::InputInt("dr",&dr); + ImGui::InputInt("d2r",&d2r); + ImGui::InputInt("sl",&sl); + ImGui::InputInt("rr",&rr); + ImGui::InputInt("sus",&sus); + ImGui::InputInt("egt",&egt); + ImGui::InputInt("algOrGlobalSus",&algOrGlobalSus); + ImGui::InputInt("instType",&instType); + ImGui::InputFloat("maxArDr",&maxArDr); + ImGui::InputFloat("maxTl",&maxTl); + ImGui::TreePop(); + } if (ImGui::TreeNode("User Interface")) { if (ImGui::Button("Inspect")) { inspectorOpen=!inspectorOpen; diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 87040bc2..49fb4360 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -99,20 +99,14 @@ void FurnaceGUI::doAction(int what) { if (++curOctave>7) { curOctave=7; } else { - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); + e->autoNoteOffAll(); } break; case GUI_ACTION_OCTAVE_DOWN: if (--curOctave<-5) { curOctave=-5; } else { - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); + e->autoNoteOffAll(); } break; case GUI_ACTION_INS_UP: @@ -142,6 +136,10 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_FOLLOW_PATTERN: followPattern=!followPattern; break; + case GUI_ACTION_FULLSCREEN: + fullScreen=!fullScreen; + SDL_SetWindowFullscreen(sdlWin,fullScreen?(SDL_WINDOW_FULLSCREEN|SDL_WINDOW_FULLSCREEN_DESKTOP):0); + break; case GUI_ACTION_PANIC: e->syncReset(); break; @@ -215,6 +213,9 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_WINDOW_LOG: nextWindow=GUI_WINDOW_LOG; break; + case GUI_ACTION_WINDOW_EFFECT_LIST: + nextWindow=GUI_WINDOW_EFFECT_LIST; + break; case GUI_ACTION_COLLAPSE_WINDOW: collapseWindow=true; @@ -287,6 +288,12 @@ void FurnaceGUI::doAction(int what) { case GUI_WINDOW_REGISTER_VIEW: regViewOpen=false; break; + case GUI_WINDOW_LOG: + logOpen=false; + break; + case GUI_WINDOW_EFFECT_LIST: + effectListOpen=false; + break; default: break; } @@ -294,16 +301,28 @@ void FurnaceGUI::doAction(int what) { break; case GUI_ACTION_PAT_NOTE_UP: - doTranspose(1); + doTranspose(1,opMaskTransposeNote); break; case GUI_ACTION_PAT_NOTE_DOWN: - doTranspose(-1); + doTranspose(-1,opMaskTransposeNote); break; case GUI_ACTION_PAT_OCTAVE_UP: - doTranspose(12); + doTranspose(12,opMaskTransposeNote); break; case GUI_ACTION_PAT_OCTAVE_DOWN: - doTranspose(-12); + doTranspose(-12,opMaskTransposeNote); + break; + case GUI_ACTION_PAT_VALUE_UP: + doTranspose(1,opMaskTransposeValue); + break; + case GUI_ACTION_PAT_VALUE_DOWN: + doTranspose(-1,opMaskTransposeValue); + break; + case GUI_ACTION_PAT_VALUE_UP_COARSE: + doTranspose(16,opMaskTransposeValue); + break; + case GUI_ACTION_PAT_VALUE_DOWN_COARSE: + doTranspose(-16,opMaskTransposeValue); break; case GUI_ACTION_PAT_SELECT_ALL: doSelectAll(); @@ -565,12 +584,14 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_LIST_ADD: curSample=e->addSample(); + updateSampleTex=true; MARK_MODIFIED; break; case GUI_ACTION_SAMPLE_LIST_DUPLICATE: if (curSample>=0 && curSample<(int)e->song.sample.size()) { DivSample* prevSample=e->getSample(curSample); curSample=e->addSample(); + updateSampleTex=true; e->lockEngine([this,prevSample]() { DivSample* sample=e->getSample(curSample); if (sample!=NULL) { @@ -599,16 +620,23 @@ void FurnaceGUI::doAction(int what) { if (curSample>=0 && curSample<(int)e->song.sample.size()) openFileDialog(GUI_FILE_SAMPLE_SAVE); break; case GUI_ACTION_SAMPLE_LIST_MOVE_UP: - if (e->moveSampleUp(curSample)) curSample--; + if (e->moveSampleUp(curSample)) { + curSample--; + updateSampleTex=true; + } break; case GUI_ACTION_SAMPLE_LIST_MOVE_DOWN: - if (e->moveSampleDown(curSample)) curSample++; + if (e->moveSampleDown(curSample)) { + curSample++; + updateSampleTex=true; + } break; case GUI_ACTION_SAMPLE_LIST_DELETE: e->delSample(curSample); MARK_MODIFIED; if (curSample>=(int)e->song.sample.size()) { curSample--; + updateSampleTex=true; } break; case GUI_ACTION_SAMPLE_LIST_EDIT: @@ -616,9 +644,11 @@ void FurnaceGUI::doAction(int what) { break; case GUI_ACTION_SAMPLE_LIST_UP: if (--curSample<0) curSample=0; + updateSampleTex=true; break; case GUI_ACTION_SAMPLE_LIST_DOWN: if (++curSample>=(int)e->song.sample.size()) curSample=((int)e->song.sample.size())-1; + updateSampleTex=true; break; case GUI_ACTION_SAMPLE_LIST_PREVIEW: e->previewSample(curSample); diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 5098a73d..722c38f2 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -35,10 +35,7 @@ void FurnaceGUI::drawEditControls() { if (ImGui::InputInt("##Octave",&curOctave,1,1)) { if (curOctave>7) curOctave=7; if (curOctave<-5) curOctave=-5; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); + e->autoNoteOffAll(); if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { nextWindow=GUI_WINDOW_PATTERN; @@ -139,10 +136,7 @@ void FurnaceGUI::drawEditControls() { if (ImGui::InputInt("##Octave",&curOctave,1,1)) { if (curOctave>7) curOctave=7; if (curOctave<-5) curOctave=-5; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); + e->autoNoteOffAll(); if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { nextWindow=GUI_WINDOW_PATTERN; @@ -213,10 +207,7 @@ void FurnaceGUI::drawEditControls() { if (ImGui::InputInt("##Octave",&curOctave,0,0)) { if (curOctave>7) curOctave=7; if (curOctave<-5) curOctave=-5; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); + e->autoNoteOffAll(); if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { nextWindow=GUI_WINDOW_PATTERN; @@ -314,10 +305,7 @@ void FurnaceGUI::drawEditControls() { if (ImGui::InputInt("##Octave",&curOctave,1,1)) { if (curOctave>7) curOctave=7; if (curOctave<-5) curOctave=-5; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); + e->autoNoteOffAll(); if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { nextWindow=GUI_WINDOW_PATTERN; diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index bd17e20a..106ec61b 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -177,17 +177,17 @@ void FurnaceGUI::doSelectAll() { } } -#define maskOut(x) \ +#define maskOut(m,x) \ if (x==0) { \ - if (!opMaskNote) continue; \ + if (!m.note) continue; \ } else if (x==1) { \ - if (!opMaskIns) continue; \ + if (!m.ins) continue; \ } else if (x==2) { \ - if (!opMaskVol) continue; \ + if (!m.vol) continue; \ } else if (((x)&1)==0) { \ - if (!opMaskEffectVal) continue; \ + if (!m.effectVal) continue; \ } else if (((x)&1)==1) { \ - if (!opMaskEffect) continue; \ + if (!m.effect) continue; \ } void FurnaceGUI::doDelete() { @@ -201,7 +201,7 @@ void FurnaceGUI::doDelete() { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][iFine]=0; @@ -238,7 +238,7 @@ void FurnaceGUI::doPullDelete() { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen; j++) { if (jsong.patLen-1) { if (iFine==0) { @@ -270,7 +270,7 @@ void FurnaceGUI::doInsert() { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen-1; j>=selStart.y; j--) { if (j==selStart.y) { if (iFine==0) { @@ -291,7 +291,7 @@ void FurnaceGUI::doInsert() { makeUndo(GUI_UNDO_PATTERN_PUSH); } -void FurnaceGUI::doTranspose(int amount) { +void FurnaceGUI::doTranspose(int amount, OperationMask& mask) { finishSelection(); prepareUndo(GUI_UNDO_PATTERN_DELETE); curNibble=false; @@ -302,7 +302,7 @@ void FurnaceGUI::doTranspose(int amount) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; @@ -469,7 +469,7 @@ void FurnaceGUI::doPaste(PasteMode mode) { note[2]=line[charPos++]; note[3]=0; - if (iFine==0 && !opMaskNote) { + if (iFine==0 && !opMaskPaste.note) { iFine++; continue; } @@ -498,22 +498,22 @@ void FurnaceGUI::doPaste(PasteMode mode) { note[2]=0; if (iFine==1) { - if (!opMaskIns) { + if (!opMaskPaste.ins) { iFine++; continue; } } else if (iFine==2) { - if (!opMaskVol) { + if (!opMaskPaste.vol) { iFine++; continue; } } else if ((iFine&1)==0) { - if (!opMaskEffectVal) { + if (!opMaskPaste.effectVal) { iFine++; continue; } } else if ((iFine&1)==1) { - if (!opMaskEffect) { + if (!opMaskPaste.effect) { iFine++; continue; } @@ -589,7 +589,7 @@ void FurnaceGUI::doInterpolate() { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; @@ -825,7 +825,7 @@ void FurnaceGUI::doCollapse(int divider) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; @@ -882,7 +882,7 @@ void FurnaceGUI::doExpand(int multiplier) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; diff --git a/src/gui/effectList.cpp b/src/gui/effectList.cpp new file mode 100644 index 00000000..c64966a4 --- /dev/null +++ b/src/gui/effectList.cpp @@ -0,0 +1,73 @@ +#include "gui.h" +#include "guiConst.h" +#include + +void FurnaceGUI::drawEffectList() { + if (nextWindow==GUI_WINDOW_EFFECT_LIST) { + effectListOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!effectListOpen) return; + if (ImGui::Begin("Effect List",&effectListOpen)) { + ImGui::Text("System at cursor: %s",e->getSystemName(e->sysOfChan[cursor.xCoarse])); + if (ImGui::BeginTable("effectList",2)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::Text("Name"); + ImGui::TableNextColumn(); + ImGui::Text("Description"); + + const char* prevName=NULL; + for (int i=0; i<256; i++) { + const char* name=e->getEffectDesc(i,cursor.xCoarse); + if (name==prevName) { + continue; + } + prevName=name; + if (name!=NULL) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::PushFont(patFont); + if (i<0x10) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[i]]); + } else if (i<0x20) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); + } else if (i<0x30) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]); + } else if (i<0x48) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); + } else if (i<0x90) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + } else if (i<0xa0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_MISC]); + } else if (i<0xc0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + } else if (i<0xd0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SPEED]); + } else if (i<0xe0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + } else { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[extFxColors[i-0xe0]]); + } + ImGui::Text("%c%c%c%c",name[0],name[1],name[2],name[3]); + ImGui::PopStyleColor(); + ImGui::PopFont(); + + ImGui::TableNextColumn(); + if (strlen(name)>6) { + ImGui::TextWrapped("%s",&name[6]); + } else { + ImGui::Text("ERROR"); + } + } + } + ImGui::EndTable(); + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EFFECT_LIST; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ba83b865..009952a6 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #define _USE_MATH_DEFINES #include "gui.h" #include "util.h" @@ -915,7 +916,10 @@ void FurnaceGUI::noteInput(int num, int key, int vol) { } pat->data[cursor.y][1]=(unsigned char)pat->data[cursor.y][1]; if (latchIns==-2) { - pat->data[cursor.y][2]=curIns; + if (curIns>=(int)e->song.ins.size()) curIns=-1; + if (curIns>=0) { + pat->data[cursor.y][2]=curIns; + } } else if (latchIns!=-1 && !e->song.ins.empty()) { pat->data[cursor.y][2]=MIN(((int)e->song.ins.size())-1,latchIns); } @@ -954,6 +958,9 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) { pat->data[cursor.y][target]=(int)e->song.ins.size()-1; } } + if (settings.absorbInsInput) { + curIns=pat->data[cursor.y][target]; + } makeUndo(GUI_UNDO_PATTERN_EDIT); if (direct) { curNibble=false; @@ -1012,6 +1019,15 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) { } } +#define changeLatch(x) \ + if (x<0) x=0; \ + if (!latchNibble && !settings.pushNibble) x=0; \ + x=(x<<4)|num; \ + latchNibble=!latchNibble; \ + if (!latchNibble) { \ + if (++latchTarget>4) latchTarget=0; \ + } + void FurnaceGUI::keyDown(SDL_Event& ev) { if (ImGuiFileDialog::Instance()->IsOpened()) return; if (aboutOpen) return; @@ -1053,6 +1069,45 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { return; } + if (latchTarget) { + if (mapped==SDLK_DELETE || mapped==SDLK_BACKSPACE) { + switch (latchTarget) { + case 1: + latchIns=-1; + break; + case 2: + latchVol=-1; + break; + case 3: + latchEffect=-1; + break; + case 4: + latchEffectVal=-1; + break; + } + } else { + try { + int num=valueKeys.at(ev.key.keysym.sym); + switch (latchTarget) { + case 1: // instrument + changeLatch(latchIns); + break; + case 2: // volume + changeLatch(latchVol); + break; + case 3: // effect + changeLatch(latchEffect); + break; + case 4: // effect value + changeLatch(latchEffectVal); + break; + } + } catch (std::out_of_range& e) { + } + } + return; + } + // PER-WINDOW KEYS switch (curWindow) { case GUI_WINDOW_PATTERN: @@ -1078,9 +1133,6 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { if (edit) { noteInput(num,key); } - if (key!=100 && key!=101 && key!=102) { - previewNote(cursor.xCoarse,num); - } } catch (std::out_of_range& e) { } } else if (edit) { // value @@ -1182,75 +1234,10 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { } } catch (std::out_of_range& e) { } - - // PER-WINDOW PREVIEW KEYS - switch (curWindow) { - case GUI_WINDOW_INS_EDIT: - case GUI_WINDOW_INS_LIST: - case GUI_WINDOW_EDIT_CONTROLS: - case GUI_WINDOW_SONG_INFO: - if (!ev.key.repeat) { - try { - int key=noteKeys.at(ev.key.keysym.scancode); - int num=12*curOctave+key; - if (key!=100 && key!=101 && key!=102) { - previewNote(cursor.xCoarse,num); - } - } catch (std::out_of_range& e) { - } - } - break; - case GUI_WINDOW_SAMPLE_EDIT: - case GUI_WINDOW_SAMPLE_LIST: - if (!ev.key.repeat) { - try { - int key=noteKeys.at(ev.key.keysym.scancode); - int num=12*curOctave+key; - if (key!=100 && key!=101 && key!=102) { - e->previewSample(curSample,num); - samplePreviewOn=true; - samplePreviewKey=ev.key.keysym.scancode; - samplePreviewNote=num; - } - } catch (std::out_of_range& e) { - } - } - break; - case GUI_WINDOW_WAVE_LIST: - case GUI_WINDOW_WAVE_EDIT: - if (!ev.key.repeat) { - try { - int key=noteKeys.at(ev.key.keysym.scancode); - int num=12*curOctave+key; - if (key!=100 && key!=101 && key!=102) { - e->previewWave(curWave,num); - wavePreviewOn=true; - wavePreviewKey=ev.key.keysym.scancode; - wavePreviewNote=num; - } - } catch (std::out_of_range& e) { - } - } - break; - default: - break; - } } void FurnaceGUI::keyUp(SDL_Event& ev) { - stopPreviewNote(ev.key.keysym.scancode,true); - if (wavePreviewOn) { - if (ev.key.keysym.scancode==wavePreviewKey) { - wavePreviewOn=false; - e->stopWavePreview(); - } - } - if (samplePreviewOn) { - if (ev.key.keysym.scancode==samplePreviewKey) { - samplePreviewOn=false; - e->stopSamplePreview(); - } - } + // nothing for now } bool dirExists(String what) { @@ -1303,9 +1290,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); hasOpened=fileDialog->openLoad( "Load Instrument", - {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.ff", + {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.bnk *.ff", "all files", ".*"}, - "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.ff},.*", + "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.bnk,.ff},.*", workingDirIns, dpiScale ); @@ -1834,8 +1821,47 @@ void FurnaceGUI::processDrags(int dragX, int dragY) { fileName+=fallback; \ } +#define drawOpMask(m) \ + ImGui::PushFont(patFont); \ + ImGui::PushID("om_" #m); \ + if (ImGui::BeginTable("opMaskTable",5,ImGuiTableFlags_Borders|ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_NoHostExtendX)) { \ + ImGui::TableNextRow(); \ + ImGui::TableNextColumn(); \ + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_ACTIVE]); \ + if (ImGui::Selectable(m.note?"C-4##opMaskNote":"---##opMaskNote",m.note,ImGuiSelectableFlags_DontClosePopups)) { \ + m.note=!m.note; \ + } \ + ImGui::PopStyleColor(); \ + ImGui::TableNextColumn(); \ + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INS]); \ + if (ImGui::Selectable(m.ins?"01##opMaskIns":"--##opMaskIns",m.ins,ImGuiSelectableFlags_DontClosePopups)) { \ + m.ins=!m.ins; \ + } \ + ImGui::PopStyleColor(); \ + ImGui::TableNextColumn(); \ + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_VOLUME_MAX]); \ + if (ImGui::Selectable(m.vol?"7F##opMaskVol":"--##opMaskVol",m.vol,ImGuiSelectableFlags_DontClosePopups)) { \ + m.vol=!m.vol; \ + } \ + ImGui::PopStyleColor(); \ + ImGui::TableNextColumn(); \ + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]); \ + if (ImGui::Selectable(m.effect?"04##opMaskEffect":"--##opMaskEffect",m.effect,ImGuiSelectableFlags_DontClosePopups)) { \ + m.effect=!m.effect; \ + } \ + ImGui::TableNextColumn(); \ + if (ImGui::Selectable(m.effectVal?"72##opMaskEffectVal":"--##opMaskEffectVal",m.effectVal,ImGuiSelectableFlags_DontClosePopups)) { \ + m.effectVal=!m.effectVal; \ + } \ + ImGui::PopStyleColor(); \ + ImGui::EndTable(); \ + } \ + ImGui::PopID(); \ + ImGui::PopFont(); + void FurnaceGUI::editOptions(bool topMenu) { char id[4096]; + editOptsVisible=true; if (ImGui::MenuItem("cut",BIND_FOR(GUI_ACTION_PAT_CUT))) doCopy(true); if (ImGui::MenuItem("copy",BIND_FOR(GUI_ACTION_PAT_COPY))) doCopy(false); if (ImGui::MenuItem("paste",BIND_FOR(GUI_ACTION_PAT_PASTE))) doPaste(); @@ -1852,61 +1878,208 @@ void FurnaceGUI::editOptions(bool topMenu) { } ImGui::Separator(); - ImGui::Text("operation mask"); - ImGui::SameLine(); + if (ImGui::BeginMenu("operation mask...")) { + drawOpMask(opMaskDelete); + ImGui::SameLine(); + ImGui::Text("delete"); + drawOpMask(opMaskPullDelete); + ImGui::SameLine(); + ImGui::Text("pull delete"); + + drawOpMask(opMaskInsert); + ImGui::SameLine(); + ImGui::Text("insert"); + + drawOpMask(opMaskPaste); + ImGui::SameLine(); + ImGui::Text("paste"); + + drawOpMask(opMaskTransposeNote); + ImGui::SameLine(); + ImGui::Text("transpose (note)"); + + drawOpMask(opMaskTransposeValue); + ImGui::SameLine(); + ImGui::Text("transpose (value)"); + + drawOpMask(opMaskInterpolate); + ImGui::SameLine(); + ImGui::Text("interpolate"); + + drawOpMask(opMaskFade); + ImGui::SameLine(); + ImGui::Text("fade"); + + drawOpMask(opMaskInvertVal); + ImGui::SameLine(); + ImGui::Text("invert values"); + + drawOpMask(opMaskScale); + ImGui::SameLine(); + ImGui::Text("scale"); + + drawOpMask(opMaskRandomize); + ImGui::SameLine(); + ImGui::Text("randomize"); + + drawOpMask(opMaskFlip); + ImGui::SameLine(); + ImGui::Text("flip"); + + drawOpMask(opMaskCollapseExpand); + ImGui::SameLine(); + ImGui::Text("collapse/expand"); + + ImGui::EndMenu(); + } + + ImGui::Text("input latch"); ImGui::PushFont(patFont); - if (ImGui::BeginTable("opMaskTable",5,ImGuiTableFlags_Borders|ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_NoHostExtendX)) { + if (ImGui::BeginTable("inputLatchTable",5,ImGuiTableFlags_Borders|ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_NoHostExtendX)) { + static char id[64]; ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_ACTIVE]); - if (ImGui::Selectable(opMaskNote?"C-4##opMaskNote":"---##opMaskNote",opMaskNote,ImGuiSelectableFlags_DontClosePopups)) { - opMaskNote=!opMaskNote; - } + ImGui::Text("C-4"); ImGui::PopStyleColor(); ImGui::TableNextColumn(); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INS]); - if (ImGui::Selectable(opMaskIns?"01##opMaskIns":"--##opMaskIns",opMaskIns,ImGuiSelectableFlags_DontClosePopups)) { - opMaskIns=!opMaskIns; + if (latchIns==-2) { + strcpy(id,"&&##LatchIns"); + } else if (latchIns==-1) { + strcpy(id,"..##LatchIns"); + } else { + snprintf(id,63,"%.2x##LatchIns",latchIns&0xff); + } + if (ImGui::Selectable(id,latchTarget==1,ImGuiSelectableFlags_DontClosePopups)) { + latchTarget=1; + latchNibble=false; + } + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + latchIns=-2; + } + if (ImGui::IsItemHovered()) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]); + ImGui::SetTooltip("&&: selected instrument\n..: no instrument"); + ImGui::PopStyleColor(); } ImGui::PopStyleColor(); ImGui::TableNextColumn(); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_VOLUME_MAX]); - if (ImGui::Selectable(opMaskVol?"7F##opMaskVol":"--##opMaskVol",opMaskVol,ImGuiSelectableFlags_DontClosePopups)) { - opMaskVol=!opMaskVol; + if (latchVol==-1) { + strcpy(id,"..##LatchVol"); + } else { + snprintf(id,63,"%.2x##LatchVol",latchVol&0xff); + } + if (ImGui::Selectable(id,latchTarget==2,ImGuiSelectableFlags_DontClosePopups)) { + latchTarget=2; + latchNibble=false; + } + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + latchVol=-1; } ImGui::PopStyleColor(); ImGui::TableNextColumn(); - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]); - if (ImGui::Selectable(opMaskEffect?"04##opMaskEffect":"--##opMaskEffect",opMaskEffect,ImGuiSelectableFlags_DontClosePopups)) { - opMaskEffect=!opMaskEffect; + if (latchEffect==-1) { + strcpy(id,"..##LatchFX"); + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INACTIVE]); + } else { + const unsigned char data=latchEffect; + snprintf(id,63,"%.2x##LatchFX",data); + if (data<0x10) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); + } else if (data<0x20) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); + } else if (data<0x30) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]); + } else if (data<0x48) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); + } else if (data<0x90) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + } else if (data<0xa0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_MISC]); + } else if (data<0xc0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + } else if (data<0xd0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SPEED]); + } else if (data<0xe0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + } else { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[extFxColors[data-0xe0]]); + } + } + + if (ImGui::Selectable(id,latchTarget==3,ImGuiSelectableFlags_DontClosePopups)) { + latchTarget=3; + latchNibble=false; + } + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + latchEffect=-1; } ImGui::TableNextColumn(); - if (ImGui::Selectable(opMaskEffectVal?"72##opMaskEffectVal":"--##opMaskEffectVal",opMaskEffectVal,ImGuiSelectableFlags_DontClosePopups)) { - opMaskEffectVal=!opMaskEffectVal; + if (latchEffectVal==-1) { + strcpy(id,"..##LatchFXV"); + } else { + snprintf(id,63,"%.2x##LatchFXV",latchEffectVal&0xff); + } + if (ImGui::Selectable(id,latchTarget==4,ImGuiSelectableFlags_DontClosePopups)) { + latchTarget=4; + latchNibble=false; + } + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + latchEffectVal=-1; } ImGui::PopStyleColor(); ImGui::EndTable(); } ImGui::PopFont(); - - ImGui::Text("input latch"); - if (ImGui::MenuItem("set latch",BIND_FOR(GUI_ACTION_PAT_LATCH))) { - // TODO + ImGui::SameLine(); + if (ImGui::Button("Set")) { + DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][curOrder],true); + latchIns=pat->data[cursor.y][2]; + latchVol=pat->data[cursor.y][3]; + latchEffect=pat->data[cursor.y][4]; + latchEffectVal=pat->data[cursor.y][5]; + latchTarget=0; + latchNibble=false; + } + ImGui::SameLine(); + if (ImGui::Button("Reset")) { + latchIns=-2; + latchVol=-1; + latchEffect=-1; + latchEffectVal=-1; + latchTarget=0; + latchNibble=false; } ImGui::Separator(); - if (ImGui::MenuItem("note up",BIND_FOR(GUI_ACTION_PAT_NOTE_UP))) doTranspose(1); - if (ImGui::MenuItem("note down",BIND_FOR(GUI_ACTION_PAT_NOTE_DOWN))) doTranspose(-1); - if (ImGui::MenuItem("octave up",BIND_FOR(GUI_ACTION_PAT_OCTAVE_UP))) doTranspose(12); - if (ImGui::MenuItem("octave down",BIND_FOR(GUI_ACTION_PAT_OCTAVE_DOWN))) doTranspose(-12); + if (ImGui::MenuItem("note up",BIND_FOR(GUI_ACTION_PAT_NOTE_UP))) doTranspose(1,opMaskTransposeNote); + if (ImGui::MenuItem("note down",BIND_FOR(GUI_ACTION_PAT_NOTE_DOWN))) doTranspose(-1,opMaskTransposeNote); + if (ImGui::MenuItem("octave up",BIND_FOR(GUI_ACTION_PAT_OCTAVE_UP))) doTranspose(12,opMaskTransposeNote); + if (ImGui::MenuItem("octave down",BIND_FOR(GUI_ACTION_PAT_OCTAVE_DOWN))) doTranspose(-12,opMaskTransposeNote); + ImGui::Separator(); + if (ImGui::MenuItem("values up",BIND_FOR(GUI_ACTION_PAT_VALUE_UP))) doTranspose(1,opMaskTransposeValue); + if (ImGui::MenuItem("values down",BIND_FOR(GUI_ACTION_PAT_VALUE_DOWN))) doTranspose(-1,opMaskTransposeValue); + if (ImGui::MenuItem("values up (+16)",BIND_FOR(GUI_ACTION_PAT_VALUE_UP_COARSE))) doTranspose(16,opMaskTransposeValue); + if (ImGui::MenuItem("values down (-16)",BIND_FOR(GUI_ACTION_PAT_VALUE_DOWN_COARSE))) doTranspose(-16,opMaskTransposeValue); + ImGui::Separator(); + ImGui::Text("transpose"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(120.0f*dpiScale); if (ImGui::InputInt("##TransposeAmount",&transposeAmount,1,1)) { if (transposeAmount<-96) transposeAmount=-96; if (transposeAmount>96) transposeAmount=96; } ImGui::SameLine(); - if (ImGui::Button("Transpose")) { - doTranspose(transposeAmount); + if (ImGui::Button("Notes")) { + doTranspose(transposeAmount,opMaskTransposeNote); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Values")) { + doTranspose(transposeAmount,opMaskTransposeNote); ImGui::CloseCurrentPopup(); } @@ -2021,10 +2194,100 @@ void FurnaceGUI::editOptions(bool topMenu) { } } +int _processEvent(void* instance, SDL_Event* event) { + return ((FurnaceGUI*)instance)->processEvent(event); +} + +int FurnaceGUI::processEvent(SDL_Event* ev) { + if (ev->type==SDL_KEYDOWN) { + if (!ev->key.repeat && latchTarget==0 && !wantCaptureKeyboard && (ev->key.keysym.mod&(~(KMOD_NUM|KMOD_CAPS|KMOD_SCROLL)))==0) { + if (settings.notePreviewBehavior==0) return 1; + switch (curWindow) { + case GUI_WINDOW_SAMPLE_EDIT: + case GUI_WINDOW_SAMPLE_LIST: + try { + int key=noteKeys.at(ev->key.keysym.scancode); + int num=12*curOctave+key; + if (key!=100 && key!=101 && key!=102) { + e->previewSample(curSample,num); + samplePreviewOn=true; + samplePreviewKey=ev->key.keysym.scancode; + samplePreviewNote=num; + } + } catch (std::out_of_range& e) { + } + break; + case GUI_WINDOW_WAVE_LIST: + case GUI_WINDOW_WAVE_EDIT: + try { + int key=noteKeys.at(ev->key.keysym.scancode); + int num=12*curOctave+key; + if (key!=100 && key!=101 && key!=102) { + e->previewWave(curWave,num); + wavePreviewOn=true; + wavePreviewKey=ev->key.keysym.scancode; + wavePreviewNote=num; + } + } catch (std::out_of_range& e) { + } + break; + case GUI_WINDOW_ORDERS: // ignore here + break; + case GUI_WINDOW_PATTERN: + if (settings.notePreviewBehavior==1) { + if (cursor.xFine!=0) break; + } else if (settings.notePreviewBehavior==2) { + if (edit && cursor.xFine!=0) break; + } + // fall-through + default: + try { + int key=noteKeys.at(ev->key.keysym.scancode); + int num=12*curOctave+key; + + if (num<-60) num=-60; // C-(-5) + if (num>119) num=119; // B-9 + + if (key!=100 && key!=101 && key!=102) { + previewNote(cursor.xCoarse,num); + } + } catch (std::out_of_range& e) { + } + break; + } + } + } else if (ev->type==SDL_KEYUP) { + stopPreviewNote(ev->key.keysym.scancode,true); + if (wavePreviewOn) { + if (ev->key.keysym.scancode==wavePreviewKey) { + wavePreviewOn=false; + e->stopWavePreview(); + } + } + if (samplePreviewOn) { + if (ev->key.keysym.scancode==samplePreviewKey) { + samplePreviewOn=false; + e->stopSamplePreview(); + } + } + } + return 1; +} + bool FurnaceGUI::loop() { + SDL_SetEventFilter(_processEvent,this); + while (!quit) { SDL_Event ev; + if (e->isPlaying()) { + WAKE_UP; + } + if (--drawHalt<=0) { + drawHalt=0; + if (settings.powerSave) SDL_WaitEventTimeout(NULL,500); + } while (SDL_PollEvent(&ev)) { + WAKE_UP; ImGui_ImplSDL2_ProcessEvent(&ev); switch (ev.type) { case SDL_MOUSEMOTION: { @@ -2131,23 +2394,7 @@ bool FurnaceGUI::loop() { } break; case SDL_KEYUP: - if (!ImGui::GetIO().WantCaptureKeyboard) { - keyUp(ev); - } else { - stopPreviewNote(ev.key.keysym.scancode,true); - if (wavePreviewOn) { - if (ev.key.keysym.scancode==wavePreviewKey) { - wavePreviewOn=false; - e->stopWavePreview(); - } - } - if (samplePreviewOn) { - if (ev.key.keysym.scancode==samplePreviewKey) { - samplePreviewOn=false; - e->stopSamplePreview(); - } - } - } + // for now break; case SDL_DROPFILE: if (ev.drop.file!=NULL) { @@ -2172,6 +2419,8 @@ bool FurnaceGUI::loop() { break; } } + + wantCaptureKeyboard=ImGui::GetIO().WantCaptureKeyboard; while (true) { midiLock.lock(); @@ -2316,6 +2565,7 @@ bool FurnaceGUI::loop() { ImGui::NewFrame(); curWindow=GUI_WINDOW_NOTHING; + editOptsVisible=false; ImGui::BeginMainMenuBar(); if (ImGui::BeginMenu("file")) { @@ -2467,6 +2717,9 @@ bool FurnaceGUI::loop() { ImGui::EndMenu(); } if (ImGui::BeginMenu("settings")) { + if (ImGui::MenuItem("full screen",BIND_FOR(GUI_ACTION_FULLSCREEN),fullScreen)) { + doAction(GUI_ACTION_FULLSCREEN); + } if (ImGui::MenuItem("lock layout",NULL,lockLayout)) { lockLayout=!lockLayout; } @@ -2512,6 +2765,7 @@ bool FurnaceGUI::loop() { ImGui::EndMenu(); } if (ImGui::BeginMenu("help")) { + if (ImGui::MenuItem("effect list",BIND_FOR(GUI_ACTION_WINDOW_EFFECT_LIST),effectListOpen)) effectListOpen=!effectListOpen; if (ImGui::MenuItem("debug menu",BIND_FOR(GUI_ACTION_WINDOW_DEBUG))) debugOpen=!debugOpen; if (ImGui::MenuItem("panic",BIND_FOR(GUI_ACTION_PANIC))) e->syncReset(); if (ImGui::MenuItem("about...",BIND_FOR(GUI_ACTION_WINDOW_ABOUT))) { @@ -2570,7 +2824,7 @@ bool FurnaceGUI::loop() { default: // effect int actualCursor=((cursor.xFine+1)&(~1)); if (p->data[cursor.y][actualCursor]>-1) { - info=e->getEffectDesc(p->data[cursor.y][actualCursor],cursor.xCoarse); + info=e->getEffectDesc(p->data[cursor.y][actualCursor],cursor.xCoarse,true); hasInfo=true; } break; @@ -2615,6 +2869,7 @@ bool FurnaceGUI::loop() { drawChannels(); drawRegView(); drawLog(); + drawEffectList(); if (inspectorOpen) ImGui::ShowMetricsWindow(&inspectorOpen); @@ -3177,6 +3432,11 @@ bool FurnaceGUI::loop() { willCommit=false; } + if (!editOptsVisible) { + latchTarget=0; + latchNibble=false; + } + if (SDL_GetWindowFlags(sdlWin)&SDL_WINDOW_MINIMIZED) { SDL_Delay(100); } @@ -3223,10 +3483,12 @@ bool FurnaceGUI::init() { channelsOpen=e->getConfBool("channelsOpen",false); regViewOpen=e->getConfBool("regViewOpen",false); logOpen=e->getConfBool("logOpen",false); + effectListOpen=e->getConfBool("effectListOpen",false); tempoView=e->getConfBool("tempoView",true); waveHex=e->getConfBool("waveHex",false); lockLayout=e->getConfBool("lockLayout",false); + fullScreen=e->getConfBool("fullScreen",false); syncSettings(); @@ -3250,7 +3512,7 @@ bool FurnaceGUI::init() { SDL_Init(SDL_INIT_VIDEO); - sdlWin=SDL_CreateWindow("Furnace",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,scrW*dpiScale,scrH*dpiScale,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI); + sdlWin=SDL_CreateWindow("Furnace",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,scrW*dpiScale,scrH*dpiScale,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI|(fullScreen?SDL_WINDOW_FULLSCREEN_DESKTOP:0)); if (sdlWin==NULL) { logE("could not open window! %s",SDL_GetError()); return false; @@ -3261,12 +3523,18 @@ bool FurnaceGUI::init() { SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(sdlWin),&dpiScaleF,NULL,NULL); dpiScale=round(dpiScaleF/96.0f); if (dpiScale<1) dpiScale=1; - if (dpiScale!=1) SDL_SetWindowSize(sdlWin,scrW*dpiScale,scrH*dpiScale); + if (dpiScale!=1) { + if (!fullScreen) { + SDL_SetWindowSize(sdlWin,scrW*dpiScale,scrH*dpiScale); + } + } if (SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(sdlWin),&displaySize)==0) { if (scrW>displaySize.w/dpiScale) scrW=(displaySize.w/dpiScale)-32; if (scrH>displaySize.h/dpiScale) scrH=(displaySize.h/dpiScale)-32; - SDL_SetWindowSize(sdlWin,scrW*dpiScale,scrH*dpiScale); + if (!fullScreen) { + SDL_SetWindowSize(sdlWin,scrW*dpiScale,scrH*dpiScale); + } } } #endif @@ -3386,6 +3654,7 @@ bool FurnaceGUI::finish() { e->setConf("channelsOpen",channelsOpen); e->setConf("regViewOpen",regViewOpen); e->setConf("logOpen",logOpen); + e->setConf("effectListOpen",effectListOpen); // commit last window size e->setConf("lastWindowWidth",scrW); @@ -3394,6 +3663,7 @@ bool FurnaceGUI::finish() { e->setConf("tempoView",tempoView); e->setConf("waveHex",waveHex); e->setConf("lockLayout",lockLayout); + e->setConf("fullScreen",fullScreen); for (int i=0; i definition; @@ -712,10 +730,11 @@ class FurnaceGUI { String mmlString[17]; String mmlStringW; - bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop; - bool displayNew; + bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, wantCaptureKeyboard; + bool displayNew, fullScreen; bool willExport[32]; int vgmExportVersion; + int drawHalt; FurnaceGUIFileDialogs curFileDialog; FurnaceGUIWarnings warnAction; @@ -820,6 +839,9 @@ class FurnaceGUI { int oplStandardWaveNames; int cursorMoveNoScroll; int lowLatency; + int notePreviewBehavior; + int powerSave; + int absorbInsInput; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -892,6 +914,9 @@ class FurnaceGUI { oplStandardWaveNames(0), cursorMoveNoScroll(0), lowLatency(0), + notePreviewBehavior(1), + powerSave(1), + absorbInsInput(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), @@ -903,31 +928,33 @@ class FurnaceGUI { char finalLayoutPath[4096]; int curIns, curWave, curSample, curOctave, curOrder, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan, soloTimeout, orderEditMode, orderCursor; - int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory; + int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory, latchTarget; int wheelX, wheelY; bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen; bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen; bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; - bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen; + bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen; /* there ought to be a better way... bool editControlsDocked, ordersDocked, insListDocked, songInfoDocked, patternDocked, insEditDocked; bool waveListDocked, waveEditDocked, sampleListDocked, sampleEditDocked, aboutDocked, settingsDocked; bool mixerDocked, debugDocked, inspectorDocked, oscDocked, volMeterDocked, statsDocked, compatFlagsDocked; - bool pianoDocked, notesDocked, channelsDocked, regViewDocked; + bool pianoDocked, notesDocked, channelsDocked, regViewDocked, logDocked, effectListDocked; */ SelectionPoint selStart, selEnd, cursor; bool selecting, curNibble, orderNibble, followOrders, followPattern, changeAllOrders; - bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, lockLayout; + bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, lockLayout, editOptsVisible, latchNibble; FurnaceGUIWindows curWindow, nextWindow; float peak[2]; float patChanX[DIV_MAX_CHANS+1]; float patChanSlideY[DIV_MAX_CHANS+1]; const int* nextDesc; - bool opMaskNote, opMaskIns, opMaskVol, opMaskEffect, opMaskEffectVal; + OperationMask opMaskDelete, opMaskPullDelete, opMaskInsert, opMaskPaste, opMaskTransposeNote, opMaskTransposeValue; + OperationMask opMaskInterpolate, opMaskFade, opMaskInvertVal, opMaskScale; + OperationMask opMaskRandomize, opMaskFlip, opMaskCollapseExpand; short latchNote, latchIns, latchVol, latchEffect, latchEffectVal; // bit 31: ctrl @@ -1073,6 +1100,7 @@ class FurnaceGUI { void drawWaveform(unsigned char type, bool opz, const ImVec2& size); void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size); void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, unsigned char egt, unsigned char algOrGlobalSus, float maxTl, float maxArDr, const ImVec2& size, unsigned short instType); + void drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size); void drawSysConf(int i); // these ones offer ctrl-wheel fine value changes. @@ -1092,7 +1120,7 @@ class FurnaceGUI { float calcBPM(int s1, int s2, float hz); - void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache); + void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel); void actualWaveList(); void actualSampleList(); @@ -1121,6 +1149,7 @@ class FurnaceGUI { void drawDebug(); void drawNewSong(); void drawLog(); + void drawEffectList(); void parseKeybinds(); void promptKey(int which); @@ -1156,7 +1185,7 @@ class FurnaceGUI { void doDelete(); void doPullDelete(); void doInsert(); - void doTranspose(int amount); + void doTranspose(int amount, OperationMask& mask); void doCopy(bool cut); void doPaste(PasteMode mode=GUI_PASTE_MODE_NORMAL); void doChangeIns(int ins); @@ -1216,6 +1245,7 @@ class FurnaceGUI { void addScroll(int amount); void setFileName(String name); void runBackupThread(); + int processEvent(SDL_Event* ev); bool loop(); bool finish(); bool init(); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 64135cdb..96ec52c7 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -18,8 +18,8 @@ */ // guiConst: constants used in the GUI like arrays, strings and other stuff -#include "guiConst.h" #include "gui.h" +#include "guiConst.h" #include "../engine/song.h" const int opOrder[4]={ @@ -146,6 +146,60 @@ const char* loopMode[DIV_SAMPLE_LOOPMODE_MAX]={ "Pingpong" }; +const FurnaceGUIColors fxColors[16]={ + GUI_COLOR_PATTERN_EFFECT_MISC, // 00 + GUI_COLOR_PATTERN_EFFECT_PITCH, // 01 + GUI_COLOR_PATTERN_EFFECT_PITCH, // 02 + GUI_COLOR_PATTERN_EFFECT_PITCH, // 03 + GUI_COLOR_PATTERN_EFFECT_PITCH, // 04 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // 05 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // 06 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // 07 + GUI_COLOR_PATTERN_EFFECT_PANNING, // 08 + GUI_COLOR_PATTERN_EFFECT_SPEED, // 09 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // 0A + GUI_COLOR_PATTERN_EFFECT_SONG, // 0B + GUI_COLOR_PATTERN_EFFECT_TIME, // 0C + GUI_COLOR_PATTERN_EFFECT_SONG, // 0D + GUI_COLOR_PATTERN_EFFECT_INVALID, // 0E + GUI_COLOR_PATTERN_EFFECT_SPEED, // 0F +}; + +const FurnaceGUIColors extFxColors[32]={ + GUI_COLOR_PATTERN_EFFECT_MISC, // E0 + GUI_COLOR_PATTERN_EFFECT_PITCH, // E1 + GUI_COLOR_PATTERN_EFFECT_PITCH, // E2 + GUI_COLOR_PATTERN_EFFECT_MISC, // E3 + GUI_COLOR_PATTERN_EFFECT_MISC, // E4 + GUI_COLOR_PATTERN_EFFECT_PITCH, // E5 + GUI_COLOR_PATTERN_EFFECT_INVALID, // E6 + GUI_COLOR_PATTERN_EFFECT_INVALID, // E7 + GUI_COLOR_PATTERN_EFFECT_INVALID, // E8 + GUI_COLOR_PATTERN_EFFECT_INVALID, // E9 + GUI_COLOR_PATTERN_EFFECT_MISC, // EA + GUI_COLOR_PATTERN_EFFECT_MISC, // EB + GUI_COLOR_PATTERN_EFFECT_TIME, // EC + GUI_COLOR_PATTERN_EFFECT_TIME, // ED + GUI_COLOR_PATTERN_EFFECT_SONG, // EE + GUI_COLOR_PATTERN_EFFECT_SONG, // EF + GUI_COLOR_PATTERN_EFFECT_SPEED, // F0 + GUI_COLOR_PATTERN_EFFECT_PITCH, // F1 + GUI_COLOR_PATTERN_EFFECT_PITCH, // F2 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // F3 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // F4 + GUI_COLOR_PATTERN_EFFECT_INVALID, // F5 + GUI_COLOR_PATTERN_EFFECT_INVALID, // F6 + GUI_COLOR_PATTERN_EFFECT_INVALID, // F7 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // F8 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // F9 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // FA + GUI_COLOR_PATTERN_EFFECT_INVALID, // FB + GUI_COLOR_PATTERN_EFFECT_INVALID, // FC + GUI_COLOR_PATTERN_EFFECT_INVALID, // FD + GUI_COLOR_PATTERN_EFFECT_INVALID, // FE + GUI_COLOR_PATTERN_EFFECT_SONG, // FF +}; + #define D FurnaceGUIActionDef #define NOT_AN_ACTION -1 @@ -175,6 +229,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("REPEAT_PATTERN", "Toggle repeat pattern", 0), D("FOLLOW_ORDERS", "Follow orders", 0), D("FOLLOW_PATTERN", "Follow pattern", 0), + D("FULLSCREEN", "Toggle full-screen", SDLK_F11), D("PANIC", "Panic", SDLK_F12), D("WINDOW_EDIT_CONTROLS", "Edit Controls", 0), @@ -200,6 +255,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("WINDOW_CHANNELS", "Channels", 0), D("WINDOW_REGISTER_VIEW", "Register View", 0), D("WINDOW_LOG", "Log Viewer", 0), + D("EFFECT_LIST", "Effect List", 0), D("COLLAPSE_WINDOW", "Collapse/expand current window", 0), D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE), @@ -210,6 +266,10 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("PAT_NOTE_DOWN", "Transpose (-1)", FURKMOD_CMD|SDLK_F1), D("PAT_OCTAVE_UP", "Transpose (+1 octave)", FURKMOD_CMD|SDLK_F4), D("PAT_OCTAVE_DOWN", "Transpose (-1 octave)", FURKMOD_CMD|SDLK_F3), + D("PAT_VALUE_UP", "Increase values (+1)", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_F2), + D("PAT_VALUE_DOWN", "Increase values (-1)", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_F1), + D("PAT_VALUE_UP_COARSE", "Increase values (+16)", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_F4), + D("PAT_VALUE_DOWN_COARSE", "Increase values (-16)", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_F3), D("PAT_SELECT_ALL", "Select all", FURKMOD_CMD|SDLK_a), D("PAT_CUT", "Cut", FURKMOD_CMD|SDLK_x), D("PAT_COPY", "Copy", FURKMOD_CMD|SDLK_c), diff --git a/src/gui/guiConst.h b/src/gui/guiConst.h index 60c16d5e..d84c17a9 100644 --- a/src/gui/guiConst.h +++ b/src/gui/guiConst.h @@ -47,4 +47,6 @@ extern const int availableSystems[]; extern const FurnaceGUIActionDef guiActions[]; extern const FurnaceGUIColorDef guiColors[]; extern const int altValues[24]; -extern const int vgmVersions[6]; \ No newline at end of file +extern const int vgmVersions[6]; +extern const FurnaceGUIColors fxColors[16]; +extern const FurnaceGUIColors extFxColors[32]; \ No newline at end of file diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 2b327d33..7403e820 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -179,6 +179,14 @@ const char* es5506FilterModes[4]={ "HP/K2, HP/K2", "HP/K2, LP/K1", "LP/K2, LP/K2", "LP/K2, LP/K1", }; +const char* panBits[3]={ + "right", "left", NULL +}; + +const char* es5506EnvelopeModes[3]={ + "k1 slowdown", "k2 slowdown", NULL +}; + const char* oneBit[2]={ "on", NULL }; @@ -216,6 +224,12 @@ const char* macroDummyMode[2]={ "Bug" }; +const char* macroFilterMode[3]={ + "Relative", + "Absolute", + "Delta" +}; + String macroHoverNote(int id, float val) { if (val<-60 || val>=120) return "???"; return fmt::sprintf("%d: %s",id,noteNames[(int)val+60]); @@ -231,6 +245,22 @@ String macroHoverLoop(int id, float val) { return ""; } +String macroHoverES5506FilterMode(int id, float val) { + switch (((int)val)&3) { + case 0: + return "HP/K2, HP/K2"; + case 1: + return "HP/K2, LP/K1"; + case 2: + return "LP/K2, LP/K2"; + case 3: + return "LP/K2, LP/K1"; + default: + return "???"; + } + return "???"; +} + String macroLFOWaves(int id, float val) { switch (((int)val)&3) { case 0: @@ -943,7 +973,7 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, ImU32 colorR=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE_RELEASE]); // Relsease triangle ImU32 colorS=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE_SUS_GUIDE]); // Sustain horiz/vert line color ImGui::ItemSize(size,style.FramePadding.y); - if (ImGui::ItemAdd(rect,ImGui::GetID("alg"))) { + if (ImGui::ItemAdd(rect,ImGui::GetID("fmEnv"))) { ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); //Adjust for OPLL global sustain setting @@ -1002,6 +1032,64 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, } } +void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size) { + ImDrawList* dl=ImGui::GetWindowDrawList(); + ImGuiWindow* window=ImGui::GetCurrentWindow(); + + ImVec2 minArea=window->DC.CursorPos; + ImVec2 maxArea=ImVec2( + minArea.x+size.x, + minArea.y+size.y + ); + ImRect rect=ImRect(minArea,maxArea); + ImGuiStyle& style=ImGui::GetStyle(); + ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE]); + //ImU32 colorS=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE_SUS_GUIDE]); // Sustain horiz/vert line color + ImGui::ItemSize(size,style.FramePadding.y); + if (ImGui::ItemAdd(rect,ImGui::GetID("gbEnv"))) { + ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); + + float volY=1.0-((float)vol/15.0); + float lenPos=(sLen>62)?1.0:((float)sLen/384.0); + float envEndPoint=((float)len/7.0)*((float)(dir?(15-vol):vol)/15.0); + + ImVec2 pos1=ImLerp(rect.Min,rect.Max,ImVec2(0.0,volY)); + ImVec2 pos2; + if (dir) { + if (len>0) { + if (lenPos0) { + if (lenPos0 || sLen<63)?((dir && sLen>62)?0.0:1.0):volY)); + + addAALine(dl,pos1,pos2,color); + if (lenPos>=envEndPoint && sLen<63 && dir) { + pos3=ImLerp(rect.Min,rect.Max,ImVec2(lenPos,0.0)); + addAALine(dl,pos2,pos3,color); + ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(lenPos,1.0)); + addAALine(dl,pos3,pos4,color); + } else { + addAALine(dl,pos2,pos3,color); + } + } +} + #define P(x) if (x) { \ MARK_MODIFIED; \ e->notifyInsChange(curIns); \ @@ -1022,10 +1110,11 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, if (ImGui::InputScalar("##IMacroLen_" macroName,ImGuiDataType_U8,¯o.len,&_ONE,&_THREE)) { MARK_MODIFIED \ if (macro.len>127) macro.len=127; \ } \ - if (macroMode) { \ - bool modeVal=macro.mode; \ - if (ImGui::Checkbox("Relative##IMacroMode_" macroName,&modeVal)) { \ - macro.mode=modeVal; \ + if (macroMode && macroModeMax>0) { \ + for (int m=0; m<=macroModeMax; m++) { \ + if (ImGui::RadioButton(displayModeName[m],macro.mode==m)) { \ + macro.mode=m; \ + } \ } \ } \ } \ @@ -1116,10 +1205,11 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, if (ImGui::InputScalar("##IOPMacroLen_" #op macroName,ImGuiDataType_U8,¯o.len,&_ONE,&_THREE)) { MARK_MODIFIED \ if (macro.len>127) macro.len=127; \ } \ - if (macroMode) { \ - bool modeVal=macro.mode; \ - if (ImGui::Checkbox("Relative##IOPMacroMode_" macroName,&modeVal)) { \ - macro.mode=modeVal; \ + if (macroMode && macroModeMax>0) { \ + for (int m=0; m<=macroModeMax; m++) { \ + if (ImGui::RadioButton(displayModeName[m],macro.mode==m)) { \ + macro.mode=m; \ + } \ } \ } \ } \ @@ -1288,6 +1378,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); String insIndex=fmt::sprintf("%.2X",curIns); + ImGui::SetNextItemWidth(72.0f*dpiScale); if (ImGui::BeginCombo("##InsSelect",insIndex.c_str())) { String name; for (size_t i=0; isong.ins.size(); i++) { @@ -2242,6 +2333,8 @@ void FurnaceGUI::drawInsEdit() { goesUp=false; ins->gb.envDir=goesUp; } + + drawGBEnv(ins->gb.envVol,ins->gb.envLen,ins->gb.soundLen,ins->gb.envDir,ImVec2(ImGui::GetContentRegionAvail().x,100.0f*dpiScale)); ImGui::EndTabItem(); } if (ins->type==DIV_INS_C64) if (ImGui::BeginTabItem("C64")) { @@ -2271,10 +2364,47 @@ void FurnaceGUI::drawInsEdit() { } ImGui::PopStyleColor(); - P(CWSliderScalar("Attack",ImGuiDataType_U8,&ins->c64.a,&_ZERO,&_FIFTEEN)); rightClickable - P(CWSliderScalar("Decay",ImGuiDataType_U8,&ins->c64.d,&_ZERO,&_FIFTEEN)); rightClickable - P(CWSliderScalar("Sustain",ImGuiDataType_U8,&ins->c64.s,&_ZERO,&_FIFTEEN)); rightClickable - P(CWSliderScalar("Release",ImGuiDataType_U8,&ins->c64.r,&_ZERO,&_FIFTEEN)); rightClickable + ImVec2 sliderSize=ImVec2(20.0f*dpiScale,128.0*dpiScale); + + if (ImGui::BeginTable("C64EnvParams",5,ImGuiTableFlags_NoHostExtendX)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + CENTER_TEXT("A"); + ImGui::TextUnformatted("A"); + ImGui::TableNextColumn(); + CENTER_TEXT("D"); + ImGui::TextUnformatted("D"); + ImGui::TableNextColumn(); + CENTER_TEXT("S"); + ImGui::TextUnformatted("S"); + ImGui::TableNextColumn(); + CENTER_TEXT("R"); + ImGui::TextUnformatted("R"); + ImGui::TableNextColumn(); + CENTER_TEXT("Envelope"); + ImGui::TextUnformatted("Envelope"); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Attack",sliderSize,ImGuiDataType_U8,&ins->c64.a,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Decay",sliderSize,ImGuiDataType_U8,&ins->c64.d,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Sustain",sliderSize,ImGuiDataType_U8,&ins->c64.s,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Release",sliderSize,ImGuiDataType_U8,&ins->c64.r,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + drawFMEnv(0,16-ins->c64.a,16-ins->c64.d,15-ins->c64.r,15-ins->c64.r,15-ins->c64.s,0,0,0,15,16,ImVec2(ImGui::GetContentRegionAvail().x,sliderSize.y),ins->type); + + ImGui::EndTable(); + } + P(CWSliderScalar("Duty",ImGuiDataType_U16,&ins->c64.duty,&_ZERO,&_FOUR_THOUSAND_NINETY_FIVE)); rightClickable bool ringMod=ins->c64.ringMod; @@ -2340,6 +2470,7 @@ void FurnaceGUI::drawInsEdit() { } ImGui::EndCombo(); } + P(ImGui::Checkbox("Reversed playback",&ins->amiga.reversed)); P(ImGui::Checkbox("Use wavetable (Amiga only)",&ins->amiga.useWave)); if (ins->amiga.useWave) { int len=ins->amiga.waveLen+1; @@ -2769,17 +2900,26 @@ void FurnaceGUI::drawInsEdit() { ex2Max=4095; } if (ins->type==DIV_INS_SAA1099) ex1Max=8; + if (ins->type==DIV_INS_ES5506) { + ex1Max=65535; + ex2Max=65535; + } int panMax=0; + bool panSingle=false; if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_GB || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_VERA) { panMax=1; + panSingle=true; } if (ins->type==DIV_INS_AMIGA) { panMax=127; } - if (ins->type==DIV_INS_X1_010 || ins->type==DIV_INS_PCE) { + if (ins->type==DIV_INS_X1_010 || ins->type==DIV_INS_PCE || ins->type==DIV_INS_MIKEY || ins->type==DIV_INS_SAA1099) { panMax=15; } + if (ins->type==DIV_INS_ES5506) { + panMax=65535; + } if (settings.macroView==0) { // modern view MACRO_BEGIN(28*dpiScale); @@ -2792,6 +2932,8 @@ void FurnaceGUI::drawInsEdit() { NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,true,mikeyFeedbackBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); } else if (ins->type==DIV_INS_C64) { NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); + } else if (ins->type==DIV_INS_ES5506) { + NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,¯oHoverES5506FilterMode,false); } else { NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); } @@ -2800,8 +2942,12 @@ void FurnaceGUI::drawInsEdit() { NORMAL_MACRO(ins->std.waveMacro,0,waveMax,"wave",waveLabel,(bitMode && ins->type!=DIV_INS_PET)?64:160,ins->std.waveMacro.open,bitMode,waveNames,false,NULL,0,0,0,((ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930)?1:0),false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_WAVE],mmlString[3],0,waveMax,NULL,false); } if (panMax>0) { - NORMAL_MACRO(ins->std.panLMacro,0,panMax,"panL","Panning (left)",(31+panMax),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); - NORMAL_MACRO(ins->std.panRMacro,0,panMax,"panR","Panning (right)",(31+panMax),ins->std.panRMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],0,panMax,NULL,false); + if (panSingle) { + NORMAL_MACRO(ins->std.panLMacro,0,2,"panL","Panning",32,ins->std.panLMacro.open,true,panBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); + } else { + NORMAL_MACRO(ins->std.panLMacro,0,panMax,"panL","Panning (left)",MIN(160,(31+panMax)),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); + NORMAL_MACRO(ins->std.panRMacro,0,panMax,"panR","Panning (right)",MIN(160,(31+panMax)),ins->std.panRMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],0,panMax,NULL,false); + } } NORMAL_MACRO(ins->std.pitchMacro,pitchMacroScroll,pitchMacroScroll+160,"pitch","Pitch",160,ins->std.pitchMacro.open,false,NULL,true,&pitchMacroScroll,-2048,2047,0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[15],-2048,2047,NULL,!ins->std.pitchMacro.mode); if (ins->type==DIV_INS_FM || @@ -2814,7 +2960,8 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_OPLL || ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || - ins->type==DIV_INS_SWAN) { + ins->type==DIV_INS_SWAN || + ins->type==DIV_INS_ES5506) { NORMAL_MACRO(ins->std.phaseResetMacro,0,1,"phaseReset","Phase Reset",32,ins->std.phaseResetMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[16],0,1,NULL,false); } if (ex1Max>0) { @@ -2828,6 +2975,8 @@ void FurnaceGUI::drawInsEdit() { NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Waveform len.",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); } else if (ins->type==DIV_INS_FDS) { NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Mod Depth",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + } else if (ins->type==DIV_INS_ES5506) { + NORMAL_MACRO(ins->std.ex1Macro,((ins->std.ex1Macro.mode!=1)?(-ex1Max):0),ex1Max,"ex1","Filter K1",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,true,2,macroFilterMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],((ins->std.ex1Macro.mode!=1)?(-ex1Max):0),ex1Max,NULL,false); } else { NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Duty",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); } @@ -2839,6 +2988,8 @@ void FurnaceGUI::drawInsEdit() { NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Waveform update",64,ins->std.ex2Macro.open,true,n163UpdateBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); } else if (ins->type==DIV_INS_FDS) { NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Mod Speed",160,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + } else if (ins->type==DIV_INS_ES5506) { + NORMAL_MACRO(ins->std.ex2Macro,((ins->std.ex2Macro.mode!=1)?(-ex2Max):0),ex2Max,"ex2","Filter K2",160,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,true,2,macroFilterMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],((ins->std.ex2Macro.mode!=1)?(-ex2Max):0),ex2Max,NULL,false); } else { NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Envelope",ex2Bit?64:160,ins->std.ex2Macro.open,ex2Bit,ayEnvBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); } @@ -2862,7 +3013,15 @@ void FurnaceGUI::drawInsEdit() { NORMAL_MACRO(ins->std.fmsMacro,0,2,"fms","Waveform load",64,ins->std.fmsMacro.open,true,n163UpdateBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[9],0,2,NULL,false); } if (ins->type==DIV_INS_FDS) { - NORMAL_MACRO(ins->std.ex3Macro,0,127,"ex3","Mod Position",160,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false); + NORMAL_MACRO(ins->std.ex3Macro,0,127,"ex3","Mod Position",160,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,127,NULL,false); + } + if (ins->type==DIV_INS_ES5506) { + NORMAL_MACRO(ins->std.ex3Macro,0,511,"ex3","Envelope counter",160,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,511,NULL,false); + NORMAL_MACRO(ins->std.ex4Macro,-128,127,"ex4","Envelope left volume ramp",160,ins->std.ex4Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],-128,127,NULL,false); + NORMAL_MACRO(ins->std.ex5Macro,-128,127,"ex5","Envelope right volume ramp",160,ins->std.ex5Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[8],-128,127,NULL,false); + NORMAL_MACRO(ins->std.ex6Macro,-128,127,"ex6","Envelope K1 ramp",160,ins->std.ex6Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[9],-128,127,NULL,false); + NORMAL_MACRO(ins->std.ex7Macro,-128,127,"ex7","Envelope K2 ramp",160,ins->std.ex7Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[10],-128,127,NULL,false); + NORMAL_MACRO(ins->std.ex8Macro,0,2,"ex8","Envelope mode",64,ins->std.ex8Macro.open,true,es5506EnvelopeModes,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[11],0,2,NULL,false); } MACRO_END; diff --git a/src/gui/log.cpp b/src/gui/log.cpp index 7dc6bf8d..b5f4d355 100644 --- a/src/gui/log.cpp +++ b/src/gui/log.cpp @@ -92,5 +92,6 @@ void FurnaceGUI::drawLog() { ImGui::EndTable(); } } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_LOG; ImGui::End(); } diff --git a/src/gui/osc.cpp b/src/gui/osc.cpp index 266dfa3b..030a84e9 100644 --- a/src/gui/osc.cpp +++ b/src/gui/osc.cpp @@ -49,12 +49,19 @@ void FurnaceGUI::readOsc() { for (int i=0; i<512; i++) { int pos=(readPos+(i*total/512))&0x7fff; oscValues[i]=(e->oscBuf[0][pos]+e->oscBuf[1][pos])*0.5f; + if (oscValues[i]>0.001f || oscValues[i]<-0.001f) { + WAKE_UP; + } } float peakDecay=0.05f*60.0f*ImGui::GetIO().DeltaTime; for (int i=0; i<2; i++) { peak[i]*=1.0-peakDecay; - if (peak[i]<0.0001) peak[i]=0.0; + if (peak[i]<0.0001) { + peak[i]=0.0; + } else { + WAKE_UP; + } float newPeak=peak[i]; for (int j=0; j -const FurnaceGUIColors fxColors[16]={ - GUI_COLOR_PATTERN_EFFECT_MISC, // 00 - GUI_COLOR_PATTERN_EFFECT_PITCH, // 01 - GUI_COLOR_PATTERN_EFFECT_PITCH, // 02 - GUI_COLOR_PATTERN_EFFECT_PITCH, // 03 - GUI_COLOR_PATTERN_EFFECT_PITCH, // 04 - GUI_COLOR_PATTERN_EFFECT_VOLUME, // 05 - GUI_COLOR_PATTERN_EFFECT_VOLUME, // 06 - GUI_COLOR_PATTERN_EFFECT_VOLUME, // 07 - GUI_COLOR_PATTERN_EFFECT_PANNING, // 08 - GUI_COLOR_PATTERN_EFFECT_SPEED, // 09 - GUI_COLOR_PATTERN_EFFECT_VOLUME, // 0A - GUI_COLOR_PATTERN_EFFECT_SONG, // 0B - GUI_COLOR_PATTERN_EFFECT_TIME, // 0C - GUI_COLOR_PATTERN_EFFECT_SONG, // 0D - GUI_COLOR_PATTERN_EFFECT_INVALID, // 0E - GUI_COLOR_PATTERN_EFFECT_SPEED, // 0F -}; - -const FurnaceGUIColors extFxColors[32]={ - GUI_COLOR_PATTERN_EFFECT_MISC, // E0 - GUI_COLOR_PATTERN_EFFECT_PITCH, // E1 - GUI_COLOR_PATTERN_EFFECT_PITCH, // E2 - GUI_COLOR_PATTERN_EFFECT_MISC, // E3 - GUI_COLOR_PATTERN_EFFECT_MISC, // E4 - GUI_COLOR_PATTERN_EFFECT_PITCH, // E5 - GUI_COLOR_PATTERN_EFFECT_INVALID, // E6 - GUI_COLOR_PATTERN_EFFECT_INVALID, // E7 - GUI_COLOR_PATTERN_EFFECT_INVALID, // E8 - GUI_COLOR_PATTERN_EFFECT_INVALID, // E9 - GUI_COLOR_PATTERN_EFFECT_MISC, // EA - GUI_COLOR_PATTERN_EFFECT_MISC, // EB - GUI_COLOR_PATTERN_EFFECT_TIME, // EC - GUI_COLOR_PATTERN_EFFECT_TIME, // ED - GUI_COLOR_PATTERN_EFFECT_SONG, // EE - GUI_COLOR_PATTERN_EFFECT_SONG, // EF - GUI_COLOR_PATTERN_EFFECT_SPEED, // F0 - GUI_COLOR_PATTERN_EFFECT_PITCH, // F1 - GUI_COLOR_PATTERN_EFFECT_PITCH, // F2 - GUI_COLOR_PATTERN_EFFECT_VOLUME, // F3 - GUI_COLOR_PATTERN_EFFECT_VOLUME, // F4 - GUI_COLOR_PATTERN_EFFECT_INVALID, // F5 - GUI_COLOR_PATTERN_EFFECT_INVALID, // F6 - GUI_COLOR_PATTERN_EFFECT_INVALID, // F7 - GUI_COLOR_PATTERN_EFFECT_VOLUME, // F8 - GUI_COLOR_PATTERN_EFFECT_VOLUME, // F9 - GUI_COLOR_PATTERN_EFFECT_VOLUME, // FA - GUI_COLOR_PATTERN_EFFECT_INVALID, // FB - GUI_COLOR_PATTERN_EFFECT_INVALID, // FC - GUI_COLOR_PATTERN_EFFECT_INVALID, // FD - GUI_COLOR_PATTERN_EFFECT_INVALID, // FE - GUI_COLOR_PATTERN_EFFECT_SONG, // FF -}; - inline float randRange(float min, float max) { return min+((float)rand()/(float)RAND_MAX)*(max-min); } // draw a pattern row -inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache) { +inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel) { static char id[32]; - bool selectedRow=(i>=sel1.y && i<=sel2.y); + bool selectedRow=(i>=sel1.y && i<=sel2.y && !inhibitSel); ImGui::TableNextRow(0,lineHeight); ImGui::TableNextColumn(); float cursorPosY=ImGui::GetCursorPos().y-ImGui::GetScrollY(); @@ -613,7 +559,7 @@ void FurnaceGUI::drawPattern() { patCache[i]=e->song.pat[i].getPattern(e->song.orders.ord[i][ord-1],true); } for (int i=0; isong.patLen+i-dummyRows+1,e->isPlaying(),lineHeight,chans,ord-1,patCache); + patternRow(e->song.patLen+i-dummyRows+1,e->isPlaying(),lineHeight,chans,ord-1,patCache,true); } } else { for (int i=0; isong.pat[i].getPattern(e->song.orders.ord[i][ord],true); } for (int i=0; isong.patLen; i++) { - patternRow(i,e->isPlaying(),lineHeight,chans,ord,patCache); + patternRow(i,e->isPlaying(),lineHeight,chans,ord,patCache,false); } // next pattern ImGui::BeginDisabled(); @@ -636,7 +582,7 @@ void FurnaceGUI::drawPattern() { patCache[i]=e->song.pat[i].getPattern(e->song.orders.ord[i][ord+1],true); } for (int i=0; i<=dummyRows; i++) { - patternRow(i,e->isPlaying(),lineHeight,chans,ord+1,patCache); + patternRow(i,e->isPlaying(),lineHeight,chans,ord+1,patCache,true); } } else { for (int i=0; i<=dummyRows; i++) { @@ -890,6 +836,7 @@ void FurnaceGUI::drawPattern() { // particle simulation ImDrawList* fdl=ImGui::GetForegroundDrawList(); + if (!particles.empty()) WAKE_UP; for (size_t i=0; igetConfInt("oplStandardWaveNames",0); settings.cursorMoveNoScroll=e->getConfInt("cursorMoveNoScroll",0); settings.lowLatency=e->getConfInt("lowLatency",0); + settings.notePreviewBehavior=e->getConfInt("notePreviewBehavior",1); + settings.powerSave=e->getConfInt("powerSave",POWER_SAVE_DEFAULT); + settings.absorbInsInput=e->getConfInt("absorbInsInput",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1670,6 +1712,9 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.oplStandardWaveNames,0,1); clampSetting(settings.cursorMoveNoScroll,0,1); clampSetting(settings.lowLatency,0,1); + clampSetting(settings.notePreviewBehavior,0,3); + clampSetting(settings.powerSave,0,1); + clampSetting(settings.absorbInsInput,0,1); // keybinds for (int i=0; isetConf("oplStandardWaveNames",settings.oplStandardWaveNames); e->setConf("cursorMoveNoScroll",settings.cursorMoveNoScroll); e->setConf("lowLatency",settings.lowLatency); + e->setConf("notePreviewBehavior",settings.notePreviewBehavior); + e->setConf("powerSave",settings.powerSave); + e->setConf("absorbInsInput",settings.absorbInsInput); // colors for (int i=0; iSetFileStyle(IGFD_FileStyleByExtension,".vgi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".s3i",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".sbi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bnk",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ff",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); diff --git a/src/main.cpp b/src/main.cpp index f8c749f7..4a3e9d0e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -239,6 +239,8 @@ void initParams() { params.push_back(TAParam("W","warranty",false,pWarranty,"","view warranty disclaimer.")); } +// TODO: CoInitializeEx on Windows? +// TODO: add crash log int main(int argc, char** argv) { initLog(); #if !(defined(__APPLE__) || defined(_WIN32))