diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 980062769..29079891d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ defaults: shell: bash env: - BUILD_TYPE: Debug + BUILD_TYPE: RelWithDebInfo jobs: build: @@ -20,11 +20,11 @@ jobs: config: - { name: 'Windows MSVC x86', os: windows-latest, compiler: msvc, arch: x86 } - { name: 'Windows MSVC x86_64', os: windows-latest, compiler: msvc, arch: x86_64 } - ##- { name: 'Windows MinGW x86', os: ubuntu-20.04, compiler: mingw, arch: x86 } - ##- { name: 'Windows MinGW x86_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 } + #- { name: 'Windows MinGW x86', os: ubuntu-20.04, compiler: mingw, arch: x86 } + #- { name: 'Windows MinGW x86_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 } - { name: 'macOS x86_64', os: macos-latest, arch: x86_64 } - { name: 'macOS ARM', os: macos-latest, arch: arm64 } - ##- { name: 'Linux x86_64', os: ubuntu-18.04, arch: x86_64 } + - { name: 'Linux x86_64', os: ubuntu-18.04, arch: x86_64 } #- { name: 'Linux ARM', os: ubuntu-18.04, arch: armhf } fail-fast: false @@ -278,6 +278,9 @@ jobs: cp -v ../LICENSE LICENSE.txt cp -v ../README.md README.txt cp -vr ../{papers,demos,instruments} ../${binPath}/furnace.exe ./ + if [ '${{ matrix.config.compiler }}' == 'msvc' ]; then + cp -v ../${binPath}/furnace.pdb ./ + fi sha256sum ../${binPath}/furnace.exe > checksum.txt popd diff --git a/CMakeLists.txt b/CMakeLists.txt index 9af0ba741..f994fe8bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -265,6 +265,9 @@ if (USE_SDL2) if (MSVC) list(APPEND DEPENDENCIES_COMPILE_OPTIONS "/DHAVE_LIBC") endif() + if (WIN32) + list(APPEND DEPENDENCIES_LIBRARIES SDL2main) + endif() message(STATUS "Using vendored SDL2") endif() else() @@ -593,6 +596,7 @@ src/gui/editControls.cpp src/gui/effectList.cpp src/gui/findReplace.cpp src/gui/gradient.cpp +src/gui/grooves.cpp src/gui/insEdit.cpp src/gui/log.cpp src/gui/mixer.cpp @@ -610,6 +614,7 @@ src/gui/scaling.cpp src/gui/settings.cpp src/gui/songInfo.cpp src/gui/songNotes.cpp +src/gui/speed.cpp src/gui/spoiler.cpp src/gui/stats.cpp src/gui/subSongs.cpp diff --git a/README.md b/README.md index 4da5e5933..baabcd03c 100644 --- a/README.md +++ b/README.md @@ -282,17 +282,13 @@ two possibilities: - the recommended way is by creating the "Sample" type instrument and assigning a sample to it. - otherwise you may employ the DefleMask-compatible method, using `17xx` effect. -> my .dmf song sounds very odd at a certain point +> my .dmf song sounds odd at a certain point -file a bug report. use the Issues page. it's probably another playback inaccuracy. - -> my .dmf song sounds correct, but it doesn't in DefleMask - -file a bug report **here**. it still is a playback inaccuracy. +Furnace's .dmf compatibility isn't perfect and it's mostly because DefleMask does things different. > my song sounds terrible after saving as .dmf! -the DefleMask format has several limitations. save in Furnace song format instead (.fur). +you should only save as .dmf if you're really sure, because the DefleMask format has several limitations. save in Furnace song format instead (.fur). > how do I solo channels? @@ -301,7 +297,7 @@ right click on the channel name. --- # footnotes -copyright (C) 2021-2022 tildearrow and contributors. +copyright (C) 2021-2023 tildearrow and contributors. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. @@ -310,4 +306,4 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -despite the fact this program works with the .dmf file format, it is NOT affiliated with Delek or DefleMask in any way, nor it is a replacement for the original program. +despite the fact this program works with the .dmf, .dmp and .dmw file formats (besides its native .fur format), it is NOT affiliated with Delek or DefleMask in any way, nor it is a replacement for the original program. diff --git a/demos/arcade/Salamander_Starfield.fur b/demos/arcade/Salamander_Starfield.fur new file mode 100644 index 000000000..be2690a5e Binary files /dev/null and b/demos/arcade/Salamander_Starfield.fur differ diff --git a/demos/arcade/iji_tor_SegaPCM.fur b/demos/arcade/iji_tor_SegaPCM.fur deleted file mode 100644 index 84a6ac5b3..000000000 Binary files a/demos/arcade/iji_tor_SegaPCM.fur and /dev/null differ diff --git a/demos/arcade/iji_tor_segaxboard.fur b/demos/arcade/iji_tor_segaxboard.fur new file mode 100644 index 000000000..602382bc9 Binary files /dev/null and b/demos/arcade/iji_tor_segaxboard.fur differ diff --git a/demos/genesis/Plok_Beach.fur b/demos/genesis/Plok_Beach.fur new file mode 100644 index 000000000..3c2fd8288 Binary files /dev/null and b/demos/genesis/Plok_Beach.fur differ diff --git a/demos/misc/Wicked_Express_X68000.fur b/demos/misc/Wicked_Express_X68000.fur new file mode 100644 index 000000000..b5ef6bdc0 Binary files /dev/null and b/demos/misc/Wicked_Express_X68000.fur differ diff --git a/demos/msx/Gyruss_Stage_2.fur b/demos/msx/Gyruss_Stage_2.fur new file mode 100644 index 000000000..9b901f0e1 Binary files /dev/null and b/demos/msx/Gyruss_Stage_2.fur differ diff --git a/demos/nes/Super_Space_Invaders_Title.fur b/demos/nes/Super_Space_Invaders_Title.fur new file mode 100644 index 000000000..fe964b0e9 Binary files /dev/null and b/demos/nes/Super_Space_Invaders_Title.fur differ diff --git a/demos/nes/the_best-1990.fur b/demos/nes/the_best-1990.fur new file mode 100644 index 000000000..c5b62f15f Binary files /dev/null and b/demos/nes/the_best-1990.fur differ diff --git a/demos/nes/turtle_byte.fur b/demos/nes/turtle_byte.fur new file mode 100644 index 000000000..b097ebc90 Binary files /dev/null and b/demos/nes/turtle_byte.fur differ diff --git a/demos/opl/e3m2_opl3.fur b/demos/opl/e3m2_opl3.fur new file mode 100644 index 000000000..63d4751ec Binary files /dev/null and b/demos/opl/e3m2_opl3.fur differ diff --git a/demos/pc98/Blue_Nebula.fur b/demos/pc98/Blue_Nebula.fur new file mode 100644 index 000000000..546331691 Binary files /dev/null and b/demos/pc98/Blue_Nebula.fur differ diff --git a/demos/pce/Bad_Dudes_Greased_Lightning.fur b/demos/pce/Bad_Dudes_Greased_Lightning.fur new file mode 100644 index 000000000..98209900e Binary files /dev/null and b/demos/pce/Bad_Dudes_Greased_Lightning.fur differ diff --git a/demos/snes/amalgam.fur b/demos/snes/amalgam.fur new file mode 100644 index 000000000..44e15b257 Binary files /dev/null and b/demos/snes/amalgam.fur differ diff --git a/demos/specs2/Tim_Follin.fur b/demos/specs2/Tim_Follin.fur new file mode 100644 index 000000000..6eade6c27 Binary files /dev/null and b/demos/specs2/Tim_Follin.fur differ diff --git a/extern/Nuked-OPLL/opll.c b/extern/Nuked-OPLL/opll.c index bd59709ab..e160ce05e 100644 --- a/extern/Nuked-OPLL/opll.c +++ b/extern/Nuked-OPLL/opll.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 Nuke.YKT + * Copyright (C) 2019-2023 Nuke.YKT * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -17,7 +17,7 @@ * siliconpr0n.org(digshadow, John McMaster): * VRC VII decap and die shot. * - * version: 1.0.1 + * version: 1.0.2 */ #include @@ -132,7 +132,7 @@ static const opll_patch_t patch_ds1001[opll_patch_max] = { { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x01, 0x00 },{ 0x00, 0x00 },{ 0x0c, 0x00 },{ 0x08, 0x00 },{ 0x0a, 0x00 },{ 0x07, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x05, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x00 },{ 0x08, 0x00 },{ 0x05, 0x00 },{ 0x09, 0x00 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0f },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x0d } }, - { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x08 } }, + { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x04 },{ 0x00, 0x08 } }, { 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0a },{ 0x00, 0x0a },{ 0x00, 0x05 },{ 0x00, 0x05 } } }; @@ -1018,6 +1018,9 @@ static void OPLL_Operator(opll_t *chip) { } } + if (!(chip->rm_enable & 0x80)) + routput = 0; + chip->ch_out = ismod1 ? routput : (output>>3); if (!ismod1) { diff --git a/extern/nfd-modified/src/nfd_win.cpp b/extern/nfd-modified/src/nfd_win.cpp index b4fa5a5fa..e4342243c 100644 --- a/extern/nfd-modified/src/nfd_win.cpp +++ b/extern/nfd-modified/src/nfd_win.cpp @@ -30,6 +30,9 @@ // hack I know #include "../../../src/utfutils.h" +// hack 2... +#include "../../../src/ta-log.h" + class NFDWinEvents: public IFileDialogEvents { nfdselcallback_t selCallback; size_t refCount; @@ -38,21 +41,21 @@ class NFDWinEvents: public IFileDialogEvents { } public: IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) { - printf("QueryInterface called DAMN IT\n"); + logV("%p: QueryInterface called DAMN IT",(const void*)this); *ppv=NULL; return E_NOTIMPL; } IFACEMETHODIMP_(ULONG) AddRef() { - printf("AddRef() called\n"); + logV("%p: AddRef() called",(const void*)this); return InterlockedIncrement(&refCount); } IFACEMETHODIMP_(ULONG) Release() { - printf("Release() called\n"); + logV("%p: Release() called",(const void*)this); LONG ret=InterlockedDecrement(&refCount); if (ret==0) { - printf("Destroying the final object.\n"); + logV("%p: Destroying the final object.",(const void*)this); delete this; } return ret; @@ -67,30 +70,40 @@ class NFDWinEvents: public IFileDialogEvents { IFACEMETHODIMP OnSelectionChange(IFileDialog* dialog) { // Get the file name + logV("%p: OnSelectionChange() called",(const void*)this); ::IShellItem *shellItem(NULL); + logV("%p: GetCurrentSelection",(const void*)this); HRESULT result = dialog->GetCurrentSelection(&shellItem); if ( !SUCCEEDED(result) ) { - printf("failure!\n"); + logV("%p: failure!",(const void*)this); return S_OK; } wchar_t *filePath(NULL); result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); if ( !SUCCEEDED(result) ) { - printf("GDN failure!\n"); + logV("%p: GDN failure!",(const void*)this); shellItem->Release(); return S_OK; } std::string utf8FilePath=utf16To8(filePath); - if (selCallback!=NULL) selCallback(utf8FilePath.c_str()); - printf("I got you for a value of %s\n",utf8FilePath.c_str()); + if (selCallback!=NULL) { + logV("%p: calling back.",(const void*)this); + selCallback(utf8FilePath.c_str()); + logV("%p: end of callback",(const void*)this); + } else { + logV("%p: no callback.",(const void*)this); + } + logV("%p: I got you for a value of %s",(const void*)this,utf8FilePath.c_str()); shellItem->Release(); + logV("%p: shellItem->Release()",(const void*)this); return S_OK; } NFDWinEvents(nfdselcallback_t callback): selCallback(callback), refCount(1) { + logV("%p: CONSTRUCT!",(const void*)this); } }; diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp index 464ce5bc9..13eb70838 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.cpp @@ -8,11 +8,11 @@ #include "k005289.hpp" -void k005289_core::tick() +void k005289_core::tick(const unsigned int cycles) { for (timer_t &elem : m_timer) { - elem.tick(); + elem.tick(cycles); } } @@ -24,12 +24,12 @@ void k005289_core::reset() } } -void k005289_core::timer_t::tick() -{ - if (bitfield(++m_counter, 0, 12) == 0) +void k005289_core::timer_t::tick(const unsigned int cycles) { + m_counter-=cycles; + while (m_counter < 0) { m_addr = bitfield(m_addr + 1, 0, 5); - m_counter = m_freq; + m_counter += 0x1000-(m_freq&0xfff); } } diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp index 2c2b0715a..981af8897 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/k005289/k005289.hpp @@ -32,7 +32,7 @@ class k005289_core : public vgsound_emu_core // internal state void reset(); - void tick(); + void tick(const unsigned int cycles=1); // accessors // Replace current frequency to lastest loaded pitch @@ -63,7 +63,7 @@ class k005289_core : public vgsound_emu_core // internal state void reset(); - void tick(); + void tick(const unsigned int cycles); // accessors // TG1/2 pin diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/scc/README.md b/extern/vgsound_emu-modified/vgsound_emu/src/scc/README.md index 2a66bd8cb..6ce9914d5 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/scc/README.md +++ b/extern/vgsound_emu-modified/vgsound_emu/src/scc/README.md @@ -1,5 +1,9 @@ # Konami SCC +## modified + +the emulation core has been modified for optimization. + ## Summary - 5 voice wavetable diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp index 07cbb60e8..8c88699b0 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.cpp @@ -10,17 +10,17 @@ #include "scc.hpp" // shared SCC features -void scc_core::tick() +void scc_core::tick(const int cycles) { m_out = 0; for (auto &elem : m_voice) { - elem.tick(); + elem.tick(cycles); m_out += elem.out(); } } -void scc_core::voice_t::tick() +void scc_core::voice_t::tick(const int cycles) { if (m_pitch >= 9) // or voice is halted { @@ -28,23 +28,27 @@ void scc_core::voice_t::tick() const u16 temp = m_counter; if (m_host.m_test.freq_4bit()) // 4 bit frequency mode { - m_counter = (m_counter & ~0x0ff) | (bitfield(bitfield(m_counter, 0, 8) - 1, 0, 8) << 0); - m_counter = (m_counter & ~0xf00) | (bitfield(bitfield(m_counter, 8, 4) - 1, 0, 4) << 8); + m_counter = (m_counter & ~0x0ff) | (bitfield(bitfield(m_counter, 0, 8) - cycles, 0, 8) << 0); + m_counter = (m_counter & ~0xf00) | (bitfield(bitfield(m_counter, 8, 4) - cycles, 0, 4) << 8); } else { - m_counter = bitfield(m_counter - 1, 0, 12); + m_counter = bitfield(m_counter - cycles, 0, 12); } // handle counter carry - const bool carry = m_host.m_test.freq_8bit() + const bool carry = (tempm_pitch) { + m_addr = bitfield(m_addr + 1, 0, 5); + m_counter+=m_pitch-1; + } } } // get output diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp index 10b153e7f..2c58ea885 100644 --- a/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp +++ b/extern/vgsound_emu-modified/vgsound_emu/src/scc/scc.hpp @@ -41,7 +41,7 @@ class scc_core : public vgsound_emu_core // internal state void reset(); - void tick(); + void tick(const int cycles=1); // accessors inline void reset_addr() { m_addr = 0; } @@ -151,7 +151,7 @@ class scc_core : public vgsound_emu_core // internal state virtual void reset(); - void tick(); + void tick(const int cycles=1); // getters inline s32 out() { return m_out; } // output to DA0...DA10 pin diff --git a/instruments/OPL/2-OP OPL3 Slap Bass.fui b/instruments/OPL/2-OP OPL3 Slap Bass.fui new file mode 100644 index 000000000..04e8aadfd Binary files /dev/null and b/instruments/OPL/2-OP OPL3 Slap Bass.fui differ diff --git a/papers/doc/3-pattern/effects.md b/papers/doc/3-pattern/effects.md index 9965d22c4..9d784d380 100644 --- a/papers/doc/3-pattern/effects.md +++ b/papers/doc/3-pattern/effects.md @@ -57,9 +57,6 @@ however, effects are continuous, which means you only need to type it once and t - `EDxx`: delay note by `xx` ticks. - `EExx`: send external command. - this effect is currently incomplete. -- `EFxx`: add or subtract global pitch. - - this effect is rather weird. use with caution. - - `80` is center. - `F0xx`: change song Hz by BPM value. - `F1xx`: single tick slide up. - `F2xx`: single tick slide down. @@ -135,7 +132,7 @@ ex | FM | OPM | OPZ | OPLL | AY-3-8910 | AY8930 | Lynx W | | LFO Shape | LFO Shape | Patch | Waveform | Waveform | | Waveform | Waveform | Waveform | Waveform | Waveform | Waveform | | | | Waveform | | 1 | | AMD | AMD | | | Duty | | FilterMode | Envelope | EnvMode | WaveLen | Mod Depth | Cutoff | Filter K1 | ClockDiv | EchoFeedback | Special | GroupAtk | 2 | | PMD | PMD | | Envelope | Envelope | | Resonance | | Envelope | WaveUpdate | Mod Speed | Resonance | Filter K2 | | Echo Length | Gain | GroupDec | - 3 | | LFO Speed | LFO Speed | | AutoEnvNum | AutoEnvNum | | Special | | AutoEnvNum | WaveLoad W | | Control | Env Count | | | | Noise | + 3 | LFOSpd | LFO Speed | LFO Speed | | AutoEnvNum | AutoEnvNum | | Special | | AutoEnvNum | WaveLoad W | | Control | Env Count | | | | Noise | A | ALG | ALG | ALG | | AutoEnvDen | AutoEnvDen | | | | AutoEnvDen | WaveLoad P | | | Control | | | | | B | FB | FB | FB | | | Noise AND | | | | | WaveLoad L | | | | | | | | C | FMS | FMS | FMS | | | Noise OR | | | | | WaveLoad T | | | | | | | | @@ -144,4 +141,4 @@ ex | FM | OPM | OPZ | OPLL | AY-3-8910 | AY8930 | Lynx 5 | | | AMD2 | | | | | | | | | | | EnvRampR | | | | | 6 | | | PMD2 | | | | | | | | | | | EnvRampK1 | | | | | 7 | | | LFO2Speed | | | | | | | | | | | EnvRampK2 | | | | | - 8 | | | LFO2Shape | | | | | | | | | | | Env Mode | | | | | \ No newline at end of file + 8 | | | LFO2Shape | | | | | | | | | | | Env Mode | | | | | diff --git a/papers/doc/7-systems/opz.md b/papers/doc/7-systems/opz.md index c7952a55b..555df814f 100644 --- a/papers/doc/7-systems/opz.md +++ b/papers/doc/7-systems/opz.md @@ -45,6 +45,16 @@ no plans have been made for TX81Z MIDI passthrough, because: - `1Bxx`: set attack of operator 2. - `1Cxx`: set attack of operator 3. - `1Dxx`: set attack of operator 4. +- `1Exx`: set LFO AM depth. +- `1Fxx`: set LFO PM depth. +- `24xx`: set LFO 2 speed. +- `25xx`: set LFO 2 waveform. `xx` may be one of the following: + - `00`: saw + - `01`: square + - `02`: triangle + - `03`: noise +- `26xx`: set LFO 2 AM depth. +- `27xx`: set LFO 2 PM depth. - `28xy`: set reverb of operator. - `x` is the operator (1-4). a value of 0 means "all operators". - `y` is the value. diff --git a/papers/format.md b/papers/format.md index 6522760e8..3f93f451d 100644 --- a/papers/format.md +++ b/papers/format.md @@ -32,6 +32,9 @@ these fields are 0 in format versions prior to 100 (0.6pre1). the format versions are: +- 139: Furnace dev139 +- 138: Furnace dev138 +- 137: Furnace dev137 - 136: Furnace dev136 - 135: Furnace dev135 - 134: Furnace dev134 @@ -398,6 +401,17 @@ size | description 4?? | patchbay | - see next section for more details. 1 | automatic patchbay (>=136) + --- | **a couple more compat flags** (>=138) + 1 | broken portamento during legato + 7 | reserved + --- | **speed pattern of first song** (>=139) + 1 | length of speed pattern (fail if this is lower than 0 or higher than 16) + 16 | speed pattern (this overrides speed 1 and speed 2 settings) + --- | **groove list** (>=139) + 1 | number of entries + ??? | groove entries. the format is: + | - 1 byte: length of groove + | - 16 bytes: groove pattern ``` # patchbay @@ -467,6 +481,9 @@ size | description | - a list of channelCount C strings S?? | channel short names | - same as above + --- | **speed pattern** (>=139) + 1 | length of speed pattern (fail if this is lower than 0 or higher than 16) + 16 | speed pattern (this overrides speed 1 and speed 2 settings) ``` # chip flags diff --git a/src/audio/abstract.cpp b/src/audio/abstract.cpp index 942e31438..e360d1cb1 100644 --- a/src/audio/abstract.cpp +++ b/src/audio/abstract.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/audio/jack.cpp b/src/audio/jack.cpp index f9502cf4f..2ffe19b63 100644 --- a/src/audio/jack.cpp +++ b/src/audio/jack.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/audio/jack.h b/src/audio/jack.h index adba7850c..8018d6c20 100644 --- a/src/audio/jack.h +++ b/src/audio/jack.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/audio/midi.cpp b/src/audio/midi.cpp index afe0dbadc..fa3faf1f4 100644 --- a/src/audio/midi.cpp +++ b/src/audio/midi.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/audio/rtmidi.cpp b/src/audio/rtmidi.cpp index 258f69292..cbf4dca42 100644 --- a/src/audio/rtmidi.cpp +++ b/src/audio/rtmidi.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,7 +58,7 @@ bool TAMidiInRtMidi::gather() { if (m.type!=TA_MIDI_SYSEX && msg.size()>1) { memcpy(m.data,msg.data()+1,MIN(msg.size()-1,7)); } else if (m.type==TA_MIDI_SYSEX) { - m.sysExData.reset(new unsigned char[msg.size()]); + m.sysExData=std::shared_ptr(new unsigned char[msg.size()],std::default_delete()); m.sysExLen=msg.size(); logD("got a SysEx of length %ld!",msg.size()); memcpy(m.sysExData.get(),msg.data(),msg.size()); diff --git a/src/audio/rtmidi.h b/src/audio/rtmidi.h index 34ddf73e6..5a8e06e0b 100644 --- a/src/audio/rtmidi.h +++ b/src/audio/rtmidi.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/audio/sdlAudio.cpp b/src/audio/sdlAudio.cpp index 2915f0343..6d0ceff06 100644 --- a/src/audio/sdlAudio.cpp +++ b/src/audio/sdlAudio.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/audio/sdlAudio.h b/src/audio/sdlAudio.h index 556adabc2..a827b059d 100644 --- a/src/audio/sdlAudio.h +++ b/src/audio/sdlAudio.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/audio/taAudio.h b/src/audio/taAudio.h index 81814f3e3..dd0d76748 100644 --- a/src/audio/taAudio.h +++ b/src/audio/taAudio.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/baseutils.cpp b/src/baseutils.cpp index aad61eb48..787496c7b 100644 --- a/src/baseutils.cpp +++ b/src/baseutils.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/baseutils.h b/src/baseutils.h index 438696aaa..0e1b31911 100644 --- a/src/baseutils.h +++ b/src/baseutils.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 87bf76dda..546c5000f 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/cli/cli.h b/src/cli/cli.h index 55ad36b38..b77df2e8a 100644 --- a/src/cli/cli.h +++ b/src/cli/cli.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/chipUtils.h b/src/engine/chipUtils.h index 8cdb87649..4ddc0738d 100644 --- a/src/engine/chipUtils.h +++ b/src/engine/chipUtils.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/config.cpp b/src/engine/config.cpp index 86c7ddc01..e7e61daa0 100644 --- a/src/engine/config.cpp +++ b/src/engine/config.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/config.h b/src/engine/config.h index a867dc829..e59674ce1 100644 --- a/src/engine/config.h +++ b/src/engine/config.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/configEngine.cpp b/src/engine/configEngine.cpp index a265f0bee..67f466a97 100644 --- a/src/engine/configEngine.cpp +++ b/src/engine/configEngine.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/dataErrors.h b/src/engine/dataErrors.h index a163f221a..e6d8fdc42 100644 --- a/src/engine/dataErrors.h +++ b/src/engine/dataErrors.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/defines.h b/src/engine/defines.h index 3461a52db..9564f418d 100644 --- a/src/engine/defines.h +++ b/src/engine/defines.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ #define DIV_MAX_CHIPS 32 #define DIV_MAX_CHANS 128 #define DIV_MAX_PATTERNS 256 +#define DIV_MAX_CHIP_DEFS 256 // in-pattern #define DIV_MAX_ROWS 256 diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 1970afed6..32540db2a 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -103,9 +103,8 @@ enum DivDispatchCmds { DIV_CMD_FM_AM_DEPTH, // (depth) DIV_CMD_FM_PM_DEPTH, // (depth) - DIV_CMD_GENESIS_LFO, // unused? - - DIV_CMD_ARCADE_LFO, // unused? + DIV_CMD_FM_LFO2, // (speed) + DIV_CMD_FM_LFO2_WAVE, // (waveform) DIV_CMD_STD_NOISE_FREQ, // (freq) DIV_CMD_STD_NOISE_MODE, // (mode) @@ -215,6 +214,9 @@ enum DivDispatchCmds { DIV_CMD_SURROUND_PANNING, // (out, val) + DIV_CMD_FM_AM2_DEPTH, // (depth) + DIV_CMD_FM_PM2_DEPTH, // (depth) + DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol DIV_CMD_MAX diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 4e4e5d96c..b77d6adeb 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 81a91d689..34e7c6b04 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -60,7 +60,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul case 0x08: return "08xy: Set panning (x: left; y: right)"; case 0x09: - return "09xx: Set speed 1"; + return "09xx: Set groove pattern (speed 1 if no grooves exist)"; case 0x0a: return "0Axy: Volume slide (0y: down; x0: up)"; case 0x0b: @@ -70,7 +70,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul case 0x0d: return "0Dxx: Jump to next pattern"; case 0x0f: - return "0Fxx: Set speed 2"; + return "0Fxx: Set speed (speed 2 if no grooves exist)"; case 0x80: return "80xx: Set panning (00: left; 80: center; FF: right)"; case 0x81: @@ -110,8 +110,6 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul return "EDxx: Note delay"; case 0xee: return "EExx: Send external command"; - case 0xef: - return "EFxx: Set global tuning (quirky!)"; case 0xf0: return "F0xx: Set tick rate (bpm)"; case 0xf1: @@ -1961,14 +1959,12 @@ String DivEngine::getPlaybackDebugInfo() { "cmdsPerSecond: %d\n" "globalPitch: %d\n" "extValue: %d\n" - "speed1: %d\n" - "speed2: %d\n" "tempoAccum: %d\n" "totalProcessed: %d\n" "bufferPos: %d\n", curOrder,prevOrder,curRow,prevRow,ticks,subticks,totalLoops,lastLoopPos,nextSpeed,divider,cycles,clockDrift, changeOrd,changePos,totalSeconds,totalTicks,totalTicksR,totalCmds,lastCmds,cmdsPerSecond,globalPitch, - (int)extValue,(int)speed1,(int)speed2,(int)tempoAccum,(int)totalProcessed,(int)bufferPos + (int)extValue,(int)tempoAccum,(int)totalProcessed,(int)bufferPos ); } @@ -2093,7 +2089,8 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) { lastLoopPos=-1; } endOfSong=false; - speedAB=false; + // whaaaaa? + curSpeed=0; playing=true; skipping=true; memset(walked,0,8192); @@ -2441,15 +2438,14 @@ void DivEngine::reset() { } extValue=0; extValuePresent=0; - speed1=curSubSong->speed1; - speed2=curSubSong->speed2; + speeds=curSubSong->speeds; firstTick=false; shallStop=false; shallStopSched=false; pendingMetroTick=0; elapsedBars=0; elapsedBeats=0; - nextSpeed=speed1; + nextSpeed=speeds.val[0]; divider=60; if (curSubSong->customTempo) { divider=curSubSong->hz; @@ -2649,12 +2645,8 @@ size_t DivEngine::getCurrentSubSong() { return curSubSongIndex; } -unsigned char DivEngine::getSpeed1() { - return speed1; -} - -unsigned char DivEngine::getSpeed2() { - return speed2; +const DivGroovePattern& DivEngine::getSpeeds() { + return speeds; } float DivEngine::getHz() { @@ -4236,7 +4228,7 @@ void DivEngine::quitDispatch() { clockDrift=0; chans=0; playing=false; - speedAB=false; + curSpeed=0; endOfSong=false; ticks=0; tempoAccum=0; @@ -4472,6 +4464,9 @@ bool DivEngine::init() { for (int i=0; i<64; i++) { vibTable[i]=127*sin(((double)i/64.0)*(2*M_PI)); } + for (int i=0; i<128; i++) { + tremTable[i]=255*0.5*(1.0-cos(((double)i/128.0)*(2*M_PI))); + } for (int i=0; i<4096; i++) { reversePitchTable[i]=round(1024.0*pow(2.0,(2048.0-(double)i)/(12.0*128.0))); pitchTable[i]=round(1024.0*pow(2.0,((double)i-2048.0)/(12.0*128.0))); diff --git a/src/engine/engine.h b/src/engine/engine.h index 31edc8c85..9b26c4737 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -47,8 +47,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev136" -#define DIV_ENGINE_VERSION 136 +#define DIV_VERSION "dev139" +#define DIV_ENGINE_VERSION 139 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 @@ -337,7 +337,6 @@ class DivEngine { bool playing; bool freelance; bool shallStop, shallStopSched; - bool speedAB; bool endOfSong; bool consoleMode; bool extValuePresent; @@ -359,7 +358,7 @@ class DivEngine { bool midiOutClock; int midiOutMode; int softLockCount; - int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats; + int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats, curSpeed; size_t curSubSongIndex; size_t bufferPos; double divider; @@ -368,7 +367,7 @@ class DivEngine { int stepPlay; int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch; unsigned char extValue, pendingMetroTick; - unsigned char speed1, speed2; + DivGroovePattern speeds; short tempoAccum; DivStatusView view; DivHaltPositions haltOn; @@ -391,9 +390,9 @@ class DivEngine { std::vector midiOuts; std::vector cmdStream; std::vector possibleInsTypes; - static DivSysDef* sysDefs[256]; - static DivSystem sysFileMapFur[256]; - static DivSystem sysFileMapDMF[256]; + static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS]; + static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS]; + static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS]; struct SamplePreview { double rate; @@ -413,6 +412,7 @@ class DivEngine { } sPreview; short vibTable[64]; + short tremTable[128]; int reversePitchTable[4096]; int pitchTable[4096]; char c163NameCS[1024]; @@ -442,7 +442,7 @@ class DivEngine { void processRow(int i, bool afterDelay); void nextOrder(); void nextRow(); - void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, bool directStream); + void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, bool directStream); // returns true if end of song. bool nextTick(bool noAccum=false, bool inhibitLowLat=false); bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal); @@ -729,11 +729,8 @@ class DivEngine { // get current subsong size_t getCurrentSubSong(); - // get speed 1 - unsigned char getSpeed1(); - - // get speed 2 - unsigned char getSpeed2(); + // get speeds + const DivGroovePattern& getSpeeds(); // get Hz float getHz(); @@ -1064,7 +1061,6 @@ class DivEngine { freelance(false), shallStop(false), shallStopSched(false), - speedAB(false), endOfSong(false), consoleMode(false), extValuePresent(false), @@ -1098,6 +1094,7 @@ class DivEngine { nextSpeed(3), elapsedBars(0), elapsedBeats(0), + curSpeed(0), curSubSongIndex(0), bufferPos(0), divider(60), @@ -1115,8 +1112,6 @@ class DivEngine { globalPitch(0), extValue(0), pendingMetroTick(0), - speed1(3), - speed2(3), tempoAccum(0), view(DIV_STATUS_NOTHING), haltOn(DIV_HALT_NONE), @@ -1158,13 +1153,14 @@ class DivEngine { memset(dispatchOfChan,0,DIV_MAX_CHANS*sizeof(int)); memset(sysOfChan,0,DIV_MAX_CHANS*sizeof(int)); memset(vibTable,0,64*sizeof(short)); + memset(tremTable,0,128*sizeof(short)); memset(reversePitchTable,0,4096*sizeof(int)); memset(pitchTable,0,4096*sizeof(int)); - memset(sysDefs,0,256*sizeof(void*)); + memset(sysDefs,0,DIV_MAX_CHIP_DEFS*sizeof(void*)); memset(walked,0,8192); memset(oscBuf,0,DIV_MAX_OUTPUTS*(sizeof(float*))); - for (int i=0; i<256; i++) { + for (int i=0; i0x1a) { + if (ds.version>0x1b) { logE("this version is not supported by Furnace yet!"); lastError="this version is not supported by Furnace yet"; delete[] file; @@ -219,14 +219,15 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { } ds.subsong[0]->timeBase=reader.readC(); - ds.subsong[0]->speed1=reader.readC(); + ds.subsong[0]->speeds.len=2; + ds.subsong[0]->speeds.val[0]=reader.readC(); if (ds.version>0x07) { - ds.subsong[0]->speed2=reader.readC(); + ds.subsong[0]->speeds.val[1]=reader.readC(); ds.subsong[0]->pal=reader.readC(); ds.subsong[0]->hz=(ds.subsong[0]->pal)?60:50; ds.subsong[0]->customTempo=reader.readC(); } else { - ds.subsong[0]->speed2=ds.subsong[0]->speed1; + ds.subsong[0]->speeds.len=1; } if (ds.version>0x0a) { String hz=reader.readString(3); @@ -827,6 +828,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { for (int i=0; idepth=DIV_SAMPLE_DEPTH_YMZ_ADPCM; } } + if (ds.version>=0x1a) { + // what the hell man... + cutStart=reader.readI(); + cutEnd=reader.readI(); + if (cutStart<0 || cutStart>length) { + logE("cutStart is out of range! (%d)",cutStart); + lastError="file is corrupt or unreadable at samples"; + delete[] file; + return false; + } + if (cutEnd<0 || cutEnd>length) { + logE("cutEnd is out of range! (%d)",cutEnd); + lastError="file is corrupt or unreadable at samples"; + delete[] file; + return false; + } + if (cutEnd0) { if (ds.version>0x08) { if (ds.version<0x0b) { @@ -876,6 +902,19 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { data=new short[length]; reader.read(data,length*2); } + + if (ds.version>0x1a) { + if (cutStart!=0 || cutEnd!=length) { + // cut data + short* newData=new short[cutEnd-cutStart]; + memcpy(newData,&data[cutStart],(cutEnd-cutStart)*sizeof(short)); + delete[] data; + data=newData; + length=cutEnd-cutStart; + cutStart=0; + cutEnd=length; + } + } #ifdef TA_BIG_ENDIAN // convert to big-endian @@ -1716,6 +1755,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<130) { ds.oldArpStrategy=true; } + if (ds.version<138) { + ds.brokenPortaLegato=true; + } ds.isDMF=false; reader.readS(); // reserved @@ -1739,8 +1781,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { reader.readI(); subSong->timeBase=reader.readC(); - subSong->speed1=reader.readC(); - subSong->speed2=reader.readC(); + subSong->speeds.len=2; + subSong->speeds.val[0]=reader.readC(); + subSong->speeds.val[1]=reader.readC(); subSong->arpLen=reader.readC(); subSong->hz=reader.readF(); subSong->pal=(subSong->hz>=53); @@ -2221,6 +2264,32 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version>=136) song.patchbayAuto=reader.readC(); + if (ds.version>=138) { + ds.brokenPortaLegato=reader.readC(); + for (int i=0; i<7; i++) { + reader.readC(); + } + } + + if (ds.version>=139) { + subSong->speeds.len=reader.readC(); + for (int i=0; i<16; i++) { + subSong->speeds.val[i]=reader.readC(); + } + + // grooves + unsigned char grooveCount=reader.readC(); + for (int i=0; i=119) { logD("reading chip flags..."); @@ -2279,8 +2348,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { subSong=ds.subsong[i+1]; subSong->timeBase=reader.readC(); - subSong->speed1=reader.readC(); - subSong->speed2=reader.readC(); + subSong->speeds.len=2; + subSong->speeds.val[0]=reader.readC(); + subSong->speeds.val[1]=reader.readC(); subSong->arpLen=reader.readC(); subSong->hz=reader.readF(); subSong->pal=(subSong->hz>=53); @@ -2328,6 +2398,13 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { for (int i=0; ichanShortName[i]=reader.readString(); } + + if (ds.version>=139) { + subSong->speeds.len=reader.readC(); + for (int i=0; i<16; i++) { + subSong->speeds.val[i]=reader.readC(); + } + } } } @@ -2574,6 +2651,32 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } } + // new YM2612/SN/X1-010 volumes + if (ds.version<137) { + for (int i=0; i0x20 && ds.name!="klisje paa klisje") { writeFxCol(0xf0,fxVal); } else { - writeFxCol(0x09,fxVal); writeFxCol(0x0f,fxVal); } break; @@ -3399,8 +3501,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) { ds.subsong[0]->pal=true; ds.subsong[0]->customTempo=true; ds.subsong[0]->pat[3].effectCols=3; - ds.subsong[0]->speed1=3; - ds.subsong[0]->speed2=3; + ds.subsong[0]->speeds.val[0]=3; + ds.subsong[0]->speeds.len=1; int lastIns[4]; int lastNote[4]; @@ -3417,10 +3519,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) { ds.subsong[0]->orders.ord[j][i]=i; DivPattern* p=ds.subsong[0]->pat[j].getPattern(i,true); if (j==3 && seq[i].speed) { - p->data[0][6]=0x09; + p->data[0][6]=0x0f; p->data[0][7]=seq[i].speed; - p->data[0][8]=0x0f; - p->data[0][9]=seq[i].speed; } bool ignoreNext=false; @@ -4307,8 +4407,9 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeI(0); w->writeC(subSong->timeBase); - w->writeC(subSong->speed1); - w->writeC(subSong->speed2); + // these are for compatibility + w->writeC(subSong->speeds.val[0]); + w->writeC((subSong->speeds.len>=2)?subSong->speeds.val[1]:subSong->speeds.val[0]); w->writeC(subSong->arpLen); w->writeF(subSong->hz); w->writeS(subSong->patLen); @@ -4489,6 +4590,27 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { } w->writeC(song.patchbayAuto); + // even more compat flags + w->writeC(song.brokenPortaLegato); + for (int i=0; i<7; i++) { + w->writeC(0); + } + + // speeds of first song + w->writeC(subSong->speeds.len); + for (int i=0; i<16; i++) { + w->writeC(subSong->speeds.val[i]); + } + + // groove list + w->writeC((unsigned char)song.grooves.size()); + for (const DivGroovePattern& i: song.grooves) { + w->writeC(i.len); + for (int j=0; j<16; j++) { + w->writeC(i.val[j]); + } + } + blockEndSeek=w->tell(); w->seek(blockStartSeek,SEEK_SET); w->writeI(blockEndSeek-blockStartSeek-4); @@ -4503,8 +4625,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeI(0); w->writeC(subSong->timeBase); - w->writeC(subSong->speed1); - w->writeC(subSong->speed2); + w->writeC(subSong->speeds.val[0]); + w->writeC((subSong->speeds.len>=2)?subSong->speeds.val[1]:subSong->speeds.val[0]); w->writeC(subSong->arpLen); w->writeF(subSong->hz); w->writeS(subSong->patLen); @@ -4543,6 +4665,12 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeString(subSong->chanShortName[i],false); } + // speeds + w->writeC(subSong->speeds.len); + for (int i=0; i<16; i++) { + w->writeC(subSong->speeds.val[i]); + } + blockEndSeek=w->tell(); w->seek(blockStartSeek,SEEK_SET); w->writeI(blockEndSeek-blockStartSeek-4); @@ -4798,8 +4926,8 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { w->writeC(curSubSong->hilightB); w->writeC(curSubSong->timeBase); - w->writeC(curSubSong->speed1); - w->writeC(curSubSong->speed2); + w->writeC(curSubSong->speeds.val[0]); + w->writeC((curSubSong->speeds.len>=2)?curSubSong->speeds.val[1]:curSubSong->speeds.val[0]); w->writeC(curSubSong->pal); w->writeC(curSubSong->customTempo); char customHz[4]; @@ -4823,6 +4951,14 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { addWarning("only the currently selected subsong will be saved"); } + if (!song.grooves.empty()) { + addWarning("grooves will not be saved"); + } + + if (curSubSong->speeds.len>2) { + addWarning("only the first two speeds will be effective"); + } + if (curSubSong->virtualTempoD!=curSubSong->virtualTempoN) { addWarning(".dmf format does not support virtual tempo"); } diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 359a7ae39..c23e437d3 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/filter.cpp b/src/engine/filter.cpp index 88b381487..5c8ab218e 100644 --- a/src/engine/filter.cpp +++ b/src/engine/filter.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/filter.h b/src/engine/filter.h index 035c05e6a..1e6597e4f 100644 --- a/src/engine/filter.h +++ b/src/engine/filter.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 433e288fa..cc1755028 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 23c353971..8829f6ce8 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/macroInt.cpp b/src/engine/macroInt.cpp index ff24ae57a..103d9ee6b 100644 --- a/src/engine/macroInt.cpp +++ b/src/engine/macroInt.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -56,7 +56,7 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic } if (delay>0) { delay--; - had=false; + if (!linger) had=false; return; } if (began && source.delay>0) { @@ -523,4 +523,4 @@ DivMacroStruct* DivMacroInt::structByName(const String& name) { return NULL; } -#undef CONSIDER \ No newline at end of file +#undef CONSIDER diff --git a/src/engine/macroInt.h b/src/engine/macroInt.h index 49a4edfcb..ab3cc1216 100644 --- a/src/engine/macroInt.h +++ b/src/engine/macroInt.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/orders.h b/src/engine/orders.h index 78a05f0c1..170eb4ad4 100644 --- a/src/engine/orders.h +++ b/src/engine/orders.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/pattern.cpp b/src/engine/pattern.cpp index 6acdc2842..f7b833e18 100644 --- a/src/engine/pattern.cpp +++ b/src/engine/pattern.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/pattern.h b/src/engine/pattern.h index 218ae2d68..644399863 100644 --- a/src/engine/pattern.h +++ b/src/engine/pattern.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 214157258..74ec8cddf 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,7 @@ */ #include "../dispatch.h" +#include "../../ta-log.h" void DivDispatch::acquire(short** buf, size_t len) { } @@ -121,7 +122,8 @@ void DivDispatch::notifyWaveChange(int ins) { } void DivDispatch::notifyInsDeletion(void* ins) { - + logE("notifyInsDeletion NOT implemented!"); + abort(); } void DivDispatch::notifyPlaybackStop() { diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 6f70a310e..1772b313d 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/amiga.h b/src/engine/platform/amiga.h index 494b5da7c..e6048a859 100644 --- a/src/engine/platform/amiga.h +++ b/src/engine/platform/amiga.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 1984e5a18..e417c3131 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -319,27 +319,6 @@ void DivPlatformArcade::tick(bool sysTick) { rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); } } - if (chan[i].keyOn || chan[i].keyOff) { - if (chan[i].hardReset && chan[i].keyOn) { - for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs[i]|opOffs[j]; - immWrite(baseAddr+ADDR_SL_RR,0x0f); - immWrite(baseAddr+ADDR_TL,0x7f); - oldWrites[baseAddr+ADDR_SL_RR]=-1; - oldWrites[baseAddr+ADDR_TL]=-1; - } - } - immWrite(0x08,i); - if (chan[i].hardReset && chan[i].keyOn) { - for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs[i]|opOffs[j]; - for (int k=0; k<9; k++) { - immWrite(baseAddr+ADDR_SL_RR,0x0f); - } - } - } - chan[i].keyOff=false; - } } for (int i=0; i<256; i++) { @@ -349,6 +328,24 @@ void DivPlatformArcade::tick(bool sysTick) { } } + int hardResetElapsed=0; + bool mustHardReset=false; + + for (int i=0; i<8; i++) { + if (chan[i].keyOn || chan[i].keyOff) { + immWrite(0x08,i); + if (chan[i].hardReset && chan[i].keyOn) { + mustHardReset=true; + for (int j=0; j<4; j++) { + unsigned short baseAddr=chanOffs[i]|opOffs[j]; + immWrite(baseAddr+ADDR_SL_RR,0x0f); + hardResetElapsed++; + } + } + chan[i].keyOff=false; + } + } + for (int i=0; i<8; i++) { if (chan[i].freqChanged) { chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].pitch2; @@ -363,14 +360,37 @@ void DivPlatformArcade::tick(bool sysTick) { if (chan[i].freq>=(95<<6)) chan[i].freq=(95<<6)-1; immWrite(i+0x28,hScale(chan[i].freq>>6)); immWrite(i+0x30,chan[i].freq<<2); + hardResetElapsed+=2; chan[i].freqChanged=false; } - if (chan[i].keyOn || chan[i].opMaskChanged) { + if ((chan[i].keyOn || chan[i].opMaskChanged) && !chan[i].hardReset) { immWrite(0x08,(chan[i].opMask<<3)|i); + hardResetElapsed++; chan[i].opMaskChanged=false; chan[i].keyOn=false; } } + + // hard reset handling + if (mustHardReset) { + for (unsigned int i=hardResetElapsed; ifm; + chan[ch].opMask= + (chan[ch].state.op[0].enable?1:0)| + (chan[ch].state.op[2].enable?2:0)| + (chan[ch].state.op[1].enable?4:0)| + (chan[ch].state.op[3].enable?8:0); + } + + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator op=chan[ch].state.op[i]; + if (KVS(ch,i)) { + if (!chan[ch].active || chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127)); + } + } else { + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } + if (chan[ch].insChanged) { + if (isMuted[ch]) { + rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); + } else { + rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7)); + } + rWrite(chanOffs[ch]+ADDR_FMS_AMS,((chan[ch].state.fms&7)<<4)|(chan[ch].state.ams&3)); + } +} + int DivPlatformArcade::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); - if (chan[c.chan].insChanged) { - chan[c.chan].state=ins->fm; - chan[c.chan].opMask= - (chan[c.chan].state.op[0].enable?1:0)| - (chan[c.chan].state.op[2].enable?2:0)| - (chan[c.chan].state.op[1].enable?4:0)| - (chan[c.chan].state.op[3].enable?8:0); - } - chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } - for (int i=0; i<4; i++) { - unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; - DivInstrumentFM::Operator op=chan[c.chan].state.op[i]; - if (KVS(c.chan,i)) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127)); - } - } else { - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,op.tl); - } - } - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); - rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); - rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); - rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); - rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - } - } - if (chan[c.chan].insChanged) { - if (isMuted[c.chan]) { - rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); - } else { - rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7)); - } - rWrite(chanOffs[c.chan]+ADDR_FMS_AMS,((chan[c.chan].state.fms&7)<<4)|(chan[c.chan].state.ams&3)); - } + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -521,6 +545,11 @@ int DivPlatformArcade::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPM); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } chan[c.chan].baseFreq=NOTE_LINEAR(c.value); chan[c.chan].freqChanged=true; break; @@ -800,6 +829,12 @@ void DivPlatformArcade::notifyInsChange(int ins) { } } +void DivPlatformArcade::notifyInsDeletion(void* ins) { + for (int i=0; i<8; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + void* DivPlatformArcade::getChanState(int ch) { return &chan[ch]; } diff --git a/src/engine/platform/arcade.h b/src/engine/platform/arcade.h index 0bbd9b2cb..edcdd8d1c 100644 --- a/src/engine/platform/arcade.h +++ b/src/engine/platform/arcade.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -57,6 +57,7 @@ class DivPlatformArcade: public DivPlatformOPM { int octave(int freq); int toFreq(int freq); + void commitState(int ch, DivInstrument* ins); void acquire_nuked(short** buf, size_t len); void acquire_ymfm(short** buf, size_t len); @@ -76,6 +77,7 @@ class DivPlatformArcade: public DivPlatformOPM { void muteChannel(int ch, bool mute); DivMacroInt* getChanMacroInt(int ch); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); void setFlags(const DivConfig& flags); int getOutputCount(); void setYMFM(bool use); diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index be2ac933c..181901137 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index ad8b46255..f20a71eac 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 3dc8eedbc..0eb509db4 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index 0e60c3508..3ffba63e7 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index d7b15d8ff..94202de19 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,7 +44,7 @@ void DivPlatformBubSysWSG::acquire(short** buf, size_t len) { for (size_t h=0; h=64) writeOscBuf=0; + if (++writeOscBuf>=8) writeOscBuf=0; out<<=6; // scale output to 16 bit @@ -332,9 +332,9 @@ void DivPlatformBubSysWSG::notifyInsDeletion(void* ins) { void DivPlatformBubSysWSG::setFlags(const DivConfig& flags) { chipClock=COLOR_NTSC; CHECK_CUSTOM_CLOCK; - rate=chipClock; + rate=chipClock/8; for (int i=0; i<2; i++) { - oscBuf[i]->rate=rate/64; + oscBuf[i]->rate=rate/8; } } diff --git a/src/engine/platform/bubsyswsg.h b/src/engine/platform/bubsyswsg.h index d544c2ecd..784396bfd 100644 --- a/src/engine/platform/bubsyswsg.h +++ b/src/engine/platform/bubsyswsg.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index d52a5dd00..139b1525b 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index 28cf5b29d..be78c2d01 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/dummy.cpp b/src/engine/platform/dummy.cpp index 28cb5d40e..541630de7 100644 --- a/src/engine/platform/dummy.cpp +++ b/src/engine/platform/dummy.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -131,6 +131,10 @@ int DivPlatformDummy::dispatch(DivCommand c) { return 1; } +void DivPlatformDummy::notifyInsDeletion(void* ins) { + // nothing +} + void DivPlatformDummy::reset() { for (int i=0; i { DivInstrumentFM state; unsigned char freqH, freqL; @@ -83,6 +85,7 @@ class DivPlatformFMBase: public DivDispatch { unsigned char lastBusy; int delay; + bool flushFirst; unsigned char regPool[512]; short oldWrites[512]; @@ -102,7 +105,7 @@ class DivPlatformFMBase: public DivDispatch { } } inline void urgentWrite(unsigned short a, unsigned char v) { - if (!skipRegisterWrites) { + if (!skipRegisterWrites && !flushFirst) { if (writes.empty()) { writes.push_back(QueuedWrite(a,v)); } else if (writes.size()>16 || writes.front().addrOrVal) { @@ -118,9 +121,11 @@ class DivPlatformFMBase: public DivDispatch { friend void putDispatchChan(void*,int,int); - DivPlatformFMBase():DivDispatch(), - lastBusy(0), - delay(0) {} + DivPlatformFMBase(): + DivDispatch(), + lastBusy(0), + delay(0), + flushFirst(false) {} }; #endif diff --git a/src/engine/platform/ga20.cpp b/src/engine/platform/ga20.cpp index c5cbbb61e..0d2a78461 100644 --- a/src/engine/platform/ga20.cpp +++ b/src/engine/platform/ga20.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/ga20.h b/src/engine/platform/ga20.h index 9c67a9fd3..9cd6869a2 100644 --- a/src/engine/platform/ga20.h +++ b/src/engine/platform/ga20.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 6e6c317c9..e94970145 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/gb.h b/src/engine/platform/gb.h index 8f70fecb8..f75cc62c2 100644 --- a/src/engine/platform/gb.h +++ b/src/engine/platform/gb.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index b547181da..239ad5f7c 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,7 +34,7 @@ void DivYM2612Interface::ymfm_set_timer(uint32_t tnum, int32_t duration_in_clock } else if (tnum==0) { countA=duration_in_clocks; } - logV("ymfm_set_timer(%d,%d)",tnum,duration_in_clocks); + //logV("ymfm_set_timer(%d,%d)",tnum,duration_in_clocks); } void DivYM2612Interface::clock() { @@ -141,23 +141,26 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) { os[0]=0; os[1]=0; for (int i=0; i<6; i++) { - if (!writes.empty() && --delay<0) { - delay=0; - QueuedWrite& w=writes.front(); - if (w.addrOrVal) { - OPN2_Write(&fm,0x1+((w.addr>>8)<<1),w.val); - //printf("write: %x = %.2x\n",w.addr,w.val); - lastBusy=0; - regPool[w.addr&0x1ff]=w.val; - writes.pop_front(); - } else { - lastBusy++; - if (fm.write_busy==0) { - //printf("busycounter: %d\n",lastBusy); - OPN2_Write(&fm,0x0+((w.addr>>8)<<1),w.addr); - w.addrOrVal=true; + if (!writes.empty()) { + if (--delay<0) { + delay=0; + QueuedWrite& w=writes.front(); + if (w.addrOrVal) { + //logV("%.3x = %.2x",w.addr,w.val); + OPN2_Write(&fm,0x1+((w.addr>>8)<<1),w.val); + lastBusy=0; + regPool[w.addr&0x1ff]=w.val; + writes.pop_front(); + } else { + lastBusy++; + if (fm.write_busy==0) { + OPN2_Write(&fm,0x0+((w.addr>>8)<<1),w.addr); + w.addrOrVal=true; + } } } + } else { + flushFirst=false; } OPN2_Clock(&fm,o); os[0]+=o[0]; os[1]+=o[1]; @@ -207,6 +210,8 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) { regPool[w.addr&0x1ff]=w.val; writes.pop_front(); lastBusy=1; + } else { + flushFirst=false; } if (ladder) { @@ -220,6 +225,9 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) { //OPN2_Write(&fm,0,0); for (int i=0; i<6; i++) { + int chOut=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6; + if (chOut<-32768) chOut=-32768; + if (chOut>32767) chOut=32767; if (i==5) { if (fm_ymfm->debug_dac_enable()) { if (softPCM) { @@ -229,10 +237,10 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) { oscBuf[i]->data[oscBuf[i]->needle++]=fm_ymfm->debug_dac_data()<<7; } } else { - oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6; + oscBuf[i]->data[oscBuf[i]->needle++]=chOut; } } else { - oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6; + oscBuf[i]->data[oscBuf[i]->needle++]=chOut; } } @@ -389,6 +397,10 @@ void DivPlatformGenesis::tick(bool sysTick) { chan[i].state.ams=chan[i].std.ams.val; rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); } + if (chan[i].std.ex3.had) { + lfoValue=(chan[i].std.ex3.val>7)?0:(8|(chan[i].std.ex3.val&7)); + rWrite(0x22,lfoValue); + } if (chan[i].std.ex4.had && chan[i].active) { chan[i].opMask=chan[i].std.ex4.val&15; chan[i].opMaskChanged=true; @@ -454,31 +466,28 @@ void DivPlatformGenesis::tick(bool sysTick) { for (int i=0; i<512; i++) { if (pendingWrites[i]!=oldWrites[i]) { + if (i==0x2b && pendingWrites[i]!=0 && !parent->song.brokenDACMode) { + if (chan[5].keyOn) chan[5].keyOn=false; + chan[5].keyOff=true; + } immWrite(i,pendingWrites[i]&0xff); oldWrites[i]=pendingWrites[i]; } } + int hardResetElapsed=0; + bool mustHardReset=false; + for (int i=0; i<6; i++) { if (i==2 && extMode) continue; if (chan[i].keyOn || chan[i].keyOff) { + immWrite(0x28,0x00|konOffs[i]); if (chan[i].hardReset && chan[i].keyOn) { + mustHardReset=true; for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; immWrite(baseAddr+ADDR_SL_RR,0x0f); - immWrite(baseAddr+ADDR_TL,0x7f); - oldWrites[baseAddr+ADDR_SL_RR]=-1; - oldWrites[baseAddr+ADDR_TL]=-1; - //rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - } - } - immWrite(0x28,0x00|konOffs[i]); - if (chan[i].hardReset && chan[i].keyOn) { - for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs[i]|opOffs[j]; - for (int k=0; k<5; k++) { - immWrite(baseAddr+ADDR_SL_RR,0x0f); - } + hardResetElapsed++; } } chan[i].keyOff=false; @@ -507,6 +516,7 @@ void DivPlatformGenesis::tick(bool sysTick) { if (i<6) { immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8); immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff); + hardResetElapsed+=2; } if (chan[i].furnaceDac && chan[i].dacMode) { double off=1.0; @@ -525,12 +535,38 @@ void DivPlatformGenesis::tick(bool sysTick) { } chan[i].freqChanged=false; } - if (chan[i].keyOn || chan[i].opMaskChanged) { - if (i<6) immWrite(0x28,(chan[i].opMask<<4)|konOffs[i]); + if ((chan[i].keyOn || chan[i].opMaskChanged) && !chan[i].hardReset) { + if (i<6) { + immWrite(0x28,(chan[i].opMask<<4)|konOffs[i]); + hardResetElapsed++; + } chan[i].opMaskChanged=false; chan[i].keyOn=false; } } + + // hard reset handling + if (mustHardReset) { + for (unsigned int i=hardResetElapsed; ifm; + chan[ch].opMask= + (chan[ch].state.op[0].enable?1:0)| + (chan[ch].state.op[2].enable?2:0)| + (chan[ch].state.op[1].enable?4:0)| + (chan[ch].state.op[3].enable?8:0); + } + + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator& op=chan[ch].state.op[i]; + if (isMuted[ch]) { + rWrite(baseAddr+ADDR_TL,127); + } else { + if (KVS(ch,i)) { + if (!chan[ch].active || chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127)); + } + } else { + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + } + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } + if (chan[ch].insChanged) { + rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); + rWrite(chanOffs[ch]+ADDR_LRAF,(IS_REALLY_MUTED(ch)?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); + } +} + int DivPlatformGenesis::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { @@ -586,7 +663,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } } if (c.chan>=5 && chan[c.chan].dacMode) { - if (skipRegisterWrites) break; + //if (skipRegisterWrites) break; if (ins->type==DIV_INS_AMIGA) { // Furnace mode if (c.value!=DIV_NOTE_NULL) chan[c.chan].dacSample=ins->amiga.getSample(c.value); if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) { @@ -638,49 +715,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } if (c.chan>=6) break; - if (chan[c.chan].insChanged) { - chan[c.chan].state=ins->fm; - chan[c.chan].opMask= - (chan[c.chan].state.op[0].enable?1:0)| - (chan[c.chan].state.op[2].enable?2:0)| - (chan[c.chan].state.op[1].enable?4:0)| - (chan[c.chan].state.op[3].enable?8:0); - } - chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } - - for (int i=0; i<4; i++) { - unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; - DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; - if (isMuted[c.chan]) { - rWrite(baseAddr+ADDR_TL,127); - } else { - if (KVS(c.chan,i)) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127)); - } - } else { - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,op.tl); - } - } - } - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); - rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); - rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); - rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); - rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); - } - } - if (chan[c.chan].insChanged) { - rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); - rWrite(chanOffs[c.chan]+ADDR_LRAF,(IS_REALLY_MUTED(c.chan)?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4)); - } + + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -863,6 +903,11 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } else if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) { chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); } else { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); } chan[c.chan].note=c.value; @@ -1117,14 +1162,20 @@ void DivPlatformGenesis::forceIns() { rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); if (chan[i].active) { - chan[i].keyOn=true; - chan[i].freqChanged=true; + if (i<5 || !chan[i].dacMode) { + chan[i].keyOn=true; + chan[i].freqChanged=true; + } } } + immWrite(0x2b,0x00); + //rWrite(0x2a,0x00); if (chan[5].dacMode) { - rWrite(0x2b,0x80); + chan[5].dacSample=-1; + chan[6].dacSample=-1; } immWrite(0x22,lfoValue); + flushFirst=true; } void DivPlatformGenesis::toggleRegisterDump(bool enable) { @@ -1151,6 +1202,10 @@ int DivPlatformGenesis::getRegisterPoolSize() { return 512; } +float DivPlatformGenesis::getPostAmp() { + return 2.0f; +} + void DivPlatformGenesis::reset() { while (!writes.empty()) writes.pop_front(); memset(regPool,0,512); @@ -1178,6 +1233,7 @@ void DivPlatformGenesis::reset() { lfoValue=8; softPCMTimer=0; extMode=false; + flushFirst=false; if (softPCM) { chan[5].dacMode=true; @@ -1214,6 +1270,9 @@ void DivPlatformGenesis::notifyInsChange(int ins) { } void DivPlatformGenesis::notifyInsDeletion(void* ins) { + for (int i=0; i<10; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } } void DivPlatformGenesis::poke(unsigned int addr, unsigned short val) { @@ -1280,6 +1339,7 @@ int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, const DivC dumpWrites=false; ladder=false; skipRegisterWrites=false; + flushFirst=false; for (int i=0; i<10; i++) { isMuted[i]=false; oscBuf[i]=new DivDispatchOscBuffer; diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index f57c33a38..37449eb91 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -92,6 +92,7 @@ class DivPlatformGenesis: public DivPlatformOPN { friend void putDispatchChan(void*,int,int); inline void processDAC(int iRate); + inline void commitState(int ch, DivInstrument* ins); void acquire_nuked(short** buf, size_t len); void acquire_ymfm(short** buf, size_t len); @@ -114,10 +115,11 @@ class DivPlatformGenesis: public DivPlatformOPN { void setYMFM(bool use); bool keyOffAffectsArp(int ch); bool keyOffAffectsPorta(int ch); + float getPostAmp(); void toggleRegisterDump(bool enable); void setFlags(const DivConfig& flags); void notifyInsChange(int ins); - void notifyInsDeletion(void* ins); + virtual void notifyInsDeletion(void* ins); void setSoftPCM(bool value); int getPortaFloor(int ch); void poke(unsigned int addr, unsigned short val); diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 4eb34bc23..cc763d098 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,44 @@ #define IS_REALLY_MUTED(x) (isMuted[x] && (x<5 || !softPCM || (isMuted[5] && isMuted[6]))) +void DivPlatformGenesisExt::commitStateExt(int ch, DivInstrument* ins) { + int ordch=orderedOps[ch]; + + if (opChan[ch].insChanged) { + chan[2].state.alg=ins->fm.alg; + if (ch==0 || fbAllOps) { + chan[2].state.fb=ins->fm.fb; + } + chan[2].state.fms=ins->fm.fms; + chan[2].state.ams=ins->fm.ams; + chan[2].state.op[ordch]=ins->fm.op[ordch]; + } + + unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; + DivInstrumentFM::Operator& op=chan[2].state.op[ordch]; + // TODO: how does this work?! + if (isOpMuted[ch]) { + rWrite(baseAddr+0x40,127); + } else { + if (opChan[ch].insChanged) { + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); + } + } + if (opChan[ch].insChanged) { + rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); + rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+0x70,op.d2r&31); + rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); + rWrite(baseAddr+0x90,op.ssgEnv&15); + opChan[ch].mask=op.enable; + } + if (opChan[ch].insChanged) { // TODO how does this work? + rWrite(chanOffs[2]+0xb0,(chan[2].state.alg&7)|(chan[2].state.fb<<3)); + rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4)); + } +} + int DivPlatformGenesisExt::dispatch(DivCommand c) { if (c.chan<2) { return DivPlatformGenesis::dispatch(c); @@ -44,16 +82,6 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); - if (opChan[ch].insChanged) { - chan[2].state.alg=ins->fm.alg; - if (ch==0 || fbAllOps) { - chan[2].state.fb=ins->fm.fb; - } - chan[2].state.fms=ins->fm.fms; - chan[2].state.ams=ins->fm.ams; - chan[2].state.op[ordch]=ins->fm.op[ordch]; - } - if (noExtMacros) { opChan[ch].macroInit(NULL); } else { @@ -62,30 +90,8 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { if (!opChan[ch].std.vol.will) { opChan[ch].outVol=opChan[ch].vol; } - - unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; - DivInstrumentFM::Operator& op=chan[2].state.op[ordch]; - // TODO: how does this work?! - if (isOpMuted[ch]) { - rWrite(baseAddr+0x40,127); - } else { - if (opChan[ch].insChanged) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); - } - } - if (opChan[ch].insChanged) { - rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4)); - rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); - rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7)); - rWrite(baseAddr+0x70,op.d2r&31); - rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); - rWrite(baseAddr+0x90,op.ssgEnv&15); - opChan[ch].mask=op.enable; - } - if (opChan[ch].insChanged) { // TODO how does this work? - rWrite(chanOffs[2]+0xb0,(chan[2].state.alg&7)|(chan[2].state.fb<<3)); - rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4)); - } + + commitStateExt(ch,ins); opChan[ch].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -202,6 +208,11 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { } break; case DIV_CMD_LEGATO: { + if (opChan[ch].insChanged) { + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); + commitStateExt(ch,ins); + opChan[ch].insChanged=false; + } opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].freqChanged=true; break; @@ -578,6 +589,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) { if (opChan[i].freq>0x3fff) opChan[i].freq=0x3fff; immWrite(opChanOffsH[i],opChan[i].freq>>8); immWrite(opChanOffsL[i],opChan[i].freq&0xff); + opChan[i].freqChanged=false; } writeMask|=(unsigned char)(opChan[i].mask && opChan[i].active)<<(4+i); if (opChan[i].keyOn) { @@ -640,10 +652,10 @@ void DivPlatformGenesisExt::forceIns() { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (i==2 && extMode) { // extended channel - if (isOpMuted[j]) { + if (isOpMuted[orderedOps[j]]) { rWrite(baseAddr+0x40,127); } else if (KVS(i,j)) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[orderedOps[j]].outVol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } @@ -677,6 +689,8 @@ void DivPlatformGenesisExt::forceIns() { } } if (chan[5].dacMode) { + chan[5].dacSample=-1; + chan[6].dacSample=-1; rWrite(0x2b,0x80); } immWrite(0x22,lfoValue); @@ -744,6 +758,13 @@ void DivPlatformGenesisExt::notifyInsChange(int ins) { } } +void DivPlatformGenesisExt::notifyInsDeletion(void* ins) { + DivPlatformGenesis::notifyInsDeletion(ins); + for (int i=0; i<4; i++) { + opChan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + int DivPlatformGenesisExt::getPortaFloor(int ch) { return (ch>8)?12:0; } diff --git a/src/engine/platform/genesisext.h b/src/engine/platform/genesisext.h index 304f609fe..c668d5104 100644 --- a/src/engine/platform/genesisext.h +++ b/src/engine/platform/genesisext.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,6 +29,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis { bool isOpMuted[4]; friend void putDispatchChip(void*,int); friend void putDispatchChan(void*,int,int); + inline void commitStateExt(int ch, DivInstrument* ins); public: int dispatch(DivCommand c); void* getChanState(int chan); @@ -41,6 +42,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis { bool keyOffAffectsArp(int ch); bool keyOffAffectsPorta(int ch); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); int getPortaFloor(int ch); void setCSMChannel(unsigned char ch); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); diff --git a/src/engine/platform/k007232.cpp b/src/engine/platform/k007232.cpp index 6a333c6c6..daa661f6f 100644 --- a/src/engine/platform/k007232.cpp +++ b/src/engine/platform/k007232.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/k007232.h b/src/engine/platform/k007232.h index 8d9aeb28b..842310da5 100644 --- a/src/engine/platform/k007232.h +++ b/src/engine/platform/k007232.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 07be2d6f6..7dec2102d 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/lynx.h b/src/engine/platform/lynx.h index ec2208d38..7cde207e4 100644 --- a/src/engine/platform/lynx.h +++ b/src/engine/platform/lynx.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 6e44ea1cb..b130f2cbf 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/mmc5.h b/src/engine/platform/mmc5.h index 3e83ff06d..291a1baf4 100644 --- a/src/engine/platform/mmc5.h +++ b/src/engine/platform/mmc5.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/msm5232.cpp b/src/engine/platform/msm5232.cpp index 7405f8d36..eb8d5cbf5 100644 --- a/src/engine/platform/msm5232.cpp +++ b/src/engine/platform/msm5232.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/msm5232.h b/src/engine/platform/msm5232.h index 26d2a34b0..b1d83cf01 100644 --- a/src/engine/platform/msm5232.h +++ b/src/engine/platform/msm5232.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/msm6258.cpp b/src/engine/platform/msm6258.cpp index af1472af2..6591eda5e 100644 --- a/src/engine/platform/msm6258.cpp +++ b/src/engine/platform/msm6258.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -353,6 +353,9 @@ void DivPlatformMSM6258::notifyInsChange(int ins) { } void DivPlatformMSM6258::notifyInsDeletion(void* ins) { + for (int i=0; i<1; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } } void DivPlatformMSM6258::setFlags(const DivConfig& flags) { diff --git a/src/engine/platform/msm6258.h b/src/engine/platform/msm6258.h index 47d079cab..0c19d9763 100644 --- a/src/engine/platform/msm6258.h +++ b/src/engine/platform/msm6258.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/msm6295.cpp b/src/engine/platform/msm6295.cpp index bc41091c0..de7fedd5b 100644 --- a/src/engine/platform/msm6295.cpp +++ b/src/engine/platform/msm6295.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -326,6 +326,9 @@ void DivPlatformMSM6295::notifyInsChange(int ins) { } void DivPlatformMSM6295::notifyInsDeletion(void* ins) { + for (int i=0; i<4; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } } const void* DivPlatformMSM6295::getSampleMem(int index) { diff --git a/src/engine/platform/msm6295.h b/src/engine/platform/msm6295.h index 77a82f580..df1406933 100644 --- a/src/engine/platform/msm6295.h +++ b/src/engine/platform/msm6295.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index a65532793..afd4898a0 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/n163.h b/src/engine/platform/n163.h index 30eb222ce..49c0ff05e 100644 --- a/src/engine/platform/n163.h +++ b/src/engine/platform/n163.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/namcowsg.cpp b/src/engine/platform/namcowsg.cpp index cb4540f5c..ca1de7a54 100644 --- a/src/engine/platform/namcowsg.cpp +++ b/src/engine/platform/namcowsg.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -230,7 +230,7 @@ void DivPlatformNamcoWSG::tick(bool sysTick) { if (chan[i].std.pitch.had) { if (chan[i].std.pitch.mode) { chan[i].pitch2+=chan[i].std.pitch.val; - CLAMP_VAR(chan[i].pitch2,-32768,32767); + CLAMP_VAR(chan[i].pitch2,-1048575,1048575); } else { chan[i].pitch2=chan[i].std.pitch.val; } @@ -244,6 +244,7 @@ void DivPlatformNamcoWSG::tick(bool sysTick) { if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE); + if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>1048575) chan[i].freq=1048575; if (chan[i].keyOn) { } diff --git a/src/engine/platform/namcowsg.h b/src/engine/platform/namcowsg.h index 87a2517ef..b29434137 100644 --- a/src/engine/platform/namcowsg.h +++ b/src/engine/platform/namcowsg.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 435384c46..88c7253ee 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index 22ccfd913..cb30dbf78 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 4e798c6e5..ff57f9afb 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -446,35 +446,34 @@ void DivPlatformOPL::tick(bool sysTick) { } } } + } - if (i0x100) { + weWillWriteRRLater[(baseAddr&0xff)|32]=true; + } else { + weWillWriteRRLater[(baseAddr&0xff)]=true; } + immWrite(baseAddr+ADDR_SL_RR,0x0f); + hardResetElapsed++; } } } @@ -562,6 +561,11 @@ void DivPlatformOPL::tick(bool sysTick) { for (int i=0; i<512; i++) { if (pendingWrites[i]!=oldWrites[i]) { + if ((i>=0x80 && i<0xa0)) { + if (weWillWriteRRLater[i-0x80]) continue; + } else if ((i>=0x180 && i<0x1a0)) { + if (weWillWriteRRLater[32|(i-0x180)]) continue; + } immWrite(i,pendingWrites[i]&0xff); oldWrites[i]=pendingWrites[i]; } @@ -580,11 +584,15 @@ void DivPlatformOPL::tick(bool sysTick) { immWrite(chanMap[i]+ADDR_FREQ,chan[i].freqL); } if (imelodicChans && ins->type==DIV_INS_OPL_DRUMS) { + for (int i=0; i<4; i++) { + chan[melodicChans+i+1].state.alg=ins->fm.alg; + chan[melodicChans+i+1].state.fb=ins->fm.fb; + chan[melodicChans+i+1].state.opllPreset=ins->fm.opllPreset; + chan[melodicChans+i+1].state.fixedDrums=ins->fm.fixedDrums; + chan[melodicChans+i+1].state.kickFreq=ins->fm.kickFreq; + chan[melodicChans+i+1].state.snareHatFreq=ins->fm.snareHatFreq; + chan[melodicChans+i+1].state.tomTopFreq=ins->fm.tomTopFreq; + chan[melodicChans+i+1].state.op[0]=ins->fm.op[i]; + } + } else { + chan[ch].state=ins->fm; + } + } + + if (chan[ch].insChanged) { + if (ch>melodicChans && ins->type==DIV_INS_OPL_DRUMS) { + for (int i=0; i<4; i++) { + int ch=melodicChans+1+i; + unsigned char slot=slots[0][ch]; + if (slot==255) continue; + unsigned short baseAddr=slotMap[slot]; + DivInstrumentFM::Operator& op=chan[ch].state.op[0]; + chan[ch].fourOp=false; + + if (isMuted[ch]) { + rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); + } else { + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6)); + } + + rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult); + rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr); + rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr); + if (oplType>1) { + rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3)); + } + + if (isMuted[ch]) { + oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1; + rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)); + } else { + oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1; + rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4)); + } + } + } else { + int ops=(slots[3][ch]!=255 && chan[ch].state.ops==4 && oplType==3)?4:2; + chan[ch].fourOp=(ops==4); + if (chan[ch].fourOp) { + /* + if (chan[ch+1].active) { + chan[ch+1].keyOff=true; + chan[ch+1].keyOn=false; + chan[ch+1].active=false; + }*/ + chan[ch+1].insChanged=true; + chan[ch+1].macroInit(NULL); + } + update4OpMask=true; + for (int i=0; imelodicChans) { + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6)); + } else { + rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); + } + } + + rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult); + rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr); + rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr); + if (oplType>1) { + rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3)); + } + } + + if (isMuted[ch]) { + oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1; + rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)); + if (ops==4) { + oldWrites[chanMap[ch+1]+ADDR_LR_FB_ALG]=-1; + rWrite(chanMap[ch+1]+ADDR_LR_FB_ALG,((chan[ch].state.alg>>1)&1)|(chan[ch].state.fb<<1)); + } + } else { + oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1; + rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4)); + if (ops==4) { + oldWrites[chanMap[ch+1]+ADDR_LR_FB_ALG]=-1; + rWrite(chanMap[ch+1]+ADDR_LR_FB_ALG,((chan[ch].state.alg>>1)&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4)); + } + } + } + } +} + int DivPlatformOPL::dispatch(DivCommand c) { if (c.chan>=totalChans && c.chan!=adpcmChan) return 0; // ineffective in 4-op mode @@ -771,114 +914,12 @@ int DivPlatformOPL::dispatch(DivCommand c) { } DivInstrument* ins=parent->getIns(chan[c.chan].ins,c.chan>melodicChans?DIV_INS_OPL_DRUMS:DIV_INS_OPL); - if (chan[c.chan].insChanged) { - if (c.chan>melodicChans && ins->type==DIV_INS_OPL_DRUMS) { - for (int i=0; i<4; i++) { - chan[melodicChans+i+1].state.alg=ins->fm.alg; - chan[melodicChans+i+1].state.fb=ins->fm.fb; - chan[melodicChans+i+1].state.opllPreset=ins->fm.opllPreset; - chan[melodicChans+i+1].state.fixedDrums=ins->fm.fixedDrums; - chan[melodicChans+i+1].state.kickFreq=ins->fm.kickFreq; - chan[melodicChans+i+1].state.snareHatFreq=ins->fm.snareHatFreq; - chan[melodicChans+i+1].state.tomTopFreq=ins->fm.tomTopFreq; - chan[melodicChans+i+1].state.op[0]=ins->fm.op[i]; - } - } else { - chan[c.chan].state=ins->fm; - } - } - chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } - if (chan[c.chan].insChanged) { - if (c.chan>melodicChans && ins->type==DIV_INS_OPL_DRUMS) { - for (int i=0; i<4; i++) { - int ch=melodicChans+1+i; - unsigned char slot=slots[0][ch]; - if (slot==255) continue; - unsigned short baseAddr=slotMap[slot]; - DivInstrumentFM::Operator& op=chan[ch].state.op[0]; - chan[ch].fourOp=false; - if (isMuted[ch]) { - rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); - } else { - rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6)); - } - - rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult); - rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr); - rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr); - if (oplType>1) { - rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3)); - } - - if (isMuted[ch]) { - oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1; - rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)); - } else { - oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1; - rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&15)<<4)); - } - } - } else { - int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; - chan[c.chan].fourOp=(ops==4); - if (chan[c.chan].fourOp) { - /* - if (chan[c.chan+1].active) { - chan[c.chan+1].keyOff=true; - chan[c.chan+1].keyOn=false; - chan[c.chan+1].active=false; - }*/ - chan[c.chan+1].insChanged=true; - chan[c.chan+1].macroInit(NULL); - } - update4OpMask=true; - for (int i=0; imelodicChans) { - rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG_BROKEN(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6)); - } else { - rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); - } - } - - rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult); - rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr); - rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr); - if (oplType>1) { - rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3)); - } - } - - if (isMuted[c.chan]) { - oldWrites[chanMap[c.chan]+ADDR_LR_FB_ALG]=-1; - rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1)); - if (ops==4) { - oldWrites[chanMap[c.chan+1]+ADDR_LR_FB_ALG]=-1; - rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1)); - } - } else { - oldWrites[chanMap[c.chan]+ADDR_LR_FB_ALG]=-1; - rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&15)<<4)); - if (ops==4) { - oldWrites[chanMap[c.chan+1]+ADDR_LR_FB_ALG]=-1; - rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&15)<<4)); - } - } - } - } - + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -1075,6 +1116,11 @@ int DivPlatformOPL::dispatch(DivCommand c) { iface.sampleBank=sampleBank; break; case DIV_CMD_LEGATO: { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } chan[c.chan].baseFreq=(c.chan==adpcmChan)?(NOTE_ADPCMB(c.value)):(NOTE_FREQUENCY(c.value)); chan[c.chan].note=c.value; chan[c.chan].freqChanged=true; @@ -1509,7 +1555,11 @@ DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) { } DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) { - if (ch>=18) return NULL; + if (oplType==759) { + if (ch>=totalChans+1) return NULL; + } else { + if (ch>=totalChans) return NULL; + } if (oplType==3 && ch<12) { if (chan[ch&(~1)].fourOp) { if (ch&1) { @@ -1640,6 +1690,9 @@ void DivPlatformOPL::notifyInsChange(int ins) { } void DivPlatformOPL::notifyInsDeletion(void* ins) { + for (int i=0; i #include @@ -331,6 +332,55 @@ void DivPlatformOPLL::muteChannel(int ch, bool mute) { isMuted[ch]=mute; } +void DivPlatformOPLL::commitState(int ch, DivInstrument* ins) { + if (chan[ch].insChanged) { + chan[ch].state=ins->fm; + } + + if (chan[ch].insChanged) { + // update custom preset + if (chan[ch].state.opllPreset==0) { + DivInstrumentFM::Operator& mod=chan[ch].state.op[0]; + DivInstrumentFM::Operator& car=chan[ch].state.op[1]; + rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult)); + rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult)); + rWrite(0x02,(mod.ksl<<6)|(mod.tl&63)); + rWrite(0x03,(car.ksl<<6)|((chan[ch].state.fms&1)<<4)|((chan[ch].state.ams&1)<<3)|chan[ch].state.fb); + rWrite(0x04,(mod.ar<<4)|(mod.dr)); + rWrite(0x05,(car.ar<<4)|(car.dr)); + rWrite(0x06,(mod.sl<<4)|(mod.rr)); + rWrite(0x07,(car.sl<<4)|(car.rr)); + lastCustomMemory=ch; + } + if (chan[ch].state.opllPreset==16) { // compatible drums mode + if (ch>=6) { + drums=true; + immWrite(0x16,0x20); + immWrite(0x26,0x05); + immWrite(0x16,0x20); + immWrite(0x26,0x05); + immWrite(0x17,0x50); + immWrite(0x27,0x05); + immWrite(0x17,0x50); + immWrite(0x27,0x05); + immWrite(0x18,0xC0); + immWrite(0x28,0x01); + } + } else { + if (ch>=6) { + if (drums) { + drums=false; + immWrite(0x0e,0); + drumState=0; + } + } + if (ch<9) { + rWrite(0x30+ch,((15-VOL_SCALE_LOG_BROKEN(chan[ch].outVol,15-chan[ch].state.op[1].tl,15))&15)|(chan[ch].state.opllPreset<<4)); + } + } + } +} + int DivPlatformOPLL::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { @@ -375,49 +425,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { break; } - if (chan[c.chan].insChanged) { - // update custom preset - if (chan[c.chan].state.opllPreset==0) { - DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0]; - DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; - rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult)); - rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult)); - rWrite(0x02,(mod.ksl<<6)|(mod.tl&63)); - rWrite(0x03,(car.ksl<<6)|((chan[c.chan].state.fms&1)<<4)|((chan[c.chan].state.ams&1)<<3)|chan[c.chan].state.fb); - rWrite(0x04,(mod.ar<<4)|(mod.dr)); - rWrite(0x05,(car.ar<<4)|(car.dr)); - rWrite(0x06,(mod.sl<<4)|(mod.rr)); - rWrite(0x07,(car.sl<<4)|(car.rr)); - lastCustomMemory=c.chan; - } - if (chan[c.chan].state.opllPreset==16) { // compatible drums mode - if (c.chan>=6) { - drums=true; - immWrite(0x16,0x20); - immWrite(0x26,0x05); - immWrite(0x16,0x20); - immWrite(0x26,0x05); - immWrite(0x17,0x50); - immWrite(0x27,0x05); - immWrite(0x17,0x50); - immWrite(0x27,0x05); - immWrite(0x18,0xC0); - immWrite(0x28,0x01); - } - } else { - if (c.chan>=6) { - if (drums) { - drums=false; - immWrite(0x0e,0); - drumState=0; - } - } - if (c.chan<9) { - rWrite(0x30+c.chan,((15-VOL_SCALE_LOG_BROKEN(chan[c.chan].outVol,15-chan[c.chan].state.op[1].tl,15))&15)|(chan[c.chan].state.opllPreset<<4)); - } - } - } - + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -541,6 +549,13 @@ int DivPlatformOPLL::dispatch(DivCommand c) { } case DIV_CMD_LEGATO: { if (c.chan>=9 && !properDrums) return 0; + if (c.chan<6 || (!drums && !properDrums)) { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPLL); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } + } chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].note=c.value; chan[c.chan].freqChanged=true; @@ -940,6 +955,9 @@ void DivPlatformOPLL::notifyInsChange(int ins) { } void DivPlatformOPLL::notifyInsDeletion(void* ins) { + for (int i=0; i<11; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } } void DivPlatformOPLL::poke(unsigned int addr, unsigned short val) { @@ -958,6 +976,10 @@ void DivPlatformOPLL::setYMFM(bool use) { useYMFM=use; } +float DivPlatformOPLL::getPostAmp() { + return 1.5f; +} + void DivPlatformOPLL::setFlags(const DivConfig& flags) { int clockSel=flags.getInt("clockSel",0); if (clockSel==3) { diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 74d7b1bc3..4e72936da 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -73,6 +73,7 @@ class DivPlatformOPLL: public DivDispatch { int octave(int freq); int toFreq(int freq); + void commitState(int ch, DivInstrument* ins); friend void putDispatchChip(void*,int); friend void putDispatchChan(void*,int,int); @@ -95,6 +96,7 @@ class DivPlatformOPLL: public DivDispatch { void setYMFM(bool use); bool keyOffAffectsArp(int ch); bool keyOffAffectsPorta(int ch); + float getPostAmp(); void toggleRegisterDump(bool enable); void setVRC7(bool vrc); void setProperDrums(bool pd); diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 3efbd7c8c..2ed98b36b 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -163,21 +163,15 @@ void DivPlatformPCE::tick(bool sysTick) { if (chan[i].std.duty.had && i>=4) { chan[i].noise=chan[i].std.duty.val; chan[i].freqChanged=true; - int noiseSeek=chan[i].note; - if (noiseSeek<0) noiseSeek=0; - chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0); } if (NEW_ARP_STRAT) { chan[i].handleArp(); - int noiseSeek=chan[i].fixedArp?chan[i].baseNoteOverride:(chan[i].note+chan[i].arpOff); - if (noiseSeek<0) noiseSeek=0; - chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0); } else if (chan[i].std.arp.had) { if (!chan[i].inPorta) { int noiseSeek=parent->calcArp(chan[i].note,chan[i].std.arp.val); chan[i].baseFreq=NOTE_PERIODIC(noiseSeek); if (noiseSeek<0) noiseSeek=0; - chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0); + chan[i].noiseSeek=noiseSeek; } chan[i].freqChanged=true; } @@ -246,6 +240,15 @@ void DivPlatformPCE::tick(bool sysTick) { if (chan[i].freq>4095) chan[i].freq=4095; chWrite(i,0x02,chan[i].freq&0xff); chWrite(i,0x03,chan[i].freq>>8); + + if (i>=4) { + int noiseSeek=(chan[i].fixedArp?chan[i].baseNoteOverride:(chan[i].note+chan[i].arpOff))+chan[i].pitch2; + if (!parent->song.properNoiseLayout && noiseSeek<0) noiseSeek=0; + if (!NEW_ARP_STRAT) { + noiseSeek=chan[i].noiseSeek; + } + chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0); + } if (chan[i].keyOn) { //rWrite(16+i*5,0x80); //chWrite(i,0x04,0x80|chan[i].vol); @@ -331,9 +334,8 @@ int DivPlatformPCE::dispatch(DivCommand c) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; - int noiseSeek=chan[c.chan].note; - if (noiseSeek<0) noiseSeek=0; - chWrite(c.chan,0x07,chan[c.chan].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0); + chan[c.chan].noiseSeek=c.value; + if (chan[c.chan].noiseSeek<0) chan[c.chan].noiseSeek=0; } chan[c.chan].active=true; chan[c.chan].keyOn=true; @@ -431,7 +433,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { } case DIV_CMD_STD_NOISE_MODE: chan[c.chan].noise=c.value; - chWrite(c.chan,0x07,chan[c.chan].noise?(0x80|chan[c.chan].note):0); + chan[c.chan].freqChanged=true; break; case DIV_CMD_SAMPLE_MODE: chan[c.chan].pcm=c.value; diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index 6493840c1..fd4f81320 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,7 +34,7 @@ class DivPlatformPCE: public DivDispatch { unsigned char pan; bool noise, pcm, furnaceDac, deferredWaveUpdate; signed short wave; - int macroVolMul; + int macroVolMul, noiseSeek; DivWaveSynth ws; Channel(): SharedChannel(31), @@ -51,7 +51,8 @@ class DivPlatformPCE: public DivDispatch { furnaceDac(false), deferredWaveUpdate(false), wave(-1), - macroVolMul(31) {} + macroVolMul(31), + noiseSeek(0) {} }; Channel chan[6]; DivDispatchOscBuffer* oscBuf[6]; diff --git a/src/engine/platform/pcmdac.cpp b/src/engine/platform/pcmdac.cpp index 2843f0797..935a795d0 100644 --- a/src/engine/platform/pcmdac.cpp +++ b/src/engine/platform/pcmdac.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/pcmdac.h b/src/engine/platform/pcmdac.h index 9224ccc7d..9de928ffe 100644 --- a/src/engine/platform/pcmdac.h +++ b/src/engine/platform/pcmdac.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index 70c846f7c..9694b66c0 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/pcspkr.h b/src/engine/platform/pcspkr.h index 554c0d074..23b3c0b49 100644 --- a/src/engine/platform/pcspkr.h +++ b/src/engine/platform/pcspkr.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/pet.cpp b/src/engine/platform/pet.cpp index b4399bcbc..4d085581e 100644 --- a/src/engine/platform/pet.cpp +++ b/src/engine/platform/pet.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/pet.h b/src/engine/platform/pet.h index 5942817c2..771d5cbb5 100644 --- a/src/engine/platform/pet.h +++ b/src/engine/platform/pet.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/pokemini.cpp b/src/engine/platform/pokemini.cpp index 9b543153c..aa8adc0d7 100644 --- a/src/engine/platform/pokemini.cpp +++ b/src/engine/platform/pokemini.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/pokemini.h b/src/engine/platform/pokemini.h index 80233eb42..4e14bf572 100644 --- a/src/engine/platform/pokemini.h +++ b/src/engine/platform/pokemini.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/pokey.cpp b/src/engine/platform/pokey.cpp index 8294d9dcc..ae4b311e1 100644 --- a/src/engine/platform/pokey.cpp +++ b/src/engine/platform/pokey.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/pokey.h b/src/engine/platform/pokey.h index 79206a745..f24ea56c8 100644 --- a/src/engine/platform/pokey.h +++ b/src/engine/platform/pokey.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/pong.cpp b/src/engine/platform/pong.cpp index 27fd76b38..7d478b83b 100644 --- a/src/engine/platform/pong.cpp +++ b/src/engine/platform/pong.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/pong.h b/src/engine/platform/pong.h index 8259b14cf..4fb13477c 100644 --- a/src/engine/platform/pong.h +++ b/src/engine/platform/pong.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index 57ab138c6..f73f85dab 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -606,7 +606,7 @@ void DivPlatformQSound::forceIns() { for (int i=0; i<19; i++) { chan[i].insChanged=true; chan[i].freqChanged=true; - chan[i].sample=-1; + //chan[i].sample=-1; } } @@ -645,7 +645,7 @@ bool DivPlatformQSound::keyOffAffectsArp(int ch) { } void DivPlatformQSound::notifyInsChange(int ins) { - for (int i=0; i<4; i++) { + for (int i=0; i<19; i++) { if (chan[i].ins==ins) { chan[i].insChanged=true; } @@ -657,7 +657,7 @@ void DivPlatformQSound::notifyWaveChange(int wave) { } void DivPlatformQSound::notifyInsDeletion(void* ins) { - for (int i=0; i<4; i++) { + for (int i=0; i<19; i++) { chan[i].std.notifyInsDeletion((DivInstrument*)ins); } } @@ -707,11 +707,11 @@ const void* DivPlatformQSound::getSampleMem(int index) { } size_t DivPlatformQSound::getSampleMemCapacity(int index) { - return index == 0 ? 16777216 : index == 1 ? MAX(0,16777216 - sampleMemUsage) : 0; + return index == 0 ? 16777216 : index == 1 ? (sampleMemUsage>=16777216?1:(16777216 - sampleMemUsage)) : 0; } size_t DivPlatformQSound::getSampleMemUsage(int index) { - return index == 0 ? sampleMemLen : index == 1 ? MAX(0,sampleMemLenBS - sampleMemUsage) : 0; + return index == 0 ? sampleMemLen : index == 1 ? ((sampleMemUsage>=sampleMemLenBS)?0:(sampleMemLenBS - sampleMemUsage)) : 0; } bool DivPlatformQSound::isSampleLoaded(int index, int sample) { diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index c04514ed8..9c090f19c 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/rf5c68.cpp b/src/engine/platform/rf5c68.cpp index e5484dc77..7301bb40c 100644 --- a/src/engine/platform/rf5c68.cpp +++ b/src/engine/platform/rf5c68.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/rf5c68.h b/src/engine/platform/rf5c68.h index 98fc1a759..94ced515e 100644 --- a/src/engine/platform/rf5c68.h +++ b/src/engine/platform/rf5c68.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index 924bc73ac..d66692ddf 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/saa.h b/src/engine/platform/saa.h index 97bdbc60b..43e3cc875 100644 --- a/src/engine/platform/saa.h +++ b/src/engine/platform/saa.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/scc.cpp b/src/engine/platform/scc.cpp index fcb55f257..cd97ff309 100644 --- a/src/engine/platform/scc.cpp +++ b/src/engine/platform/scc.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -82,9 +82,7 @@ const char** DivPlatformSCC::getRegisterSheet() { void DivPlatformSCC::acquire(short** buf, size_t len) { for (size_t h=0; htick(); - } + scc->tick(16); short out=(short)scc->out()<<5; buf[0][h]=out; diff --git a/src/engine/platform/scc.h b/src/engine/platform/scc.h index 1c7364723..b8b892af3 100644 --- a/src/engine/platform/scc.h +++ b/src/engine/platform/scc.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index 25af42113..91ba1000b 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -406,6 +406,12 @@ void DivPlatformSegaPCM::notifyInsChange(int ins) { } } +void DivPlatformSegaPCM::notifyInsDeletion(void* ins) { + for (int i=0; i<16; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + void* DivPlatformSegaPCM::getChanState(int ch) { return &chan[ch]; } diff --git a/src/engine/platform/segapcm.h b/src/engine/platform/segapcm.h index 3164e1b98..a57c8084e 100644 --- a/src/engine/platform/segapcm.h +++ b/src/engine/platform/segapcm.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -89,6 +89,7 @@ class DivPlatformSegaPCM: public DivDispatch { void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); void renderSamples(int chipID); void setFlags(const DivConfig& flags); int getOutputCount(); diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index cfe04d183..518dd6e73 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,7 +22,7 @@ #include "../../ta-log.h" #include -#define rWrite(a,v) {if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(0x200+a,v);}}} +#define rWrite(a,v) {if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);}}} const char* regCheatSheetSN[]={ "DATA", "0", @@ -39,6 +39,10 @@ const char** DivPlatformSMS::getRegisterSheet() { return stereo?regCheatSheetGG:regCheatSheetSN; } +float DivPlatformSMS::getPostAmp() { + return 1.5f; +} + void DivPlatformSMS::acquire_nuked(short** buf, size_t len) { int oL=0; int oR=0; diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index cd0a7a1f6..a8bb42d6b 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -81,6 +81,7 @@ class DivPlatformSMS: public DivDispatch { int getOutputCount(); bool keyOffAffectsArp(int ch); bool keyOffAffectsPorta(int ch); + float getPostAmp(); int getPortaFloor(int ch); void setFlags(const DivConfig& flags); void notifyInsDeletion(void* ins); diff --git a/src/engine/platform/snes.cpp b/src/engine/platform/snes.cpp index 6fb942d48..7184cdc43 100644 --- a/src/engine/platform/snes.cpp +++ b/src/engine/platform/snes.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -811,7 +811,7 @@ const void* DivPlatformSNES::getSampleMem(int index) { size_t DivPlatformSNES::getSampleMemCapacity(int index) { // TODO change it based on current echo buffer size - return index == 0 ? 65536 : 0; + return index == 0 ? (65536-echoDelay*2048) : 0; } size_t DivPlatformSNES::getSampleMemUsage(int index) { @@ -825,7 +825,7 @@ bool DivPlatformSNES::isSampleLoaded(int index, int sample) { } void DivPlatformSNES::renderSamples(int sysID) { - memset(copyOfSampleMem,0,getSampleMemCapacity()); + memset(copyOfSampleMem,0,65536); memset(sampleOff,0,256*sizeof(unsigned int)); memset(sampleLoaded,0,256*sizeof(bool)); diff --git a/src/engine/platform/snes.h b/src/engine/platform/snes.h index 884c7d804..8783e61c3 100644 --- a/src/engine/platform/snes.h +++ b/src/engine/platform/snes.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/sound/su.cpp b/src/engine/platform/sound/su.cpp index 4d98892cc..9033a0d79 100644 --- a/src/engine/platform/sound/su.cpp +++ b/src/engine/platform/sound/su.cpp @@ -1,3 +1,25 @@ +/* su.cpp/su.h - Sound Unit emulator + * Copyright (C) 2015-2023 tildearrow + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + #define _USE_MATH_DEFINES #include "su.h" #include @@ -17,13 +39,13 @@ void SoundUnit::NextSample(short* l, short* r) { // run channels for (int i=0; i<8; i++) { - if (chan[i].vol==0 && !chan[i].flags.swvol) { + if (chan[i].vol==0 && !chan[i].flags1.swvol) { fns[i]=0; continue; } - if (chan[i].flags.pcm) { + if (chan[i].flags0.pcm) { ns[i]=pcm[chan[i].pcmpos]; - } else switch (chan[i].flags.shape) { + } else switch (chan[i].flags0.shape) { case 0: ns[i]=(((cycle[i]>>15)&127)>chan[i].duty)*127; break; @@ -47,11 +69,11 @@ void SoundUnit::NextSample(short* l, short* r) { break; } - if (chan[i].flags.ring) { + if (chan[i].flags0.ring) { ns[i]=(ns[i]*ns[(i+1)&7])>>7; } - if (chan[i].flags.pcm) { + if (chan[i].flags0.pcm) { if (chan[i].freq>0x8000) { pcmdec[i]+=0x8000; } else { @@ -62,18 +84,18 @@ void SoundUnit::NextSample(short* l, short* r) { if (chan[i].pcmpos>4)&3) { case 0: cycle[i]+=chan[i].freq*1-(chan[i].freq>>3); @@ -92,7 +114,7 @@ void SoundUnit::NextSample(short* l, short* r) { cycle[i]+=chan[i].freq; } if ((cycle[i]&0xf80000)!=(ocycle[i]&0xf80000)) { - if (chan[i].flags.shape==4) { + if (chan[i].flags0.shape==4) { lfsr[i]=(lfsr[i]>>1|(((lfsr[i]) ^ (lfsr[i] >> 2) ^ (lfsr[i] >> 3) ^ (lfsr[i] >> 5) ) & 1)<<31); } else { switch ((chan[i].duty>>4)&3) { @@ -114,7 +136,7 @@ void SoundUnit::NextSample(short* l, short* r) { } } } - if (chan[i].flags.restim) { + if (chan[i].flags1.restim) { if (--rcycle[i]<=0) { cycle[i]=0; rcycle[i]=chan[i].restimer; @@ -122,19 +144,18 @@ void SoundUnit::NextSample(short* l, short* r) { } } } - fns[i]=ns[i]*chan[i].vol*(chan[i].flags.pcm?4:2); - if (chan[i].flags.fmode!=0) { + fns[i]=ns[i]*chan[i].vol*(chan[i].flags0.pcm?4:2); + if (chan[i].flags0.fmode!=0) { int ff=chan[i].cutoff; nslow[i]=nslow[i]+(((ff)*nsband[i])>>16); nshigh[i]=fns[i]-nslow[i]-(((256-chan[i].reson)*nsband[i])>>8); nsband[i]=(((ff)*nshigh[i])>>16)+nsband[i]; - fns[i]=(((chan[i].flags.fmode&1)?(nslow[i]):(0))+((chan[i].flags.fmode&2)?(nshigh[i]):(0))+((chan[i].flags.fmode&4)?(nsband[i]):(0))); + fns[i]=(((chan[i].flags0.fmode&1)?(nslow[i]):(0))+((chan[i].flags0.fmode&2)?(nshigh[i]):(0))+((chan[i].flags0.fmode&4)?(nsband[i]):(0))); } nsL[i]=(fns[i]*SCpantabL[(unsigned char)chan[i].pan])>>8; nsR[i]=(fns[i]*SCpantabR[(unsigned char)chan[i].pan])>>8; oldfreq[i]=chan[i].freq; - oldflags[i]=chan[i].flags.flags; - if (chan[i].flags.swvol) { + if (chan[i].flags1.swvol) { if (--swvolt[i]<=0) { swvolt[i]=chan[i].swvol.speed; if (chan[i].swvol.dir) { @@ -174,7 +195,7 @@ void SoundUnit::NextSample(short* l, short* r) { } } } - if (chan[i].flags.swfreq) { + if (chan[i].flags1.swfreq) { if (--swfreqt[i]<=0) { swfreqt[i]=chan[i].swfreq.speed; if (chan[i].swfreq.dir) { @@ -198,7 +219,7 @@ void SoundUnit::NextSample(short* l, short* r) { } } } - if (chan[i].flags.swcut) { + if (chan[i].flags1.swcut) { if (--swcutt[i]<=0) { swcutt[i]=chan[i].swcut.speed; if (chan[i].swcut.dir) { @@ -222,11 +243,11 @@ void SoundUnit::NextSample(short* l, short* r) { } } } - if (chan[i].flags.resosc) { + if (chan[i].flags1.resosc) { cycle[i]=0; rcycle[i]=chan[i].restimer; ocycle[i]=0; - chan[i].flags.resosc=0; + chan[i].flags1.resosc=0; } if (muted[i]) { nsL[i]=0; @@ -377,7 +398,6 @@ void SoundUnit::Reset() { swcutt[i]=1; lfsr[i]=0xaaaa; oldfreq[i]=0; - oldflags[i]=0; pcmdec[i]=0; } dsCounterL=0; @@ -393,7 +413,7 @@ void SoundUnit::Reset() { #ifdef TA_BIG_ENDIAN const unsigned char suBERemap[32]={ - 0x01, 0x00, 0x02, 0x03, 0x05, 0x04, 0x07, 0x06, 0x08, 0x09, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, + 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, 0x07, 0x06, 0x08, 0x09, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e, 0x11, 0x10, 0x12, 0x13, 0x15, 0x14, 0x16, 0x17, 0x19, 0x18, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f, 0x1e }; #endif diff --git a/src/engine/platform/sound/su.h b/src/engine/platform/sound/su.h index 546acfc9c..d8e0f2709 100644 --- a/src/engine/platform/sound/su.h +++ b/src/engine/platform/sound/su.h @@ -1,7 +1,27 @@ +/* su.cpp/su.h - Sound Unit emulator + * Copyright (C) 2015-2023 tildearrow + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #include #include #include -#include class SoundUnit { signed char SCsine[256]; @@ -25,7 +45,6 @@ class SoundUnit { signed char ilFeedback0; signed char ilFeedback1; unsigned short oldfreq[8]; - unsigned short oldflags[8]; unsigned int pcmSize; bool dsOut; short dsCounterL, dsCounterR; @@ -44,12 +63,17 @@ class SoundUnit { signed char vol; signed char pan; union { - unsigned short flags; + unsigned char val; struct { unsigned char shape: 3; unsigned char pcm: 1; unsigned char ring: 1; unsigned char fmode: 3; + }; + } flags0; + union { + unsigned char val; + struct { unsigned char resosc: 1; unsigned char resfilt: 1; unsigned char pcmloop: 1; @@ -59,7 +83,7 @@ class SoundUnit { unsigned char swcut: 1; unsigned char padding: 1; }; - } flags; + } flags1; unsigned short cutoff; unsigned char duty; unsigned char reson; diff --git a/src/engine/platform/sound/ymfm/ymfm.h b/src/engine/platform/sound/ymfm/ymfm.h index 47ff90072..b1ba7c83a 100644 --- a/src/engine/platform/sound/ymfm/ymfm.h +++ b/src/engine/platform/sound/ymfm/ymfm.h @@ -350,7 +350,7 @@ public: { // create file char name[20]; - sprintf(name, "wavlog-%02d.wav", m_index); + snprintf(name, 20, "wavlog-%02d.wav", m_index); FILE *out = fopen(name, "wb"); // make the wav file header diff --git a/src/engine/platform/sound/ymfm/ymfm_opm.cpp b/src/engine/platform/sound/ymfm/ymfm_opm.cpp index 31d0a7467..958dae579 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opm.cpp +++ b/src/engine/platform/sound/ymfm/ymfm_opm.cpp @@ -363,7 +363,7 @@ std::string opm_registers::log_keyon(uint32_t choffs, uint32_t opoffs) char buffer[256]; char *end = &buffer[0]; - end += sprintf(end, "%u.%02u freq=%04X dt2=%u dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", + end += snprintf(end, 256-(end-buffer), "%u.%02u freq=%04X dt2=%u dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", chnum, opnum, ch_block_freq(choffs), op_detune2(opoffs), @@ -383,14 +383,14 @@ std::string opm_registers::log_keyon(uint32_t choffs, uint32_t opoffs) bool am = (lfo_am_depth() != 0 && ch_lfo_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0); if (am) - end += sprintf(end, " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth()); + end += snprintf(end, 256-(end-buffer), " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth()); bool pm = (lfo_pm_depth() != 0 && ch_lfo_pm_sens(choffs) != 0); if (pm) - end += sprintf(end, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); + end += snprintf(end, 256-(end-buffer), " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); if (am || pm) - end += sprintf(end, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]); + end += snprintf(end, 256-(end-buffer), " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]); if (noise_enable() && opoffs == 31) - end += sprintf(end, " noise=1"); + end += snprintf(end, 256-(end-buffer), " noise=1"); return buffer; } diff --git a/src/engine/platform/sound/ymfm/ymfm_opn.cpp b/src/engine/platform/sound/ymfm/ymfm_opn.cpp index 053ad9770..25d921a95 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opn.cpp +++ b/src/engine/platform/sound/ymfm/ymfm_opn.cpp @@ -411,7 +411,7 @@ std::string opn_registers_base::log_keyon(uint32_t choffs, uint32_t opof char buffer[256]; char *end = &buffer[0]; - end += sprintf(end, "%u.%02u freq=%04X dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X", + end += snprintf(end, 256-(end-buffer), "%u.%02u freq=%04X dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X", chnum, opnum, block_freq, op_detune(opoffs), @@ -427,21 +427,21 @@ std::string opn_registers_base::log_keyon(uint32_t choffs, uint32_t opof op_sustain_level(opoffs)); if (OUTPUTS > 1) - end += sprintf(end, " out=%c%c", + end += snprintf(end, 256-(end-buffer), " out=%c%c", ch_output_0(choffs) ? 'L' : '-', ch_output_1(choffs) ? 'R' : '-'); if (op_ssg_eg_enable(opoffs)) - end += sprintf(end, " ssg=%X", op_ssg_eg_mode(opoffs)); + end += snprintf(end, 256-(end-buffer), " ssg=%X", op_ssg_eg_mode(opoffs)); bool am = (op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0); if (am) - end += sprintf(end, " am=%u", ch_lfo_am_sens(choffs)); + end += snprintf(end, 256-(end-buffer), " am=%u", ch_lfo_am_sens(choffs)); bool pm = (ch_lfo_pm_sens(choffs) != 0); if (pm) - end += sprintf(end, " pm=%u", ch_lfo_pm_sens(choffs)); + end += snprintf(end, 256-(end-buffer), " pm=%u", ch_lfo_pm_sens(choffs)); if (am || pm) - end += sprintf(end, " lfo=%02X", lfo_rate()); + end += snprintf(end, 256-(end-buffer), " lfo=%02X", lfo_rate()); if (multi_freq() && choffs == 2) - end += sprintf(end, " multi=1"); + end += snprintf(end, 256-(end-buffer), " multi=1"); return buffer; } diff --git a/src/engine/platform/sound/ymfm/ymfm_opz.cpp b/src/engine/platform/sound/ymfm/ymfm_opz.cpp index 37c6a5fce..b20bea3c1 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opz.cpp +++ b/src/engine/platform/sound/ymfm/ymfm_opz.cpp @@ -575,14 +575,14 @@ std::string opz_registers::log_keyon(uint32_t choffs, uint32_t opoffs) char buffer[256]; char *end = &buffer[0]; - end += sprintf(end, "%u.%02u", chnum, opnum); + end += snprintf(end, 256-(end-buffer), "%u.%02u", chnum, opnum); if (op_fix_mode(opoffs)) - end += sprintf(end, " fixfreq=%X fine=%X shift=%X", op_fix_frequency(opoffs), op_fine(opoffs), op_fix_range(opoffs)); + end += snprintf(end, 256-(end-buffer), " fixfreq=%X fine=%X shift=%X", op_fix_frequency(opoffs), op_fine(opoffs), op_fix_range(opoffs)); else - end += sprintf(end, " freq=%04X dt2=%u fine=%X", ch_block_freq(choffs), op_detune2(opoffs), op_fine(opoffs)); + end += snprintf(end, 256-(end-buffer), " freq=%04X dt2=%u fine=%X", ch_block_freq(choffs), op_detune2(opoffs), op_fine(opoffs)); - end += sprintf(end, " dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", + end += snprintf(end, 256-(end-buffer), " dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", op_detune(opoffs), ch_feedback(choffs), ch_algorithm(choffs), @@ -598,32 +598,32 @@ std::string opz_registers::log_keyon(uint32_t choffs, uint32_t opoffs) ch_output_1(choffs) ? 'R' : '-'); if (op_eg_shift(opoffs) != 0) - end += sprintf(end, " egshift=%u", op_eg_shift(opoffs)); + end += snprintf(end, 256-(end-buffer), " egshift=%u", op_eg_shift(opoffs)); bool am = (lfo_am_depth() != 0 && ch_lfo_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0); if (am) - end += sprintf(end, " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth()); + end += snprintf(end, 256-(end-buffer), " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth()); bool pm = (lfo_pm_depth() != 0 && ch_lfo_pm_sens(choffs) != 0); if (pm) - end += sprintf(end, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); + end += snprintf(end, 256-(end-buffer), " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); if (am || pm) - end += sprintf(end, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]); + end += snprintf(end, 256-(end-buffer), " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]); bool am2 = (lfo2_am_depth() != 0 && ch_lfo2_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0); if (am2) - end += sprintf(end, " am2=%u/%02X", ch_lfo2_am_sens(choffs), lfo2_am_depth()); + end += snprintf(end, 256-(end-buffer), " am2=%u/%02X", ch_lfo2_am_sens(choffs), lfo2_am_depth()); bool pm2 = (lfo2_pm_depth() != 0 && ch_lfo2_pm_sens(choffs) != 0); if (pm2) - end += sprintf(end, " pm2=%u/%02X", ch_lfo2_pm_sens(choffs), lfo2_pm_depth()); + end += snprintf(end, 256-(end-buffer), " pm2=%u/%02X", ch_lfo2_pm_sens(choffs), lfo2_pm_depth()); if (am2 || pm2) - end += sprintf(end, " lfo2=%02X/%c", lfo2_rate(), "WQTN"[lfo2_waveform()]); + end += snprintf(end, 256-(end-buffer), " lfo2=%02X/%c", lfo2_rate(), "WQTN"[lfo2_waveform()]); if (op_reverb_rate(opoffs) != 0) - end += sprintf(end, " rev=%u", op_reverb_rate(opoffs)); + end += snprintf(end, 256-(end-buffer), " rev=%u", op_reverb_rate(opoffs)); if (op_waveform(opoffs) != 0) - end += sprintf(end, " wf=%u", op_waveform(opoffs)); + end += snprintf(end, 256-(end-buffer), " wf=%u", op_waveform(opoffs)); if (noise_enable() && opoffs == 31) - end += sprintf(end, " noise=1"); + end += snprintf(end, 256-(end-buffer), " noise=1"); return buffer; } diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 51b04e0e6..7367f50ce 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -497,6 +497,9 @@ void DivPlatformSoundUnit::reset() { rWrite(0x9d,ilCtrl); rWrite(0xbc,ilSize); rWrite(0xbd,fil1); + + // copy sample memory + memcpy(su->pcm,sampleMem,sampleMemSize?65536:8192); } int DivPlatformSoundUnit::getOutputCount() { @@ -545,7 +548,7 @@ void DivPlatformSoundUnit::poke(std::vector& wlist) { } const void* DivPlatformSoundUnit::getSampleMem(int index) { - return (index==0)?su->pcm:NULL; + return (index==0)?sampleMem:NULL; } size_t DivPlatformSoundUnit::getSampleMemCapacity(int index) { @@ -563,7 +566,7 @@ bool DivPlatformSoundUnit::isSampleLoaded(int index, int sample) { } void DivPlatformSoundUnit::renderSamples(int sysID) { - memset(su->pcm,0,getSampleMemCapacity(0)); + memset(sampleMem,0,sampleMemSize?65536:8192); memset(sampleOffSU,0,256*sizeof(unsigned int)); memset(sampleLoaded,0,256*sizeof(bool)); @@ -582,10 +585,10 @@ void DivPlatformSoundUnit::renderSamples(int sysID) { break; } if (memPos+paddedLen>=getSampleMemCapacity(0)) { - memcpy(su->pcm+memPos,s->data8,getSampleMemCapacity(0)-memPos); + memcpy(sampleMem+memPos,s->data8,getSampleMemCapacity(0)-memPos); logW("out of PCM memory for sample %d!",i); } else { - memcpy(su->pcm+memPos,s->data8,paddedLen); + memcpy(sampleMem+memPos,s->data8,paddedLen); sampleLoaded[i]=true; } sampleOffSU[i]=memPos; @@ -593,6 +596,8 @@ void DivPlatformSoundUnit::renderSamples(int sysID) { } sampleMemLen=memPos; sysIDCache=sysID; + + memcpy(su->pcm,sampleMem,sampleMemSize?65536:8192); } int DivPlatformSoundUnit::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { @@ -604,6 +609,8 @@ int DivPlatformSoundUnit::init(DivEngine* p, int channels, int sugRate, const Di oscBuf[i]=new DivDispatchOscBuffer; } su=new SoundUnit(); + sampleMem=new unsigned char[65536]; + memset(sampleMem,0,65536); sysIDCache=0; setFlags(flags); reset(); @@ -615,6 +622,7 @@ void DivPlatformSoundUnit::quit() { delete oscBuf[i]; } delete su; + delete sampleMem; } DivPlatformSoundUnit::~DivPlatformSoundUnit() { diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h index 518edafa4..13b4d4bba 100644 --- a/src/engine/platform/su.h +++ b/src/engine/platform/su.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -89,6 +89,7 @@ class DivPlatformSoundUnit: public DivDispatch { short tempR; unsigned char sampleBank, lfoMode, lfoSpeed; SoundUnit* su; + unsigned char* sampleMem; size_t sampleMemLen; unsigned char regPool[128]; double NOTE_SU(int ch, int note); diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index f5a2dfb9a..4e0b83724 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ #include #define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);}} +#define postWrite(a,v) postDACWrites.emplace(a,v); #define CHIP_DIVIDER 32 @@ -186,7 +187,7 @@ void DivPlatformSwan::tick(bool sysTick) { } } dacRate=((double)chipClock/2)/MAX(1,off*chan[i].freq); - if (dumpWrites) addWrite(0xffff0001,dacRate); + if (dumpWrites) postWrite(0xffff0001,dacRate); } if (chan[i].freq>2048) chan[i].freq=2048; if (chan[i].freq<1) chan[i].freq=1; @@ -216,7 +217,33 @@ void DivPlatformSwan::tick(bool sysTick) { } } } + if (chan[3].std.phaseReset.had) { + if (noise>0) { + rWrite(0x0e,((noise-1)&0x07)|0x18); + sndCtrl|=0x80; + } else { + sndCtrl&=~0x80; + } + } + unsigned char origSndCtrl=sndCtrl; + bool phaseResetHappens=false; + for (int i=0; i<4; i++) { + if (chan[i].std.phaseReset.had) { + phaseResetHappens=true; + sndCtrl&=~(1<amiga.getSample(c.value); if (dacSample<0 || dacSample>=parent->song.sampleLen) { dacSample=-1; - if (dumpWrites) addWrite(0xffff0002,0); + if (dumpWrites) postWrite(0xffff0002,0); break; } else { if (dumpWrites) { - addWrite(0xffff0000,dacSample); + postWrite(0xffff0000,dacSample); } } if (c.value!=DIV_NOTE_NULL) { @@ -260,14 +287,14 @@ int DivPlatformSwan::dispatch(DivCommand c) { dacSample=12*sampleBank+chan[1].note%12; if (dacSample>=parent->song.sampleLen) { dacSample=-1; - if (dumpWrites) addWrite(0xffff0002,0); + if (dumpWrites) postWrite(0xffff0002,0); break; } else { - if (dumpWrites) addWrite(0xffff0000,dacSample); + if (dumpWrites) postWrite(0xffff0000,dacSample); } dacRate=parent->getSample(dacSample)->rate; if (dumpWrites) { - addWrite(0xffff0001,dacRate); + postWrite(0xffff0001,dacRate); } chan[1].active=true; chan[1].keyOn=true; @@ -298,7 +325,7 @@ int DivPlatformSwan::dispatch(DivCommand c) { case DIV_CMD_NOTE_OFF: if (c.chan==1&&pcm) { dacSample=-1; - if (dumpWrites) addWrite(0xffff0002,0); + if (dumpWrites) postWrite(0xffff0002,0); pcm=false; } chan[c.chan].active=false; @@ -463,6 +490,7 @@ int DivPlatformSwan::getRegisterPoolSize() { void DivPlatformSwan::reset() { while (!writes.empty()) writes.pop(); + while (!postDACWrites.empty()) postDACWrites.pop(); memset(regPool,0,128); for (int i=0; i<4; i++) { chan[i]=Channel(); diff --git a/src/engine/platform/swan.h b/src/engine/platform/swan.h index 6f129e6dd..148856c06 100644 --- a/src/engine/platform/swan.h +++ b/src/engine/platform/swan.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -51,6 +51,7 @@ class DivPlatformSwan: public DivDispatch { QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {} }; std::queue writes; + std::queue postDACWrites; WSwan* ws; void updateWave(int ch); friend void putDispatchChip(void*,int); diff --git a/src/engine/platform/t6w28.cpp b/src/engine/platform/t6w28.cpp index e81d156f1..0d7b9223c 100644 --- a/src/engine/platform/t6w28.cpp +++ b/src/engine/platform/t6w28.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/t6w28.h b/src/engine/platform/t6w28.h index dd49d1b86..994a2fe66 100644 --- a/src/engine/platform/t6w28.h +++ b/src/engine/platform/t6w28.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index 02f422011..1e6db02ca 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -135,22 +135,27 @@ void DivPlatformTIA::tick(bool sysTick) { int bf=chan[i].baseFreq; if (!parent->song.oldArpStrategy) { if (!chan[i].fixedArp) { - bf+=chan[i].baseFreq+chan[i].arpOff; + bf+=chan[i].arpOff<<8; } } - chan[i].freq=dealWithFreq(chan[i].shape,bf,chan[i].pitch)+chan[i].pitch2; - if ((chan[i].shape==4 || chan[i].shape==5) && !(chan[i].baseFreq&0x80000000 && ((chan[i].baseFreq&0x7fffffff)<32))) { - if (bf<39*256) { - rWrite(0x15+i,6); - chan[i].freq=dealWithFreq(6,bf,chan[i].pitch)+chan[i].pitch2; - } else if (bf<59*256) { - rWrite(0x15+i,12); - chan[i].freq=dealWithFreq(12,bf,chan[i].pitch)+chan[i].pitch2; - } else { - rWrite(0x15+i,chan[i].shape); + if (chan[i].fixedArp) { + chan[i].freq=chan[i].baseNoteOverride&31; + } else { + chan[i].freq=dealWithFreq(chan[i].shape,bf,chan[i].pitch+chan[i].pitch2); + if ((chan[i].shape==4 || chan[i].shape==5) && !(chan[i].baseFreq&0x80000000 && ((chan[i].baseFreq&0x7fffffff)<32))) { + if (bf<39*256) { + rWrite(0x15+i,6); + chan[i].freq=dealWithFreq(6,bf,chan[i].pitch+chan[i].pitch2); + } else if (bf<59*256) { + rWrite(0x15+i,12); + chan[i].freq=dealWithFreq(12,bf,chan[i].pitch+chan[i].pitch2); + } else { + rWrite(0x15+i,chan[i].shape); + } } + if (chan[i].freq>31) chan[i].freq=31; } - if (chan[i].freq>31) chan[i].freq=31; + if (chan[i].keyOff) { rWrite(0x19+i,0); } diff --git a/src/engine/platform/tia.h b/src/engine/platform/tia.h index b4b867dc4..95ae99173 100644 --- a/src/engine/platform/tia.h +++ b/src/engine/platform/tia.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index fa3d4d18a..80e69f195 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,8 @@ #define ADDR_WS_FINE 0x100 // actually 0xc0 but bit 5 of data selects address #define ADDR_EGS_REV 0x120 +// actually 0x38 but bits 7 and 2 select address +#define ADDR_FMS2_AMS2 0x140 const char* regCheatSheetOPZ[]={ "Test", "00", @@ -139,7 +141,8 @@ void DivPlatformTX81Z::tick(bool sysTick) { } if (chan[i].std.wave.had) { - rWrite(0x1b,chan[i].std.wave.val&3); + lfoShape=chan[i].std.wave.val&3; + immWrite(0x1b,lfoShape|(lfoShape2<<2)); } if (chan[i].std.pitch.had) { @@ -177,7 +180,28 @@ void DivPlatformTX81Z::tick(bool sysTick) { } if (chan[i].std.ex3.had) { - immWrite(0x18,chan[i].std.ex3.val); + lfoValue=chan[i].std.ex3.val; + immWrite(0x18,lfoValue); + } + + if (chan[i].std.ex5.had) { + amDepth2=chan[i].std.ex5.val; + immWrite(0x17,amDepth2); + } + + if (chan[i].std.ex6.had) { + pmDepth2=chan[i].std.ex6.val; + immWrite(0x17,0x80|pmDepth2); + } + + if (chan[i].std.ex7.had) { + lfoValue2=chan[i].std.ex7.val; + immWrite(0x16,lfoValue2); + } + + if (chan[i].std.ex8.had) { + lfoShape2=chan[i].std.ex8.val&3; + immWrite(0x1b,lfoShape|(lfoShape2<<2)); } if (chan[i].std.alg.had) { @@ -266,28 +290,6 @@ void DivPlatformTX81Z::tick(bool sysTick) { rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); } } - if (chan[i].keyOn || chan[i].keyOff) { - if (chan[i].hardReset && chan[i].keyOn) { - for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs[i]|opOffs[j]; - immWrite(baseAddr+ADDR_SL_RR,0x0f); - immWrite(baseAddr+ADDR_TL,0x7f); - oldWrites[baseAddr+ADDR_SL_RR]=-1; - oldWrites[baseAddr+ADDR_TL]=-1; - } - } - //if (chan[i].keyOn) immWrite(0x08,i); - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x00|(chan[i].chVolR<<7)); - if (chan[i].hardReset && chan[i].keyOn) { - for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs[i]|opOffs[j]; - for (int k=0; k<9; k++) { - immWrite(baseAddr+ADDR_SL_RR,0x0f); - } - } - } - chan[i].keyOff=false; - } } for (int i=0; i<256; i++) { @@ -308,6 +310,30 @@ void DivPlatformTX81Z::tick(bool sysTick) { oldWrites[i]=pendingWrites[i]; } } + for (int i=320; i<328; i++) { + if (pendingWrites[i]!=oldWrites[i]) { + immWrite(0x38+(i&7),(0x84|pendingWrites[i])); + oldWrites[i]=pendingWrites[i]; + } + } + + int hardResetElapsed=0; + bool mustHardReset=false; + + for (int i=0; i<8; i++) { + if (chan[i].keyOn || chan[i].keyOff) { + immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x00|(chan[i].chVolR<<7)); + if (chan[i].hardReset && chan[i].keyOn) { + mustHardReset=true; + for (int j=0; j<4; j++) { + unsigned short baseAddr=chanOffs[i]|opOffs[j]; + immWrite(baseAddr+ADDR_SL_RR,0x0f); + hardResetElapsed++; + } + } + chan[i].keyOff=false; + } + } for (int i=0; i<8; i++) { if (chan[i].freqChanged) { @@ -323,14 +349,34 @@ void DivPlatformTX81Z::tick(bool sysTick) { if (chan[i].freq>=(95<<6)) chan[i].freq=(95<<6)-1; immWrite(i+0x28,hScale(chan[i].freq>>6)); immWrite(i+0x30,(chan[i].freq<<2)|(chan[i].chVolL==chan[i].chVolR)); + hardResetElapsed+=2; chan[i].freqChanged=false; } - if (chan[i].keyOn) { - //immWrite(0x08,i); + if (chan[i].keyOn && !chan[i].hardReset) { immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x40|(chan[i].chVolR<<7)); chan[i].keyOn=false; } } + + // hard reset handling + if (mustHardReset) { + for (unsigned int i=hardResetElapsed; ifm; + } + + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator op=chan[ch].state.op[i]; + if (isMuted[ch]) { + rWrite(baseAddr+ADDR_TL,127); + } else { + if (KVS(ch,i)) { + if (!chan[ch].active || chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127)); + } + } else { + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + } + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4)); + rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6)); + } + } + if (chan[ch].insChanged) { + /* + if (isMuted[ch]) { + rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); + } else { + rWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7)); + }*/ + rWrite(chanOffs[ch]+ADDR_FMS_AMS,((chan[ch].state.fms&7)<<4)|(chan[ch].state.ams&3)); + rWrite(chanOffs[ch]+ADDR_FMS2_AMS2,((chan[ch].state.fms2&7)<<4)|(chan[ch].state.ams2&3)); + } +} + int DivPlatformTX81Z::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPZ); - if (chan[c.chan].insChanged) { - chan[c.chan].state=ins->fm; - } - chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } - for (int i=0; i<4; i++) { - unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; - DivInstrumentFM::Operator op=chan[c.chan].state.op[i]; - if (isMuted[c.chan]) { - rWrite(baseAddr+ADDR_TL,127); - } else { - if (KVS(c.chan,i)) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127)); - } - } else { - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,op.tl); - } - } - } - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); - rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); - rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); - rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); - rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4)); - rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6)); - } - } - if (chan[c.chan].insChanged) { - /* - if (isMuted[c.chan]) { - rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); - } else { - rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7)); - }*/ - rWrite(chanOffs[c.chan]+ADDR_FMS_AMS,((chan[c.chan].state.fms&7)<<4)|(chan[c.chan].state.ams&3)); - //rWrite(chanOffs[c.chan]+ADDR_FMS_AMS,0x84|((chan[c.chan].state.fms2&7)<<4)|(chan[c.chan].state.ams2&3)); - } + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -498,16 +548,33 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPZ); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } chan[c.chan].baseFreq=NOTE_LINEAR(c.value); chan[c.chan].freqChanged=true; break; } case DIV_CMD_FM_LFO: { - rWrite(0x18,c.value); + lfoValue=c.value; + immWrite(0x18,lfoValue); break; } case DIV_CMD_FM_LFO_WAVE: { - rWrite(0x1b,c.value&3); + lfoShape=c.value&3; + immWrite(0x1b,lfoShape|(lfoShape2<<2)); + break; + } + case DIV_CMD_FM_LFO2: { + lfoValue2=c.value; + immWrite(0x16,lfoValue2); + break; + } + case DIV_CMD_FM_LFO2_WAVE: { + lfoShape2=c.value&3; + immWrite(0x1b,lfoShape|(lfoShape2<<2)); break; } case DIV_CMD_FM_FB: { @@ -785,6 +852,16 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { immWrite(0x19,0x80|pmDepth); break; } + case DIV_CMD_FM_AM2_DEPTH: { + amDepth2=c.value; + immWrite(0x17,amDepth); + break; + } + case DIV_CMD_FM_PM2_DEPTH: { + pmDepth2=c.value; + immWrite(0x17,0x80|pmDepth); + break; + } case DIV_CMD_FM_HARD_RESET: chan[c.chan].hardReset=c.value; break; @@ -855,7 +932,7 @@ void DivPlatformTX81Z::forceIns() { 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)); }*/ rWrite(chanOffs[i]+ADDR_FMS_AMS,((chan[i].state.fms&7)<<4)|(chan[i].state.ams&3)); - //rWrite(chanOffs[i]+ADDR_FMS_AMS,0x84|((chan[i].state.fms2&7)<<4)|(chan[i].state.ams2&3)); + rWrite(chanOffs[i]+ADDR_FMS2_AMS2,((chan[i].state.fms2&7)<<4)|(chan[i].state.ams2&3)); if (chan[i].active) { chan[i].keyOn=true; chan[i].freqChanged=true; @@ -863,6 +940,11 @@ void DivPlatformTX81Z::forceIns() { } immWrite(0x19,amDepth); immWrite(0x19,0x80|pmDepth); + immWrite(0x17,amDepth2); + immWrite(0x17,0x80|pmDepth2); + immWrite(0x18,lfoValue); + immWrite(0x16,lfoValue2); + immWrite(0x1b,lfoShape|(lfoShape2<<2)); } void DivPlatformTX81Z::notifyInsChange(int ins) { @@ -873,6 +955,12 @@ void DivPlatformTX81Z::notifyInsChange(int ins) { } } +void DivPlatformTX81Z::notifyInsDeletion(void* ins) { + for (int i=0; i<8; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + void* DivPlatformTX81Z::getChanState(int ch) { return &chan[ch]; } @@ -927,12 +1015,19 @@ void DivPlatformTX81Z::reset() { delay=0; amDepth=0x7f; pmDepth=0x7f; + amDepth2=0x7f; + pmDepth2=0x7f; + lfoValue=0; + lfoValue2=0; + lfoShape=0; + lfoShape2=0; - //rWrite(0x18,0x10); immWrite(0x18,0x00); // LFO Freq Off + immWrite(0x16,0x00); immWrite(0x19,amDepth); immWrite(0x19,0x80|pmDepth); - //rWrite(0x1b,0x00); + immWrite(0x17,amDepth2); + immWrite(0x17,0x80|pmDepth2); extMode=false; } diff --git a/src/engine/platform/tx81z.h b/src/engine/platform/tx81z.h index 4dea61e70..d1ebd543e 100644 --- a/src/engine/platform/tx81z.h +++ b/src/engine/platform/tx81z.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,7 +45,7 @@ class DivPlatformTX81Z: public DivPlatformOPM { DivDispatchOscBuffer* oscBuf[8]; int baseFreqOff; int pcmL, pcmR, pcmCycles; - unsigned char amDepth, pmDepth; + unsigned char amDepth, pmDepth, amDepth2, pmDepth2; ymfm::ym2414* fm_ymfm; ymfm::ym2414::output_data out_ymfm; @@ -57,6 +57,7 @@ class DivPlatformTX81Z: public DivPlatformOPM { int octave(int freq); int toFreq(int freq); + void commitState(int ch, DivInstrument* ins); friend void putDispatchChip(void*,int); public: @@ -72,6 +73,7 @@ class DivPlatformTX81Z: public DivPlatformOPM { void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); void setFlags(const DivConfig& flags); int getOutputCount(); void poke(unsigned int addr, unsigned short val); diff --git a/src/engine/platform/vb.cpp b/src/engine/platform/vb.cpp index 374aebaaf..3701b5aa4 100644 --- a/src/engine/platform/vb.cpp +++ b/src/engine/platform/vb.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/vb.h b/src/engine/platform/vb.h index 7475c7453..09193f25b 100644 --- a/src/engine/platform/vb.h +++ b/src/engine/platform/vb.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index ba9f95fe0..820f399a5 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -411,7 +411,7 @@ int DivPlatformVERA::getOutputCount() { } void DivPlatformVERA::notifyInsDeletion(void* ins) { - for (int i=0; i<2; i++) { + for (int i=0; i<17; i++) { chan[i].std.notifyInsDeletion((DivInstrument*)ins); } } diff --git a/src/engine/platform/vera.h b/src/engine/platform/vera.h index 762a32c87..8781dc953 100644 --- a/src/engine/platform/vera.h +++ b/src/engine/platform/vera.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp index 3a4e25721..253c4fb76 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/vic20.h b/src/engine/platform/vic20.h index 3fc9fade5..1b8584a75 100644 --- a/src/engine/platform/vic20.h +++ b/src/engine/platform/vic20.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index f798d6e8d..b08255d36 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/vrc6.h b/src/engine/platform/vrc6.h index 5facd13a6..5688cb5c6 100644 --- a/src/engine/platform/vrc6.h +++ b/src/engine/platform/vrc6.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 12e6280d9..091da9f1a 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -903,6 +903,10 @@ bool DivPlatformX1_010::keyOffAffectsArp(int ch) { return true; } +float DivPlatformX1_010::getPostAmp() { + return 4.0f; +} + void DivPlatformX1_010::notifyWaveChange(int wave) { for (int i=0; i<16; i++) { if (chan[i].wave==wave) { diff --git a/src/engine/platform/x1_010.h b/src/engine/platform/x1_010.h index eaa4258af..a3af7b291 100644 --- a/src/engine/platform/x1_010.h +++ b/src/engine/platform/x1_010.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -140,6 +140,7 @@ class DivPlatformX1_010: public DivDispatch, public vgsound_emu_mem_intf { void muteChannel(int ch, bool mute); int getOutputCount(); bool keyOffAffectsArp(int ch); + float getPostAmp(); void setFlags(const DivConfig& flags); void notifyWaveChange(int wave); void notifyInsDeletion(void* ins); diff --git a/src/engine/platform/ym2203.cpp b/src/engine/platform/ym2203.cpp index c8b5ad900..c02b074d1 100644 --- a/src/engine/platform/ym2203.cpp +++ b/src/engine/platform/ym2203.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -412,29 +412,6 @@ void DivPlatformYM2203::tick(bool sysTick) { rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); } } - - if (chan[i].keyOn || chan[i].keyOff) { - if (chan[i].hardReset && chan[i].keyOn) { - for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs[i]|opOffs[j]; - immWrite(baseAddr+ADDR_SL_RR,0x0f); - immWrite(baseAddr+ADDR_TL,0x7f); - oldWrites[baseAddr+ADDR_SL_RR]=-1; - oldWrites[baseAddr+ADDR_TL]=-1; - //rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - } - } - immWrite(0x28,0x00|konOffs[i]); - if (chan[i].hardReset && chan[i].keyOn) { - for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs[i]|opOffs[j]; - for (int k=0; k<100; k++) { - immWrite(baseAddr+ADDR_SL_RR,0x0f); - } - } - } - chan[i].keyOff=false; - } } for (int i=16; i<256; i++) { @@ -444,6 +421,25 @@ void DivPlatformYM2203::tick(bool sysTick) { } } + int hardResetElapsed=0; + bool mustHardReset=false; + + for (int i=0; i<3; i++) { + if (i==2 && extMode) continue; + if (chan[i].keyOn || chan[i].keyOff) { + immWrite(0x28,0x00|konOffs[i]); + if (chan[i].hardReset && chan[i].keyOn) { + mustHardReset=true; + for (int j=0; j<4; j++) { + unsigned short baseAddr=chanOffs[i]|opOffs[j]; + immWrite(baseAddr+ADDR_SL_RR,0x0f); + hardResetElapsed++; + } + } + chan[i].keyOff=false; + } + } + for (int i=0; i<3; i++) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { @@ -465,14 +461,78 @@ void DivPlatformYM2203::tick(bool sysTick) { if (chan[i].freq>0x3fff) chan[i].freq=0x3fff; immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8); immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff); + hardResetElapsed+=2; chan[i].freqChanged=false; } - if (chan[i].keyOn || chan[i].opMaskChanged) { + if ((chan[i].keyOn || chan[i].opMaskChanged) && !chan[i].hardReset) { immWrite(0x28,(chan[i].opMask<<4)|konOffs[i]); + hardResetElapsed++; chan[i].opMaskChanged=false; chan[i].keyOn=false; } } + + // hard reset handling + if (mustHardReset) { + for (unsigned int i=hardResetElapsed; ifm; + chan[ch].opMask= + (chan[ch].state.op[0].enable?1:0)| + (chan[ch].state.op[2].enable?2:0)| + (chan[ch].state.op[1].enable?4:0)| + (chan[ch].state.op[3].enable?8:0); + } + + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator& op=chan[ch].state.op[i]; + if (isMuted[ch]) { + rWrite(baseAddr+ADDR_TL,127); + } else { + if (KVS(ch,i)) { + if (!chan[ch].active || chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127)); + } + } else { + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + } + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } + if (chan[ch].insChanged) { + rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); + } } int DivPlatformYM2203::dispatch(DivCommand c) { @@ -490,43 +550,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) { } } - if (chan[c.chan].insChanged) { - chan[c.chan].state=ins->fm; - chan[c.chan].opMask= - (chan[c.chan].state.op[0].enable?1:0)| - (chan[c.chan].state.op[2].enable?2:0)| - (chan[c.chan].state.op[1].enable?4:0)| - (chan[c.chan].state.op[3].enable?8:0); - } - - for (int i=0; i<4; i++) { - unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; - DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; - if (isMuted[c.chan]) { - rWrite(baseAddr+ADDR_TL,127); - } else { - if (KVS(c.chan,i)) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127)); - } - } else { - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,op.tl); - } - } - } - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); - rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); - rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); - rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); - rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); - } - } - if (chan[c.chan].insChanged) { - rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); - } + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -617,6 +641,11 @@ int DivPlatformYM2203::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); chan[c.chan].freqChanged=true; break; @@ -987,6 +1016,9 @@ void DivPlatformYM2203::notifyInsChange(int ins) { void DivPlatformYM2203::notifyInsDeletion(void* ins) { ay->notifyInsDeletion(ins); + for (int i=0; i<3; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } } void DivPlatformYM2203::setSkipRegisterWrites(bool value) { diff --git a/src/engine/platform/ym2203.h b/src/engine/platform/ym2203.h index 2f4da5b49..fb96a2b60 100644 --- a/src/engine/platform/ym2203.h +++ b/src/engine/platform/ym2203.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -55,6 +55,8 @@ class DivPlatformYM2203: public DivPlatformOPN { friend void putDispatchChip(void*,int); + inline void commitState(int ch, DivInstrument* ins); + void acquire_combo(short** buf, size_t len); void acquire_ymfm(short** buf, size_t len); @@ -73,7 +75,7 @@ class DivPlatformYM2203: public DivPlatformOPN { int getOutputCount(); bool keyOffAffectsArp(int ch); void notifyInsChange(int ins); - void notifyInsDeletion(void* ins); + virtual void notifyInsDeletion(void* ins); void setSkipRegisterWrites(bool val); void poke(unsigned int addr, unsigned short val); void poke(std::vector& wlist); diff --git a/src/engine/platform/ym2203ext.cpp b/src/engine/platform/ym2203ext.cpp index 701ae9e5e..c14be9dc7 100644 --- a/src/engine/platform/ym2203ext.cpp +++ b/src/engine/platform/ym2203ext.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,41 @@ #define CHIP_FREQBASE fmFreqBase #define CHIP_DIVIDER fmDivBase +void DivPlatformYM2203Ext::commitStateExt(int ch, DivInstrument* ins) { + int ordch=orderedOps[ch]; + + if (opChan[ch].insChanged) { + chan[2].state.alg=ins->fm.alg; + if (ch==0 || fbAllOps) { + chan[2].state.fb=ins->fm.fb; + } + chan[2].state.op[ordch]=ins->fm.op[ordch]; + } + + unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; + DivInstrumentFM::Operator& op=chan[2].state.op[ordch]; + // TODO: how does this work?! + if (isOpMuted[ch]) { + rWrite(baseAddr+0x40,127); + } else { + if (opChan[ch].insChanged) { + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); + } + } + if (opChan[ch].insChanged) { + rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); + rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+0x70,op.d2r&31); + rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); + rWrite(baseAddr+0x90,op.ssgEnv&15); + opChan[ch].mask=op.enable; + } + if (opChan[ch].insChanged) { // TODO how does this work? + rWrite(chanOffs[2]+0xb0,(chan[2].state.alg&7)|(chan[2].state.fb<<3)); + } +} + int DivPlatformYM2203Ext::dispatch(DivCommand c) { if (c.chan<2) { return DivPlatformYM2203::dispatch(c); @@ -42,14 +77,6 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); - if (opChan[ch].insChanged) { - chan[2].state.alg=ins->fm.alg; - if (ch==0 || fbAllOps) { - chan[2].state.fb=ins->fm.fb; - } - chan[2].state.op[ordch]=ins->fm.op[ordch]; - } - if (noExtMacros) { opChan[ch].macroInit(NULL); } else { @@ -58,29 +85,8 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) { if (!opChan[ch].std.vol.will) { opChan[ch].outVol=opChan[ch].vol; } - - unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; - DivInstrumentFM::Operator& op=chan[2].state.op[ordch]; - // TODO: how does this work?! - if (isOpMuted[ch]) { - rWrite(baseAddr+0x40,127); - } else { - if (opChan[ch].insChanged) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); - } - } - if (opChan[ch].insChanged) { - rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4)); - rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); - rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7)); - rWrite(baseAddr+0x70,op.d2r&31); - rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); - rWrite(baseAddr+0x90,op.ssgEnv&15); - opChan[ch].mask=op.enable; - } - if (opChan[ch].insChanged) { // TODO how does this work? - rWrite(chanOffs[2]+0xb0,(chan[2].state.alg&7)|(chan[2].state.fb<<3)); - } + + commitStateExt(ch,ins); opChan[ch].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -166,6 +172,11 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { + if (opChan[ch].insChanged) { + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); + commitStateExt(ch,ins); + opChan[ch].insChanged=false; + } opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].freqChanged=true; break; @@ -547,10 +558,10 @@ void DivPlatformYM2203Ext::forceIns() { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (i==2 && extMode) { // extended channel - if (isOpMuted[j]) { + if (isOpMuted[orderedOps[j]]) { rWrite(baseAddr+0x40,127); } else if (KVS(i,j)) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[orderedOps[j]].outVol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } @@ -641,6 +652,13 @@ void DivPlatformYM2203Ext::notifyInsChange(int ins) { } } +void DivPlatformYM2203Ext::notifyInsDeletion(void* ins) { + DivPlatformYM2203::notifyInsDeletion(ins); + for (int i=0; i<4; i++) { + opChan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + int DivPlatformYM2203Ext::init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) { DivPlatformYM2203::init(parent,channels,sugRate,flags); for (int i=0; i<4; i++) { diff --git a/src/engine/platform/ym2203ext.h b/src/engine/platform/ym2203ext.h index 14535e2d1..731e2a202 100644 --- a/src/engine/platform/ym2203ext.h +++ b/src/engine/platform/ym2203ext.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,6 +28,7 @@ class DivPlatformYM2203Ext: public DivPlatformYM2203 { OPNOpChannel opChan[4]; bool isOpMuted[4]; friend void putDispatchChip(void*,int); + inline void commitStateExt(int ch, DivInstrument* ins); public: int dispatch(DivCommand c); void* getChanState(int chan); @@ -39,6 +40,7 @@ class DivPlatformYM2203Ext: public DivPlatformYM2203 { void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); ~DivPlatformYM2203Ext(); diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index 2fa133cf9..1c501fe52 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -547,6 +547,10 @@ void DivPlatformYM2608::tick(bool sysTick) { chan[i].state.ams=chan[i].std.ams.val; 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.ex3.had) { + lfoValue=(chan[i].std.ex3.val>7)?0:(8|(chan[i].std.ex3.val&7)); + rWrite(0x22,lfoValue); + } if (chan[i].std.ex4.had && chan[i].active) { chan[i].opMask=chan[i].std.ex4.val&15; chan[i].opMaskChanged=true; @@ -604,29 +608,6 @@ void DivPlatformYM2608::tick(bool sysTick) { rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); } } - - if (chan[i].keyOn || chan[i].keyOff) { - if (chan[i].hardReset && chan[i].keyOn) { - for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs[i]|opOffs[j]; - immWrite(baseAddr+ADDR_SL_RR,0x0f); - immWrite(baseAddr+ADDR_TL,0x7f); - oldWrites[baseAddr+ADDR_SL_RR]=-1; - oldWrites[baseAddr+ADDR_TL]=-1; - //rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - } - } - immWrite(0x28,0x00|konOffs[i]); - if (chan[i].hardReset && chan[i].keyOn) { - for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs[i]|opOffs[j]; - for (int k=0; k<100; k++) { - immWrite(baseAddr+ADDR_SL_RR,0x0f); - } - } - } - chan[i].keyOff=false; - } } for (int i=16; i<512; i++) { @@ -636,6 +617,25 @@ void DivPlatformYM2608::tick(bool sysTick) { } } + int hardResetElapsed=0; + bool mustHardReset=false; + + for (int i=0; i<6; i++) { + if (i==2 && extMode) continue; + if (chan[i].keyOn || chan[i].keyOff) { + immWrite(0x28,0x00|konOffs[i]); + if (chan[i].hardReset && chan[i].keyOn) { + mustHardReset=true; + for (int j=0; j<4; j++) { + unsigned short baseAddr=chanOffs[i]|opOffs[j]; + immWrite(baseAddr+ADDR_SL_RR,0x0f); + hardResetElapsed++; + } + } + chan[i].keyOff=false; + } + } + for (int i=0; i<6; i++) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { @@ -657,10 +657,12 @@ void DivPlatformYM2608::tick(bool sysTick) { if (chan[i].freq>0x3fff) chan[i].freq=0x3fff; immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8); immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff); + hardResetElapsed+=2; chan[i].freqChanged=false; } - if (chan[i].keyOn || chan[i].opMaskChanged) { + if ((chan[i].keyOn || chan[i].opMaskChanged) && !chan[i].hardReset) { immWrite(0x28,(chan[i].opMask<<4)|konOffs[i]); + hardResetElapsed++; chan[i].opMaskChanged=false; chan[i].keyOn=false; } @@ -677,6 +679,7 @@ void DivPlatformYM2608::tick(bool sysTick) { if (globalRSSVolume!=(chan[i].std.duty.val&0x3f)) { globalRSSVolume=chan[i].std.duty.val&0x3f; immWrite(0x11,globalRSSVolume); + hardResetElapsed++; } } if (chan[i].std.panL.had) { @@ -689,6 +692,7 @@ void DivPlatformYM2608::tick(bool sysTick) { } if (!isMuted[i] && (chan[i].std.vol.had || chan[i].std.panL.had)) { immWrite(0x18+(i-9),isMuted[i]?0:((chan[i].pan<<6)|chan[i].outVol)); + hardResetElapsed++; } } if (chan[i].keyOff) { @@ -707,6 +711,7 @@ void DivPlatformYM2608::tick(bool sysTick) { if (chan[15].std.vol.had) { chan[15].outVol=(chan[15].vol*MIN(chan[15].macroVolMul,chan[15].std.vol.val))/chan[15].macroVolMul; immWrite(0x10b,chan[15].outVol); + hardResetElapsed++; } if (NEW_ARP_STRAT) { @@ -733,6 +738,7 @@ void DivPlatformYM2608::tick(bool sysTick) { chan[15].pan=chan[15].std.panL.val&3; if (!isMuted[15]) { immWrite(0x101,(isMuted[15]?0:(chan[15].pan<<6))|2); + hardResetElapsed++; } } } @@ -753,12 +759,17 @@ void DivPlatformYM2608::tick(bool sysTick) { } immWrite(0x109,chan[15].freq&0xff); immWrite(0x10a,(chan[15].freq>>8)&0xff); + hardResetElapsed+=2; if (chan[15].keyOn || chan[15].keyOff) { - if (chan[15].keyOff) immWrite(0x100,0x01); // reset + if (chan[15].keyOff) { + immWrite(0x100,0x01); // reset + hardResetElapsed++; + } if (chan[15].active && chan[15].keyOn && !chan[15].keyOff) { if (chan[15].sample>=0 && chan[15].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[15].sample); immWrite(0x100,(s->isLoopable())?0xb0:0xa0); // start/repeat + hardResetElapsed++; } } chan[15].keyOn=false; @@ -769,11 +780,13 @@ void DivPlatformYM2608::tick(bool sysTick) { if (writeRSSOff) { immWrite(0x10,0x80|writeRSSOff); + hardResetElapsed++; writeRSSOff=0; } if (writeRSSOn) { immWrite(0x10,writeRSSOn); + hardResetElapsed++; writeRSSOn=0; } @@ -783,8 +796,68 @@ void DivPlatformYM2608::tick(bool sysTick) { for (DivRegWrite& i: ay->getRegisterWrites()) { if (i.addr>15) continue; immWrite(i.addr&15,i.val); + hardResetElapsed++; } ay->getRegisterWrites().clear(); + + // hard reset handling + if (mustHardReset) { + for (unsigned int i=hardResetElapsed; ifm; + chan[ch].opMask= + (chan[ch].state.op[0].enable?1:0)| + (chan[ch].state.op[2].enable?2:0)| + (chan[ch].state.op[1].enable?4:0)| + (chan[ch].state.op[3].enable?8:0); + } + + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator& op=chan[ch].state.op[i]; + if (KVS(ch,i)) { + if (!chan[ch].active || chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127)); + } + } else { + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } + if (chan[ch].insChanged) { + rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); + rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); + } } int DivPlatformYM2608::dispatch(DivCommand c) { @@ -900,40 +973,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) { } } - if (chan[c.chan].insChanged) { - chan[c.chan].state=ins->fm; - chan[c.chan].opMask= - (chan[c.chan].state.op[0].enable?1:0)| - (chan[c.chan].state.op[2].enable?2:0)| - (chan[c.chan].state.op[1].enable?4:0)| - (chan[c.chan].state.op[3].enable?8:0); - } - - for (int i=0; i<4; i++) { - unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; - DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; - if (KVS(c.chan,i)) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127)); - } - } else { - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,op.tl); - } - } - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); - rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); - rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); - rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); - rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); - } - } - if (chan[c.chan].insChanged) { - rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); - rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4)); - } + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -1061,6 +1101,13 @@ int DivPlatformYM2608::dispatch(DivCommand c) { break; case DIV_CMD_LEGATO: { if (c.chan==15 && !chan[c.chan].furnacePCM) break; + if (c.chan<=psgChanOffs) { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } + } chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value); chan[c.chan].freqChanged=true; break; @@ -1456,6 +1503,12 @@ void DivPlatformYM2608::notifyInsChange(int ins) { void DivPlatformYM2608::notifyInsDeletion(void* ins) { ay->notifyInsDeletion(ins); + for (int i=0; i& wlist); diff --git a/src/engine/platform/ym2608Interface.cpp b/src/engine/platform/ym2608Interface.cpp index 242721f09..d357caa23 100644 --- a/src/engine/platform/ym2608Interface.cpp +++ b/src/engine/platform/ym2608Interface.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/ym2608ext.cpp b/src/engine/platform/ym2608ext.cpp index 0a6abe7a5..96409ce76 100644 --- a/src/engine/platform/ym2608ext.cpp +++ b/src/engine/platform/ym2608ext.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,44 @@ #define CHIP_FREQBASE fmFreqBase #define CHIP_DIVIDER fmDivBase +void DivPlatformYM2608Ext::commitStateExt(int ch, DivInstrument* ins) { + int ordch=orderedOps[ch]; + + if (opChan[ch].insChanged) { + chan[2].state.alg=ins->fm.alg; + if (ch==0 || fbAllOps) { + chan[2].state.fb=ins->fm.fb; + } + chan[2].state.fms=ins->fm.fms; + chan[2].state.ams=ins->fm.ams; + chan[2].state.op[ordch]=ins->fm.op[ordch]; + } + + unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; + DivInstrumentFM::Operator& op=chan[2].state.op[ordch]; + // TODO: how does this work?! + if (isOpMuted[ch]) { + rWrite(baseAddr+0x40,127); + } else { + if (opChan[ch].insChanged) { + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); + } + } + if (opChan[ch].insChanged) { + rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); + rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+0x70,op.d2r&31); + rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); + rWrite(baseAddr+0x90,op.ssgEnv&15); + opChan[ch].mask=op.enable; + } + if (opChan[ch].insChanged) { // TODO how does this work? + rWrite(chanOffs[2]+0xb0,(chan[2].state.alg&7)|(chan[2].state.fb<<3)); + rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4)); + } +} + int DivPlatformYM2608Ext::dispatch(DivCommand c) { if (c.chan<2) { return DivPlatformYM2608::dispatch(c); @@ -42,16 +80,6 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); - if (opChan[ch].insChanged) { - chan[2].state.alg=ins->fm.alg; - if (ch==0 || fbAllOps) { - chan[2].state.fb=ins->fm.fb; - } - chan[2].state.fms=ins->fm.fms; - chan[2].state.ams=ins->fm.ams; - chan[2].state.op[ordch]=ins->fm.op[ordch]; - } - if (noExtMacros) { opChan[ch].macroInit(NULL); } else { @@ -60,30 +88,8 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) { if (!opChan[ch].std.vol.will) { opChan[ch].outVol=opChan[ch].vol; } - - unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; - DivInstrumentFM::Operator& op=chan[2].state.op[ordch]; - // TODO: how does this work?! - if (isOpMuted[ch]) { - rWrite(baseAddr+0x40,127); - } else { - if (opChan[ch].insChanged) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); - } - } - if (opChan[ch].insChanged) { - rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4)); - rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); - rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7)); - rWrite(baseAddr+0x70,op.d2r&31); - rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); - rWrite(baseAddr+0x90,op.ssgEnv&15); - opChan[ch].mask=op.enable; - } - if (opChan[ch].insChanged) { // TODO how does this work? - rWrite(chanOffs[2]+0xb0,(chan[2].state.alg&7)|(chan[2].state.fb<<3)); - rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4)); - } + + commitStateExt(ch,ins); opChan[ch].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -184,6 +190,11 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { + if (opChan[ch].insChanged) { + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); + commitStateExt(ch,ins); + opChan[ch].insChanged=false; + } opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].freqChanged=true; break; @@ -571,10 +582,10 @@ void DivPlatformYM2608Ext::forceIns() { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (i==2 && extMode) { // extended channel - if (isOpMuted[j]) { + if (isOpMuted[orderedOps[j]]) { rWrite(baseAddr+0x40,127); } else if (KVS(i,j)) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[orderedOps[j]].outVol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } @@ -679,6 +690,13 @@ void DivPlatformYM2608Ext::notifyInsChange(int ins) { } } +void DivPlatformYM2608Ext::notifyInsDeletion(void* ins) { + DivPlatformYM2608::notifyInsDeletion(ins); + for (int i=0; i<4; i++) { + opChan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + int DivPlatformYM2608Ext::init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) { DivPlatformYM2608::init(parent,channels,sugRate,flags); for (int i=0; i<4; i++) { diff --git a/src/engine/platform/ym2608ext.h b/src/engine/platform/ym2608ext.h index 01afb3617..c9348b48f 100644 --- a/src/engine/platform/ym2608ext.h +++ b/src/engine/platform/ym2608ext.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,6 +28,7 @@ class DivPlatformYM2608Ext: public DivPlatformYM2608 { OPNOpChannelStereo opChan[4]; bool isOpMuted[4]; friend void putDispatchChip(void*,int); + inline void commitStateExt(int ch, DivInstrument* ins); public: int dispatch(DivCommand c); void* getChanState(int chan); @@ -39,6 +40,7 @@ class DivPlatformYM2608Ext: public DivPlatformYM2608 { void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); ~DivPlatformYM2608Ext(); diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index eb398d179..94da94447 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -480,6 +480,10 @@ void DivPlatformYM2610::tick(bool sysTick) { chan[i].state.ams=chan[i].std.ams.val; 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.ex3.had) { + lfoValue=(chan[i].std.ex3.val>7)?0:(8|(chan[i].std.ex3.val&7)); + rWrite(0x22,lfoValue); + } if (chan[i].std.ex4.had && chan[i].active) { chan[i].opMask=chan[i].std.ex4.val&15; chan[i].opMaskChanged=true; @@ -537,29 +541,6 @@ void DivPlatformYM2610::tick(bool sysTick) { rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); } } - - if (chan[i].keyOn || chan[i].keyOff) { - if (chan[i].hardReset && chan[i].keyOn) { - for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs[i]|opOffs[j]; - immWrite(baseAddr+ADDR_SL_RR,0x0f); - immWrite(baseAddr+ADDR_TL,0x7f); - oldWrites[baseAddr+ADDR_SL_RR]=-1; - oldWrites[baseAddr+ADDR_TL]=-1; - //rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - } - } - immWrite(0x28,0x00|konOffs[i]); - if (chan[i].hardReset && chan[i].keyOn) { - for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs[i]|opOffs[j]; - for (int k=0; k<100; k++) { - immWrite(baseAddr+ADDR_SL_RR,0x0f); - } - } - } - chan[i].keyOff=false; - } } for (int i=16; i<512; i++) { @@ -569,6 +550,25 @@ void DivPlatformYM2610::tick(bool sysTick) { } } + int hardResetElapsed=0; + bool mustHardReset=false; + + for (int i=0; i0x3fff) chan[i].freq=0x3fff; immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8); immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff); + hardResetElapsed+=2; chan[i].freqChanged=false; } - if (chan[i].keyOn || chan[i].opMaskChanged) { + if ((chan[i].keyOn || chan[i].opMaskChanged) && !chan[i].hardReset) { immWrite(0x28,(chan[i].opMask<<4)|konOffs[i]); + hardResetElapsed++; chan[i].opMaskChanged=false; chan[i].keyOn=false; } @@ -610,6 +612,7 @@ void DivPlatformYM2610::tick(bool sysTick) { if (globalADPCMAVolume!=(chan[i].std.duty.val&0x3f)) { globalADPCMAVolume=chan[i].std.duty.val&0x3f; immWrite(0x101,globalADPCMAVolume); + hardResetElapsed++; } } if (chan[i].std.panL.had) { @@ -622,6 +625,7 @@ void DivPlatformYM2610::tick(bool sysTick) { } if (!isMuted[i] && (chan[i].std.vol.had || chan[i].std.panL.had)) { immWrite(0x108+(i-adpcmAChanOffs),isMuted[i]?0:((chan[i].pan<<6)|chan[i].outVol)); + hardResetElapsed++; } } if (chan[i].keyOff) { @@ -642,6 +646,7 @@ void DivPlatformYM2610::tick(bool sysTick) { if (chan[adpcmBChanOffs].std.vol.had) { chan[adpcmBChanOffs].outVol=(chan[adpcmBChanOffs].vol*MIN(chan[adpcmBChanOffs].macroVolMul,chan[adpcmBChanOffs].std.vol.val))/chan[adpcmBChanOffs].macroVolMul; immWrite(0x1b,chan[adpcmBChanOffs].outVol); + hardResetElapsed++; } if (NEW_ARP_STRAT) { @@ -668,6 +673,7 @@ void DivPlatformYM2610::tick(bool sysTick) { chan[adpcmBChanOffs].pan=chan[adpcmBChanOffs].std.panL.val&3; if (!isMuted[adpcmBChanOffs]) { immWrite(0x11,(isMuted[adpcmBChanOffs]?0:(chan[adpcmBChanOffs].pan<<6))); + hardResetElapsed++; } } } @@ -687,13 +693,16 @@ void DivPlatformYM2610::tick(bool sysTick) { } immWrite(0x19,chan[adpcmBChanOffs].freq&0xff); immWrite(0x1a,(chan[adpcmBChanOffs].freq>>8)&0xff); + hardResetElapsed+=2; } if (chan[adpcmBChanOffs].keyOn || chan[adpcmBChanOffs].keyOff) { immWrite(0x10,0x01); // reset + hardResetElapsed++; if (chan[adpcmBChanOffs].active && chan[adpcmBChanOffs].keyOn && !chan[adpcmBChanOffs].keyOff) { if (chan[adpcmBChanOffs].sample>=0 && chan[adpcmBChanOffs].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[adpcmBChanOffs].sample); immWrite(0x10,(s->isLoopable())?0x90:0x80); // start/repeat + hardResetElapsed++; } } chan[adpcmBChanOffs].keyOn=false; @@ -704,11 +713,13 @@ void DivPlatformYM2610::tick(bool sysTick) { if (writeADPCMAOff) { immWrite(0x100,0x80|writeADPCMAOff); + hardResetElapsed++; writeADPCMAOff=0; } if (writeADPCMAOn) { immWrite(0x100,writeADPCMAOn); + hardResetElapsed++; writeADPCMAOn=0; } @@ -718,8 +729,68 @@ void DivPlatformYM2610::tick(bool sysTick) { for (DivRegWrite& i: ay->getRegisterWrites()) { if (i.addr>15) continue; immWrite(i.addr&15,i.val); + hardResetElapsed++; } ay->getRegisterWrites().clear(); + + // hard reset handling + if (mustHardReset) { + for (unsigned int i=hardResetElapsed; ifm; + chan[ch].opMask= + (chan[ch].state.op[0].enable?1:0)| + (chan[ch].state.op[2].enable?2:0)| + (chan[ch].state.op[1].enable?4:0)| + (chan[ch].state.op[3].enable?8:0); + } + + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator& op=chan[ch].state.op[i]; + if (KVS(ch,i)) { + if (!chan[ch].active || chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127)); + } + } else { + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } + if (chan[ch].insChanged) { + rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); + rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); + } } int DivPlatformYM2610::dispatch(DivCommand c) { @@ -876,40 +947,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { } } - if (chan[c.chan].insChanged) { - chan[c.chan].state=ins->fm; - chan[c.chan].opMask= - (chan[c.chan].state.op[0].enable?1:0)| - (chan[c.chan].state.op[2].enable?2:0)| - (chan[c.chan].state.op[1].enable?4:0)| - (chan[c.chan].state.op[3].enable?8:0); - } - - for (int i=0; i<4; i++) { - unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; - DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; - if (KVS(c.chan,i)) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127)); - } - } else { - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,op.tl); - } - } - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); - rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); - rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); - rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); - rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); - } - } - if (chan[c.chan].insChanged) { - rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); - rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4)); - } + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -1037,6 +1075,13 @@ int DivPlatformYM2610::dispatch(DivCommand c) { break; case DIV_CMD_LEGATO: { if (c.chan==adpcmBChanOffs && !chan[c.chan].furnacePCM) break; + if (c.chan<=psgChanOffs) { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } + } chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value); chan[c.chan].freqChanged=true; break; @@ -1404,6 +1449,12 @@ void DivPlatformYM2610::notifyInsChange(int ins) { void DivPlatformYM2610::notifyInsDeletion(void* ins) { ay->notifyInsDeletion(ins); + for (int i=0; i& wlist); diff --git a/src/engine/platform/ym2610Interface.cpp b/src/engine/platform/ym2610Interface.cpp index 1b13b3743..916cab0ad 100644 --- a/src/engine/platform/ym2610Interface.cpp +++ b/src/engine/platform/ym2610Interface.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 8d4b16ad7..79792df01 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -547,6 +547,10 @@ void DivPlatformYM2610B::tick(bool sysTick) { chan[i].state.ams=chan[i].std.ams.val; 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.ex3.had) { + lfoValue=(chan[i].std.ex3.val>7)?0:(8|(chan[i].std.ex3.val&7)); + rWrite(0x22,lfoValue); + } if (chan[i].std.ex4.had && chan[i].active) { chan[i].opMask=chan[i].std.ex4.val&15; chan[i].opMaskChanged=true; @@ -604,29 +608,6 @@ void DivPlatformYM2610B::tick(bool sysTick) { rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); } } - - if (chan[i].keyOn || chan[i].keyOff) { - if (chan[i].hardReset && chan[i].keyOn) { - for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs[i]|opOffs[j]; - immWrite(baseAddr+ADDR_SL_RR,0x0f); - immWrite(baseAddr+ADDR_TL,0x7f); - oldWrites[baseAddr+ADDR_SL_RR]=-1; - oldWrites[baseAddr+ADDR_TL]=-1; - //rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - } - } - immWrite(0x28,0x00|konOffs[i]); - if (chan[i].hardReset && chan[i].keyOn) { - for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs[i]|opOffs[j]; - for (int k=0; k<100; k++) { - immWrite(baseAddr+ADDR_SL_RR,0x0f); - } - } - } - chan[i].keyOff=false; - } } for (int i=16; i<512; i++) { @@ -636,6 +617,25 @@ void DivPlatformYM2610B::tick(bool sysTick) { } } + int hardResetElapsed=0; + bool mustHardReset=false; + + for (int i=0; i0x3fff) chan[i].freq=0x3fff; immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8); immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff); + hardResetElapsed+=2; chan[i].freqChanged=false; } - if (chan[i].keyOn || chan[i].opMaskChanged) { + if ((chan[i].keyOn || chan[i].opMaskChanged) && !chan[i].hardReset) { immWrite(0x28,(chan[i].opMask<<4)|konOffs[i]); + hardResetElapsed++; chan[i].opMaskChanged=false; chan[i].keyOn=false; } @@ -677,6 +679,7 @@ void DivPlatformYM2610B::tick(bool sysTick) { if (globalADPCMAVolume!=(chan[i].std.duty.val&0x3f)) { globalADPCMAVolume=chan[i].std.duty.val&0x3f; immWrite(0x101,globalADPCMAVolume); + hardResetElapsed++; } } if (chan[i].std.panL.had) { @@ -689,6 +692,7 @@ void DivPlatformYM2610B::tick(bool sysTick) { } if (!isMuted[i] && (chan[i].std.vol.had || chan[i].std.panL.had)) { immWrite(0x108+(i-adpcmAChanOffs),isMuted[i]?0:((chan[i].pan<<6)|chan[i].outVol)); + hardResetElapsed++; } } if (chan[i].keyOff) { @@ -709,6 +713,7 @@ void DivPlatformYM2610B::tick(bool sysTick) { if (chan[adpcmBChanOffs].std.vol.had) { chan[adpcmBChanOffs].outVol=(chan[adpcmBChanOffs].vol*MIN(chan[adpcmBChanOffs].macroVolMul,chan[adpcmBChanOffs].std.vol.val))/chan[adpcmBChanOffs].macroVolMul; immWrite(0x1b,chan[adpcmBChanOffs].outVol); + hardResetElapsed++; } if (NEW_ARP_STRAT) { @@ -735,6 +740,7 @@ void DivPlatformYM2610B::tick(bool sysTick) { chan[adpcmBChanOffs].pan=chan[adpcmBChanOffs].std.panL.val&3; if (!isMuted[adpcmBChanOffs]) { immWrite(0x11,(isMuted[adpcmBChanOffs]?0:(chan[adpcmBChanOffs].pan<<6))); + hardResetElapsed++; } } } @@ -754,13 +760,16 @@ void DivPlatformYM2610B::tick(bool sysTick) { } immWrite(0x19,chan[adpcmBChanOffs].freq&0xff); immWrite(0x1a,(chan[adpcmBChanOffs].freq>>8)&0xff); + hardResetElapsed+=2; } if (chan[adpcmBChanOffs].keyOn || chan[adpcmBChanOffs].keyOff) { immWrite(0x10,0x01); // reset + hardResetElapsed++; if (chan[adpcmBChanOffs].active && chan[adpcmBChanOffs].keyOn && !chan[adpcmBChanOffs].keyOff) { if (chan[adpcmBChanOffs].sample>=0 && chan[adpcmBChanOffs].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[adpcmBChanOffs].sample); immWrite(0x10,(s->isLoopable())?0x90:0x80); // start/repeat + hardResetElapsed++; } } chan[adpcmBChanOffs].keyOn=false; @@ -771,11 +780,13 @@ void DivPlatformYM2610B::tick(bool sysTick) { if (writeADPCMAOff) { immWrite(0x100,0x80|writeADPCMAOff); + hardResetElapsed++; writeADPCMAOff=0; } if (writeADPCMAOn) { immWrite(0x100,writeADPCMAOn); + hardResetElapsed++; writeADPCMAOn=0; } @@ -785,8 +796,68 @@ void DivPlatformYM2610B::tick(bool sysTick) { for (DivRegWrite& i: ay->getRegisterWrites()) { if (i.addr>15) continue; immWrite(i.addr&15,i.val); + hardResetElapsed++; } ay->getRegisterWrites().clear(); + + // hard reset handling + if (mustHardReset) { + for (unsigned int i=hardResetElapsed; ifm; + chan[ch].opMask= + (chan[ch].state.op[0].enable?1:0)| + (chan[ch].state.op[2].enable?2:0)| + (chan[ch].state.op[1].enable?4:0)| + (chan[ch].state.op[3].enable?8:0); + } + + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator& op=chan[ch].state.op[i]; + if (KVS(ch,i)) { + if (!chan[ch].active || chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[ch].outVol&0x7f,127)); + } + } else { + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + if (chan[ch].insChanged) { + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } + if (chan[ch].insChanged) { + rWrite(chanOffs[ch]+ADDR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); + rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); + } } int DivPlatformYM2610B::dispatch(DivCommand c) { @@ -943,40 +1014,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { } } - if (chan[c.chan].insChanged) { - chan[c.chan].state=ins->fm; - chan[c.chan].opMask= - (chan[c.chan].state.op[0].enable?1:0)| - (chan[c.chan].state.op[2].enable?2:0)| - (chan[c.chan].state.op[1].enable?4:0)| - (chan[c.chan].state.op[3].enable?8:0); - } - - for (int i=0; i<4; i++) { - unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; - DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; - if (KVS(c.chan,i)) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127)); - } - } else { - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,op.tl); - } - } - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); - rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); - rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); - rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); - rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); - rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); - } - } - if (chan[c.chan].insChanged) { - rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); - rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4)); - } + commitState(c.chan,ins); chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -1104,6 +1142,13 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { break; case DIV_CMD_LEGATO: { if (c.chan==adpcmBChanOffs && !chan[c.chan].furnacePCM) break; + if (c.chan<=psgChanOffs) { + if (chan[c.chan].insChanged) { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); + commitState(c.chan,ins); + chan[c.chan].insChanged=false; + } + } chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value); chan[c.chan].freqChanged=true; break; @@ -1475,6 +1520,12 @@ void DivPlatformYM2610B::notifyInsChange(int ins) { void DivPlatformYM2610B::notifyInsDeletion(void* ins) { ay->notifyInsDeletion(ins); + for (int i=0; i& wlist); diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index c8d0b6454..e025ec2fa 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,44 @@ #include "ym2610bext.h" #include +void DivPlatformYM2610BExt::commitStateExt(int ch, DivInstrument* ins) { + int ordch=orderedOps[ch]; + + if (opChan[ch].insChanged) { + chan[extChanOffs].state.alg=ins->fm.alg; + if (ch==0 || fbAllOps) { + chan[extChanOffs].state.fb=ins->fm.fb; + } + chan[extChanOffs].state.fms=ins->fm.fms; + chan[extChanOffs].state.ams=ins->fm.ams; + chan[extChanOffs].state.op[ordch]=ins->fm.op[ordch]; + } + + unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch]; + DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[ordch]; + // TODO: how does this work?! + if (isOpMuted[ch]) { + rWrite(baseAddr+0x40,127); + } else { + if (opChan[ch].insChanged) { + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); + } + } + if (opChan[ch].insChanged) { + rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); + rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+0x70,op.d2r&31); + rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); + rWrite(baseAddr+0x90,op.ssgEnv&15); + opChan[ch].mask=op.enable; + } + if (opChan[ch].insChanged) { // TODO how does this work? + rWrite(chanOffs[extChanOffs]+0xb0,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3)); + rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } +} + int DivPlatformYM2610BExt::dispatch(DivCommand c) { if (c.changetIns(opChan[ch].ins,DIV_INS_FM); - if (opChan[ch].insChanged) { - chan[extChanOffs].state.alg=ins->fm.alg; - if (ch==0 || fbAllOps) { - chan[extChanOffs].state.fb=ins->fm.fb; - } - chan[extChanOffs].state.fms=ins->fm.fms; - chan[extChanOffs].state.ams=ins->fm.ams; - chan[extChanOffs].state.op[ordch]=ins->fm.op[ordch]; - } - if (noExtMacros) { opChan[ch].macroInit(NULL); } else { @@ -56,30 +84,8 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { if (!opChan[ch].std.vol.will) { opChan[ch].outVol=opChan[ch].vol; } - - unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch]; - DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[ordch]; - // TODO: how does this work?! - if (isOpMuted[ch]) { - rWrite(baseAddr+0x40,127); - } else { - if (opChan[ch].insChanged) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); - } - } - if (opChan[ch].insChanged) { - rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4)); - rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); - rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7)); - rWrite(baseAddr+0x70,op.d2r&31); - rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); - rWrite(baseAddr+0x90,op.ssgEnv&15); - opChan[ch].mask=op.enable; - } - if (opChan[ch].insChanged) { // TODO how does this work? - rWrite(chanOffs[extChanOffs]+0xb0,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3)); - rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); - } + + commitStateExt(ch,ins); opChan[ch].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -180,6 +186,11 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { + if (opChan[ch].insChanged) { + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); + commitStateExt(ch,ins); + opChan[ch].insChanged=false; + } opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].freqChanged=true; break; @@ -567,10 +578,10 @@ void DivPlatformYM2610BExt::forceIns() { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (i==extChanOffs && extMode) { // extended channel - if (isOpMuted[j]) { + if (isOpMuted[orderedOps[j]]) { rWrite(baseAddr+0x40,127); } else if (KVS(i,j)) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[orderedOps[j]].outVol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } @@ -670,6 +681,13 @@ void DivPlatformYM2610BExt::notifyInsChange(int ins) { } } +void DivPlatformYM2610BExt::notifyInsDeletion(void* ins) { + DivPlatformYM2610B::notifyInsDeletion(ins); + for (int i=0; i<4; i++) { + opChan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + int DivPlatformYM2610BExt::init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) { DivPlatformYM2610B::init(parent,channels,sugRate,flags); for (int i=0; i<4; i++) { diff --git a/src/engine/platform/ym2610bext.h b/src/engine/platform/ym2610bext.h index c77649597..b14ba99c4 100644 --- a/src/engine/platform/ym2610bext.h +++ b/src/engine/platform/ym2610bext.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,6 +28,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B { OPNOpChannelStereo opChan[4]; bool isOpMuted[4]; friend void putDispatchChip(void*,int); + inline void commitStateExt(int ch, DivInstrument* ins); public: int dispatch(DivCommand c); void* getChanState(int chan); @@ -39,6 +40,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B { void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); ~DivPlatformYM2610BExt(); diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 54bbd2b7e..ccb877483 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,44 @@ #include "ym2610ext.h" #include +void DivPlatformYM2610Ext::commitStateExt(int ch, DivInstrument* ins) { + int ordch=orderedOps[ch]; + + if (opChan[ch].insChanged) { + chan[extChanOffs].state.alg=ins->fm.alg; + if (ch==0 || fbAllOps) { + chan[extChanOffs].state.fb=ins->fm.fb; + } + chan[extChanOffs].state.fms=ins->fm.fms; + chan[extChanOffs].state.ams=ins->fm.ams; + chan[extChanOffs].state.op[ordch]=ins->fm.op[ordch]; + } + + unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch]; + DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[ordch]; + // TODO: how does this work?! + if (isOpMuted[ch]) { + rWrite(baseAddr+0x40,127); + } else { + if (opChan[ch].insChanged) { + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); + } + } + if (opChan[ch].insChanged) { + rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); + rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+0x70,op.d2r&31); + rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); + rWrite(baseAddr+0x90,op.ssgEnv&15); + opChan[ch].mask=op.enable; + } + if (opChan[ch].insChanged) { // TODO how does this work? + rWrite(chanOffs[extChanOffs]+0xb0,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3)); + rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); + } +} + int DivPlatformYM2610Ext::dispatch(DivCommand c) { if (c.changetIns(opChan[ch].ins,DIV_INS_FM); - if (opChan[ch].insChanged) { - chan[extChanOffs].state.alg=ins->fm.alg; - if (ch==0 || fbAllOps) { - chan[extChanOffs].state.fb=ins->fm.fb; - } - chan[extChanOffs].state.fms=ins->fm.fms; - chan[extChanOffs].state.ams=ins->fm.ams; - chan[extChanOffs].state.op[ordch]=ins->fm.op[ordch]; - } - if (noExtMacros) { opChan[ch].macroInit(NULL); } else { @@ -56,30 +84,8 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { if (!opChan[ch].std.vol.will) { opChan[ch].outVol=opChan[ch].vol; } - - unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch]; - DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[ordch]; - // TODO: how does this work?! - if (isOpMuted[ch]) { - rWrite(baseAddr+0x40,127); - } else { - if (opChan[ch].insChanged) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127)); - } - } - if (opChan[ch].insChanged) { - rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4)); - rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); - rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7)); - rWrite(baseAddr+0x70,op.d2r&31); - rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); - rWrite(baseAddr+0x90,op.ssgEnv&15); - opChan[ch].mask=op.enable; - } - if (opChan[ch].insChanged) { // TODO how does this work? - rWrite(chanOffs[extChanOffs]+0xb0,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3)); - rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4)); - } + + commitStateExt(ch,ins); opChan[ch].insChanged=false; if (c.value!=DIV_NOTE_NULL) { @@ -180,6 +186,11 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { + if (opChan[ch].insChanged) { + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); + commitStateExt(ch,ins); + opChan[ch].insChanged=false; + } opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].freqChanged=true; break; @@ -567,10 +578,10 @@ void DivPlatformYM2610Ext::forceIns() { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (i==extChanOffs && extMode) { // extended channel - if (isOpMuted[j]) { + if (isOpMuted[orderedOps[j]]) { rWrite(baseAddr+0x40,127); } else if (KVS(i,j)) { - rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[orderedOps[j]].outVol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } @@ -670,6 +681,13 @@ void DivPlatformYM2610Ext::notifyInsChange(int ins) { } } +void DivPlatformYM2610Ext::notifyInsDeletion(void* ins) { + DivPlatformYM2610::notifyInsDeletion(ins); + for (int i=0; i<4; i++) { + opChan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + int DivPlatformYM2610Ext::init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) { DivPlatformYM2610::init(parent,channels,sugRate,flags); for (int i=0; i<4; i++) { diff --git a/src/engine/platform/ym2610ext.h b/src/engine/platform/ym2610ext.h index 5e78c53f4..2ce887c36 100644 --- a/src/engine/platform/ym2610ext.h +++ b/src/engine/platform/ym2610ext.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,6 +28,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 { OPNOpChannelStereo opChan[4]; bool isOpMuted[4]; friend void putDispatchChip(void*,int); + inline void commitStateExt(int ch, DivInstrument* ins); public: int dispatch(DivCommand c); void* getChanState(int chan); @@ -39,6 +40,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 { void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void notifyInsChange(int ins); + void notifyInsDeletion(void* ins); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void quit(); ~DivPlatformYM2610Ext(); diff --git a/src/engine/platform/ym2610shared.h b/src/engine/platform/ym2610shared.h index dfcf571da..5af52ba6f 100644 --- a/src/engine/platform/ym2610shared.h +++ b/src/engine/platform/ym2610shared.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/ymz280b.cpp b/src/engine/platform/ymz280b.cpp index a5fdf10bd..e10dda43f 100644 --- a/src/engine/platform/ymz280b.cpp +++ b/src/engine/platform/ymz280b.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/ymz280b.h b/src/engine/platform/ymz280b.h index 78ea544ba..3fb4a7dc6 100644 --- a/src/engine/platform/ymz280b.h +++ b/src/engine/platform/ymz280b.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/zxbeeper.cpp b/src/engine/platform/zxbeeper.cpp index f8762c7f2..047dc2cb5 100644 --- a/src/engine/platform/zxbeeper.cpp +++ b/src/engine/platform/zxbeeper.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/platform/zxbeeper.h b/src/engine/platform/zxbeeper.h index a9b3594c3..3e120354b 100644 --- a/src/engine/platform/zxbeeper.h +++ b/src/engine/platform/zxbeeper.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 33f2d66c7..823b2f8a9 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -103,9 +103,8 @@ const char* cmdName[]={ "FM_AM_DEPTH", "FM_PM_DEPTH", - "GENESIS_LFO", - - "ARCADE_LFO", + "FM_LFO2", + "FM_LFO2_WAVE", "STD_NOISE_FREQ", "STD_NOISE_MODE", @@ -215,6 +214,9 @@ const char* cmdName[]={ "SURROUND_PANNING", + "FM_AM2_DEPTH", + "FM_PM2_DEPTH", + "ALWAYS_SET_VOLUME" }; @@ -398,11 +400,22 @@ void DivEngine::processRow(int i, bool afterDelay) { if (effectVal==-1) effectVal=0; switch (effect) { - case 0x09: // speed 1 - if (effectVal>0) speed1=effectVal; + case 0x09: // select groove pattern/speed 1 + if (song.grooves.empty()) { + if (effectVal>0) speeds.val[0]=effectVal; + } else { + if (effectVal<(short)song.grooves.size()) { + speeds=song.grooves[effectVal]; + curSpeed=0; + } + } break; - case 0x0f: // speed 2 - if (effectVal>0) speed2=effectVal; + case 0x0f: // speed 1/speed 2 + if (speeds.len==2 && song.grooves.empty()) { + if (effectVal>0) speeds.val[1]=effectVal; + } else { + if (effectVal>0) speeds.val[0]=effectVal; + } break; case 0x0b: // change order if (changeOrd==-1 || song.jumpTreatment==0) { @@ -687,6 +700,13 @@ void DivEngine::processRow(int i, bool afterDelay) { // - then a volume slide down starts to the low boundary, and then when this is reached a volume slide up begins // - this process repeats until 0700 or 0Axy are found // - note that a volume value does not stop tremolo - instead it glitches this whole thing up + if (chan[i].tremoloDepth==0) { + chan[i].tremoloPos=0; + } + chan[i].tremoloDepth=effectVal&15; + chan[i].tremoloRate=effectVal>>4; + // tremolo and vol slides are incompatiblw + chan[i].volSpeed=0; break; case 0x0a: // volume ramp // TODO: non-0x-or-x0 value should be treated as 00 @@ -696,6 +716,9 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=(effectVal>>4)*64; } + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; } else { chan[i].volSpeed=0; } @@ -836,10 +859,16 @@ void DivEngine::processRow(int i, bool afterDelay) { if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); break; case 0xf3: // fine volume ramp up + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; chan[i].volSpeed=effectVal; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0xf4: // fine volume ramp down + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; chan[i].volSpeed=-effectVal; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; @@ -866,6 +895,9 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=(effectVal>>4)*256; } + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; } else { chan[i].volSpeed=0; } @@ -896,7 +928,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].vibratoPos=0; } dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); - if (chan[i].legato) { + if (chan[i].legato && (!chan[i].inPorta || song.brokenPortaLegato)) { dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); dispatchCmd(DivCommand(DIV_CMD_HINT_LEGATO,i,chan[i].note)); } else { @@ -1050,6 +1082,9 @@ void DivEngine::nextRow() { } if (song.brokenSpeedSel) { + unsigned char speed2=(speeds.len>=2)?speeds.val[1]:speeds.val[0]; + unsigned char speed1=speeds.val[0]; + if ((curSubSong->patLen&1) && curOrder&1) { ticks=((curRow&1)?speed2:speed1)*(curSubSong->timeBase+1); nextSpeed=(curRow&1)?speed1:speed2; @@ -1058,14 +1093,10 @@ void DivEngine::nextRow() { nextSpeed=(curRow&1)?speed2:speed1; } } else { - if (speedAB) { - ticks=speed2*(curSubSong->timeBase+1); - nextSpeed=speed1; - } else { - ticks=speed1*(curSubSong->timeBase+1); - nextSpeed=speed2; - } - speedAB=!speedAB; + ticks=speeds.val[curSpeed]*(curSubSong->timeBase+1); + curSpeed++; + if (curSpeed>=speeds.len) curSpeed=0; + nextSpeed=speeds.val[curSpeed]; } // post row details @@ -1237,6 +1268,10 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { } else { dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); } + } else if (chan[i].tremoloDepth>0) { + chan[i].tremoloPos+=chan[i].tremoloRate; + chan[i].tremoloPos&=127; + dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,MAX(0,chan[i].volume-(tremTable[chan[i].tremoloPos]*chan[i].tremoloDepth))>>8)); } } if (chan[i].vibratoDepth>0) { diff --git a/src/engine/safeReader.cpp b/src/engine/safeReader.cpp index 76e337a3d..cf2effbec 100644 --- a/src/engine/safeReader.cpp +++ b/src/engine/safeReader.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -136,7 +136,9 @@ float SafeReader::readF() { memcpy(&ret,&buf[curSeek],4); curSeek+=4; ret=((ret>>24)|((ret&0xff0000)>>8)|((ret&0xff00)<<8)|((ret&0xff)<<24)); - return *((float*)(&ret)); + float realRet; + memcpy(&realRet,&ret,4); + return realRet; } double SafeReader::readD() { @@ -153,7 +155,9 @@ double SafeReader::readD() { retB[5]=ret[2]; retB[6]=ret[1]; retB[7]=ret[0]; - return *((double*)retB); + double realRet; + memcpy(&realRet,retB,8); + return realRet; } #else short SafeReader::readS() { diff --git a/src/engine/safeReader.h b/src/engine/safeReader.h index 1f7149834..9e85eeee8 100644 --- a/src/engine/safeReader.h +++ b/src/engine/safeReader.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/safeWriter.cpp b/src/engine/safeWriter.cpp index 5af7fa09d..a0c295d30 100644 --- a/src/engine/safeWriter.cpp +++ b/src/engine/safeWriter.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/safeWriter.h b/src/engine/safeWriter.h index 414417fd2..28a3d9b91 100644 --- a/src/engine/safeWriter.h +++ b/src/engine/safeWriter.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index 08dee993d..f882a3d0c 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -827,18 +827,15 @@ bool DivSample::resampleBlep(double r) { unsigned int posInt=0; double factor=r/(double)rate; float* sincITable=DivFilterTables::getSincIntegralTable(); - float s[16]; - memset(s,0,16*sizeof(float)); + float* floatData=new float[finalCount]; + memset(floatData,0,finalCount*sizeof(float)); if (depth==DIV_SAMPLE_DEPTH_16BIT) { memset(data16,0,finalCount*sizeof(short)); for (int i=0; i32767) result=32767; - data16[i]=result; + data16[i]=oldData16[posInt]; } posFrac+=1.0; @@ -853,28 +850,25 @@ bool DivSample::resampleBlep(double r) { for (int j=0; j<8; j++) { if (i-j>0) { - float result=data16[i-j]+t1[j]*-delta; - if (result<-32768) result=-32768; - if (result>32767) result=32767; - data16[i-j]=result; + floatData[i-j]+=t1[j]*-delta; } if (i+j+132767) result=32767; - data16[i+j+1]=result; + floatData[i+j+1]+=t2[j]*delta; } } } } + for (int i=0; i32767) result=32767; + data16[i]=round(result); + } } else if (depth==DIV_SAMPLE_DEPTH_8BIT) { memset(data8,0,finalCount); for (int i=0; i127) result=127; - data8[i]=result; + data8[i]=oldData8[posInt]; } posFrac+=1.0; @@ -889,21 +883,22 @@ bool DivSample::resampleBlep(double r) { for (int j=0; j<8; j++) { if (i-j>0) { - float result=data8[i-j]+t1[j]*-delta; - if (result<-128) result=-128; - if (result>127) result=127; - data8[i-j]=result; + floatData[i-j]+=t1[j]*-delta; } if (i+j+1127) result=127; - data8[i+j+1]=result; + floatData[i+j+1]+=t2[j]*delta; } } } } + for (int i=0; i127) result=127; + data16[i]=round(result); + } } + delete[] floatData; RESAMPLE_END; return true; diff --git a/src/engine/sample.h b/src/engine/sample.h index 42f202fc4..35eda8c5e 100644 --- a/src/engine/sample.h +++ b/src/engine/sample.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/sfWrapper.cpp b/src/engine/sfWrapper.cpp index f12dc95be..62ddf949f 100644 --- a/src/engine/sfWrapper.cpp +++ b/src/engine/sfWrapper.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/sfWrapper.h b/src/engine/sfWrapper.h index 6e984a454..e4746b530 100644 --- a/src/engine/sfWrapper.h +++ b/src/engine/sfWrapper.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/song.cpp b/src/engine/song.cpp index 7dc136836..828f0a1bc 100644 --- a/src/engine/song.cpp +++ b/src/engine/song.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/song.h b/src/engine/song.h index dfe579bbb..72fbea0ef 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -127,10 +127,20 @@ enum DivSystem { DIV_SYSTEM_YM2608_CSM }; +struct DivGroovePattern { + unsigned char val[16]; + unsigned char len; + DivGroovePattern(): + len(1) { + memset(val,6,16); + } +}; + struct DivSubSong { String name, notes; unsigned char hilightA, hilightB; - unsigned char timeBase, speed1, speed2, arpLen; + unsigned char timeBase, arpLen; + DivGroovePattern speeds; short virtualTempoN, virtualTempoD; bool pal; bool customTempo; @@ -153,8 +163,6 @@ struct DivSubSong { hilightA(4), hilightB(16), timeBase(0), - speed1(6), - speed2(6), arpLen(1), virtualTempoN(150), virtualTempoD(150), @@ -330,6 +338,7 @@ struct DivSong { bool autoSystem; bool oldArpStrategy; bool patchbayAuto; + bool brokenPortaLegato; std::vector ins; std::vector wave; @@ -337,6 +346,7 @@ struct DivSong { std::vector subsong; std::vector patchbay; + std::vector grooves; DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsOPLDrums, nullInsQSound; DivWavetable nullWave; @@ -439,7 +449,8 @@ struct DivSong { disableSampleMacro(false), autoSystem(true), oldArpStrategy(false), - patchbayAuto(true) { + patchbayAuto(true), + brokenPortaLegato(false) { for (int i=0; i}}, + {0x27, {DIV_CMD_FM_PM2_DEPTH, "27xx: Set PM 2 depth (0 to 7F)", effectValAnd<127>}}, {0x28, {DIV_CMD_FM_REV, "28xy: Set reverb (x: operator from 1 to 4 (0 for all ops); y: reverb from 0 to 7)", effectOpVal<4>, effectValAnd<7>}}, {0x2a, {DIV_CMD_FM_WS, "2Axy: Set waveform (x: operator from 1 to 4 (0 for all ops); y: waveform from 0 to 7)", effectOpVal<4>, effectValAnd<7>}}, {0x2b, {DIV_CMD_FM_EG_SHIFT, "2Bxy: Set envelope generator shift (x: operator from 1 to 4 (0 for all ops); y: shift from 0 to 3)", effectOpVal<4>, effectValAnd<3>}}, @@ -1400,7 +1404,7 @@ void DivEngine::registerSystems() { } sysDefs[DIV_SYSTEM_LYNX]=new DivSysDef( - "Atari Lynx", NULL, 0xa8, 0, 4, false, true, 0, false, 1U<id!=0) { sysFileMapFur[sysDefs[i]->id]=(DivSystem)i; diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 9e4557de3..28dcbac44 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,11 +24,12 @@ constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0; -void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, bool directStream) { +void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, bool directStream) { unsigned char baseAddr1=isSecond?0xa0:0x50; unsigned char baseAddr2=isSecond?0x80:0; unsigned short baseAddr2S=isSecond?0x8000:0; unsigned char smsAddr=isSecond?0x30:0x50; + unsigned char ggAddr=isSecond?0x3f:0x4f; unsigned char rf5c68Addr=isSecond?0xb1:0xb0; if (write.addr==0xffffffff) { // Furnace fake reset switch (sys) { @@ -341,14 +342,14 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write } break; case DIV_SYSTEM_LYNX: - w->writeC(0x4e); + w->writeC(0x40); w->writeC(0x44); w->writeC(0xff); //stereo attenuation select - w->writeC(0x4e); + w->writeC(0x40); w->writeC(0x50); w->writeC(0x00); //stereo channel disable for (int i=0; i<4; i++) { //stereo attenuation value - w->writeC(0x4e); + w->writeC(0x40); w->writeC(0x40+i); w->writeC(0xff); } @@ -541,14 +542,19 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write switch (write.addr&0xff) { case 0: // play sample if (write.valwriteC(0x95); - w->writeC(streamID); - w->writeS(write.val); // sample number - w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0)|(sampleDir[streamID]?0x10:0)); // flags - if (sample->isLoopable() && !sampleDir[streamID]) { - loopTimer[streamID]=sample->length8; - loopSample[streamID]=write.val; + if (playingSample[streamID]!=write.val) { + pendingFreq[streamID]=write.val; + } else { + DivSample* sample=song.sample[write.val]; + w->writeC(0x95); + w->writeC(streamID); + w->writeS(write.val); // sample number + w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0)|(sampleDir[streamID]?0x10:0)); // flags + if (sample->isLoopable() && !sampleDir[streamID]) { + loopTimer[streamID]=sample->length8; + loopSample[streamID]=write.val; + } + playingSample[streamID]=write.val; } } break; @@ -557,11 +563,26 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write w->writeC(streamID); w->writeI(write.val); loopFreq[streamID]=write.val; + if (pendingFreq[streamID]!=-1) { + DivSample* sample=song.sample[pendingFreq[streamID]]; + w->writeC(0x95); + w->writeC(streamID); + w->writeS(pendingFreq[streamID]); // sample number + w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0)|(sampleDir[streamID]?0x10:0)); // flags + if (sample->isLoopable() && !sampleDir[streamID]) { + loopTimer[streamID]=sample->length8; + loopSample[streamID]=pendingFreq[streamID]; + } + playingSample[streamID]=pendingFreq[streamID]; + pendingFreq[streamID]=-1; + } break; case 2: // stop sample w->writeC(0x94); w->writeC(streamID); loopSample[streamID]=-1; + playingSample[streamID]=-1; + pendingFreq[streamID]=-1; break; case 3: // set sample direction sampleDir[streamID]=write.val; @@ -593,7 +614,11 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write } break; case DIV_SYSTEM_SMS: - w->writeC(smsAddr); + if (write.addr==1) { + w->writeC(ggAddr); + } else { + w->writeC(smsAddr); + } w->writeC(write.val); break; case DIV_SYSTEM_T6W28: @@ -711,7 +736,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write w->writeC(write.val&0xff); break; case DIV_SYSTEM_LYNX: - w->writeC(0x4e); + w->writeC(0x40); w->writeC(write.addr&0xff); w->writeC(write.val&0xff); break; @@ -863,16 +888,20 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write } #define CHIP_VOL(_id,_mult) { \ - double _vol=fabs((float)song.systemVol[i])*4.0*_mult; \ + double _vol=fabs((float)song.systemVol[i])*256.0*_mult; \ if (_vol<0.0) _vol=0.0; \ if (_vol>32767.0) _vol=32767.0; \ + chipVolSum+=(unsigned int)(_vol/_mult); \ + chipAccounting++; \ chipVol.push_back((_id)|(0x80000000)|(((unsigned int)_vol)<<16)); \ } #define CHIP_VOL_SECOND(_id,_mult) { \ - double _vol=fabs((float)song.systemVol[i])*4.0*_mult; \ + double _vol=fabs((float)song.systemVol[i])*256.0*_mult; \ if (_vol<0.0) _vol=0.0; \ if (_vol>32767.0) _vol=32767.0; \ + chipVolSum+=(unsigned int)(_vol/_mult); \ + chipAccounting++; \ chipVol.push_back((_id)|(0x80000100)|(((unsigned int)_vol)<<16)); \ } @@ -957,6 +986,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p int hasLynx=0; int howManyChips=0; + int chipVolSum=0; + int chipAccounting=0; int loopPos=-1; int loopTick=-1; @@ -979,7 +1010,9 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p double loopFreq[DIV_MAX_CHANS]; int loopSample[DIV_MAX_CHANS]; bool sampleDir[DIV_MAX_CHANS]; - std::vector chipVol; + int pendingFreq[DIV_MAX_CHANS]; + int playingSample[DIV_MAX_CHANS]; + std::vector chipVol; std::vector delayedWrites[DIV_MAX_CHIPS]; std::vector> sortedWrites; @@ -987,6 +1020,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p loopTimer[i]=0; loopFreq[i]=0; loopSample[i]=-1; + pendingFreq[i]=-1; + playingSample[i]=-1; sampleDir[i]=false; } @@ -1016,7 +1051,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_SMS: if (!hasSN) { hasSN=disCont[i].dispatch->chipClock; - CHIP_VOL(0,1.0); + CHIP_VOL(0,4.0); willExport[i]=true; switch (song.systemFlags[i].getInt("chipType",0)) { case 1: // real SN @@ -1035,7 +1070,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p } else if (!(hasSN&0x40000000)) { isSecond[i]=true; willExport[i]=true; - CHIP_VOL_SECOND(0,1.0); + CHIP_VOL_SECOND(0,4.0); hasSN|=0x40000000; howManyChips++; } @@ -1043,9 +1078,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_GB: if (!hasGB) { hasGB=disCont[i].dispatch->chipClock; + CHIP_VOL(19,0.75); willExport[i]=true; } else if (!(hasGB&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(19,0.75); willExport[i]=true; hasGB|=0x40000000; howManyChips++; @@ -1054,10 +1091,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_PCE: if (!hasPCE) { hasPCE=disCont[i].dispatch->chipClock; + CHIP_VOL(27,0.98); willExport[i]=true; writePCESamples=true; } else if (!(hasPCE&0x40000000)) { isSecond[i]=true; + CHIP_VOL(27,0.98); willExport[i]=true; hasPCE|=0x40000000; howManyChips++; @@ -1066,10 +1105,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_NES: if (!hasNES) { hasNES=disCont[i].dispatch->chipClock; + CHIP_VOL(20,1.7); willExport[i]=true; writeNESSamples=true; } else if (!(hasNES&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(20,1.7); willExport[i]=true; hasNES|=0x40000000; howManyChips++; @@ -1079,10 +1120,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_SEGAPCM_COMPAT: if (!hasSegaPCM) { hasSegaPCM=4000000; + CHIP_VOL(4,0.67); willExport[i]=true; writeSegaPCM=1; } else if (!(hasSegaPCM&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(4,0.67); willExport[i]=true; writeSegaPCM=2; hasSegaPCM|=0x40000000; @@ -1092,10 +1135,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_X1_010: if (!hasX1) { hasX1=disCont[i].dispatch->chipClock; + CHIP_VOL(38,2.0); willExport[i]=true; writeX1010[0]=disCont[i].dispatch; } else if (!(hasX1&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(38,2.0); willExport[i]=true; writeX1010[1]=disCont[i].dispatch; hasX1|=0x40000000; @@ -1110,10 +1155,14 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_YM2610B_EXT: if (!hasOPNB) { hasOPNB=disCont[i].dispatch->chipClock; + CHIP_VOL(8,1.0); + CHIP_VOL(0x88,1.25); willExport[i]=true; writeADPCM_OPNB[0]=disCont[i].dispatch; } else if (!(hasOPNB&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(8,1.0); + CHIP_VOL_SECOND(0x88,1.25); willExport[i]=true; writeADPCM_OPNB[1]=disCont[i].dispatch; hasOPNB|=0x40000000; @@ -1158,9 +1207,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p if (hasStereo && song.systemFlags[i].getBool("stereo",false)) { ayFlags|=0x80; } + CHIP_VOL(18,1.0); willExport[i]=true; } else if (!(hasAY&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(18,1.0); willExport[i]=true; hasAY|=0x40000000; howManyChips++; @@ -1170,9 +1221,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_SAA1099: if (!hasSAA) { hasSAA=disCont[i].dispatch->chipClock; + CHIP_VOL(35,1.0); willExport[i]=true; } else if (!(hasSAA&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(35,1.0); willExport[i]=true; hasSAA|=0x40000000; howManyChips++; @@ -1184,10 +1237,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_YM2612_DUALPCM_EXT: if (!hasOPN2) { hasOPN2=disCont[i].dispatch->chipClock; + CHIP_VOL(2,1.6); willExport[i]=true; writeDACSamples=true; } else if (!(hasOPN2&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(2,1.6); willExport[i]=true; hasOPN2|=0x40000000; howManyChips++; @@ -1196,9 +1251,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_YM2151: if (!hasOPM) { hasOPM=disCont[i].dispatch->chipClock; + CHIP_VOL(3,1.0); willExport[i]=true; } else if (!(hasOPM&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(3,1.0); willExport[i]=true; hasOPM|=0x40000000; howManyChips++; @@ -1209,10 +1266,14 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p if (!hasOPN) { hasOPN=disCont[i].dispatch->chipClock; willExport[i]=true; + CHIP_VOL(6,1.0); + CHIP_VOL(0x86,1.7); writeDACSamples=true; } else if (!(hasOPN&0x40000000)) { isSecond[i]=true; willExport[i]=true; + CHIP_VOL_SECOND(6,1.0); + CHIP_VOL_SECOND(0x86,1.7); hasOPN|=0x40000000; howManyChips++; } @@ -1221,10 +1282,14 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_YM2608_EXT: if (!hasOPNA) { hasOPNA=disCont[i].dispatch->chipClock; + CHIP_VOL(7,1.0); + CHIP_VOL(0x87,1.3); willExport[i]=true; writeADPCM_OPNA[0]=disCont[i].dispatch; } else if (!(hasOPNA&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(7,1.0); + CHIP_VOL_SECOND(0x87,1.3); willExport[i]=true; writeADPCM_OPNA[1]=disCont[i].dispatch; hasOPNA|=0x40000000; @@ -1236,9 +1301,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_VRC7: if (!hasOPLL) { hasOPLL=disCont[i].dispatch->chipClock; + CHIP_VOL(1,3.2); willExport[i]=true; } else if (!(hasOPLL&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(1,3.2); willExport[i]=true; hasOPLL|=0x40000000; howManyChips++; @@ -1261,9 +1328,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_POKEY: if (!hasPOKEY) { hasPOKEY=disCont[i].dispatch->chipClock; + CHIP_VOL(30,0.8); willExport[i]=true; } else if (!(hasPOKEY&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(30,0.8); willExport[i]=true; hasPOKEY|=0x40000000; howManyChips++; @@ -1286,10 +1355,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p // However I think it it not necessary because old VGM players will still // not be able to handle the 64kb sample bank trick hasQSound=disCont[i].dispatch->chipClock; + CHIP_VOL(31,1.0); willExport[i]=true; writeQSound[0]=disCont[i].dispatch; } else if (!(hasQSound&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(31,1.0); willExport[i]=false; writeQSound[1]=disCont[i].dispatch; addWarning("dual QSound is not supported by the VGM format"); @@ -1298,6 +1369,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_SWAN: if (!hasSwan) { hasSwan=disCont[i].dispatch->chipClock; + CHIP_VOL(33,1.0); willExport[i]=true; // funny enough, VGM doesn't have support for WSC's sound DMA by design // so DAC stream it goes @@ -1305,6 +1377,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p writeDACSamples=true; } else if (!(hasSwan&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(33,1.0); willExport[i]=true; hasSwan|=0x40000000; howManyChips++; @@ -1313,9 +1386,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_VBOY: if (!hasVSU) { hasVSU=disCont[i].dispatch->chipClock; + CHIP_VOL(34,0.72); willExport[i]=true; } else if (!(hasVSU&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(34,0.72); willExport[i]=true; hasVSU|=0x40000000; howManyChips++; @@ -1325,9 +1400,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_OPL_DRUMS: if (!hasOPL) { hasOPL=disCont[i].dispatch->chipClock; + CHIP_VOL(9,1.0); willExport[i]=true; } else if (!(hasOPL&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(9,1.0); willExport[i]=true; hasOPL|=0x40000000; howManyChips++; @@ -1337,10 +1414,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_Y8950_DRUMS: if (!hasY8950) { hasY8950=disCont[i].dispatch->chipClock; + CHIP_VOL(11,1.0); willExport[i]=true; writeADPCM_Y8950[0]=disCont[i].dispatch; } else if (!(hasY8950&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(11,1.0); willExport[i]=true; writeADPCM_Y8950[1]=disCont[i].dispatch; hasY8950|=0x40000000; @@ -1351,9 +1430,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_OPL2_DRUMS: if (!hasOPL2) { hasOPL2=disCont[i].dispatch->chipClock; + CHIP_VOL(10,1.0); willExport[i]=true; } else if (!(hasOPL2&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(10,1.0); willExport[i]=true; hasOPL2|=0x40000000; howManyChips++; @@ -1363,9 +1444,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_OPL3_DRUMS: if (!hasOPL3) { hasOPL3=disCont[i].dispatch->chipClock; + CHIP_VOL(12,1.0); willExport[i]=true; } else if (!(hasOPL3&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(12,1.0); willExport[i]=true; hasOPL3|=0x40000000; howManyChips++; @@ -1378,9 +1461,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p if (song.system[i]==DIV_SYSTEM_SCC_PLUS) { hasK051649|=0x80000000; } + CHIP_VOL(25,1.0); willExport[i]=true; } else if (!(hasK051649&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(25,1.0); willExport[i]=true; hasK051649|=0x40000000; if (song.system[i]==DIV_SYSTEM_SCC_PLUS) { @@ -1392,10 +1477,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_YMZ280B: if (!hasZ280) { hasZ280=disCont[i].dispatch->chipClock; + CHIP_VOL(15,0.72); willExport[i]=true; writeZ280[0]=disCont[i].dispatch; } else if (!(hasZ280&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(15,0.72); willExport[i]=true; writeZ280[1]=disCont[i].dispatch; hasZ280|=0x40000000; @@ -1411,11 +1498,13 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p if (!hasRFC1) { hasRFC1=disCont[i].dispatch->chipClock; isSecond[i]=true; + CHIP_VOL(16,1.6); willExport[i]=true; writeRF5C68[1]=disCont[i].dispatch; } } else if (!hasRFC) { hasRFC=disCont[i].dispatch->chipClock; + CHIP_VOL(5,1.6); willExport[i]=true; writeRF5C68[0]=disCont[i].dispatch; } @@ -1423,10 +1512,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_MSM6295: if (!hasOKIM6295) { hasOKIM6295=disCont[i].dispatch->chipClock; + CHIP_VOL(24,1.0); willExport[i]=true; writeMSM6295[0]=disCont[i].dispatch; } else if (!(hasOKIM6295&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(24,1.0); willExport[i]=true; writeMSM6295[1]=disCont[i].dispatch; hasOKIM6295|=0x40000000; @@ -1436,10 +1527,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_GA20: if (!hasGA20) { hasGA20=disCont[i].dispatch->chipClock; + CHIP_VOL(40,0.4); willExport[i]=true; writeGA20[0]=disCont[i].dispatch; } else if (!(hasGA20&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(40,0.4); willExport[i]=true; writeGA20[1]=disCont[i].dispatch; hasGA20|=0x40000000; @@ -1449,7 +1542,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_T6W28: if (!hasSN) { hasSN=0xc0000000|disCont[i].dispatch->chipClock; - CHIP_VOL(0,1.0); + CHIP_VOL(0,2.0); snNoiseConfig=3; snNoiseSize=15; willExport[i]=true; @@ -1524,8 +1617,9 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p w->writeC(0); // OPN w->writeC(0); // OPNA } - if (version>=0x160) { - int calcVolume=32.0*(log(song.masterVol)/log(2.0)); + if (version>=0x160) { // global volume + double abnormalVol=song.masterVol*(double)chipVolSum/(256.0*MAX(1,chipAccounting)); + int calcVolume=32.0*(log(abnormalVol)/log(2.0)); if (calcVolume<-63) calcVolume=-63; if (calcVolume>192) calcVolume=192; w->writeC(calcVolume&0xff); // volume @@ -1596,7 +1690,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p w->writeI(hasX1); w->writeI(hasC352); w->writeI(hasGA20); - w->writeI(hasLynx); + w->writeI(version>=0x172?hasLynx:0); //Mikey introduced in 1.72 } else { w->writeI(0); w->writeI(0); @@ -1991,7 +2085,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p for (int i=0; i& writes=disCont[i].dispatch->getRegisterWrites(); for (DivRegWrite& j: writes) { - performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i],directStream); + performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i],pendingFreq,playingSample,directStream); writeCount++; } writes.clear(); @@ -2031,7 +2125,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p lastOne=i.second.time; } // write write - performVGMWrite(w,song.system[i.first],i.second.write,streamIDs[i.first],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i.first],directStream); + performVGMWrite(w,song.system[i.first],i.second.write,streamIDs[i.first],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i.first],pendingFreq,playingSample,directStream); writeCount++; } sortedWrites.clear(); @@ -2164,7 +2258,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p w->writeI(0); } else { w->writeI(loopPos-0x1c); - w->writeI(tickCount-loopTick-1); + w->writeI(tickCount-loopTick); } } else { w->writeI(0); diff --git a/src/engine/waveSynth.cpp b/src/engine/waveSynth.cpp index e68e36751..757c2c7eb 100644 --- a/src/engine/waveSynth.cpp +++ b/src/engine/waveSynth.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/waveSynth.h b/src/engine/waveSynth.h index 26f5b259b..81a3cbe19 100644 --- a/src/engine/waveSynth.h +++ b/src/engine/waveSynth.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/wavetable.cpp b/src/engine/wavetable.cpp index 608b0e89d..a14c28bbe 100644 --- a/src/engine/wavetable.cpp +++ b/src/engine/wavetable.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/wavetable.h b/src/engine/wavetable.h index 0f518ab53..52405082e 100644 --- a/src/engine/wavetable.h +++ b/src/engine/wavetable.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/winStuff.cpp b/src/engine/winStuff.cpp index 79065e88b..4452a5143 100644 --- a/src/engine/winStuff.cpp +++ b/src/engine/winStuff.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/winStuff.h b/src/engine/winStuff.h index 0e49bccd3..a569f6a09 100644 --- a/src/engine/winStuff.h +++ b/src/engine/winStuff.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/zsm.cpp b/src/engine/zsm.cpp index 89eb01496..5fdcd6236 100644 --- a/src/engine/zsm.cpp +++ b/src/engine/zsm.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/zsm.h b/src/engine/zsm.h index af0979e7c..b452171f8 100644 --- a/src/engine/zsm.h +++ b/src/engine/zsm.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/engine/zsmOps.cpp b/src/engine/zsmOps.cpp index f5fa83090..0207a93d9 100644 --- a/src/engine/zsmOps.cpp +++ b/src/engine/zsmOps.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/fileutils.cpp b/src/fileutils.cpp index d2574ee1b..a58f92a0a 100644 --- a/src/fileutils.cpp +++ b/src/fileutils.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/fileutils.h b/src/fileutils.h index ea4afabc2..ded2bfb41 100644 --- a/src/fileutils.h +++ b/src/fileutils.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 532cb6bde..350aba450 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -69,11 +69,13 @@ const char* aboutLine[]={ "brickblock369", "Burnt Fishy", "CaptainMalware", + "Clingojam", "DeMOSic", "DevEd", "Dippy", "djtuBIG-MaliceX", "dumbut", + "EpicTyphlosion", "FΛDE", "Forte", "Fragmare", @@ -91,6 +93,7 @@ const char* aboutLine[]={ "LVintageNerd", "Mahbod Karamoozian", "Martin Demsky", + "MelonadeM", "Miker", "nicco1690", "", @@ -173,7 +176,7 @@ const char* aboutLine[]={ "NEOART Costa Rica", "all members of Deflers of Noice!", "", - "copyright © 2021-2022 tildearrow", + "copyright © 2021-2023 tildearrow", "(and contributors).", "licensed under GPLv2+! see", "LICENSE for more information.", diff --git a/src/gui/actionUtil.h b/src/gui/actionUtil.h index 81685af48..51fffa002 100644 --- a/src/gui/actionUtil.h +++ b/src/gui/actionUtil.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index 7b0f8a027..7bcece959 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/channels.cpp b/src/gui/channels.cpp index e61138851..48d5ed8cb 100644 --- a/src/gui/channels.cpp +++ b/src/gui/channels.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/clock.cpp b/src/gui/clock.cpp index e8450d10a..5db890558 100644 --- a/src/gui/clock.cpp +++ b/src/gui/clock.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index 11c996c20..10fbb57c0 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -214,7 +214,7 @@ void FurnaceGUI::drawCompatFlags() { e->song.delayBehavior=2; } if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("no checks (like FamiTracker)"); + ImGui::SetTooltip("no checks"); } ImGui::Text("Simultaneous jump (0B+0D) treatment:"); @@ -293,6 +293,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("behavior changed in 0.6pre2"); } + ImGui::Checkbox("Broken portamento during legato",&e->song.brokenPortaLegato); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.6pre4"); + } } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS; ImGui::End(); diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp index 88d50d2b8..09da1b257 100644 --- a/src/gui/cursor.cpp +++ b/src/gui/cursor.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,8 +50,10 @@ void FurnaceGUI::startSelection(int xCoarse, int xFine, int y, bool fullRow) { selecting=true; selectingFull=false; dragSourceX=xCoarse; + dragSourceXFine=xFine; dragSourceY=y; dragDestinationX=xCoarse; + dragDestinationXFine=xFine; dragDestinationY=y; dragStart=selStart; dragEnd=selEnd; @@ -86,6 +88,7 @@ void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y, bool fullRow) { if (!selecting) return; if (dragging) { dragDestinationX=xCoarse; + if (dragStart.xFine>=3 && dragStart.xCoarse==dragEnd.xCoarse) dragDestinationXFine=(dragSourceXFine&1)?((xFine-1)|1):((xFine+1)&(~1)); dragDestinationY=y; cursorDrag.xCoarse=xCoarse; cursorDrag.xFine=xFine; @@ -104,6 +107,15 @@ void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y, bool fullRow) { dragDestinationX=lastChannel-(dragEnd.xCoarse-dragSourceX)-1; } + if (dragStart.xFine>=3 && dragStart.xCoarse==dragEnd.xCoarse) { + if (dragEnd.xFine+(dragDestinationXFine-dragSourceXFine)>(2+e->curPat[dragDestinationX].effectCols*2)) { + dragDestinationXFine=(2+e->curPat[dragDestinationX].effectCols*2)-dragEnd.xFine+dragSourceXFine; + } + if (dragStart.xFine+(dragDestinationXFine-dragSourceXFine)<3) { + dragDestinationXFine=3-dragStart.xFine+dragSourceXFine; + } + } + if (dragStart.y+(dragDestinationY-dragSourceY)<0) { dragDestinationY=dragSourceY-dragStart.y; } @@ -113,10 +125,10 @@ void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y, bool fullRow) { } selStart.xCoarse=dragStart.xCoarse+(dragDestinationX-dragSourceX); - selStart.xFine=dragStart.xFine; + selStart.xFine=dragStart.xFine+(dragDestinationXFine-dragSourceXFine); selStart.y=dragStart.y+(dragDestinationY-dragSourceY); selEnd.xCoarse=dragEnd.xCoarse+(dragDestinationX-dragSourceX); - selEnd.xFine=dragEnd.xFine; + selEnd.xFine=dragEnd.xFine+(dragDestinationXFine-dragSourceXFine); selEnd.y=dragEnd.y+(dragDestinationY-dragSourceY); } else { if (selectingFull) { @@ -156,7 +168,7 @@ void FurnaceGUI::finishSelection() { selectingFull=false; if (dragging) { - if (dragSourceX==dragDestinationX && dragSourceY==dragDestinationY) { + if (dragSourceX==dragDestinationX && dragSourceY==dragDestinationY && dragSourceXFine==dragDestinationXFine) { cursor=cursorDrag; selStart=cursorDrag; selEnd=cursorDrag; diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 46b10800d..8edde40d1 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/debug.cpp b/src/gui/debug.cpp index 5a6123823..c9093188c 100644 --- a/src/gui/debug.cpp +++ b/src/gui/debug.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/debug.h b/src/gui/debug.h index e3c911a84..4ec4cd87c 100644 --- a/src/gui/debug.h +++ b/src/gui/debug.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 2149bfaf2..c07c000bb 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 288a5504d..83ab4e37e 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -163,7 +163,7 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_TX81Z_REQUEST: { TAMidiMessage msg; msg.type=TA_MIDI_SYSEX; - msg.sysExData.reset(new unsigned char[15]); + msg.sysExData.reset(new unsigned char[15],std::default_delete()); msg.sysExLen=15; memcpy(msg.sysExData.get(),avRequest,15); if (!e->sendMidiMessage(msg)) { @@ -193,6 +193,9 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_WINDOW_SONG_INFO: nextWindow=GUI_WINDOW_SONG_INFO; break; + case GUI_ACTION_WINDOW_SPEED: + nextWindow=GUI_WINDOW_SPEED; + break; case GUI_ACTION_WINDOW_PATTERN: nextWindow=GUI_WINDOW_PATTERN; break; @@ -262,6 +265,9 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_WINDOW_FIND: nextWindow=GUI_WINDOW_FIND; break; + case GUI_ACTION_WINDOW_GROOVES: + nextWindow=GUI_WINDOW_GROOVES; + break; case GUI_ACTION_COLLAPSE_WINDOW: collapseWindow=true; @@ -274,6 +280,9 @@ void FurnaceGUI::doAction(int what) { case GUI_WINDOW_SONG_INFO: songInfoOpen=false; break; + case GUI_WINDOW_SPEED: + speedOpen=false; + break; case GUI_WINDOW_ORDERS: ordersOpen=false; break; @@ -352,6 +361,9 @@ void FurnaceGUI::doAction(int what) { case GUI_WINDOW_FIND: findOpen=false; break; + case GUI_WINDOW_GROOVES: + groovesOpen=false; + break; default: break; } @@ -386,10 +398,10 @@ void FurnaceGUI::doAction(int what) { doSelectAll(); break; case GUI_ACTION_PAT_CUT: - doCopy(true); + doCopy(true,true,selStart,selEnd); break; case GUI_ACTION_PAT_COPY: - doCopy(false); + doCopy(false,true,selStart,selEnd); break; case GUI_ACTION_PAT_PASTE: doPaste(); diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 173b64de4..e8968e698 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -506,7 +506,9 @@ void FurnaceGUI::drawMobileControls() { ImGui::SameLine(); ImGui::Button("Legacy .dmf"); ImGui::SameLine(); - ImGui::Button("Export Audio"); + if (ImGui::Button("Export Audio")) { + openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE); + } ImGui::SameLine(); if (ImGui::Button("Export VGM")) { openFileDialog(GUI_FILE_EXPORT_VGM); diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index f18efece3..0d8341b28 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -358,66 +358,75 @@ void FurnaceGUI::doTranspose(int amount, OperationMask& mask) { makeUndo(GUI_UNDO_PATTERN_DELETE); } -void FurnaceGUI::doCopy(bool cut) { - finishSelection(); - if (cut) { - curNibble=false; - prepareUndo(GUI_UNDO_PATTERN_CUT); +String FurnaceGUI::doCopy(bool cut, bool writeClipboard, const SelectionPoint& sStart, const SelectionPoint& sEnd) { + if (writeClipboard) { + finishSelection(); + if (cut) { + curNibble=false; + prepareUndo(GUI_UNDO_PATTERN_CUT); + } } - clipboard=fmt::sprintf("org.tildearrow.furnace - Pattern Data (%d)\n%d",DIV_ENGINE_VERSION,selStart.xFine); + String clipb=fmt::sprintf("org.tildearrow.furnace - Pattern Data (%d)\n%d",DIV_ENGINE_VERSION,sStart.xFine); - for (int j=selStart.y; j<=selEnd.y; j++) { - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; + for (int j=sStart.y; j<=sEnd.y; j++) { + int iCoarse=sStart.xCoarse; + int iFine=sStart.xFine; if (iFine>3 && !(iFine&1)) { iFine--; } - clipboard+='\n'; - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + clipb+='\n'; + for (; iCoarse<=sEnd.xCoarse; iCoarse++) { if (!e->curSubSong->chanShow[iCoarse]) continue; DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true); - for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarsecurPat[iCoarse].effectCols*2 && (iCoarsedata[j][0],pat->data[j][1]); + clipb+=noteNameNormal(pat->data[j][0],pat->data[j][1]); if (cut) { pat->data[j][0]=0; pat->data[j][1]=0; } } else { if (pat->data[j][iFine+1]==-1) { - clipboard+=".."; + clipb+=".."; } else { - clipboard+=fmt::sprintf("%.2X",pat->data[j][iFine+1]); + clipb+=fmt::sprintf("%.2X",pat->data[j][iFine+1]); } if (cut) { pat->data[j][iFine+1]=-1; } } } - clipboard+='|'; + clipb+='|'; iFine=0; } } - SDL_SetClipboardText(clipboard.c_str()); - if (cut) { - makeUndo(GUI_UNDO_PATTERN_CUT); + if (writeClipboard) { + SDL_SetClipboardText(clipb.c_str()); + if (cut) { + makeUndo(GUI_UNDO_PATTERN_CUT); + } + clipboard=clipb; } + return clipb; } -void FurnaceGUI::doPaste(PasteMode mode, int arg) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_PASTE); - char* clipText=SDL_GetClipboardText(); - if (clipText!=NULL) { - if (clipText[0]) { - clipboard=clipText; +void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String clipb) { + if (readClipboard) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_PASTE); + char* clipText=SDL_GetClipboardText(); + if (clipText!=NULL) { + if (clipText[0]) { + clipboard=clipText; + } + SDL_free(clipText); } - SDL_free(clipText); + clipb=clipboard; } std::vector data; String tempS; - for (char i: clipboard) { + for (char i: clipb) { if (i=='\r') continue; if (i=='\n') { data.push_back(tempS); @@ -562,15 +571,18 @@ void FurnaceGUI::doPaste(PasteMode mode, int arg) { i=1; } } - if (settings.cursorPastePos) { - cursor.y=j; - if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1; - selStart=cursor; - selEnd=cursor; - updateScroll(cursor.y); - } - makeUndo(GUI_UNDO_PATTERN_PASTE); + if (readClipboard) { + if (settings.cursorPastePos) { + cursor.y=j; + if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1; + selStart=cursor; + selEnd=cursor; + updateScroll(cursor.y); + } + + makeUndo(GUI_UNDO_PATTERN_PASTE); + } } void FurnaceGUI::doChangeIns(int ins) { @@ -926,72 +938,21 @@ void FurnaceGUI::doExpand(int multiplier) { } void FurnaceGUI::doDrag() { - DivPattern* patBuffer=NULL; int len=dragEnd.xCoarse-dragStart.xCoarse+1; - DETERMINE_FIRST_LAST; - if (len<1) return; - patBuffer=new DivPattern[len]; prepareUndo(GUI_UNDO_PATTERN_DRAG); // copy and clear - { - int iCoarse=dragStart.xCoarse; - int iFine=dragStart.xFine; - int iCoarseP=0; - for (; iCoarse<=dragEnd.xCoarse; iCoarse++) { - if (!e->curSubSong->chanShow[iCoarse]) continue; - DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true); - for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarsedata[j][iFine]; - pat->data[j][iFine]=0; - if (dragStart.y==dragEnd.y) pat->data[j][2]=-1; - } - patBuffer[iCoarseP].data[row][iFine+1]=pat->data[j][iFine+1]; - pat->data[j][iFine+1]=(iFine<1)?0:-1; + String c=doCopy(true,false,dragStart,dragEnd); - if (dragStart.y==dragEnd.y && iFine>2 && iFine&1 && settings.effectDeletionAltersValue) { - pat->data[j][iFine+2]=-1; - } - row++; - } - } - iFine=0; - iCoarseP++; - } - } + logV("copy: %s",c); // replace - { - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int iCoarseP=0; - for (; iCoarse<=selEnd.xCoarse && iCoarsePlastChannel) continue; - if (!e->curSubSong->chanShow[iCoarse]) continue; - DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true); - for (; iFine<3+e->curPat[iCoarse].effectCols*2 && (iCoarse=e->curSubSong->patLen) continue; - if (iFine==0) { - pat->data[j][iFine]=patBuffer[iCoarseP].data[row][iFine]; - } - pat->data[j][iFine+1]=patBuffer[iCoarseP].data[row][iFine+1]; - } - } - iFine=0; - iCoarseP++; - } - } + cursor=selStart; + doPaste(GUI_PASTE_MODE_NORMAL,0,false,c); - delete[] patBuffer; makeUndo(GUI_UNDO_PATTERN_DRAG); } diff --git a/src/gui/findReplace.cpp b/src/gui/findReplace.cpp index e66108cfc..60b6c899b 100644 --- a/src/gui/findReplace.cpp +++ b/src/gui/findReplace.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/fonts.cpp b/src/gui/fonts.cpp index ee2e4f1b8..4ab0f6a4c 100644 --- a/src/gui/fonts.cpp +++ b/src/gui/fonts.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/fonts.h b/src/gui/fonts.h index a0a42d251..baaf1e594 100644 --- a/src/gui/fonts.h +++ b/src/gui/fonts.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/gradient.cpp b/src/gui/gradient.cpp index e4af10b57..a36768ead 100644 --- a/src/gui/gradient.cpp +++ b/src/gui/gradient.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/grooves.cpp b/src/gui/grooves.cpp new file mode 100644 index 000000000..560a09e68 --- /dev/null +++ b/src/gui/grooves.cpp @@ -0,0 +1,154 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2023 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" +#include "imgui.h" +#include "misc/cpp/imgui_stdlib.h" +#include "IconsFontAwesome4.h" +#include +#include "intConst.h" + +void FurnaceGUI::drawGrooves() { + if (nextWindow==GUI_WINDOW_GROOVES) { + groovesOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!groovesOpen) return; + ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(canvasW,canvasH)); + if (ImGui::Begin("Grooves",&groovesOpen,globalWinFlags)) { + int delGroove=-1; + + ImGui::Text("use effect 09xx to select a groove pattern."); + if (!e->song.grooves.empty()) if (ImGui::BeginTable("GrooveList",3,ImGuiTableFlags_Borders)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed); + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::Text("#"); + ImGui::TableNextColumn(); + ImGui::Text("pattern"); + ImGui::TableNextColumn(); + ImGui::Text("remove"); + + int index=0; + for (DivGroovePattern& i: e->song.grooves) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::PushFont(patFont); + ImGui::Text("%.2X",index); + ImGui::PopFont(); + + ImGui::TableNextColumn(); + + String grooveStr; + + if (curGroove==index) { + int intVersion[256]; + unsigned char intVersionLen=i.len; + unsigned char ignoredLoop=0; + unsigned char ignoredRel=0; + memset(intVersion,0,sizeof(int)); + for (int j=0; j<16; j++) { + intVersion[j]=i.val[j]; + } + if (intVersionLen>16) intVersionLen=16; + grooveStr=fmt::sprintf("##_GRI%d",index); + bool wantedFocus=wantGrooveListFocus; + if (wantGrooveListFocus) { + wantGrooveListFocus=false; + ImGui::SetItemDefaultFocus(); + ImGui::SetKeyboardFocusHere(); + } + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputText(grooveStr.c_str(),&grooveListString)) { + decodeMMLStr(grooveListString,intVersion,intVersionLen,ignoredLoop,1,255,ignoredRel); + if (intVersionLen<1) { + intVersionLen=1; + intVersion[0]=6; + } + if (intVersionLen>16) intVersionLen=16; + e->lockEngine([&i,intVersion,intVersionLen]() { + i.len=intVersionLen; + for (int j=0; j<16; j++) { + i.val[j]=intVersion[j]; + } + }); + MARK_MODIFIED; + } + if (!ImGui::IsItemActive() && !wantedFocus) { + curGroove=-1; + //encodeMMLStr(grooveListString,intVersion,intVersionLen,-1,-1,false); + } + } else { + String grooveStr; + + for (int j=0; j0) { + grooveStr+=' '; + } + grooveStr+=fmt::sprintf("%d",(int)i.val[j]); + } + + size_t groovePrevLen=grooveStr.size(); + + grooveStr+=fmt::sprintf("##_GR%d",index); + + if (ImGui::Selectable(grooveStr.c_str(),false)) { + curGroove=index; + grooveListString=grooveStr.substr(0,groovePrevLen); + wantGrooveListFocus=true; + } + } + + ImGui::TableNextColumn(); + String grooveID=fmt::sprintf(ICON_FA_TIMES "##GRR%d",index); + if (ImGui::Button(grooveID.c_str())) { + delGroove=index; + } + + index++; + } + + ImGui::EndTable(); + } + + if (delGroove>=0) { + e->lockEngine([this,delGroove]() { + e->song.grooves.erase(e->song.grooves.begin()+delGroove); + }); + MARK_MODIFIED; + } + + if (ImGui::Button(ICON_FA_PLUS "##AddGroove")) { + e->lockEngine([this]() { + e->song.grooves.push_back(DivGroovePattern()); + }); + MARK_MODIFIED; + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) { + curWindow=GUI_WINDOW_GROOVES; + } else { + curGroove=-1; + } + ImGui::End(); +} diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 1ec735f1d..5b20c47eb 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2,7 +2,7 @@ // OK, sorry for inserting the define here but I'm so tired of this extension /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -988,18 +988,25 @@ void FurnaceGUI::prepareLayout() { fclose(check); } -float FurnaceGUI::calcBPM(int s1, int s2, float hz, int vN, int vD) { +float FurnaceGUI::calcBPM(const DivGroovePattern& speeds, float hz, int vN, int vD) { float hl=e->curSubSong->hilightA; if (hl<=0.0f) hl=4.0f; float timeBase=e->curSubSong->timeBase+1; - float speedSum=s1+s2; + float speedSum=0; + for (int i=0; iwalkSong(loopOrder,loopRow,loopEnd); memset(lastIns,-1,sizeof(int)*DIV_MAX_CHANS); if (!followPattern) e->setOrder(curOrder); @@ -1026,6 +1033,9 @@ void FurnaceGUI::stop() { curNibble=false; orderNibble=false; activeNotes.clear(); + memset(chanOscVol,0,DIV_MAX_CHANS*sizeof(float)); + memset(chanOscPitch,0,DIV_MAX_CHANS*sizeof(float)); + memset(chanOscBright,0,DIV_MAX_CHANS*sizeof(float)); } void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) { @@ -1421,7 +1431,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { hasOpened=fileDialog->openLoad( "Open File", {"compatible files", "*.fur *.dmf *.mod *.fc13 *.fc14 *.smod *.fc", - "all files", ".*"}, + "all files", "*"}, "compatible files{.fur,.dmf,.mod,.fc13,.fc14,.smod,.fc},.*", workingDirSong, dpiScale @@ -1487,7 +1497,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { "VOPM preset bank", "*.opm", "Wohlstand WOPL bank", "*.wopl", "Wohlstand WOPN bank", "*.wopn", - "all files", ".*"}, + "all files", "*"}, "all compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.opli,.opni,.y12,.bnk,.ff,.gyb,.opm,.wopl,.wopn},.*", workingDirIns, dpiScale, @@ -1554,7 +1564,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { hasOpened=fileDialog->openLoad( "Load Wavetable", {"compatible files", "*.fuw *.dmw", - "all files", ".*"}, + "all files", "*"}, "compatible files{.fuw,.dmw},.*", workingDirWave, dpiScale, @@ -1598,7 +1608,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { hasOpened=fileDialog->openLoad( "Load Sample", {"compatible files", "*.wav *.dmc *.brr", - "all files", ".*"}, + "all files", "*"}, "compatible files{.wav,.dmc,.brr},.*", workingDirSample, dpiScale, @@ -1611,7 +1621,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { if (!dirExists(workingDirSample)) workingDirSample=getHomeDir(); hasOpened=fileDialog->openLoad( "Load Raw Sample", - {"all files", ".*"}, + {"all files", "*"}, ".*", workingDirSample, dpiScale @@ -1778,7 +1788,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { hasOpened=fileDialog->openLoad( "Load ROM", {"compatible files", "*.rom *.bin", - "all files", ".*"}, + "all files", "*"}, "compatible files{.rom,.bin},.*", workingDirROM, dpiScale @@ -1790,7 +1800,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { "Open Test", {"compatible files", "*.fur *.dmf *.mod", "another option", "*.wav *.ttf", - "all files", ".*"}, + "all files", "*"}, "compatible files{.fur,.dmf,.mod},another option{.wav,.ttf},.*", workingDirTest, dpiScale, @@ -1809,7 +1819,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { "Open Test (Multi)", {"compatible files", "*.fur *.dmf *.mod", "another option", "*.wav *.ttf", - "all files", ".*"}, + "all files", "*"}, "compatible files{.fur,.dmf,.mod},another option{.wav,.ttf},.*", workingDirTest, dpiScale, @@ -2297,8 +2307,8 @@ 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("cut",BIND_FOR(GUI_ACTION_PAT_CUT))) doCopy(true,true,selStart,selEnd); + if (ImGui::MenuItem("copy",BIND_FOR(GUI_ACTION_PAT_COPY))) doCopy(false,true,selStart,selEnd); if (ImGui::MenuItem("paste",BIND_FOR(GUI_ACTION_PAT_PASTE))) doPaste(); if (ImGui::BeginMenu("paste special...")) { if (ImGui::MenuItem("paste mix",BIND_FOR(GUI_ACTION_PAT_PASTE_MIX))) doPaste(GUI_PASTE_MODE_MIX_FG); @@ -3132,6 +3142,14 @@ bool FurnaceGUI::loop() { break; case SDL_DISPLAYEVENT: { switch (ev.display.event) { + case SDL_DISPLAYEVENT_CONNECTED: + logD("display %d connected!",ev.display.display); + updateWindow=true; + break; + case SDL_DISPLAYEVENT_DISCONNECTED: + logD("display %d disconnected!",ev.display.display); + updateWindow=true; + break; case SDL_DISPLAYEVENT_ORIENTATION: logD("display oriented to %d",ev.display.data1); updateWindow=true; @@ -3399,6 +3417,12 @@ bool FurnaceGUI::loop() { eventTimeEnd=SDL_GetPerformanceCounter(); + if (SDL_GetWindowFlags(sdlWin)&SDL_WINDOW_MINIMIZED) { + SDL_Delay(30); + drawHalt=0; + continue; + } + layoutTimeBegin=SDL_GetPerformanceCounter(); ImGui_ImplSDLRenderer_NewFrame(); @@ -3493,7 +3517,7 @@ bool FurnaceGUI::loop() { if (ImGui::BeginMenu("export VGM...")) { ImGui::Text("settings:"); if (ImGui::BeginCombo("format version",fmt::sprintf("%d.%.2x",vgmExportVersion>>8,vgmExportVersion&0xff).c_str())) { - for (int i=0; i<6; i++) { + for (int i=0; i<7; i++) { if (ImGui::Selectable(fmt::sprintf("%d.%.2x",vgmVersions[i]>>8,vgmVersions[i]&0xff).c_str(),vgmExportVersion==vgmVersions[i])) { vgmExportVersion=vgmVersions[i]; } @@ -3709,6 +3733,7 @@ bool FurnaceGUI::loop() { if (ImGui::BeginMenu("window")) { if (ImGui::MenuItem("song information",BIND_FOR(GUI_ACTION_WINDOW_SONG_INFO),songInfoOpen)) songInfoOpen=!songInfoOpen; if (ImGui::MenuItem("subsongs",BIND_FOR(GUI_ACTION_WINDOW_SUBSONGS),subSongsOpen)) subSongsOpen=!subSongsOpen; + if (ImGui::MenuItem("speed",BIND_FOR(GUI_ACTION_WINDOW_SPEED),speedOpen)) speedOpen=!speedOpen; if (settings.unifiedDataView) { if (ImGui::MenuItem("assets",BIND_FOR(GUI_ACTION_WINDOW_INS_LIST),insListOpen)) insListOpen=!insListOpen; } else { @@ -3719,6 +3744,7 @@ bool FurnaceGUI::loop() { if (ImGui::MenuItem("orders",BIND_FOR(GUI_ACTION_WINDOW_ORDERS),ordersOpen)) ordersOpen=!ordersOpen; if (ImGui::MenuItem("pattern",BIND_FOR(GUI_ACTION_WINDOW_PATTERN),patternOpen)) patternOpen=!patternOpen; if (ImGui::MenuItem("mixer",BIND_FOR(GUI_ACTION_WINDOW_MIXER),mixerOpen)) mixerOpen=!mixerOpen; + if (ImGui::MenuItem("grooves",BIND_FOR(GUI_ACTION_WINDOW_GROOVES),groovesOpen)) groovesOpen=!groovesOpen; if (ImGui::MenuItem("channels",BIND_FOR(GUI_ACTION_WINDOW_CHANNELS),channelsOpen)) channelsOpen=!channelsOpen; if (ImGui::MenuItem("pattern manager",BIND_FOR(GUI_ACTION_WINDOW_PAT_MANAGER),patManagerOpen)) patManagerOpen=!patManagerOpen; if (ImGui::MenuItem("chip manager",BIND_FOR(GUI_ACTION_WINDOW_SYS_MANAGER),sysManagerOpen)) sysManagerOpen=!sysManagerOpen; @@ -3757,7 +3783,21 @@ bool FurnaceGUI::loop() { if (e->isPlaying()) { int totalTicks=e->getTotalTicks(); int totalSeconds=e->getTotalSeconds(); - ImGui::Text("| Speed %d:%d @ %gHz (%g BPM) | Order %d/%d | Row %d/%d | %d:%.2d:%.2d.%.2d",e->getSpeed1(),e->getSpeed2(),e->getCurHz(),calcBPM(e->getSpeed1(),e->getSpeed2(),e->getCurHz(),e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD),e->getOrder(),e->curSubSong->ordersLen,e->getRow(),e->curSubSong->patLen,totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000); + + String info; + + DivGroovePattern gp=e->getSpeeds(); + if (gp.len==2) { + info=fmt::sprintf("| Speed %d:%d",gp.val[0],gp.val[1]); + } else if (gp.len==1) { + info=fmt::sprintf("| Speed %d",gp.val[0]); + } else { + info="| Groove"; + } + + info+=fmt::sprintf(" @ %gHz (%g BPM) | Order %d/%d | Row %d/%d | %d:%.2d:%.2d.%.2d",e->getCurHz(),calcBPM(e->getSpeeds(),e->getCurHz(),e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD),e->getOrder(),e->curSubSong->ordersLen,e->getRow(),e->curSubSong->patLen,totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000); + + ImGui::TextUnformatted(info.c_str()); } else { bool hasInfo=false; String info; @@ -3895,6 +3935,8 @@ bool FurnaceGUI::loop() { drawSpoiler(); drawPattern(); drawEditControls(); + drawSpeed(); + drawGrooves(); drawSongInfo(); drawOrders(); drawSampleList(); @@ -5148,7 +5190,12 @@ bool FurnaceGUI::loop() { renderTimeDelta=renderTimeEnd-renderTimeBegin; eventTimeDelta=eventTimeEnd-eventTimeBegin; - if (--soloTimeout<0) soloTimeout=0; + soloTimeout-=ImGui::GetIO().DeltaTime; + if (soloTimeout<0) { + soloTimeout=0; + } else { + WAKE_UP; + } wheelX=0; wheelY=0; @@ -5220,6 +5267,8 @@ bool FurnaceGUI::init() { patManagerOpen=e->getConfBool("patManagerOpen",false); sysManagerOpen=e->getConfBool("sysManagerOpen",false); clockOpen=e->getConfBool("clockOpen",false); + speedOpen=e->getConfBool("speedOpen",true); + groovesOpen=e->getConfBool("groovesOpen",false); regViewOpen=e->getConfBool("regViewOpen",false); logOpen=e->getConfBool("logOpen",false); effectListOpen=e->getConfBool("effectListOpen",false); @@ -5332,6 +5381,10 @@ bool FurnaceGUI::init() { logD("auto-detecting UI scale factor."); dpiScale=getScaleFactor(videoBackend); logD("scale factor: %f",dpiScale); + if (dpiScale<0.1f) { + logW("scale what?"); + dpiScale=1.0f; + } } #if !(defined(__APPLE__) || defined(_WIN32)) @@ -5590,6 +5643,8 @@ void FurnaceGUI::commitState() { e->setConf("patManagerOpen",patManagerOpen); e->setConf("sysManagerOpen",sysManagerOpen); e->setConf("clockOpen",clockOpen); + e->setConf("speedOpen",speedOpen); + e->setConf("groovesOpen",groovesOpen); e->setConf("regViewOpen",regViewOpen); e->setConf("logOpen",logOpen); e->setConf("effectListOpen",effectListOpen); @@ -5784,7 +5839,6 @@ FurnaceGUI::FurnaceGUI(): editStep(1), exportLoops(0), soloChan(-1), - soloTimeout(0), orderEditMode(0), orderCursor(-1), loopOrder(-1), @@ -5803,6 +5857,8 @@ FurnaceGUI::FurnaceGUI(): dragDestinationY(0), oldBeat(-1), oldBar(-1), + curGroove(-1), + soloTimeout(0.0f), exportFadeOut(5.0), editControlsOpen(true), ordersOpen(true), @@ -5836,6 +5892,8 @@ FurnaceGUI::FurnaceGUI(): patManagerOpen(false), sysManagerOpen(false), clockOpen(false), + speedOpen(true), + groovesOpen(false), clockShowReal(true), clockShowRow(true), clockShowBeat(true), @@ -5864,10 +5922,12 @@ FurnaceGUI::FurnaceGUI(): latchNibble(false), nonLatchNibble(false), keepLoopAlive(false), + keepGrooveAlive(false), orderScrollLocked(false), orderScrollTolerance(false), dragMobileMenu(false), dragMobileEditButton(false), + wantGrooveListFocus(false), curWindow(GUI_WINDOW_NOTHING), nextWindow(GUI_WINDOW_NOTHING), curWindowLast(GUI_WINDOW_NOTHING), diff --git a/src/gui/gui.h b/src/gui/gui.h index 15319f747..ba8dc933f 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -292,6 +292,7 @@ enum FurnaceGUIWindows { GUI_WINDOW_NOTHING=0, GUI_WINDOW_EDIT_CONTROLS, GUI_WINDOW_SONG_INFO, + GUI_WINDOW_SPEED, GUI_WINDOW_ORDERS, GUI_WINDOW_INS_LIST, GUI_WINDOW_PATTERN, @@ -320,6 +321,7 @@ enum FurnaceGUIWindows { GUI_WINDOW_SUBSONGS, GUI_WINDOW_FIND, GUI_WINDOW_CLOCK, + GUI_WINDOW_GROOVES, GUI_WINDOW_SPOILER }; @@ -439,6 +441,7 @@ enum FurnaceGUIActions { GUI_ACTION_WINDOW_INS_LIST, GUI_ACTION_WINDOW_INS_EDIT, GUI_ACTION_WINDOW_SONG_INFO, + GUI_ACTION_WINDOW_SPEED, GUI_ACTION_WINDOW_PATTERN, GUI_ACTION_WINDOW_WAVE_LIST, GUI_ACTION_WINDOW_WAVE_EDIT, @@ -464,6 +467,7 @@ enum FurnaceGUIActions { GUI_ACTION_WINDOW_SUBSONGS, GUI_ACTION_WINDOW_FIND, GUI_ACTION_WINDOW_CLOCK, + GUI_ACTION_WINDOW_GROOVES, GUI_ACTION_COLLAPSE_WINDOW, GUI_ACTION_CLOSE_WINDOW, @@ -1095,7 +1099,7 @@ class FurnaceGUI { String workingDirVGMExport, workingDirZSMExport, workingDirROMExport, workingDirFont, workingDirColors, workingDirKeybinds; String workingDirLayout, workingDirROM, workingDirTest; String mmlString[32]; - String mmlStringW, mmlStringSNES; + String mmlStringW, mmlStringSNES, grooveString, grooveListString; std::vector sysSearchResults; std::vector newSongSearchResults; @@ -1294,6 +1298,8 @@ class FurnaceGUI { int exportLoops; double exportFadeOut; int macroLayout; + float doubleClickTime; + int oneDigitEffects; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -1428,6 +1434,8 @@ class FurnaceGUI { exportLoops(0), exportFadeOut(0.0), macroLayout(0), + doubleClickTime(0.3f), + oneDigitEffects(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), @@ -1447,9 +1455,11 @@ class FurnaceGUI { DivInstrument* prevInsData; - int curIns, curWave, curSample, curOctave, curOrder, prevIns, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan, soloTimeout, orderEditMode, orderCursor; + int curIns, curWave, curSample, curOctave, curOrder, prevIns, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan,orderEditMode, orderCursor; int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory, latchTarget; - int wheelX, wheelY, dragSourceX, dragSourceY, dragDestinationX, dragDestinationY, oldBeat, oldBar; + int wheelX, wheelY, dragSourceX, dragSourceXFine, dragSourceY, dragDestinationX, dragDestinationXFine, dragDestinationY, oldBeat, oldBar; + int curGroove; + float soloTimeout; double exportFadeOut; @@ -1457,7 +1467,8 @@ class FurnaceGUI { bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen; bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen; - bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen, clockOpen; + bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen, clockOpen, speedOpen; + bool groovesOpen; bool clockShowReal, clockShowRow, clockShowBeat, clockShowMetro, clockShowTime; float clockMetroTick[16]; @@ -1465,7 +1476,7 @@ class FurnaceGUI { SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd; bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI; bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble; - bool keepLoopAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu, dragMobileEditButton; + bool keepLoopAlive, keepGrooveAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu, dragMobileEditButton, wantGrooveListFocus; FurnaceGUIWindows curWindow, nextWindow, curWindowLast; std::atomic curWindowThreadSafe; float peak[DIV_MAX_OUTPUTS]; @@ -1794,7 +1805,7 @@ class FurnaceGUI { void pushAccentColors(const ImVec4& one, const ImVec4& two, const ImVec4& border, const ImVec4& borderShadow); void popAccentColors(); - float calcBPM(int s1, int s2, float hz, int vN, int vD); + float calcBPM(const DivGroovePattern& speeds, float hz, int vN, int vD); void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel); @@ -1813,6 +1824,8 @@ class FurnaceGUI { void drawMobileOrderSel(); void drawEditControls(); void drawSongInfo(bool asChild=false); + void drawSpeed(bool asChild=false); + void drawGrooves(); void drawOrders(); void drawPattern(); void drawInsList(bool asChild=false); @@ -1884,8 +1897,8 @@ class FurnaceGUI { void doPullDelete(); void doInsert(); void doTranspose(int amount, OperationMask& mask); - void doCopy(bool cut); - void doPaste(PasteMode mode=GUI_PASTE_MODE_NORMAL, int arg=0); + String doCopy(bool cut, bool writeClipboard, const SelectionPoint& sStart, const SelectionPoint& sEnd); + void doPaste(PasteMode mode=GUI_PASTE_MODE_NORMAL, int arg=0, bool readClipboard=true, String clipb=""); void doChangeIns(int ins); void doInterpolate(); void doFade(int p0, int p1, bool mode); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index d19070fdd..0445704ed 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -70,13 +70,14 @@ const int altValues[24]={ 0, 10, 1, 11, 2, 3, 12, 4, 13, 5, 14, 6, 7, 15, 8, -1, 9, -1, -1, -1, -1, -1, -1, -1 }; -const int vgmVersions[6]={ +const int vgmVersions[7]={ 0x150, 0x151, 0x160, 0x161, 0x170, - 0x171 + 0x171, + 0x172 }; const char* insTypes[DIV_INS_MAX+1]={ @@ -493,6 +494,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("WINDOW_INS_LIST", "Instrument List", 0), D("WINDOW_INS_EDIT", "Instrument Editor", 0), D("WINDOW_SONG_INFO", "Song Information", 0), + D("WINDOW_SPEED", "Speed", 0), D("WINDOW_PATTERN", "Pattern", 0), D("WINDOW_WAVE_LIST", "Wavetable List", 0), D("WINDOW_WAVE_EDIT", "Wavetable Editor", 0), @@ -518,6 +520,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("WINDOW_SUBSONGS", "Subsongs", 0), D("WINDOW_FIND", "Find/Replace", FURKMOD_CMD|SDLK_f), D("WINDOW_CLOCK", "Clock", 0), + D("WINDOW_GROOVES", "Grooves", 0), D("COLLAPSE_WINDOW", "Collapse/expand current window", 0), D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE), diff --git a/src/gui/guiConst.h b/src/gui/guiConst.h index cb5eb5408..00b350495 100644 --- a/src/gui/guiConst.h +++ b/src/gui/guiConst.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -54,5 +54,5 @@ extern const int* chipCategories[]; extern const FurnaceGUIActionDef guiActions[]; extern const FurnaceGUIColorDef guiColors[]; extern const int altValues[24]; -extern const int vgmVersions[6]; +extern const int vgmVersions[7]; extern const FurnaceGUIColors fxColors[256]; \ No newline at end of file diff --git a/src/gui/icon.c b/src/gui/icon.c index bae81ab7c..80429eb6b 100644 --- a/src/gui/icon.c +++ b/src/gui/icon.c @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/icon.h b/src/gui/icon.h index 43135be86..6e866f25f 100644 --- a/src/gui/icon.h +++ b/src/gui/icon.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index c72958ed9..1d93cb671 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -352,6 +352,10 @@ const int detuneUnmap[2][11]={ {0, 0, 0, 3, 4, 5, 6, 7, 2, 1, 0} }; +const int kslMap[4]={ + 0, 2, 1, 3 +}; + // do not change these! // anything other than a checkbox will look ugly! // @@ -1778,14 +1782,22 @@ void FurnaceGUI::drawMacros(std::vector& macros, FurnaceGUI ImGui::TableNextColumn(); float availableWidth=ImGui::GetContentRegionAvail().x-reservedSpace; int totalFit=MIN(255,availableWidth/MAX(1,macroPointSize*dpiScale)); - if (macroDragScroll>255-totalFit) { - macroDragScroll=255-totalFit; + int scrollMax=0; + for (FurnaceGUIMacroDesc& i: macros) { + if (i.macro->len>scrollMax) scrollMax=i.macro->len; } + scrollMax-=totalFit; + if (scrollMax<0) scrollMax=0; + if (macroDragScroll>scrollMax) { + macroDragScroll=scrollMax; + } + ImGui::BeginDisabled(scrollMax<1); ImGui::SetNextItemWidth(availableWidth); - if (CWSliderInt("##MacroScroll",¯oDragScroll,0,255-totalFit,"")) { + if (CWSliderInt("##MacroScroll",¯oDragScroll,0,scrollMax,"")) { if (macroDragScroll<0) macroDragScroll=0; - if (macroDragScroll>255-totalFit) macroDragScroll=255-totalFit; + if (macroDragScroll>scrollMax) macroDragScroll=scrollMax; } + ImGui::EndDisabled(); // draw macros for (FurnaceGUIMacroDesc& i: macros) { @@ -1834,11 +1846,13 @@ void FurnaceGUI::drawMacros(std::vector& macros, FurnaceGUI ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::TableNextColumn(); + ImGui::BeginDisabled(scrollMax<1); ImGui::SetNextItemWidth(availableWidth); - if (CWSliderInt("##MacroScroll",¯oDragScroll,0,255-totalFit,"")) { + if (CWSliderInt("##MacroScroll",¯oDragScroll,0,scrollMax,"")) { if (macroDragScroll<0) macroDragScroll=0; - if (macroDragScroll>255-totalFit) macroDragScroll=255-totalFit; + if (macroDragScroll>scrollMax) macroDragScroll=scrollMax; } + ImGui::EndDisabled(); ImGui::EndTable(); } break; @@ -2778,7 +2792,11 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { P(CWVSliderScalar("##RS",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE)); } else { - P(CWVSliderScalar("##KSL",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); + int ksl=ins->type==DIV_INS_OPLL?op.ksl:kslMap[op.ksl&3]; + if (CWVSliderInt("##KSL",ImVec2(20.0f*dpiScale,sliderHeight),&ksl,0,3)) { + op.ksl=(ins->type==DIV_INS_OPLL?ksl:kslMap[ksl&3]); + PARAMETER; + } } if (ins->type==DIV_INS_OPZ) { @@ -3226,7 +3244,7 @@ void FurnaceGUI::drawInsEdit() { break; case DIV_INS_OPL: - case DIV_INS_OPL_DRUMS: + case DIV_INS_OPL_DRUMS: { // waveform drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight)); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -3268,9 +3286,14 @@ void FurnaceGUI::drawInsEdit() { ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_KSL)); - P(CWSliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE,tempID)); rightClickable + int ksl=kslMap[op.ksl&3]; + if (CWSliderInt("##KSL",&ksl,0,3,tempID)) { + op.ksl=kslMap[ksl&3]; + PARAMETER; + } rightClickable break; + } case DIV_INS_OPZ: { // waveform drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight)); @@ -3613,7 +3636,11 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextColumn(); ImGui::Text("%s",FM_NAME(FM_RS)); } else { - P(CWSliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); rightClickable + int ksl=ins->type==DIV_INS_OPLL?op.ksl:kslMap[op.ksl&3]; + if (CWSliderInt("##KSL",&ksl,0,3)) { + op.ksl=(ins->type==DIV_INS_OPLL?ksl:kslMap[ksl&3]); + PARAMETER; + } rightClickable ImGui::TableNextColumn(); ImGui::Text("%s",FM_NAME(FM_KSL)); } @@ -3779,6 +3806,9 @@ void FurnaceGUI::drawInsEdit() { } } + if (ins->type==DIV_INS_FM) { + macroList.push_back(FurnaceGUIMacroDesc("LFO Speed",&ins->std.ex3Macro,0,8,96,uiColors[GUI_COLOR_MACRO_OTHER])); + } if (ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { macroList.push_back(FurnaceGUIMacroDesc("AM Depth",&ins->std.ex1Macro,0,127,128,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("PM Depth",&ins->std.ex2Macro,0,127,128,uiColors[GUI_COLOR_MACRO_OTHER])); diff --git a/src/gui/intConst.cpp b/src/gui/intConst.cpp index 9a41486e1..167fdd7e8 100644 --- a/src/gui/intConst.cpp +++ b/src/gui/intConst.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/intConst.h b/src/gui/intConst.h index ff11a4968..082f421a7 100644 --- a/src/gui/intConst.h +++ b/src/gui/intConst.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/log.cpp b/src/gui/log.cpp index 0419b6a7d..0dc56e5de 100644 --- a/src/gui/log.cpp +++ b/src/gui/log.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/macstuff.h b/src/gui/macstuff.h index 73ca0c47f..76afff169 100644 --- a/src/gui/macstuff.h +++ b/src/gui/macstuff.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/mixer.cpp b/src/gui/mixer.cpp index c9aa96b80..35c083f28 100644 --- a/src/gui/mixer.cpp +++ b/src/gui/mixer.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/newSong.cpp b/src/gui/newSong.cpp index 0df3a2538..8fdafeb9b 100644 --- a/src/gui/newSong.cpp +++ b/src/gui/newSong.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/orders.cpp b/src/gui/orders.cpp index f3349d71d..914217787 100644 --- a/src/gui/orders.cpp +++ b/src/gui/orders.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/osc.cpp b/src/gui/osc.cpp index 7f4142211..f04587c8b 100644 --- a/src/gui/osc.cpp +++ b/src/gui/osc.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/patManager.cpp b/src/gui/patManager.cpp index 161a42cb2..6f5e3205b 100644 --- a/src/gui/patManager.cpp +++ b/src/gui/patManager.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 3502b8a52..b02b11501 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -287,10 +287,14 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int if (pat->data[i][index]>0xff) { snprintf(id,63,"??##PE%d_%d_%d",k,i,j); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else { + } else if (pat->data[i][index]>0x10 || settings.oneDigitEffects==0) { const unsigned char data=pat->data[i][index]; snprintf(id,63,"%.2X##PE%d_%d_%d",data,k,i,j); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); + } else { + const unsigned char data=pat->data[i][index]; + snprintf(id,63," %.1X##PE%d_%d_%d",data,k,i,j); + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); } } ImGui::SameLine(0.0f,0.0f); @@ -755,7 +759,7 @@ void FurnaceGUI::drawPattern() { soloTimeout=0; } else { e->toggleMute(i); - soloTimeout=20; + soloTimeout=settings.doubleClickTime; soloChan=i; } } @@ -965,7 +969,7 @@ void FurnaceGUI::drawPattern() { // overflow changes order // TODO: this is very unreliable and sometimes it can warp you out of the song - if (settings.scrollChangesOrder && !e->isPlaying()) { + if (settings.scrollChangesOrder && !e->isPlaying() && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) { if (wheelY!=0) { if (wheelY>0) { if (ImGui::GetScrollY()<=0) { diff --git a/src/gui/piano.cpp b/src/gui/piano.cpp index d999d7fd2..fe451c3f7 100644 --- a/src/gui/piano.cpp +++ b/src/gui/piano.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/plot_nolerp.cpp b/src/gui/plot_nolerp.cpp index f1120ed4a..2fd7994bc 100644 --- a/src/gui/plot_nolerp.cpp +++ b/src/gui/plot_nolerp.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -367,7 +367,7 @@ int PlotCustomEx(ImGuiPlotType plot_type, const char* label, float (*values_gett if (plot_type == ImGuiPlotType_Lines) ImGui::SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx + 1, v1); else if (plot_type == ImGuiPlotType_Histogram) - ImGui::SetTooltip("%d: %8.4g", v_idx+values_display_offset, v0); + ImGui::SetTooltip("%d: %d", v_idx+values_display_offset, (int)v0); } idx_hovered = v_idx; } diff --git a/src/gui/plot_nolerp.h b/src/gui/plot_nolerp.h index 48332b339..50334dda6 100644 --- a/src/gui/plot_nolerp.h +++ b/src/gui/plot_nolerp.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 921fa88ac..fe4d2b98d 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/regView.cpp b/src/gui/regView.cpp index 8b2176eb2..729f17c6e 100644 --- a/src/gui/regView.cpp +++ b/src/gui/regView.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index a8631b2ac..115223cc8 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/sampleUtil.h b/src/gui/sampleUtil.h index 187f5ebf6..bdef166f4 100644 --- a/src/gui/sampleUtil.h +++ b/src/gui/sampleUtil.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/scaling.cpp b/src/gui/scaling.cpp index 7c42a1e33..e3d8d6f80 100644 --- a/src/gui/scaling.cpp +++ b/src/gui/scaling.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +24,8 @@ #ifdef _WIN32 #include -typedef HRESULT (*GDFM)(HMONITOR,int,UINT*,UINT*); +#include "shellScalingStub.h" +typedef HRESULT (WINAPI *GDFM)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); #endif #ifdef __APPLE__ @@ -71,9 +72,9 @@ double getScaleFactor(const char* driverHint) { return 1.0; } - unsigned int dpiX=96; - unsigned int dpiY=96; - HRESULT result=ta_GetDpiForMonitor(disp,0,&dpiX,&dpiY); + UINT dpiX=96; + UINT dpiY=96; + HRESULT result=ta_GetDpiForMonitor(disp,MDT_EFFECTIVE_DPI,&dpiX,&dpiY); if (result!=S_OK) { logW("GetDpiForMonitor failure (%.8x) - no scaling detection available!",result); diff --git a/src/gui/scaling.h b/src/gui/scaling.h index ce1a364a6..60710a34f 100644 --- a/src/gui/scaling.h +++ b/src/gui/scaling.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index b998d3215..50a36a9e8 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -492,6 +492,13 @@ void FurnaceGUI::drawSettings() { ImGui::Separator(); + if (CWSliderFloat("Double-click time (seconds)",&settings.doubleClickTime,0.02,1.0,"%.2f")) { + if (settings.doubleClickTime<0.02) settings.doubleClickTime=0.02; + if (settings.doubleClickTime>1.0) settings.doubleClickTime=1.0; + + applyUISettings(false); + } + ImGui::Text("Toggle channel solo on:"); if (ImGui::RadioButton("Right-click or double-click##soloA",settings.soloAction==0)) { settings.soloAction=0; @@ -1595,6 +1602,11 @@ void FurnaceGUI::drawSettings() { settings.germanNotation=germanNotationB; } + bool oneDigitEffectsB=settings.oneDigitEffects; + if (ImGui::Checkbox("Single-digit effects for 00-0F",&oneDigitEffectsB)) { + settings.oneDigitEffects=oneDigitEffectsB; + } + bool centerPatternB=settings.centerPattern; if (ImGui::Checkbox("Center pattern view",¢erPatternB)) { settings.centerPattern=centerPatternB; @@ -2557,6 +2569,8 @@ void FurnaceGUI::syncSettings() { settings.exportLoops=e->getConfInt("exportLoops",0); settings.exportFadeOut=e->getConfDouble("exportFadeOut",0.0); settings.macroLayout=e->getConfInt("macroLayout",0); + settings.doubleClickTime=e->getConfFloat("doubleClickTime",0.3f); + settings.oneDigitEffects=e->getConfInt("oneDigitEffects",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -2667,6 +2681,8 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.ordersCursor,0,1); clampSetting(settings.persistFadeOut,0,1); clampSetting(settings.macroLayout,0,4); + clampSetting(settings.doubleClickTime,0.02,1.0); + clampSetting(settings.oneDigitEffects,0,1); if (settings.exportLoops<0.0) settings.exportLoops=0.0; if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0; @@ -2870,6 +2886,8 @@ void FurnaceGUI::commitSettings() { e->setConf("exportLoops",settings.exportLoops); e->setConf("exportFadeOut",settings.exportFadeOut); e->setConf("macroLayout",settings.macroLayout); + e->setConf("doubleClickTime",settings.doubleClickTime); + e->setConf("oneDigitEffects",settings.oneDigitEffects); // colors for (int i=0; iAddFontFromFileTTF(settings.mainFontPath.c_str(),e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.mainFontPath.c_str(),MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),NULL,fontRange))==NULL) { logW("could not load UI font! reverting to default font"); settings.mainFont=0; - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),NULL,fontRange))==NULL) { logE("could not load UI font! falling back to Proggy Clean."); mainFont=ImGui::GetIO().Fonts->AddFontDefault(); } } } else if (settings.mainFont==5) { // system font - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_1,e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_2,e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_3,e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_1,MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_2,MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_3,MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),NULL,fontRange))==NULL) { logW("could not load UI font! reverting to default font"); settings.mainFont=0; - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),NULL,fontRange))==NULL) { logE("could not load UI font! falling back to Proggy Clean."); mainFont=ImGui::GetIO().Fonts->AddFontDefault(); } @@ -3542,21 +3563,21 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { } } } else { - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,fontRange))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),NULL,fontRange))==NULL) { logE("could not load UI font! falling back to Proggy Clean."); mainFont=ImGui::GetIO().Fonts->AddFontDefault(); } } // two fallback fonts - mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_liberationSans_compressed_data,font_liberationSans_compressed_size,e->getConfInt("mainFontSize",18)*dpiScale,&fc1,fontRange); - mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_unifont_compressed_data,font_unifont_compressed_size,e->getConfInt("mainFontSize",18)*dpiScale,&fc1,fontRange); + mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_liberationSans_compressed_data,font_liberationSans_compressed_size,MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),&fc1,fontRange); + mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_unifont_compressed_data,font_unifont_compressed_size,MAX(1,e->getConfInt("mainFontSize",18)*dpiScale),&fc1,fontRange); ImFontConfig fc; fc.MergeMode=true; fc.GlyphMinAdvanceX=e->getConfInt("iconSize",16)*dpiScale; static const ImWchar fontRangeIcon[]={ICON_MIN_FA,ICON_MAX_FA,0}; - if ((iconFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(iconFont_compressed_data,iconFont_compressed_size,e->getConfInt("iconSize",16)*dpiScale,&fc,fontRangeIcon))==NULL) { + if ((iconFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(iconFont_compressed_data,iconFont_compressed_size,MAX(1,e->getConfInt("iconSize",16)*dpiScale),&fc,fontRangeIcon))==NULL) { logE("could not load icon font!"); } @@ -3565,21 +3586,21 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { patFont=mainFont; } else { if (settings.patFont==6) { // custom font - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.patFontPath.c_str(),e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.patFontPath.c_str(),MAX(1,e->getConfInt("patFontSize",18)*dpiScale),NULL,upTo800))==NULL) { logW("could not load pattern font! reverting to default font"); settings.patFont=0; - if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],MAX(1,e->getConfInt("patFontSize",18)*dpiScale),NULL,upTo800))==NULL) { logE("could not load pattern font! falling back to Proggy Clean."); patFont=ImGui::GetIO().Fonts->AddFontDefault(); } } } else if (settings.patFont==5) { // system font - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_1,e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_2,e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_3,e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_1,MAX(1,e->getConfInt("patFontSize",18)*dpiScale),NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_2,MAX(1,e->getConfInt("patFontSize",18)*dpiScale),NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_3,MAX(1,e->getConfInt("patFontSize",18)*dpiScale),NULL,upTo800))==NULL) { logW("could not load pattern font! reverting to default font"); settings.patFont=0; - if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],MAX(1,e->getConfInt("patFontSize",18)*dpiScale),NULL,upTo800))==NULL) { logE("could not load pattern font! falling back to Proggy Clean."); patFont=ImGui::GetIO().Fonts->AddFontDefault(); } @@ -3587,7 +3608,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { } } } else { - if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,upTo800))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],MAX(1,e->getConfInt("patFontSize",18)*dpiScale),NULL,upTo800))==NULL) { logE("could not load pattern font!"); patFont=ImGui::GetIO().Fonts->AddFontDefault(); } @@ -3596,7 +3617,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { // 0x39B = Λ static const ImWchar bigFontRange[]={0x20,0xFF,0x39b,0x39b,0}; - if ((bigFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_plexSans_compressed_data,font_plexSans_compressed_size,40*dpiScale,NULL,bigFontRange))==NULL) { + if ((bigFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_plexSans_compressed_data,font_plexSans_compressed_size,MAX(1,40*dpiScale),NULL,bigFontRange))==NULL) { logE("could not load big UI font!"); } diff --git a/src/gui/shellScalingStub.h b/src/gui/shellScalingStub.h new file mode 100644 index 000000000..88ddf2f7e --- /dev/null +++ b/src/gui/shellScalingStub.h @@ -0,0 +1,36 @@ +/* + * Copyright 2016 Sebastian Lackner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_SHELLSCALINGAPI_H + +typedef enum MONITOR_DPI_TYPE +{ + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI, +} MONITOR_DPI_TYPE; + +typedef enum PROCESS_DPI_AWARENESS +{ + PROCESS_DPI_UNAWARE, + PROCESS_SYSTEM_DPI_AWARE, + PROCESS_PER_MONITOR_DPI_AWARE +} PROCESS_DPI_AWARENESS; + +#endif /* __WINE_SHELLSCALINGAPI_H */ diff --git a/src/gui/songInfo.cpp b/src/gui/songInfo.cpp index e20468575..0bd94fdc4 100644 --- a/src/gui/songInfo.cpp +++ b/src/gui/songInfo.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ void FurnaceGUI::drawSongInfo(bool asChild) { nextWindow=GUI_WINDOW_NOTHING; } if (!songInfoOpen && !asChild) return; - bool began=asChild?ImGui::BeginChild("Song Information"):ImGui::Begin("Song Information",&songInfoOpen,globalWinFlags); + bool began=asChild?ImGui::BeginChild("Song Info##Song Information"):ImGui::Begin("Song Info##Song Information",&songInfoOpen,globalWinFlags); if (began) { if (ImGui::BeginTable("NameAuthor",2,ImGuiTableFlags_SizingStretchProp)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); @@ -100,138 +100,16 @@ void FurnaceGUI::drawSongInfo(bool asChild) { ImGui::EndTable(); } - if (ImGui::BeginTable("OtherProps",3,ImGuiTableFlags_SizingStretchProp)) { + if (ImGui::BeginTable("OtherProps",2,ImGuiTableFlags_SizingStretchProp)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); - ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("TimeBase"); - ImGui::TableNextColumn(); - float avail=ImGui::GetContentRegionAvail().x; - ImGui::SetNextItemWidth(avail); - unsigned char realTB=e->curSubSong->timeBase+1; - if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED - if (realTB<1) realTB=1; - if (realTB>16) realTB=16; - e->curSubSong->timeBase=realTB-1; - } - ImGui::TableNextColumn(); - ImGui::Text("%.2f BPM",calcBPM(e->curSubSong->speed1,e->curSubSong->speed2,e->curSubSong->hz,e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD)); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Speed"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->curSubSong->speed1,&_ONE,&_THREE)) { MARK_MODIFIED - if (e->curSubSong->speed1<1) e->curSubSong->speed1=1; - if (e->isPlaying()) play(); - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->curSubSong->speed2,&_ONE,&_THREE)) { MARK_MODIFIED - if (e->curSubSong->speed2<1) e->curSubSong->speed2=1; - if (e->isPlaying()) play(); - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Virtual Tempo"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##VTempoN",ImGuiDataType_S16,&e->curSubSong->virtualTempoN,&_ONE,&_THREE)) { MARK_MODIFIED - if (e->curSubSong->virtualTempoN<1) e->curSubSong->virtualTempoN=1; - if (e->curSubSong->virtualTempoN>255) e->curSubSong->virtualTempoN=255; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Numerator"); - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##VTempoD",ImGuiDataType_S16,&e->curSubSong->virtualTempoD,&_ONE,&_THREE)) { MARK_MODIFIED - if (e->curSubSong->virtualTempoD<1) e->curSubSong->virtualTempoD=1; - if (e->curSubSong->virtualTempoD>255) e->curSubSong->virtualTempoD=255; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Denominator (set to base tempo)"); - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Highlight"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->curSubSong->hilightA,&_ONE,&_THREE)) { - MARK_MODIFIED; - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->curSubSong->hilightB,&_ONE,&_THREE)) { - MARK_MODIFIED; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Pattern Length"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - int patLen=e->curSubSong->patLen; - if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED - if (patLen<1) patLen=1; - if (patLen>DIV_MAX_PATTERNS) patLen=DIV_MAX_PATTERNS; - e->curSubSong->patLen=patLen; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Song Length"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - int ordLen=e->curSubSong->ordersLen; - if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED - if (ordLen<1) ordLen=1; - if (ordLen>DIV_MAX_PATTERNS) ordLen=DIV_MAX_PATTERNS; - e->curSubSong->ordersLen=ordLen; - if (curOrder>=ordLen) { - setOrder(ordLen-1); - } - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ImGui::Selectable(tempoView?"Base Tempo##TempoOrHz":"Tick Rate##TempoOrHz")) { - tempoView=!tempoView; - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - float setHz=tempoView?e->curSubSong->hz*2.5:e->curSubSong->hz; - if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { MARK_MODIFIED - if (tempoView) setHz/=2.5; - if (setHz<1) setHz=1; - if (setHz>999) setHz=999; - e->setSongRate(setHz,setHz<52); - } - if (tempoView) { - ImGui::TableNextColumn(); - ImGui::Text("= %gHz",e->curSubSong->hz); - } else { - if (e->curSubSong->hz>=49.98 && e->curSubSong->hz<=50.02) { - ImGui::TableNextColumn(); - ImGui::Text("PAL"); - } - if (e->curSubSong->hz>=59.9 && e->curSubSong->hz<=60.11) { - ImGui::TableNextColumn(); - ImGui::Text("NTSC"); - } - } ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Tuning (A-4)"); ImGui::TableNextColumn(); float tune=e->song.tuning; + float avail=ImGui::GetContentRegionAvail().x; ImGui::SetNextItemWidth(avail); if (ImGui::InputFloat("##Tuning",&tune,1.0f,3.0f,"%g")) { MARK_MODIFIED if (tune<220.0f) tune=220.0f; diff --git a/src/gui/songNotes.cpp b/src/gui/songNotes.cpp index eb63cbf3a..d0218f7bf 100644 --- a/src/gui/songNotes.cpp +++ b/src/gui/songNotes.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/speed.cpp b/src/gui/speed.cpp new file mode 100644 index 000000000..796f0ec19 --- /dev/null +++ b/src/gui/speed.cpp @@ -0,0 +1,258 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2023 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" +#include "imgui.h" +#include "misc/cpp/imgui_stdlib.h" +#include "intConst.h" + +void FurnaceGUI::drawSpeed(bool asChild) { + if (nextWindow==GUI_WINDOW_SPEED) { + speedOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!speedOpen && !asChild) return; + bool began=asChild?ImGui::BeginChild("Speed"):ImGui::Begin("Speed",&speedOpen,globalWinFlags); + if (began) { + if (ImGui::BeginTable("Props",2,ImGuiTableFlags_SizingStretchProp)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::SmallButton(tempoView?"Base Tempo##TempoOrHz":"Tick Rate##TempoOrHz")) { + tempoView=!tempoView; + } + if (ImGui::IsItemHovered()) { + if (tempoView) { + ImGui::SetTooltip("click to display tick rate"); + } else { + ImGui::SetTooltip("click to display base tempo"); + } + } + ImGui::TableNextColumn(); + float avail=ImGui::GetContentRegionAvail().x; + float halfAvail=(avail-ImGui::GetStyle().ItemSpacing.x)*0.5; + ImGui::SetNextItemWidth(halfAvail); + float setHz=tempoView?e->curSubSong->hz*2.5:e->curSubSong->hz; + if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { MARK_MODIFIED + if (tempoView) setHz/=2.5; + if (setHz<1) setHz=1; + if (setHz>999) setHz=999; + e->setSongRate(setHz,setHz<52); + } + if (tempoView) { + ImGui::SameLine(); + ImGui::Text("= %gHz",e->curSubSong->hz); + } else { + if (e->curSubSong->hz>=49.98 && e->curSubSong->hz<=50.02) { + ImGui::SameLine(); + ImGui::Text("PAL"); + } + if (e->curSubSong->hz>=59.9 && e->curSubSong->hz<=60.11) { + ImGui::SameLine(); + ImGui::Text("NTSC"); + } + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (keepGrooveAlive || e->curSubSong->speeds.len>2) { + if (ImGui::SmallButton("Groove")) { + e->lockEngine([this]() { + e->curSubSong->speeds.len=1; + }); + if (e->isPlaying()) play(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("click for one speed"); + } + } else if (e->curSubSong->speeds.len>1) { + if (ImGui::SmallButton("Speeds")) { + e->lockEngine([this]() { + e->curSubSong->speeds.len=4; + e->curSubSong->speeds.val[2]=e->curSubSong->speeds.val[0]; + e->curSubSong->speeds.val[3]=e->curSubSong->speeds.val[1]; + }); + if (e->isPlaying()) play(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("click for groove pattern"); + } + } else { + if (ImGui::SmallButton("Speed")) { + e->lockEngine([this]() { + e->curSubSong->speeds.len=2; + e->curSubSong->speeds.val[1]=e->curSubSong->speeds.val[0]; + }); + if (e->isPlaying()) play(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("click for two (alternating) speeds"); + } + } + ImGui::TableNextColumn(); + if (keepGrooveAlive || e->curSubSong->speeds.len>2) { + int intVersion[256]; + unsigned char intVersionLen=e->curSubSong->speeds.len; + unsigned char ignoredLoop=0; + unsigned char ignoredRel=0; + memset(intVersion,0,sizeof(int)); + for (int i=0; i<16; i++) { + intVersion[i]=e->curSubSong->speeds.val[i]; + } + if (intVersionLen>16) intVersionLen=16; + + keepGrooveAlive=false; + + ImGui::SetNextItemWidth(avail); + if (ImGui::InputText("##SpeedG",&grooveString)) { + decodeMMLStr(grooveString,intVersion,intVersionLen,ignoredLoop,1,255,ignoredRel); + if (intVersionLen<1) { + intVersionLen=1; + intVersion[0]=6; + } + if (intVersionLen>16) intVersionLen=16; + e->lockEngine([this,intVersion,intVersionLen]() { + e->curSubSong->speeds.len=intVersionLen; + for (int i=0; i<16; i++) { + e->curSubSong->speeds.val[i]=intVersion[i]; + } + }); + if (e->isPlaying()) play(); + MARK_MODIFIED; + } + if (!ImGui::IsItemActive()) { + encodeMMLStr(grooveString,intVersion,intVersionLen,-1,-1,false); + } else { + keepGrooveAlive=true; + } + } else { + ImGui::SetNextItemWidth(halfAvail); + if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->curSubSong->speeds.val[0],&_ONE,&_THREE)) { MARK_MODIFIED + if (e->curSubSong->speeds.val[0]<1) e->curSubSong->speeds.val[0]=1; + if (e->isPlaying()) play(); + } + if (e->curSubSong->speeds.len>1) { + ImGui::SameLine(); + ImGui::SetNextItemWidth(halfAvail); + if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->curSubSong->speeds.val[1],&_ONE,&_THREE)) { MARK_MODIFIED + if (e->curSubSong->speeds.val[1]<1) e->curSubSong->speeds.val[1]=1; + if (e->isPlaying()) play(); + } + } + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Virtual Tempo"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(halfAvail); + if (ImGui::InputScalar("##VTempoN",ImGuiDataType_S16,&e->curSubSong->virtualTempoN,&_ONE,&_THREE)) { MARK_MODIFIED + if (e->curSubSong->virtualTempoN<1) e->curSubSong->virtualTempoN=1; + if (e->curSubSong->virtualTempoN>255) e->curSubSong->virtualTempoN=255; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Numerator"); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(halfAvail); + if (ImGui::InputScalar("##VTempoD",ImGuiDataType_S16,&e->curSubSong->virtualTempoD,&_ONE,&_THREE)) { MARK_MODIFIED + if (e->curSubSong->virtualTempoD<1) e->curSubSong->virtualTempoD=1; + if (e->curSubSong->virtualTempoD>255) e->curSubSong->virtualTempoD=255; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Denominator (set to base tempo)"); + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Divider"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(halfAvail); + unsigned char realTB=e->curSubSong->timeBase+1; + if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED + if (realTB<1) realTB=1; + if (realTB>16) realTB=16; + e->curSubSong->timeBase=realTB-1; + } + ImGui::SameLine(); + ImGui::Text("%.2f BPM",calcBPM(e->curSubSong->speeds,e->curSubSong->hz,e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Highlight"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(halfAvail); + if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->curSubSong->hilightA,&_ONE,&_THREE)) { + MARK_MODIFIED; + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(halfAvail); + if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->curSubSong->hilightB,&_ONE,&_THREE)) { + MARK_MODIFIED; + } + ImGui::EndTable(); + } + + ImGui::Separator(); + + if (ImGui::BeginTable("Props2",3,ImGuiTableFlags_SizingStretchProp)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Pattern Length"); + ImGui::TableNextColumn(); + float avail=ImGui::GetContentRegionAvail().x; + ImGui::SetNextItemWidth(avail); + int patLen=e->curSubSong->patLen; + if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED + if (patLen<1) patLen=1; + if (patLen>DIV_MAX_PATTERNS) patLen=DIV_MAX_PATTERNS; + e->curSubSong->patLen=patLen; + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Song Length"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + int ordLen=e->curSubSong->ordersLen; + if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED + if (ordLen<1) ordLen=1; + if (ordLen>DIV_MAX_PATTERNS) ordLen=DIV_MAX_PATTERNS; + e->curSubSong->ordersLen=ordLen; + if (curOrder>=ordLen) { + setOrder(ordLen-1); + } + } + + ImGui::EndTable(); + } + } + if (!asChild && ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SPEED; + if (asChild) { + ImGui::EndChild(); + } else { + ImGui::End(); + } +} diff --git a/src/gui/spoiler.cpp b/src/gui/spoiler.cpp index dc64b14ce..05e6d0ead 100644 --- a/src/gui/spoiler.cpp +++ b/src/gui/spoiler.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/stats.cpp b/src/gui/stats.cpp index 636ecd529..33e7029c1 100644 --- a/src/gui/stats.cpp +++ b/src/gui/stats.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/subSongs.cpp b/src/gui/subSongs.cpp index 9801ab57c..b6e49e5b2 100644 --- a/src/gui/subSongs.cpp +++ b/src/gui/subSongs.cpp @@ -106,6 +106,12 @@ void FurnaceGUI::drawSubSongs() { if (ImGui::InputText("##SubSongName",&e->curSubSong->name,ImGuiInputTextFlags_UndoRedo)) { MARK_MODIFIED; } + + if (ImGui::GetContentRegionAvail().y>(10.0f*dpiScale)) { + if (ImGui::InputTextMultiline("##SubSongNotes",&e->curSubSong->notes,ImGui::GetContentRegionAvail(),ImGuiInputTextFlags_UndoRedo)) { + MARK_MODIFIED; + } + } } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SUBSONGS; ImGui::End(); diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 1229b0db6..75f535c09 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/sysManager.cpp b/src/gui/sysManager.cpp index dda5ed963..7a214beca 100644 --- a/src/gui/sysManager.cpp +++ b/src/gui/sysManager.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/sysPicker.cpp b/src/gui/sysPicker.cpp index f59e547ad..8607de05d 100644 --- a/src/gui/sysPicker.cpp +++ b/src/gui/sysPicker.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/util.cpp b/src/gui/util.cpp index 3998f2bb4..80ed73781 100644 --- a/src/gui/util.cpp +++ b/src/gui/util.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/util.h b/src/gui/util.h index 8071884b8..6dd2c3a6f 100644 --- a/src/gui/util.h +++ b/src/gui/util.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/volMeter.cpp b/src/gui/volMeter.cpp index f050341dd..a4cf11d7e 100644 --- a/src/gui/volMeter.cpp +++ b/src/gui/volMeter.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/gui/waveEdit.cpp b/src/gui/waveEdit.cpp index fcc10bd03..cecc3635e 100644 --- a/src/gui/waveEdit.cpp +++ b/src/gui/waveEdit.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/log.cpp b/src/log.cpp index 8fec13189..5f24269d8 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main.cpp b/src/main.cpp index a16bce1cf..094e5b93e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,6 +32,10 @@ #include #include #include + +#include "gui/shellScalingStub.h" + +typedef HRESULT (WINAPI *SPDA)(PROCESS_DPI_AWARENESS); #else #include #endif @@ -148,7 +152,7 @@ TAParamResult pLogLevel(String val) { TAParamResult pVersion(String) { printf("Furnace version " DIV_VERSION ".\n\n"); - printf("copyright (C) 2021-2022 tildearrow and contributors.\n"); + printf("copyright (C) 2021-2023 tildearrow and contributors.\n"); printf("licensed under the GNU General Public License version 2 or later\n"); printf(".\n\n"); printf("this is free software with ABSOLUTELY NO WARRANTY.\n"); @@ -339,6 +343,22 @@ void reportError(String what) { int main(int argc, char** argv) { initLog(); #ifdef _WIN32 + // set DPI awareness + HMODULE shcore=LoadLibraryW(L"shcore.dll"); + if (shcore!=NULL) { + SPDA ta_SetProcessDpiAwareness=(SPDA)GetProcAddress(shcore,"SetProcessDpiAwareness"); + if (ta_SetProcessDpiAwareness!=NULL) { + HRESULT result=ta_SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); + if (result!=S_OK) { + // ??? + } + } + if (!FreeLibrary(shcore)) { + // ??? + } + } + + // co initialize ex HRESULT coResult=CoInitializeEx(NULL,COINIT_MULTITHREADED); if (coResult!=S_OK) { logE("CoInitializeEx failed!"); @@ -613,7 +633,3 @@ int main(int argc, char** argv) { #endif return 0; } - -#ifdef _WIN32 -#include "winMain.cpp" -#endif diff --git a/src/ta-log.h b/src/ta-log.h index 160201e03..d36f5da7d 100644 --- a/src/ta-log.h +++ b/src/ta-log.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/ta-utils.h b/src/ta-utils.h index feddbc1e9..f0c896d00 100644 --- a/src/ta-utils.h +++ b/src/ta-utils.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/utfutils.cpp b/src/utfutils.cpp index 4c727777b..889b9a9bd 100644 --- a/src/utfutils.cpp +++ b/src/utfutils.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/utfutils.h b/src/utfutils.h index 76c894708..e55aa07ab 100644 --- a/src/utfutils.h +++ b/src/utfutils.h @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/winMain.cpp b/src/winMain.cpp index 853b11530..85dd27770 100644 --- a/src/winMain.cpp +++ b/src/winMain.cpp @@ -1,6 +1,6 @@ /** * Furnace Tracker - multi-system chiptune tracker - * Copyright (C) 2021-2022 tildearrow and contributors + * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by