From 1dd02dd3897b731a483d88e7ac3a05a8807c068c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 19 Aug 2023 18:52:08 -0500 Subject: [PATCH 01/19] fix samples going away during audio export issue #1387 --- src/engine/engine.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index fea380863..4c1df9f7a 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -824,6 +824,7 @@ void DivEngine::runExportThread() { quitDispatch(); initDispatch(true); + renderSamples(); switch (exportMode) { case DIV_EXPORT_MODE_ONE: { @@ -837,6 +838,9 @@ void DivEngine::runExportThread() { sf=sfWrap.doOpen(exportPath.c_str(),SFM_WRITE,&si); if (sf==NULL) { logE("could not open file for writing! (%s)",sf_strerror(NULL)); + quitDispatch(); + initDispatch(false); + renderSamples(); exporting=false; return; } @@ -1151,6 +1155,7 @@ void DivEngine::runExportThread() { quitDispatch(); initDispatch(false); + renderSamples(); stopExport=false; } #else From 396f51c75fc4e9cf9f027adbeb41634286d68783 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 19 Aug 2023 19:19:14 -0500 Subject: [PATCH 02/19] ExtCh: fix 18xx being a post effect --- src/engine/sysDef.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 12cf2d489..9fc6ab9d1 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -449,7 +449,12 @@ void DivEngine::registerSystems() { {0x30, {DIV_CMD_FM_HARD_RESET, "30xx: Toggle hard envelope reset on new notes"}}, }; - EffectHandlerMap fmOPN2EffectHandlerMap(fmEffectHandlerMap); + EffectHandlerMap fmExtChEffectHandlerMap(fmEffectHandlerMap); + fmExtChEffectHandlerMap.insert({ + {0x18, {DIV_CMD_FM_EXTCH, "18xx: Toggle extended channel 3 mode"}}, + }); + + EffectHandlerMap fmOPN2EffectHandlerMap(fmExtChEffectHandlerMap); fmOPN2EffectHandlerMap.insert({ {0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (LEGACY)"}}, {0xdf, {DIV_CMD_SAMPLE_DIR, "DFxx: Set sample playback direction (0: normal; 1: reverse)"}}, @@ -522,7 +527,6 @@ void DivEngine::registerSystems() { fmOPNPostEffectHandlerMap.insert({ {0x10, {DIV_CMD_FM_LFO, "10xy: Setup LFO (x: enable; y: speed)"}}, - {0x18, {DIV_CMD_FM_EXTCH, "18xx: Toggle extended channel 3 mode"}}, {0x55, {DIV_CMD_FM_SSG, "55xy: Set SSG envelope (x: operator from 1 to 4 (0 for all ops); y: 0-7 on, 8 off)", effectOpVal<4>, effectValAnd<15>}}, }); EffectHandlerMap fmOPN2PostEffectHandlerMap(fmOPNPostEffectHandlerMap); @@ -769,7 +773,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA}, {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNAPostEffectHandlerMap ); @@ -1028,7 +1032,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY}, {}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNPostEffectHandlerMap ); @@ -1040,7 +1044,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_NOISE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY}, {}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNPostEffectHandlerMap ); @@ -1064,7 +1068,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMB}, {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNAPostEffectHandlerMap ); @@ -1076,7 +1080,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMB}, {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNAPostEffectHandlerMap ); @@ -1368,7 +1372,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMB}, {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNAPostEffectHandlerMap ); @@ -1380,7 +1384,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMB}, {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNAPostEffectHandlerMap ); @@ -1457,7 +1461,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMB}, {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNAPostEffectHandlerMap ); @@ -1469,7 +1473,7 @@ void DivEngine::registerSystems() { {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMB}, {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, - fmEffectHandlerMap, + fmExtChEffectHandlerMap, fmOPNAPostEffectHandlerMap ); From 993c5904e29a7a862d942d07d8f3ec68079147c6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 19 Aug 2023 23:30:35 -0500 Subject: [PATCH 03/19] C140: fix possible crash --- src/engine/platform/c140.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/engine/platform/c140.cpp b/src/engine/platform/c140.cpp index e5de79488..c9e83338c 100644 --- a/src/engine/platform/c140.cpp +++ b/src/engine/platform/c140.cpp @@ -449,6 +449,7 @@ void DivPlatformC140::renderSamples(int sysID) { if (memPos+length>=(getSampleMemCapacity())) { if (s->depth==DIV_SAMPLE_DEPTH_MULAW) { for (unsigned int i=0; i<(getSampleMemCapacity())-memPos; i++) { + if (i>=s->lengthMuLaw) break; unsigned char x=s->dataMuLaw[i]^0xff; if (x&0x80) x^=15; unsigned char c140Mu=(x&0x80)|((x&15)<<3)|((x&0x70)>>4); @@ -461,6 +462,7 @@ void DivPlatformC140::renderSamples(int sysID) { } else { if (s->depth==DIV_SAMPLE_DEPTH_MULAW) { for (unsigned int i=0; i=s->lengthMuLaw) break; unsigned char x=s->dataMuLaw[i]^0xff; if (x&0x80) x^=15; unsigned char c140Mu=(x&0x80)|((x&15)<<3)|((x&0x70)>>4); From f3167d2fc9a1a1bc1fed53806ac6df5b1e0a875d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 19 Aug 2023 23:39:27 -0500 Subject: [PATCH 04/19] C140: fix possible overflow --- src/engine/platform/sound/c140.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/sound/c140.c b/src/engine/platform/sound/c140.c index 3d48c1300..9cdc4ecb1 100644 --- a/src/engine/platform/sound/c140.c +++ b/src/engine/platform/sound/c140.c @@ -5,7 +5,7 @@ MODIFIED Namco C140 sound emulator - MODIFIED VERSION by cam900 -MODIFICATION by tildearrow - adds muting function +MODIFICATION by tildearrow - adds muting function and fixes overflow THIS IS NOT THE ORIGINAL VERSION - you can find the original one in commit 72d04777c013988ed8cf6da27c62a9d784a59dff @@ -99,8 +99,8 @@ void c140_voice_tick(struct c140_t *c140, const unsigned char v, const int cycle s1 = c140->mulaw[(s1 >> 8) & 0xff]; s2 = c140->mulaw[(s2 >> 8) & 0xff]; } - // interpolate - signed int sample = s1 + (((voice->frac) * (s2 - s1)) >> 16); + // interpolate (originally was >>16, but I had to reduce it to 15 to prevent overflow) + signed int sample = s1 + (((voice->frac >> 1) * (s2 - s1)) >> 15); voice->lout = sample * voice->lvol; voice->rout = sample * voice->rvol; } From e3eea680d5e56deb09c57708cc38642bfe146f2c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 19 Aug 2023 23:52:13 -0500 Subject: [PATCH 05/19] ExtCh: panning macro issue #1382 --- src/engine/platform/genesisext.cpp | 11 +++++++++++ src/engine/platform/ym2608ext.cpp | 11 +++++++++++ src/engine/platform/ym2610bext.cpp | 11 +++++++++++ src/engine/platform/ym2610ext.cpp | 11 +++++++++++ 4 files changed, 44 insertions(+) diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index b743a02ba..e6a9cd148 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -559,6 +559,17 @@ void DivPlatformGenesisExt::tick(bool sysTick) { rWrite(0x22,lfoValue); } + if (opChan[i].std.panL.had) { + opChan[i].pan=opChan[i].std.panL.val&3; + if (parent->song.sharedExtStat) { + for (int j=0; j<4; j++) { + if (i==j) continue; + opChan[j].pan=opChan[i].pan; + } + } + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + // param macros unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]]; DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]]; diff --git a/src/engine/platform/ym2608ext.cpp b/src/engine/platform/ym2608ext.cpp index a2e0f25a5..3bc14a39f 100644 --- a/src/engine/platform/ym2608ext.cpp +++ b/src/engine/platform/ym2608ext.cpp @@ -508,6 +508,17 @@ void DivPlatformYM2608Ext::tick(bool sysTick) { rWrite(0x22,lfoValue); } + if (opChan[i].std.panL.had) { + opChan[i].pan=opChan[i].std.panL.val&3; + if (parent->song.sharedExtStat) { + for (int j=0; j<4; j++) { + if (i==j) continue; + opChan[j].pan=opChan[i].pan; + } + } + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + // param macros unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]]; diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 7f38830b3..48bb24fd1 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -504,6 +504,17 @@ void DivPlatformYM2610BExt::tick(bool sysTick) { rWrite(0x22,lfoValue); } + if (opChan[i].std.panL.had) { + opChan[i].pan=opChan[i].std.panL.val&3; + if (parent->song.sharedExtStat) { + for (int j=0; j<4; j++) { + if (i==j) continue; + opChan[j].pan=opChan[i].pan; + } + } + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + // param macros unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[i]]; DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[i]]; diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 9f84aa72c..3c18a6e4c 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -504,6 +504,17 @@ void DivPlatformYM2610Ext::tick(bool sysTick) { rWrite(0x22,lfoValue); } + if (opChan[i].std.panL.had) { + opChan[i].pan=opChan[i].std.panL.val&3; + if (parent->song.sharedExtStat) { + for (int j=0; j<4; j++) { + if (i==j) continue; + opChan[j].pan=opChan[i].pan; + } + } + rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } + // param macros unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[i]]; DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[i]]; From 7479296d5451b67a9e8bed8d070affa1eb658e4e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 20 Aug 2023 00:25:01 -0500 Subject: [PATCH 06/19] UPDATE FMT TO 10.1.0 -> **PLEASE READ!** <- after pulling this commit, type: ``` git submodule update extern/fmt ``` --- extern/fmt | 2 +- src/log.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/extern/fmt b/extern/fmt index afbcf1e8e..e57ca2e36 160000 --- a/extern/fmt +++ b/extern/fmt @@ -1 +1 @@ -Subproject commit afbcf1e8eafc5d7f27e29c7397f22521eaa33fac +Subproject commit e57ca2e3685b160617d3d95fcd9e789c4e06ca88 diff --git a/src/log.cpp b/src/log.cpp index 9cfabe901..2fbf24dd3 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -97,7 +97,11 @@ int writeLog(int level, const char* msg, fmt::printf_args args) { time_t thisMakesNoSense=time(NULL); int pos=(logPosition.fetch_add(1))&TA_LOG_MASK; +#if FMT_VERSION >= 100100 + logEntries[pos].text.assign(fmt::vsprintf(fmt::basic_string_view(msg),args)); +#else logEntries[pos].text.assign(fmt::vsprintf(msg,args)); +#endif // why do I have to pass a pointer // can't I just pass the time_t directly?! #ifdef _WIN32 From 5720e5670ddba644f69f8c4dd0ba926e950dc3fa Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 20 Aug 2023 00:46:17 -0500 Subject: [PATCH 07/19] AAAAAAAAAAAA --- src/gui/fileDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/fileDialog.cpp b/src/gui/fileDialog.cpp index b9ec5f69f..17d30a40f 100644 --- a/src/gui/fileDialog.cpp +++ b/src/gui/fileDialog.cpp @@ -69,7 +69,7 @@ void _nfdThread(const NFDState state, std::atomic* ok, std::vector (*errorOutput)=true; break; default: - logE("NFD unknown return code %d!\n",ret); + logE("NFD unknown return code %d!\n",(int)ret); break; } (*ok)=true; From 9814d7dd8814742a4af7e054c897228b45720ae2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 20 Aug 2023 01:41:01 -0500 Subject: [PATCH 08/19] GUI: why isn't there a wave reverse option yet --- src/gui/waveEdit.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/gui/waveEdit.cpp b/src/gui/waveEdit.cpp index 3905b7614..de7cee606 100644 --- a/src/gui/waveEdit.cpp +++ b/src/gui/waveEdit.cpp @@ -1093,7 +1093,7 @@ void FurnaceGUI::drawWaveEdit() { MARK_MODIFIED; }); } - if (ImGui::Button("Invert",buttonSize)) { + if (ImGui::Button("Invert",buttonSizeHalf)) { e->lockEngine([this,wave]() { for (int i=0; ilen; i++) { wave->data[i]=wave->max-wave->data[i]; @@ -1101,6 +1101,18 @@ void FurnaceGUI::drawWaveEdit() { MARK_MODIFIED; }); } + ImGui::SameLine(); + if (ImGui::Button("Reverse",buttonSizeHalf)) { + e->lockEngine([this,wave]() { + int origData[256]; + memcpy(origData,wave->data,wave->len*sizeof(int)); + + for (int i=0; ilen; i++) { + wave->data[i]=origData[wave->len-1-i]; + } + MARK_MODIFIED; + }); + } if (ImGui::Button("Half",buttonSizeHalf)) { int origData[256]; From 9397f95499da8811091af00e90a340877211db43 Mon Sep 17 00:00:00 2001 From: Electric Keet Date: Sun, 20 Aug 2023 14:39:03 -0700 Subject: [PATCH 09/19] Vertical label alignment for macros. I don't know how I missed these. I use them constantly! --- src/gui/insEdit.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index e6056d036..7c80e4df6 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1568,6 +1568,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Bottom"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -1590,6 +1591,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Attack"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -1609,6 +1611,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Hold"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -1628,6 +1631,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Decay"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -1650,6 +1654,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail ImGui::TableNextColumn(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Release"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -1671,6 +1676,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Bottom"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -1693,6 +1699,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail ImGui::TableNextRow(); ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Speed"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -1711,6 +1718,7 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail } rightClickable ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Shape"); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); From 6faf80b5c6b99b8909659fbb37fef5e745e4e684 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 20 Aug 2023 19:07:29 -0500 Subject: [PATCH 10/19] update readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index f4d4af3c3..e097be826 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,10 @@ for other operating systems, you may [build the source](#developer-info). - Ricoh RF5C68 used in Sega CD and FM Towns - OKI MSM6258 and MSM6295 - Konami K007232 + - Konami K053260 - Irem GA20 - Ensoniq ES5506 + - Namco C140 - wavetable chips: - HuC6280 used in PC Engine - Konami Bubble System WSG @@ -73,6 +75,7 @@ for other operating systems, you may [build the source](#developer-info). - QuadTone engine - Pokémon Mini - Commodore PET + - TED used in Commodore Plus/4 - Casio PV-1000 - TIA used in Atari 2600 - POKEY used in Atari 8-bit computers From 91d7157b5d99959d9d50d9fb9fe4f2d658035040 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 20 Aug 2023 19:07:38 -0500 Subject: [PATCH 11/19] GUI: Amiga rate limit --- src/gui/sampleEdit.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 25a4aa3b0..02d0aed58 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -293,6 +293,9 @@ void FurnaceGUI::drawSampleEdit() { if (sample->samples>131070) { SAMPLE_WARN(warnLength,"Amiga: maximum sample length is 131070"); } + if (dispatch!=NULL) { + MAX_RATE("Amiga",31250.0); + } break; case DIV_SYSTEM_SEGAPCM: case DIV_SYSTEM_SEGAPCM_COMPAT: From 80a9677b0e2f083b2aad3673ef14ccca4ba821ae Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 20 Aug 2023 19:11:02 -0500 Subject: [PATCH 12/19] update readme again --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e097be826..08ccfc85c 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ for other operating systems, you may [build the source](#developer-info). # quick references - **discussion**: see the [Discussions](https://github.com/tildearrow/furnace/discussions) section, the [official Revolt](https://rvlt.gg/GRPS6tmc) or the [official Discord server](https://discord.gg/EfrwT2wq7z). -- **help**: check out the [documentation](doc/README.md). it's about 80% complete. +- **help**: check out the [documentation](doc/README.md). it's about 90% complete. ## packages From d74172680fce804c456ddbc3bf12f8609ce0b0c8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 20 Aug 2023 20:18:27 -0500 Subject: [PATCH 13/19] GUI: prepare FM preview for the other OPs --- src/gui/fmPreview.cpp | 66 ++++++++++++++++++++++++++++++++++--------- src/gui/gui.cpp | 4 +++ src/gui/gui.h | 11 +++++++- src/gui/insEdit.cpp | 42 +++++++++++++++++++++------ 4 files changed, 99 insertions(+), 24 deletions(-) diff --git a/src/gui/fmPreview.cpp b/src/gui/fmPreview.cpp index eaf449ed9..9e9ba6a10 100644 --- a/src/gui/fmPreview.cpp +++ b/src/gui/fmPreview.cpp @@ -20,8 +20,12 @@ #define _USE_MATH_DEFINES #include "gui.h" #include "../../extern/opn/ym3438.h" +#include "../../extern/opm/opm.h" +#include "../../extern/opl/opl3.h" +#include "../../extern/Nuked-OPLL/opll.h" +#include "../engine/platform/sound/ymfm/ymfm_opz.h" -#define FM_WRITE(addr,val) \ +#define OPN_WRITE(addr,val) \ OPN2_Write((ym3438_t*)fmPreviewOPN,0,(addr)); \ do { \ OPN2_Clock((ym3438_t*)fmPreviewOPN,out); \ @@ -35,7 +39,7 @@ const unsigned char dtTableFMP[8]={ 7,6,5,0,1,2,3,4 }; -void FurnaceGUI::renderFMPreview(const DivInstrumentFM& params, int pos) { +void FurnaceGUI::renderFMPreviewOPN(const DivInstrumentFM& params, int pos) { if (fmPreviewOPN==NULL) { fmPreviewOPN=new ym3438_t; } @@ -57,19 +61,19 @@ void FurnaceGUI::renderFMPreview(const DivInstrumentFM& params, int pos) { for (int i=0; i<4; i++) { const DivInstrumentFM::Operator& op=params.op[i]; unsigned short baseAddr=i*4; - FM_WRITE(baseAddr+0x40,op.tl); - FM_WRITE(baseAddr+0x30,(op.mult&15)|(dtTableFMP[op.dt&7]<<4)); - FM_WRITE(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); - FM_WRITE(baseAddr+0x60,(op.dr&31)|(op.am<<7)); - FM_WRITE(baseAddr+0x70,op.d2r&31); - FM_WRITE(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); - FM_WRITE(baseAddr+0x90,op.ssgEnv&15); + OPN_WRITE(baseAddr+0x40,op.tl); + OPN_WRITE(baseAddr+0x30,(op.mult&15)|(dtTableFMP[op.dt&7]<<4)); + OPN_WRITE(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); + OPN_WRITE(baseAddr+0x60,(op.dr&31)|(op.am<<7)); + OPN_WRITE(baseAddr+0x70,op.d2r&31); + OPN_WRITE(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); + OPN_WRITE(baseAddr+0x90,op.ssgEnv&15); } - FM_WRITE(0xb0,(params.alg&7)|((params.fb&7)<<3)); - FM_WRITE(0xb4,0xc0|(params.fms&7)|((params.ams&3)<<4)); - FM_WRITE(0xa4,mult0?0x1c:0x14); // frequency - FM_WRITE(0xa0,0); - FM_WRITE(0x28,0xf0); // key on + OPN_WRITE(0xb0,(params.alg&7)|((params.fb&7)<<3)); + OPN_WRITE(0xb4,0xc0|(params.fms&7)|((params.ams&3)<<4)); + OPN_WRITE(0xa4,mult0?0x1c:0x14); // frequency + OPN_WRITE(0xa0,0); + OPN_WRITE(0x28,0xf0); // key on } // render @@ -84,3 +88,37 @@ void FurnaceGUI::renderFMPreview(const DivInstrumentFM& params, int pos) { fmPreview[i]=aOut; } } + +void FurnaceGUI::renderFMPreviewOPM(const DivInstrumentFM& params, int pos) { +} + +void FurnaceGUI::renderFMPreviewOPLL(const DivInstrumentFM& params, int pos) { +} + +void FurnaceGUI::renderFMPreviewOPL(const DivInstrumentFM& params, int pos) { +} + +void FurnaceGUI::renderFMPreviewOPZ(const DivInstrumentFM& params, int pos) { +} + +void FurnaceGUI::renderFMPreview(const DivInstrument* ins, int pos) { + switch (ins->type) { + case DIV_INS_FM: + renderFMPreviewOPN(ins->fm,pos); + break; + case DIV_INS_OPM: + renderFMPreviewOPM(ins->fm,pos); + break; + case DIV_INS_OPLL: + renderFMPreviewOPLL(ins->fm,pos); + break; + case DIV_INS_OPL: + renderFMPreviewOPL(ins->fm,pos); + break; + case DIV_INS_OPZ: + renderFMPreviewOPZ(ins->fm,pos); + break; + default: + break; + } +} diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index e1cf93acc..5ec643da6 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -6872,6 +6872,10 @@ FurnaceGUI::FurnaceGUI(): fmPreviewOn(false), fmPreviewPaused(false), fmPreviewOPN(NULL), + fmPreviewOPM(NULL), + fmPreviewOPL(NULL), + fmPreviewOPLL(NULL), + fmPreviewOPZ(NULL), editString(NULL), pendingRawSampleDepth(8), pendingRawSampleChannels(1), diff --git a/src/gui/gui.h b/src/gui/gui.h index 42750bb12..3edbda1b3 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1349,6 +1349,10 @@ class FurnaceGUI { short fmPreview[FM_PREVIEW_SIZE]; bool updateFMPreview, fmPreviewOn, fmPreviewPaused; void* fmPreviewOPN; + void* fmPreviewOPM; + void* fmPreviewOPL; + void* fmPreviewOPLL; + void* fmPreviewOPZ; String* editString; String pendingRawSample; @@ -2137,7 +2141,12 @@ class FurnaceGUI { bool drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange, bool fromMenu=false); void kvsConfig(DivInstrument* ins); void drawFMPreview(const ImVec2& size); - void renderFMPreview(const DivInstrumentFM& params, int pos=0); + void renderFMPreview(const DivInstrument* ins, int pos=0); + void renderFMPreviewOPN(const DivInstrumentFM& params, int pos=0); + void renderFMPreviewOPM(const DivInstrumentFM& params, int pos=0); + void renderFMPreviewOPLL(const DivInstrumentFM& params, int pos=0); + void renderFMPreviewOPL(const DivInstrumentFM& params, int pos=0); + void renderFMPreviewOPZ(const DivInstrumentFM& params, int pos=0); // these ones offer ctrl-wheel fine value changes. bool CWSliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format=NULL, ImGuiSliderFlags flags=0); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 7c80e4df6..1878e8da3 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1284,7 +1284,7 @@ inline bool enBit30(const int val) { void FurnaceGUI::kvsConfig(DivInstrument* ins) { - if (ins->type==DIV_INS_FM && fmPreviewOn) { + if (fmPreviewOn) { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("left click to restart\nmiddle click to pause\nright click to see algorithm"); } @@ -1299,10 +1299,10 @@ void FurnaceGUI::kvsConfig(DivInstrument* ins) { ImGui::SetTooltip("left click to configure TL scaling\nright click to see FM preview"); } } - if (ImGui::IsItemClicked(ImGuiMouseButton_Right) && ins->type==DIV_INS_FM) { + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { fmPreviewOn=!fmPreviewOn; } - if (!fmPreviewOn || ins->type!=DIV_INS_FM) { + if (!fmPreviewOn) { int opCount=4; if (ins->type==DIV_INS_OPLL) opCount=2; if (ins->type==DIV_INS_OPL) opCount=(ins->fm.ops==4)?4:2; @@ -2289,7 +2289,7 @@ void FurnaceGUI::drawInsEdit() { } else { DivInstrument* ins=e->song.ins[curIns]; if (updateFMPreview) { - renderFMPreview(ins->fm); + renderFMPreview(ins); updateFMPreview=false; } if (settings.insEditColorize) { @@ -2477,10 +2477,10 @@ void FurnaceGUI::drawInsEdit() { P(CWSliderScalar(FM_NAME(FM_ALG),ImGuiDataType_U8,&ins->fm.alg,&_ZERO,&_SEVEN)); rightClickable P(CWSliderScalar(FM_NAME(FM_AMS),ImGuiDataType_U8,&ins->fm.ams,&_ZERO,&_THREE)); rightClickable ImGui::TableNextColumn(); - if (ins->type==DIV_INS_FM && fmPreviewOn) { + if (fmPreviewOn) { drawFMPreview(ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); if (!fmPreviewPaused) { - renderFMPreview(ins->fm,1); + renderFMPreview(ins,1); WAKE_UP; } } else { @@ -2498,7 +2498,15 @@ void FurnaceGUI::drawInsEdit() { P(CWSliderScalar(FM_NAME(FM_AMS),ImGuiDataType_U8,&ins->fm.ams,&_ZERO,&_THREE)); rightClickable P(CWSliderScalar(FM_NAME(FM_AMS2),ImGuiDataType_U8,&ins->fm.ams2,&_ZERO,&_THREE)); rightClickable ImGui::TableNextColumn(); - drawAlgorithm(ins->fm.alg,FM_ALGS_4OP,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + if (fmPreviewOn) { + drawFMPreview(ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + if (!fmPreviewPaused) { + renderFMPreview(ins,1); + WAKE_UP; + } + } else { + drawAlgorithm(ins->fm.alg,FM_ALGS_4OP,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + } kvsConfig(ins); if (ImGui::Button("Request from TX81Z")) { @@ -2532,7 +2540,15 @@ void FurnaceGUI::drawInsEdit() { } } ImGui::TableNextColumn(); - drawAlgorithm(ins->fm.alg&algMax,fourOp?FM_ALGS_4OP_OPL:FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + if (fmPreviewOn) { + drawFMPreview(ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + if (!fmPreviewPaused) { + renderFMPreview(ins,1); + WAKE_UP; + } + } else { + drawAlgorithm(ins->fm.alg&algMax,fourOp?FM_ALGS_4OP_OPL:FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + } kvsConfig(ins); break; } @@ -2557,7 +2573,15 @@ void FurnaceGUI::drawInsEdit() { } ImGui::EndDisabled(); ImGui::TableNextColumn(); - drawAlgorithm(0,FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,24.0*dpiScale)); + if (fmPreviewOn) { + drawFMPreview(ImVec2(ImGui::GetContentRegionAvail().x,24.0*dpiScale)); + if (!fmPreviewPaused) { + renderFMPreview(ins,1); + WAKE_UP; + } + } else { + drawAlgorithm(0,FM_ALGS_2OP_OPL,ImVec2(ImGui::GetContentRegionAvail().x,24.0*dpiScale)); + } ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); From 33d932f548fce01f38f7541eb1fe791931cf98bf Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Aug 2023 04:30:49 -0500 Subject: [PATCH 14/19] PCM DAC: fix osc buf issue #1393 --- src/engine/platform/pcmdac.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/pcmdac.cpp b/src/engine/platform/pcmdac.cpp index 3a00e431a..940efb3e7 100644 --- a/src/engine/platform/pcmdac.cpp +++ b/src/engine/platform/pcmdac.cpp @@ -229,7 +229,7 @@ void DivPlatformPCMDAC::acquire(short** buf, size_t len) { } else { output=output*chan[0].vol*chan[0].envVol/16384; } - oscBuf->data[oscBuf->needle++]=output>>1; + oscBuf->data[oscBuf->needle++]=((output>>depthScale)<>1; if (outStereo) { buf[0][h]=((output*chan[0].panL)>>(depthScale+8))<>(depthScale+8))< Date: Mon, 21 Aug 2023 16:02:44 +0200 Subject: [PATCH 15/19] Add AY hw env tutorial --- doc/9-guides/README.md | 3 ++- doc/9-guides/envelope.md | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 doc/9-guides/envelope.md diff --git a/doc/9-guides/README.md b/doc/9-guides/README.md index aa6d5cbf6..3919779ab 100644 --- a/doc/9-guides/README.md +++ b/doc/9-guides/README.md @@ -4,4 +4,5 @@ here is a small collection of useful tricks and techniques to really make Furnac - [using samples with limited playback rates](limited-samples.md) - [choosing emulation cores](emulation-cores.md) -- [guide on using OPLL patch macro](opllswitching.md) \ No newline at end of file +- [guide on using OPLL patch macro](opllswitching.md) +- [guide on using AY/SAA hardware envelope](envelope.md) \ No newline at end of file diff --git a/doc/9-guides/envelope.md b/doc/9-guides/envelope.md new file mode 100644 index 000000000..ec9fc1249 --- /dev/null +++ b/doc/9-guides/envelope.md @@ -0,0 +1,27 @@ +# AY-3-8910/8930/SAA1099 envelope guide + +AY-3-8910 programmable sound generator, aside of normal 4-bit volume control, has an hardware volume envelope - a feature that allows you for defining shape of volume envelope at arbibrary speed, according to 8 preset envelope shapes. One may think, what is any upside of hardware envelope? Well, it's somewhat independent of tone/noise generators, and it goes so high in frequency, it can be used melodically! This guide explains how to abuse AY/SAA envelope. + +## AY-3-8910/AY8930 + +going into instrument editor, first set the waveform macro value to `envelope`. This will disable any output, but don't worry. Then, go to `Envelope` macro and select `enable`. You will hear a very high-pitched squeak. This is because you must set envelope period - the frequency at which hardware envelope runs. You can do it in two ways: +- either via 23xx and 24xx effects (envelope coarse and fine period) or... +- 29xx auto-envelope period effect and macros + +Auto-envelope works via numerator and denominator. In general, the higher the numerator, the higher the envelope pitch. The higher the denominator, the lower the envelope pitch. Why there are both of these? Because, envelope generator might be used to mask the tone output (i.e. affect the square wave as well). To do it, set the waveform macro values to both square and envelope. Then, the higher the denominator value, then the lower the envelope pitch relative to the square wave output, analogously the numerator. With square + envelope setting, a lot of wild, detuned, synth instruments can do made. + +Back to the hardware envelope itself. Depending of the `Envelope` macro value, different envelope shapes can be obtained. The most basic one, at 8 is a sawtooth wave. The `direction` value will invert the envelope, producing the reverse sawtooth. The `alternate` value produces an interesting pseudo-triangular wave, similiar to halved sine. That one can also be reversed. `Hold` option disables the envelope. + +WARNING: the envelope pitch resolution is fairly low, at high pitched it will be detuned. Hence, it was used mostly for bass. +WARNING: there is only one hardware envelope generator. So, you cant use two pitches/two waveforms at once. + +## SAA1099 + +SAA envelope works a bit differently, It doesn't have its own pitch, it reles on a channel 2/5 pitch. It also has much more parameters than AY envelope. To use it: go to waveform macro, and set it to 0 (unless you want to have sqaure wave mask). Then, set up an envelope macro: tuen on enabled, loop and, depending on a desired shape, cut and direction. Resolution will give you higher pitch range than on AY. +Then lay two notes in pattern editor: the one in channel 2 will control the envelope pitch, the one in channel 3 can be any note you wish, its just to enable the envelope output. + +## examples + +- [Demoscene-type Beat by Duccinator](https://www.youtube.com/watch?v=qcBgmpPrlUA) +- [Philips SAA1099 Test by Duccinator](https://www.youtube.com/watch?v=IBh2gr09zjs) +- [Touhou Kaikidan: Mystic Square title theme by ZUN](https://www.youtube.com/watch?v=tUKei7Pz0Fw) /rare instance of AY envelope used for drums, it can be used to mask the noise generator output too \ No newline at end of file From 6ba8527cb60227c25b3305d22a7469d413e94e3b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Aug 2023 14:14:09 -0500 Subject: [PATCH 16/19] fix some threading issues --- src/engine/engine.cpp | 46 +++++++++++++++++++++++++++++++++---------- src/engine/engine.h | 3 +++ src/gui/gui.cpp | 1 + 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 4c1df9f7a..f3484b902 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -822,10 +822,6 @@ void DivEngine::runExportThread() { size_t curFadeOutSample=0; bool isFadingOut=false; - quitDispatch(); - initDispatch(true); - renderSamples(); - switch (exportMode) { case DIV_EXPORT_MODE_ONE: { SNDFILE* sf; @@ -838,9 +834,6 @@ void DivEngine::runExportThread() { sf=sfWrap.doOpen(exportPath.c_str(),SFM_WRITE,&si); if (sf==NULL) { logE("could not open file for writing! (%s)",sf_strerror(NULL)); - quitDispatch(); - initDispatch(false); - renderSamples(); exporting=false; return; } @@ -1153,9 +1146,6 @@ void DivEngine::runExportThread() { } } - quitDispatch(); - initDispatch(false); - renderSamples(); stopExport=false; } #else @@ -1163,6 +1153,11 @@ void DivEngine::runExportThread() { } #endif +bool DivEngine::shallSwitchCores() { + // TODO: detect whether we should + return true; +} + bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode, double fadeOutTime) { #ifndef HAVE_SNDFILE logE("Furnace was not compiled with libsndfile. cannot export!"); @@ -1192,6 +1187,20 @@ bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode, } else { remainingLoops=-1; } + + if (shallSwitchCores()) { + bool isMutedBefore[DIV_MAX_CHANS]; + memcpy(isMutedBefore,isMuted,DIV_MAX_CHANS*sizeof(bool)); + quitDispatch(); + initDispatch(true); + renderSamplesP(); + for (int i=0; iisExporting()) { + e->finishAudioFile(); ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); From f5a881917f35a08b1e34ae459f320f8ddae247ff Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Aug 2023 14:17:53 -0500 Subject: [PATCH 17/19] fix --- src/engine/engine.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index f3484b902..7c572acdc 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1194,9 +1194,9 @@ bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode, quitDispatch(); initDispatch(true); renderSamplesP(); - for (int i=0; i Date: Mon, 21 Aug 2023 14:25:22 -0500 Subject: [PATCH 18/19] fix hang --- src/engine/engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 7c572acdc..de13e7750 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1215,8 +1215,8 @@ void DivEngine::waitAudioFile() { bool DivEngine::haltAudioFile() { stopExport=true; - waitAudioFile(); stop(); + waitAudioFile(); finishAudioFile(); return true; } From 510bcfb56d51cc0ff9d685b8e3db0215475b1943 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Aug 2023 14:30:08 -0500 Subject: [PATCH 19/19] re-enable DX11 on 32-bit Windows build --- CMakeLists.txt | 14 +++++--------- scripts/release-win32.sh | 2 +- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 85fa2b354..cb5586a97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -757,15 +757,11 @@ endif() if (WITH_RENDER_DX11) if (WIN32) - if (SUPPORT_XP) - message(FATAL_ERROR "SUPPORT_XP is on. cannot enable DirectX 11 backend.") - else() - list(APPEND GUI_SOURCES src/gui/render/renderDX11.cpp) - list(APPEND GUI_SOURCES extern/imgui_patched/backends/imgui_impl_dx11.cpp) - list(APPEND DEPENDENCIES_DEFINES HAVE_RENDER_DX11) - list(APPEND DEPENDENCIES_LIBRARIES d3d11) - message(STATUS "UI render backend: DirectX 11") - endif() + list(APPEND GUI_SOURCES src/gui/render/renderDX11.cpp) + list(APPEND GUI_SOURCES extern/imgui_patched/backends/imgui_impl_dx11.cpp) + list(APPEND DEPENDENCIES_DEFINES HAVE_RENDER_DX11) + list(APPEND DEPENDENCIES_LIBRARIES d3d11) + message(STATUS "UI render backend: DirectX 11") else() message(FATAL_ERROR "DirectX 11 render backend only for Windows!") endif() diff --git a/scripts/release-win32.sh b/scripts/release-win32.sh index fc056f5ec..21595dc4b 100755 --- a/scripts/release-win32.sh +++ b/scripts/release-win32.sh @@ -15,7 +15,7 @@ fi cd win32build # TODO: potential Arch-ism? -i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Werror" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=ON -DWITH_RENDER_DX11=OFF .. || exit 1 +i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Werror" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=ON -DWITH_RENDER_DX11=ON .. || exit 1 make -j8 || exit 1 cd ..