Merge branch 'master' of https://github.com/tildearrow/furnace into ymf289b

This commit is contained in:
cam900 2023-02-23 21:15:49 +09:00
commit e694687fff
415 changed files with 152066 additions and 7336 deletions

View file

@ -11,7 +11,7 @@ defaults:
shell: bash shell: bash
env: env:
BUILD_TYPE: Release BUILD_TYPE: RelWithDebInfo
jobs: jobs:
build: build:
@ -20,8 +20,8 @@ jobs:
config: config:
- { name: 'Windows MSVC x86', os: windows-latest, compiler: msvc, arch: x86 } - { 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 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', 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_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 }
- { name: 'macOS x86_64', os: macos-latest, arch: x86_64 } - { name: 'macOS x86_64', os: macos-latest, arch: x86_64 }
- { name: 'macOS ARM', os: macos-latest, arch: arm64 } - { 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 }
@ -277,7 +277,10 @@ jobs:
cp -v ../LICENSE LICENSE.txt cp -v ../LICENSE LICENSE.txt
cp -v ../README.md README.txt cp -v ../README.md README.txt
cp -vr ../{papers,demos,instruments} ../${binPath}/furnace.exe ./ cp -vr ../papers ../${binPath}/furnace.exe ./
if [ '${{ matrix.config.compiler }}' == 'msvc' ]; then
cp -v ../${binPath}/furnace.pdb ./
fi
sha256sum ../${binPath}/furnace.exe > checksum.txt sha256sum ../${binPath}/furnace.exe > checksum.txt
popd popd

View file

@ -70,6 +70,7 @@ option(SUPPORT_XP "Build a Windows XP-compatible binary" OFF)
option(WARNINGS_ARE_ERRORS "Whether warnings in furnace's C++ code should be treated as errors" OFF) option(WARNINGS_ARE_ERRORS "Whether warnings in furnace's C++ code should be treated as errors" OFF)
option(WITH_DEMOS "Install demo songs" ON) option(WITH_DEMOS "Install demo songs" ON)
option(WITH_INSTRUMENTS "Install instruments" ON) option(WITH_INSTRUMENTS "Install instruments" ON)
option(WITH_WAVETABLES "Install wavetables" ON)
set(DEPENDENCIES_INCLUDE_DIRS "") set(DEPENDENCIES_INCLUDE_DIRS "")
@ -365,6 +366,7 @@ src/engine/platform/sound/sn76496.cpp
src/engine/platform/sound/ay8910.cpp src/engine/platform/sound/ay8910.cpp
src/engine/platform/sound/saa1099.cpp src/engine/platform/sound/saa1099.cpp
src/engine/platform/sound/namco.cpp src/engine/platform/sound/namco.cpp
src/engine/platform/sound/segapcm.cpp
src/engine/platform/sound/gb/apu.c src/engine/platform/sound/gb/apu.c
src/engine/platform/sound/gb/timing.c src/engine/platform/sound/gb/timing.c
src/engine/platform/sound/pce_psg.cpp src/engine/platform/sound/pce_psg.cpp
@ -399,6 +401,7 @@ src/engine/platform/sound/c64/wave8580_PST.cc
src/engine/platform/sound/c64/wave8580_P_T.cc src/engine/platform/sound/c64/wave8580_P_T.cc
src/engine/platform/sound/c64/wave8580__ST.cc src/engine/platform/sound/c64/wave8580__ST.cc
src/engine/platform/sound/c64_fp/array.cpp
src/engine/platform/sound/c64_fp/Dac.cpp src/engine/platform/sound/c64_fp/Dac.cpp
src/engine/platform/sound/c64_fp/EnvelopeGenerator.cpp src/engine/platform/sound/c64_fp/EnvelopeGenerator.cpp
src/engine/platform/sound/c64_fp/ExternalFilter.cpp src/engine/platform/sound/c64_fp/ExternalFilter.cpp
@ -454,6 +457,8 @@ src/engine/platform/sound/snes/SPC_DSP.cpp
src/engine/platform/sound/ga20/iremga20.cpp src/engine/platform/sound/ga20/iremga20.cpp
src/engine/platform/sound/sm8521.c
src/engine/platform/oplAInterface.cpp src/engine/platform/oplAInterface.cpp
src/engine/platform/ym2608Interface.cpp src/engine/platform/ym2608Interface.cpp
src/engine/platform/ym2610Interface.cpp src/engine/platform/ym2610Interface.cpp
@ -530,6 +535,7 @@ src/engine/platform/pokemini.cpp
src/engine/platform/pong.cpp src/engine/platform/pong.cpp
src/engine/platform/vic20.cpp src/engine/platform/vic20.cpp
src/engine/platform/vrc6.cpp src/engine/platform/vrc6.cpp
src/engine/platform/es5506.cpp
src/engine/platform/scc.cpp src/engine/platform/scc.cpp
src/engine/platform/ymz280b.cpp src/engine/platform/ymz280b.cpp
src/engine/platform/namcowsg.cpp src/engine/platform/namcowsg.cpp
@ -537,6 +543,7 @@ src/engine/platform/rf5c68.cpp
src/engine/platform/snes.cpp src/engine/platform/snes.cpp
src/engine/platform/k007232.cpp src/engine/platform/k007232.cpp
src/engine/platform/ga20.cpp src/engine/platform/ga20.cpp
src/engine/platform/sm8521.cpp
src/engine/platform/pcmdac.cpp src/engine/platform/pcmdac.cpp
src/engine/platform/dummy.cpp src/engine/platform/dummy.cpp
) )
@ -566,6 +573,7 @@ extern/imgui_patched/misc/cpp/imgui_stdlib.cpp
extern/igfd/ImGuiFileDialog.cpp extern/igfd/ImGuiFileDialog.cpp
src/gui/plot_nolerp.cpp src/gui/plot_nolerp.cpp
src/gui/font_exo.cpp src/gui/font_exo.cpp
src/gui/font_liberationSans.cpp src/gui/font_liberationSans.cpp
src/gui/font_mononoki.cpp src/gui/font_mononoki.cpp
@ -576,12 +584,24 @@ src/gui/font_ptMono.cpp
src/gui/font_unifont.cpp src/gui/font_unifont.cpp
src/gui/font_icon.cpp src/gui/font_icon.cpp
src/gui/fonts.cpp src/gui/fonts.cpp
src/gui/image_icon.cpp
src/gui/image_talogo.cpp
src/gui/image_tachip.cpp
src/gui/image_logo.cpp
src/gui/image_wordmark.cpp
src/gui/image_introbg.cpp
src/gui/image_pat.cpp
src/gui/image.cpp
src/gui/debug.cpp src/gui/debug.cpp
src/gui/fileDialog.cpp src/gui/fileDialog.cpp
src/gui/intConst.cpp src/gui/intConst.cpp
src/gui/guiConst.cpp src/gui/guiConst.cpp
src/gui/introTune.cpp
src/gui/about.cpp src/gui/about.cpp
src/gui/channels.cpp src/gui/channels.cpp
src/gui/chanOsc.cpp src/gui/chanOsc.cpp
@ -596,7 +616,9 @@ src/gui/editControls.cpp
src/gui/effectList.cpp src/gui/effectList.cpp
src/gui/findReplace.cpp src/gui/findReplace.cpp
src/gui/gradient.cpp src/gui/gradient.cpp
src/gui/grooves.cpp
src/gui/insEdit.cpp src/gui/insEdit.cpp
src/gui/intro.cpp
src/gui/log.cpp src/gui/log.cpp
src/gui/mixer.cpp src/gui/mixer.cpp
src/gui/midiMap.cpp src/gui/midiMap.cpp
@ -613,6 +635,7 @@ src/gui/scaling.cpp
src/gui/settings.cpp src/gui/settings.cpp
src/gui/songInfo.cpp src/gui/songInfo.cpp
src/gui/songNotes.cpp src/gui/songNotes.cpp
src/gui/speed.cpp
src/gui/spoiler.cpp src/gui/spoiler.cpp
src/gui/stats.cpp src/gui/stats.cpp
src/gui/subSongs.cpp src/gui/subSongs.cpp
@ -620,6 +643,7 @@ src/gui/sysConf.cpp
src/gui/sysEx.cpp src/gui/sysEx.cpp
src/gui/sysManager.cpp src/gui/sysManager.cpp
src/gui/sysPicker.cpp src/gui/sysPicker.cpp
src/gui/tutorial.cpp
src/gui/util.cpp src/gui/util.cpp
src/gui/waveEdit.cpp src/gui/waveEdit.cpp
src/gui/volMeter.cpp src/gui/volMeter.cpp
@ -638,8 +662,6 @@ if (APPLE)
endif() endif()
if (NOT WIN32 AND NOT APPLE) if (NOT WIN32 AND NOT APPLE)
list(APPEND GUI_SOURCES src/gui/icon.c)
CHECK_INCLUDE_FILE(sys/io.h SYS_IO_FOUND) CHECK_INCLUDE_FILE(sys/io.h SYS_IO_FOUND)
CHECK_INCLUDE_FILE(linux/input.h LINUX_INPUT_FOUND) CHECK_INCLUDE_FILE(linux/input.h LINUX_INPUT_FOUND)
CHECK_INCLUDE_FILE(linux/kd.h LINUX_KD_FOUND) CHECK_INCLUDE_FILE(linux/kd.h LINUX_KD_FOUND)
@ -756,9 +778,7 @@ if (WARNINGS_ARE_ERRORS)
) )
endif() endif()
if (WIN32) if(ANDROID AND NOT TERMUX)
add_executable(furnace WIN32 ${USED_SOURCES})
elseif(ANDROID AND NOT TERMUX)
add_library(furnace SHARED ${USED_SOURCES}) add_library(furnace SHARED ${USED_SOURCES})
else() else()
add_executable(furnace ${USED_SOURCES}) add_executable(furnace ${USED_SOURCES})
@ -795,6 +815,9 @@ if (NOT ANDROID OR TERMUX)
if (WITH_INSTRUMENTS) if (WITH_INSTRUMENTS)
install(DIRECTORY instruments DESTINATION ${CMAKE_INSTALL_DATADIR}/furnace) install(DIRECTORY instruments DESTINATION ${CMAKE_INSTALL_DATADIR}/furnace)
endif() endif()
if (WITH_WAVETABLES)
install(DIRECTORY wavetables DESTINATION ${CMAKE_INSTALL_DATADIR}/furnace)
endif()
foreach(num 16 32 64 128 256 512) foreach(num 16 32 64 128 256 512)
set(res ${num}x${num}) set(res ${num}x${num})
install(FILES res/icon.iconset/icon_${res}.png RENAME furnace.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/${res}/apps) install(FILES res/icon.iconset/icon_${res}.png RENAME furnace.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/${res}/apps)

View file

@ -214,6 +214,7 @@ Available options:
| `WARNINGS_ARE_ERRORS` | `OFF` (but consider enabling this & reporting any errors that arise from it!) | Whether warnings in furnace's C++ code should be treated as errors | | `WARNINGS_ARE_ERRORS` | `OFF` (but consider enabling this & reporting any errors that arise from it!) | Whether warnings in furnace's C++ code should be treated as errors |
| `WITH_DEMOS` | `ON` | Install demo songs on `make install` | | `WITH_DEMOS` | `ON` | Install demo songs on `make install` |
| `WITH_INSTRUMENTS` | `ON` | Install demo instruments on `make install` | | `WITH_INSTRUMENTS` | `ON` | Install demo instruments on `make install` |
| `WITH_WAVETABLES` | `ON` | Install wavetables on `make install` |
## console usage ## console usage
@ -282,17 +283,13 @@ two possibilities:
- the recommended way is by creating the "Sample" type instrument and assigning a sample to it. - 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. - 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. Furnace's .dmf compatibility isn't perfect and it's mostly because DefleMask does things different.
> my .dmf song sounds correct, but it doesn't in DefleMask
file a bug report **here**. it still is a playback inaccuracy.
> my song sounds terrible after saving as .dmf! > 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? > how do I solo channels?
@ -301,7 +298,7 @@ right click on the channel name.
--- ---
# footnotes # 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. 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 +307,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. 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.

BIN
demos/amiga/serendipid.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
demos/opl/e3m2_opl3.fur Normal file

Binary file not shown.

BIN
demos/snes/amalgam.fur Normal file

Binary file not shown.

BIN
demos/specs2/Tim_Follin.fur Normal file

Binary file not shown.

View file

@ -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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -17,7 +17,7 @@
* siliconpr0n.org(digshadow, John McMaster): * siliconpr0n.org(digshadow, John McMaster):
* VRC VII decap and die shot. * VRC VII decap and die shot.
* *
* version: 1.0.1 * version: 1.0.2
*/ */
#include <string.h> #include <string.h>
@ -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 },{ 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 },{ 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, 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 } } { 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); chip->ch_out = ismod1 ? routput : (output>>3);
if (!ismod1) { if (!ismod1) {

View file

@ -30,6 +30,9 @@
// hack I know // hack I know
#include "../../../src/utfutils.h" #include "../../../src/utfutils.h"
// hack 2...
#include "../../../src/ta-log.h"
class NFDWinEvents: public IFileDialogEvents { class NFDWinEvents: public IFileDialogEvents {
nfdselcallback_t selCallback; nfdselcallback_t selCallback;
size_t refCount; size_t refCount;
@ -38,21 +41,21 @@ class NFDWinEvents: public IFileDialogEvents {
} }
public: public:
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) { IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) {
printf("QueryInterface called DAMN IT\n"); logV("%p: QueryInterface called DAMN IT",(const void*)this);
*ppv=NULL; *ppv=NULL;
return E_NOTIMPL; return E_NOTIMPL;
} }
IFACEMETHODIMP_(ULONG) AddRef() { IFACEMETHODIMP_(ULONG) AddRef() {
printf("AddRef() called\n"); logV("%p: AddRef() called",(const void*)this);
return InterlockedIncrement(&refCount); return InterlockedIncrement(&refCount);
} }
IFACEMETHODIMP_(ULONG) Release() { IFACEMETHODIMP_(ULONG) Release() {
printf("Release() called\n"); logV("%p: Release() called",(const void*)this);
LONG ret=InterlockedDecrement(&refCount); LONG ret=InterlockedDecrement(&refCount);
if (ret==0) { if (ret==0) {
printf("Destroying the final object.\n"); logV("%p: Destroying the final object.",(const void*)this);
delete this; delete this;
} }
return ret; return ret;
@ -67,30 +70,40 @@ class NFDWinEvents: public IFileDialogEvents {
IFACEMETHODIMP OnSelectionChange(IFileDialog* dialog) { IFACEMETHODIMP OnSelectionChange(IFileDialog* dialog) {
// Get the file name // Get the file name
logV("%p: OnSelectionChange() called",(const void*)this);
::IShellItem *shellItem(NULL); ::IShellItem *shellItem(NULL);
logV("%p: GetCurrentSelection",(const void*)this);
HRESULT result = dialog->GetCurrentSelection(&shellItem); HRESULT result = dialog->GetCurrentSelection(&shellItem);
if ( !SUCCEEDED(result) ) if ( !SUCCEEDED(result) )
{ {
printf("failure!\n"); logV("%p: failure!",(const void*)this);
return S_OK; return S_OK;
} }
wchar_t *filePath(NULL); wchar_t *filePath(NULL);
result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath);
if ( !SUCCEEDED(result) ) if ( !SUCCEEDED(result) )
{ {
printf("GDN failure!\n"); logV("%p: GDN failure!",(const void*)this);
shellItem->Release(); shellItem->Release();
return S_OK; return S_OK;
} }
std::string utf8FilePath=utf16To8(filePath); std::string utf8FilePath=utf16To8(filePath);
if (selCallback!=NULL) selCallback(utf8FilePath.c_str()); if (selCallback!=NULL) {
printf("I got you for a value of %s\n",utf8FilePath.c_str()); 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(); shellItem->Release();
logV("%p: shellItem->Release()",(const void*)this);
return S_OK; return S_OK;
} }
NFDWinEvents(nfdselcallback_t callback): NFDWinEvents(nfdselcallback_t callback):
selCallback(callback), selCallback(callback),
refCount(1) { refCount(1) {
logV("%p: CONSTRUCT!",(const void*)this);
} }
}; };

3
extern/opn/ym3438.c vendored
View file

@ -981,6 +981,9 @@ static void OPN2_ChOutput(ym3438_t *chip)
{ {
out = (Bit16s)chip->dacdata; out = (Bit16s)chip->dacdata;
out = SIGN_EXTEND(8, out); out = SIGN_EXTEND(8, out);
if (chip->chip_type & ym3438_mode_opn) {
out <<=5;
}
} }
else else
{ {

View file

@ -8,4 +8,5 @@ you can get original software from [here](https://gitlab.com/cam900/vgsound_emu/
## Modifier ## Modifier
- [cam900](https://gitlab.com/cam900) - [cam900](https://gitlab.com/cam900)
- [tildearrow](https://github.com/tildearrow)

View file

@ -19,6 +19,10 @@
#include <string> #include <string>
#include <vector> #include <vector>
#define VGS_MIN(a,b) (((a)<(b))?(a):(b))
#define VGS_MAX(a,b) (((a)>(b))?(a):(b))
#define VGS_CLAMP(x,xMin,xMax) (VGS_MIN(VGS_MAX((x),(xMin)),(xMax)))
namespace vgsound_emu namespace vgsound_emu
{ {
typedef unsigned char u8; typedef unsigned char u8;
@ -43,7 +47,7 @@ namespace vgsound_emu
return std::clamp(in, min, max); return std::clamp(in, min, max);
#else #else
// otherwise, use my own implementation of std::clamp // otherwise, use my own implementation of std::clamp
return std::min(std::max(in, min), max); return VGS_CLAMP(in,min,max);
#endif #endif
} }
@ -58,7 +62,7 @@ namespace vgsound_emu
template<typename T> template<typename T>
static inline T sign_ext(T in, u8 len) static inline T sign_ext(T in, u8 len)
{ {
len = std::max<u8>(0, (8 * sizeof(T)) - len); len = VGS_MAX(0, (8 * sizeof(T)) - len);
return T(T(in) << len) >> len; return T(T(in) << len) >> len;
} }

View file

@ -23,7 +23,7 @@ void es5504_core::tick()
// /CAS low, E low: fetch sample // /CAS low, E low: fetch sample
if (!m_e.current_edge()) if (!m_e.current_edge())
{ {
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); m_voice[m_voice_cycle].fetch(m_voice_fetch);
} }
} }
} }
@ -41,19 +41,19 @@ void es5504_core::tick()
if (m_e.falling_edge()) // Voice memory if (m_e.falling_edge()) // Voice memory
{ {
m_host_intf.clear_host_access(); m_host_intf.clear_host_access();
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); m_voice[m_voice_cycle].fetch(m_voice_fetch);
} }
} }
if (m_e.current_edge()) // Host interface if (m_e.current_edge()) // Host interface
{ {
if (m_host_intf.host_access()) if (m_host_intf.m_host_access)
{ {
if (m_host_intf.rw() && (m_e.cycle() == 2)) // Read if (m_host_intf.m_rw && (m_e.cycle() == 2)) // Read
{ {
m_hd = read(m_ha); m_hd = read(m_ha);
m_host_intf.clear_host_access(); m_host_intf.clear_host_access();
} }
else if ((!m_host_intf.rw()) && (m_e.cycle() == 2)) else if ((!m_host_intf.m_rw) && (m_e.cycle() == 2))
{ // Write { // Write
write(m_ha, m_hd); write(m_ha, m_hd);
} }
@ -81,7 +81,7 @@ void es5504_core::tick_perf()
m_intf.e_pin(false); m_intf.e_pin(false);
m_host_intf.clear_host_access(); m_host_intf.clear_host_access();
m_host_intf.clear_strobe(); m_host_intf.clear_strobe();
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); m_voice[m_voice_cycle].fetch(m_voice_fetch);
voice_tick(); voice_tick();
// rising edge // rising edge
m_e.edge().set(true); m_e.edge().set(true);
@ -92,7 +92,7 @@ void es5504_core::tick_perf()
m_intf.e_pin(false); m_intf.e_pin(false);
m_host_intf.clear_host_access(); m_host_intf.clear_host_access();
m_host_intf.clear_strobe(); m_host_intf.clear_strobe();
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); m_voice[m_voice_cycle].fetch(m_voice_fetch);
voice_tick(); voice_tick();
// rising edge // rising edge
m_e.edge().set(true); m_e.edge().set(true);
@ -126,11 +126,11 @@ void es5504_core::voice_tick()
} }
} }
void es5504_core::voice_t::fetch(u8 voice, u8 cycle) void es5504_core::voice_t::fetch(u8 cycle)
{ {
m_alu.set_sample( m_alu.set_sample(
cycle, cycle,
m_host.m_intf.read_sample(voice, m_host.m_intf.read_sample(
bitfield(m_cr.ca(), 0, 3), bitfield(m_cr.ca(), 0, 3),
bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer))); bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer)));
} }
@ -193,7 +193,7 @@ void es5504_core::voice_t::reset()
// Accessors // Accessors
u16 es5504_core::host_r(u8 address) u16 es5504_core::host_r(u8 address)
{ {
if (!m_host_intf.host_access()) if (!m_host_intf.m_host_access)
{ {
m_ha = address; m_ha = address;
if (m_e.rising_edge()) if (m_e.rising_edge())
@ -210,7 +210,7 @@ u16 es5504_core::host_r(u8 address)
void es5504_core::host_w(u8 address, u16 data) void es5504_core::host_w(u8 address, u16 data)
{ {
if (!m_host_intf.host_access()) if (!m_host_intf.m_host_access)
{ {
m_ha = address; m_ha = address;
m_hd = data; m_hd = data;

View file

@ -32,7 +32,7 @@ class es5504_core : public es550x_shared_core
// internal state // internal state
virtual void reset() override; virtual void reset() override;
virtual void fetch(u8 voice, u8 cycle) override; virtual void fetch(u8 cycle) override;
virtual void tick(u8 voice) override; virtual void tick(u8 voice) override;
// setters // setters

View file

@ -97,7 +97,7 @@ void es5505_core::tick()
// /CAS low, E low: fetch sample // /CAS low, E low: fetch sample
if (!m_e.current_edge()) if (!m_e.current_edge())
{ {
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); m_voice[m_voice_cycle].fetch(m_voice_fetch);
} }
} }
} }
@ -113,18 +113,18 @@ void es5505_core::tick()
else if (m_e.falling_edge()) // Voice memory else if (m_e.falling_edge()) // Voice memory
{ {
m_host_intf.clear_host_access(); m_host_intf.clear_host_access();
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); m_voice[m_voice_cycle].fetch(m_voice_fetch);
} }
if (m_e.current_edge()) // Host interface if (m_e.current_edge()) // Host interface
{ {
if (m_host_intf.host_access()) if (m_host_intf.m_host_access)
{ {
if (m_host_intf.rw() && (m_e.cycle() == 2)) // Read if (m_host_intf.m_rw && (m_e.cycle() == 2)) // Read
{ {
m_hd = read(m_ha); m_hd = read(m_ha);
m_host_intf.clear_host_access(); m_host_intf.clear_host_access();
} }
else if ((!m_host_intf.rw()) && (m_e.cycle() == 2)) else if ((!m_host_intf.m_rw) && (m_e.cycle() == 2))
{ // Write { // Write
write(m_ha, m_hd); write(m_ha, m_hd);
} }
@ -159,7 +159,7 @@ void es5505_core::tick_perf()
m_intf.e_pin(false); m_intf.e_pin(false);
m_host_intf.clear_host_access(); m_host_intf.clear_host_access();
m_host_intf.clear_strobe(); m_host_intf.clear_strobe();
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); m_voice[m_voice_cycle].fetch(m_voice_fetch);
voice_tick(); voice_tick();
// rising edge // rising edge
m_e.edge().set(true); m_e.edge().set(true);
@ -170,7 +170,7 @@ void es5505_core::tick_perf()
m_intf.e_pin(false); m_intf.e_pin(false);
m_host_intf.clear_host_access(); m_host_intf.clear_host_access();
m_host_intf.clear_strobe(); m_host_intf.clear_strobe();
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch); m_voice[m_voice_cycle].fetch(m_voice_fetch);
voice_tick(); voice_tick();
// rising edge // rising edge
m_e.edge().set(true); m_e.edge().set(true);
@ -211,12 +211,12 @@ void es5505_core::voice_tick()
} }
} }
void es5505_core::voice_t::fetch(u8 voice, u8 cycle) void es5505_core::voice_t::fetch(u8 cycle)
{ {
m_alu.set_sample( m_alu.set_sample(
cycle, cycle,
m_host.m_intf.read_sample(voice, m_host.m_intf.read_sample(
bitfield(m_cr.bs(), 0), bitfield(m_cr.m_bs, 0),
bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer))); bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer)));
} }
@ -301,7 +301,7 @@ void es5505_core::voice_t::reset()
// Accessors // Accessors
u16 es5505_core::host_r(u8 address) u16 es5505_core::host_r(u8 address)
{ {
if (!m_host_intf.host_access()) if (!m_host_intf.m_host_access)
{ {
m_ha = address; m_ha = address;
if (m_e.rising_edge()) if (m_e.rising_edge())
@ -318,7 +318,7 @@ u16 es5505_core::host_r(u8 address)
void es5505_core::host_w(u8 address, u16 data) void es5505_core::host_w(u8 address, u16 data)
{ {
if (!m_host_intf.host_access()) if (!m_host_intf.m_host_access)
{ {
m_ha = address; m_ha = address;
m_hd = data; m_hd = data;

View file

@ -112,7 +112,7 @@ class es5505_core : public es550x_shared_core
// internal state // internal state
virtual void reset() override; virtual void reset() override;
virtual void fetch(u8 voice, u8 cycle) override; virtual void fetch(u8 cycle) override;
virtual void tick(u8 voice) override; virtual void tick(u8 voice) override;
// setters // setters

View file

@ -9,182 +9,10 @@
#include "es5506.hpp" #include "es5506.hpp"
// Internal functions // Internal functions
// DO NOT USE THIS ONE!
void es5506_core::tick() void es5506_core::tick()
{ {
// CLKIN
if (m_clkin.tick())
{
// BCLK
if (m_clkin.edge().changed() && (!m_mode.bclk_en())) // BCLK is freely running clock
{
if (m_bclk.tick())
{
m_intf.bclk(m_bclk.current_edge());
// Serial output
if (!m_mode.lrclk_en())
{
if (m_bclk.falling_edge())
{
// LRCLK
if (m_lrclk.tick())
{
m_intf.lrclk(m_lrclk.current_edge());
if (m_lrclk.rising_edge())
{
m_w_st_curr = m_w_st;
m_w_end_curr = m_w_end;
}
if (m_lrclk.falling_edge())
{ // update width
m_lrclk.set_width_latch(m_lr_end);
}
}
}
}
// WCLK
if (!m_mode.wclk_en())
{
if (!m_mode.lrclk_en())
{
if (m_lrclk.edge().changed())
{
m_wclk = 0;
}
}
if (m_bclk.falling_edge())
{
if (m_wclk == m_w_st_curr)
{
m_intf.wclk(true);
if (m_lrclk.current_edge())
{
for (int i = 0; i < 6; i++)
{
// copy output
m_output[i].copy_output(m_output_temp[i]);
// clamp to 20 bit (upper 3 bits are
// overflow guard bits)
m_output_latch[i].clamp20(m_ch[i]);
m_output_temp[i].reset();
m_output_latch[i].clamp20();
// set signed
if (m_output_latch[i].left() < 0)
{
m_output_temp[i].set_left(-1);
}
if (m_output_latch[i].right() < 0)
{
m_output_temp[i].set_right(-1);
}
}
}
m_wclk_lr = m_lrclk.current_edge();
m_output_bit = 20;
}
if (m_wclk < m_w_end_curr)
{
s8 output_bit = --m_output_bit;
if (m_output_bit >= 0)
{
for (int i = 0; i < 6; i++)
{
if (m_wclk_lr)
{
// Right output
m_output_temp[i].serial_in(
m_wclk_lr,
bitfield(m_output_latch[i].right(), output_bit));
}
else
{
// Left output
m_output_temp[i].serial_in(
m_wclk_lr,
bitfield(m_output_latch[i].left(), output_bit));
}
}
}
}
if (m_wclk == m_w_end_curr)
{
m_intf.wclk(false);
}
m_wclk++;
}
}
}
}
// /CAS, E
if (m_clkin.falling_edge()) // falling edge triggers /CAS, E clock
{
// /CAS
if (m_cas.tick())
{
// single OTTO master mode, /CAS high, E low: get sample address
// single OTTO early mode, /CAS falling, E high: get sample
// address
if (m_cas.falling_edge())
{
if (!m_e.current_edge())
{
// single OTTO master mode, /CAS low, E low: fetch
// sample
if (m_mode.master())
{
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
}
}
else if (m_e.current_edge())
{
// dual OTTO slave mode, /CAS low, E high: fetch sample
if (m_mode.dual() && (!m_mode.master()))
{ // Dual OTTO, slave mode
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
}
}
}
}
// E
if (m_e.tick())
{
m_intf.e_pin(m_e.current_edge());
if (m_e.rising_edge())
{
m_host_intf.update_strobe();
}
else if (m_e.falling_edge())
{
m_host_intf.clear_host_access();
voice_tick();
}
if (m_e.current_edge()) // Host interface
{
if (m_host_intf.host_access())
{
if (m_host_intf.rw() && (m_e.cycle() == 0)) // Read
{
m_hd = read(m_ha);
m_host_intf.clear_host_access();
}
else if ((!m_host_intf.rw()) && (m_e.cycle() == 2))
{ // Write
write(m_ha, m_hd);
}
}
}
else if (!m_e.current_edge())
{
if (m_e.cycle() == 2)
{
// reset host access state
m_hd = 0;
m_host_intf.clear_strobe();
}
}
}
}
}
} }
// less cycle accurate, but less CPU heavy routine // less cycle accurate, but less CPU heavy routine
@ -193,12 +21,18 @@ void es5506_core::tick_perf()
// output // output
if (((!m_mode.lrclk_en()) && (!m_mode.bclk_en()) && (!m_mode.wclk_en())) && (m_w_st < m_w_end)) if (((!m_mode.lrclk_en()) && (!m_mode.bclk_en()) && (!m_mode.wclk_en())) && (m_w_st < m_w_end))
{ {
const int output_bits = 20 - (m_w_end - m_w_st); const int output_bits = (20 - (m_w_end - m_w_st));
if (output_bits < 20) if (output_bits < 20)
{ {
for (int c = 0; c < 6; c++) for (int c = 0; c < 6; c++)
{ {
m_output[c].clamp20(m_ch[c] >> output_bits); m_output[c].m_left=m_ch[c].m_left>>output_bits;
if (m_output[c].m_left<-0x80000) m_output[c].m_left=-0x80000;
if (m_output[c].m_left>0x7ffff) m_output[c].m_left=0x7ffff;
m_output[c].m_right=m_ch[c].m_right>>output_bits;
if (m_output[c].m_right<-0x80000) m_output[c].m_right=-0x80000;
if (m_output[c].m_right>0x7ffff) m_output[c].m_right=0x7ffff;
} }
} }
} }
@ -211,93 +45,63 @@ void es5506_core::tick_perf()
} }
// update // update
// falling edge voice_tick();
m_e.edge().set(false);
m_intf.e_pin(false);
m_host_intf.clear_host_access();
m_host_intf.clear_strobe();
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
voice_tick();
// rising edge
m_e.edge().set(true);
m_intf.e_pin(true);
m_host_intf.update_strobe();
// falling edge
m_e.edge().set(false);
m_intf.e_pin(false);
m_host_intf.clear_host_access();
m_host_intf.clear_strobe();
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
voice_tick();
// rising edge
m_e.edge().set(true);
m_intf.e_pin(true);
m_host_intf.update_strobe();
} }
void es5506_core::voice_tick() void es5506_core::voice_tick()
{ {
// Voice updates every 2 E clock cycle (or 4 BCLK clock cycle) // Refresh output
m_voice_update = bitfield(m_voice_fetch++, 0); m_voice_end = true;
if (m_voice_update) m_voice_cycle = 0;
{ for (int i=0; i<6; i++)
// Update voice {
m_voice[m_voice_cycle].tick(m_voice_cycle); m_ch[i].reset();
}
// Update voice
const int total=VGS_CLAMP(m_active,4,31);
for (int i=0; i<=total; i++) {
m_voice[i].tick(i);
// Refresh output const u8 ca = m_voice[i].cr().ca()&7;
if ((++m_voice_cycle) > clamp<u8>(m_active, 4, 31)) // 5 ~ 32 voices if (ca < 6)
{ {
m_voice_end = true; m_ch[ca] += m_voice[i].ch();
m_voice_cycle = 0; }
for (output_t &elem : m_ch) }
{
elem.reset();
}
for (voice_t &elem : m_voice)
{
const u8 ca = bitfield<u8>(elem.cr().ca(), 0, 3);
if (ca < 6)
{
m_ch[ca] += elem.ch();
}
elem.ch().reset();
}
}
else
{
m_voice_end = false;
}
m_voice_fetch = 0;
}
} }
void es5506_core::voice_t::fetch(u8 voice, u8 cycle) void es5506_core::voice_t::fetch(u8 cycle)
{ {
m_alu.set_sample( m_alu.set_sample(
cycle, 0,
m_host.m_intf.read_sample(voice, m_host.m_intf.read_sample(m_cr.m_bs,
m_cr.bs(), (m_alu.get_accum_integer())&((1<<m_alu.m_integer)-1)));
bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer)));
if (m_cr.cmpd()) if (m_cr.cmpd())
{ // Decompress (Upper 8 bit is used for compressed format) { // Decompress (Upper 8 bit is used for compressed format)
m_alu.set_sample(cycle, decompress(bitfield(m_alu.sample(cycle), 8, 8))); m_alu.set_sample(0, decompress((m_alu.sample(0)>>8)&255));
}
m_alu.set_sample(
1,
m_host.m_intf.read_sample(m_cr.m_bs,
(m_alu.get_accum_integer() + 1)&((1<<m_alu.m_integer)-1)));
if (m_cr.cmpd())
{ // Decompress (Upper 8 bit is used for compressed format)
m_alu.set_sample(1, decompress((m_alu.sample(1)>>8)&255));
} }
} }
void es5506_core::voice_t::tick(u8 voice) void es5506_core::voice_t::tick(u8 voice)
{ {
m_output[0] = m_output[1] = 0;
m_ch.reset();
// Filter execute // Filter execute
m_filter.tick(m_alu.interpolation());
if (m_alu.busy()) if (m_alu.busy())
{ {
if ((m_alu.m_last_accum&(~m_alu.m_fraction))!=(m_alu.m_accum&(~m_alu.m_fraction))) fetch(0);
m_filter.tick(m_alu.interpolation());
// Send to output // Send to output
m_output[0] = volume_calc(m_lvol, sign_ext<s32>(m_filter.o4_1(), 16)); m_output[0] = m_mute ? 0 : volume_calc(m_lvol, (short)m_filter.o4_1());
m_output[1] = volume_calc(m_rvol, sign_ext<s32>(m_filter.o4_1(), 16)); m_output[1] = m_mute ? 0 : volume_calc(m_rvol, (short)m_filter.o4_1());
m_ch.set_left(m_output[0]); m_ch.set_left(m_output[0]);
m_ch.set_right(m_output[1]); m_ch.set_right(m_output[1]);
@ -307,18 +111,23 @@ void es5506_core::voice_t::tick(u8 voice)
{ {
m_alu.loop_exec(); m_alu.loop_exec();
} }
} } else {
m_filter.tick(m_alu.interpolation());
m_output[0] = m_output[1] = 0;
m_ch.reset();
}
// Envelope // Envelope
if (m_ecount != 0) if (m_ecount != 0)
{ {
// Left and Right volume // Left and Right volume
if (bitfield(m_lvramp, 0, 8) != 0) if (bitfield(m_lvramp, 0, 8) != 0)
{ {
m_lvol = clamp<s32>(m_lvol + sign_ext<s32>(bitfield(m_lvramp, 0, 8), 8), 0, 0xffff); m_lvol = VGS_CLAMP(m_lvol + sign_ext<s32>(bitfield(m_lvramp, 0, 8), 8), 0, 0xffff);
} }
if (bitfield(m_rvramp, 0, 8) != 0) if (bitfield(m_rvramp, 0, 8) != 0)
{ {
m_rvol = clamp<s32>(m_rvol + sign_ext<s32>(bitfield(m_rvramp, 0, 8), 8), 0, 0xffff); m_rvol = VGS_CLAMP(m_rvol + sign_ext<s32>(bitfield(m_rvramp, 0, 8), 8), 0, 0xffff);
} }
// Filter coeffcient // Filter coeffcient
@ -326,13 +135,13 @@ void es5506_core::voice_t::tick(u8 voice)
((m_k1ramp.slow() == 0) || (bitfield(m_filtcount, 0, 3) == 0))) ((m_k1ramp.slow() == 0) || (bitfield(m_filtcount, 0, 3) == 0)))
{ {
m_filter.set_k1( m_filter.set_k1(
clamp<s32>(m_filter.k1() + sign_ext<s32>(m_k1ramp.ramp(), 8), 0, 0xffff)); VGS_CLAMP(m_filter.k1() + sign_ext<s32>(m_k1ramp.ramp(), 8), 0, 0xffff));
} }
if ((m_k2ramp.ramp() != 0) && if ((m_k2ramp.ramp() != 0) &&
((m_k2ramp.slow() == 0) || (bitfield(m_filtcount, 0, 3) == 0))) ((m_k2ramp.slow() == 0) || (bitfield(m_filtcount, 0, 3) == 0)))
{ {
m_filter.set_k2( m_filter.set_k2(
clamp<s32>(m_filter.k2() + sign_ext<s32>(m_k2ramp.ramp(), 8), 0, 0xffff)); VGS_CLAMP(m_filter.k2() + sign_ext<s32>(m_k2ramp.ramp(), 8), 0, 0xffff));
} }
m_ecount--; m_ecount--;
@ -340,7 +149,7 @@ void es5506_core::voice_t::tick(u8 voice)
m_filtcount = bitfield(m_filtcount + 1, 0, 3); m_filtcount = bitfield(m_filtcount + 1, 0, 3);
// Update IRQ // Update IRQ
m_alu.irq_exec(m_host.m_intf, m_host.m_irqv, voice); //m_alu.irq_exec(m_host.m_intf, m_host.m_irqv, voice);
} }
// Compressed format // Compressed format
@ -357,9 +166,7 @@ s16 es5506_core::voice_t::decompress(u8 sample)
// volume calculation // volume calculation
s32 es5506_core::voice_t::volume_calc(u16 volume, s32 in) s32 es5506_core::voice_t::volume_calc(u16 volume, s32 in)
{ {
u8 exponent = bitfield(volume, 12, 4); return (in * s32(0x100 | ((volume>>4)&255))) >> (20 - ((volume>>12)&15));
u8 mantissa = bitfield(volume, 4, 8);
return (in * s32(0x100 | mantissa)) >> (20 - exponent);
} }
void es5506_core::reset() void es5506_core::reset()
@ -420,36 +227,16 @@ void es5506_core::voice_t::reset()
// Accessors // Accessors
u8 es5506_core::host_r(u8 address) u8 es5506_core::host_r(u8 address)
{ {
if (!m_host_intf.host_access())
{
m_ha = address; m_ha = address;
if (m_e.rising_edge())
{ // update directly
m_hd = read(m_ha, true); m_hd = read(m_ha, true);
}
else
{
m_host_intf.set_strobe(true);
}
}
return m_hd; return m_hd;
} }
void es5506_core::host_w(u8 address, u8 data) void es5506_core::host_w(u8 address, u8 data)
{ {
if (!m_host_intf.host_access())
{
m_ha = address; m_ha = address;
m_hd = data; m_hd = data;
if (m_e.rising_edge())
{ // update directly
write(m_ha, m_hd); write(m_ha, m_hd);
}
else
{
m_host_intf.set_strobe(false);
}
}
} }
u8 es5506_core::read(u8 address, bool cpu_access) u8 es5506_core::read(u8 address, bool cpu_access)

View file

@ -27,7 +27,7 @@ class es5506_core : public es550x_shared_core
{ {
} }
void reset() inline void reset()
{ {
m_left = 0; m_left = 0;
m_right = 0; m_right = 0;
@ -39,7 +39,7 @@ class es5506_core : public es550x_shared_core
m_right = src.right(); m_right = src.right();
} }
inline s32 clamp20(s32 in) { return clamp(in, -0x80000, 0x7ffff); } inline s32 clamp20(s32 in) { return VGS_CLAMP(in, -0x80000, 0x7ffff); }
inline void clamp20(output_t &src) inline void clamp20(output_t &src)
{ {
@ -89,7 +89,7 @@ class es5506_core : public es550x_shared_core
return *this; return *this;
} }
private: public: // oh my...
s32 m_left = 0; s32 m_left = 0;
s32 m_right = 0; s32 m_right = 0;
}; };
@ -145,15 +145,14 @@ class es5506_core : public es550x_shared_core
, m_k2ramp(filter_ramp_t()) , m_k2ramp(filter_ramp_t())
, m_k1ramp(filter_ramp_t()) , m_k1ramp(filter_ramp_t())
, m_filtcount(0) , m_filtcount(0)
, m_ch(output_t())
, m_mute(false) , m_mute(false)
, m_output{0,0}
{ {
m_output.fill(0);
} }
// internal state // internal state
virtual void reset() override; virtual void reset() override;
virtual void fetch(u8 voice, u8 cycle) override; virtual void fetch(u8 cycle) override;
virtual void tick(u8 voice) override; virtual void tick(u8 voice) override;
// Setters // Setters
@ -182,6 +181,8 @@ class es5506_core : public es550x_shared_core
inline filter_ramp_t &k1ramp() { return m_k1ramp; } inline filter_ramp_t &k1ramp() { return m_k1ramp; }
inline bool muted() { return m_mute; }
output_t &ch() { return m_ch; } output_t &ch() { return m_ch; }
// for debug/preview only // for debug/preview only
@ -211,7 +212,7 @@ class es5506_core : public es550x_shared_core
u8 m_filtcount = 0; // Internal counter for slow mode u8 m_filtcount = 0; // Internal counter for slow mode
output_t m_ch; // channel output output_t m_ch; // channel output
bool m_mute = false; // mute flag (for debug purpose) bool m_mute = false; // mute flag (for debug purpose)
std::array<s32, 2> m_output; // output preview (for debug purpose) s32 m_output[2]; // output preview (for debug purpose)
}; };
// 5 bit mode // 5 bit mode
@ -313,9 +314,9 @@ class es5506_core : public es550x_shared_core
inline bool bclk_falling_edge() { return m_bclk.falling_edge(); } inline bool bclk_falling_edge() { return m_bclk.falling_edge(); }
// 6 stereo output channels // 6 stereo output channels
inline s32 lout(u8 ch) { return m_output[std::min<u8>(5, ch & 0x7)].left(); } inline s32 lout(u8 ch) { return m_output[ch&7].left(); }
inline s32 rout(u8 ch) { return m_output[std::min<u8>(5, ch & 0x7)].right(); } inline s32 rout(u8 ch) { return m_output[ch&7].right(); }
//----------------------------------------------------------------- //-----------------------------------------------------------------
// //
@ -352,7 +353,7 @@ class es5506_core : public es550x_shared_core
virtual void voice_tick() override; virtual void voice_tick() override;
private: private:
std::array<voice_t, 32> m_voice; // 32 voices voice_t m_voice[32]; // 32 voices
// Host interfaces // Host interfaces
u32 m_read_latch = 0; // 32 bit register latch for host read u32 m_read_latch = 0; // 32 bit register latch for host read
@ -372,10 +373,10 @@ class es5506_core : public es550x_shared_core
s16 m_wclk = 0; // WCLK s16 m_wclk = 0; // WCLK
bool m_wclk_lr = false; // WCLK, L/R output select bool m_wclk_lr = false; // WCLK, L/R output select
s8 m_output_bit = 0; // Bit position in output s8 m_output_bit = 0; // Bit position in output
std::array<output_t, 6> m_ch; // 6 stereo output channels output_t m_ch[6]; // 6 stereo output channels
std::array<output_t, 6> m_output; // Serial outputs output_t m_output[8]; // Serial outputs
std::array<output_t, 6> m_output_temp; // temporary signal for serial output output_t m_output_temp[6]; // temporary signal for serial output
std::array<output_t, 6> m_output_latch; // output latch output_t m_output_latch[6]; // output latch
}; };
#endif #endif

View file

@ -14,6 +14,8 @@
#include "../core/core.hpp" #include "../core/core.hpp"
#include "../core/util/clock_pulse.hpp" #include "../core/util/clock_pulse.hpp"
#include <string.h>
using namespace vgsound_emu; using namespace vgsound_emu;
// ES5504/ES5505/ES5506 interface // ES5504/ES5505/ES5506 interface
@ -39,7 +41,7 @@ class es550x_intf : public vgsound_emu_core
virtual void adc_w(u16 data) {} // ADC output virtual void adc_w(u16 data) {} // ADC output
virtual s16 read_sample(u8 voice, u8 bank, u32 address) { return 0; } virtual s16 read_sample(u8 bank, u32 address) { return 0; }
}; };
// Shared functions for ES5504/ES5505/ES5506 // Shared functions for ES5504/ES5505/ES5506
@ -135,7 +137,7 @@ class es550x_shared_core : public vgsound_emu_core
inline bool cmpd() { return m_cmpd; } inline bool cmpd() { return m_cmpd; }
protected: public:
// Channel assign - // Channel assign -
// 4 bit (16 channel or Bank) for ES5504 // 4 bit (16 channel or Bank) for ES5504
// 2 bit (4 stereo channels) for ES5505 // 2 bit (4 stereo channels) for ES5505
@ -158,6 +160,7 @@ class es550x_shared_core : public vgsound_emu_core
: vgsound_emu_core("es550x_voice_alu") : vgsound_emu_core("es550x_voice_alu")
, m_integer(integer) , m_integer(integer)
, m_fraction(fraction) , m_fraction(fraction)
, m_fraction_m9(std::max<s8>(0, m_fraction - 9))
, m_total_bits(integer + fraction) , m_total_bits(integer + fraction)
, m_accum_mask( , m_accum_mask(
u32(std::min<u64>(~0, u64(u64(1) << u64(integer + fraction)) - 1))) u32(std::min<u64>(~0, u64(u64(1) << u64(integer + fraction)) - 1)))
@ -166,13 +169,15 @@ class es550x_shared_core : public vgsound_emu_core
, m_start(0) , m_start(0)
, m_end(0) , m_end(0)
, m_accum(0) , m_accum(0)
, m_last_accum(0)
, m_sample{0,0}
{ {
m_sample.fill(0);
} }
// configurations // configurations
const u8 m_integer = 21; const u8 m_integer = 21;
const u8 m_fraction = 11; const u8 m_fraction = 11;
const u8 m_fraction_m9 = 2;
const u8 m_total_bits = 32; const u8 m_total_bits = 32;
const u32 m_accum_mask = 0xffffffff; const u32 m_accum_mask = 0xffffffff;
const bool m_transwave = true; const bool m_transwave = true;
@ -264,7 +269,7 @@ class es550x_shared_core : public vgsound_emu_core
inline s32 sample(u8 slot) { return m_sample[slot & 1]; } inline s32 sample(u8 slot) { return m_sample[slot & 1]; }
private: public:
class es550x_alu_cr_t : public vgsound_emu_core class es550x_alu_cr_t : public vgsound_emu_core
{ {
public: public:
@ -343,7 +348,7 @@ class es550x_shared_core : public vgsound_emu_core
inline u8 loop() { return (m_lpe << 0) | (m_ble << 1); } inline u8 loop() { return (m_lpe << 0) | (m_ble << 1); }
private: public:
u8 m_stop0 : 1; // Stop with ALU u8 m_stop0 : 1; // Stop with ALU
u8 m_stop1 : 1; // Stop with processor u8 m_stop1 : 1; // Stop with processor
u8 m_lpe : 1; // Loop enable u8 m_lpe : 1; // Loop enable
@ -366,8 +371,9 @@ class es550x_shared_core : public vgsound_emu_core
// 20 integer, 9 fraction for ES5504/ES5505 // 20 integer, 9 fraction for ES5504/ES5505
// 21 integer, 11 fraction for ES5506 // 21 integer, 11 fraction for ES5506
u32 m_accum = 0; u32 m_accum = 0;
u32 m_last_accum = 0;
// Samples // Samples
std::array<s32, 2> m_sample; s32 m_sample[2];
}; };
// Filter // Filter
@ -380,10 +386,7 @@ class es550x_shared_core : public vgsound_emu_core
, m_k2(0) , m_k2(0)
, m_k1(0) , m_k1(0)
{ {
for (std::array<s32, 2> &elem : m_o) memset(m_o,0,2*5*sizeof(s32));
{
std::fill(elem.begin(), elem.end(), 0);
}
} }
void reset(); void reset();
@ -428,8 +431,6 @@ class es550x_shared_core : public vgsound_emu_core
inline s32 o4_1() { return m_o[4][0]; } inline s32 o4_1() { return m_o[4][0]; }
private: private:
void lp_exec(s32 coeff, s32 in, s32 out);
void hp_exec(s32 coeff, s32 in, s32 out);
// Registers // Registers
u8 m_lp = 0; // Filter mode u8 m_lp = 0; // Filter mode
@ -441,7 +442,7 @@ class es550x_shared_core : public vgsound_emu_core
s32 m_k1 = 0; // Filter coefficient 1 s32 m_k1 = 0; // Filter coefficient 1
// Filter storage registers // Filter storage registers
std::array<std::array<s32, 2>, 5> m_o; s32 m_o[5][2];
}; };
public: public:
@ -455,7 +456,7 @@ class es550x_shared_core : public vgsound_emu_core
// internal state // internal state
virtual void reset(); virtual void reset();
virtual void fetch(u8 voice, u8 cycle) = 0; virtual void fetch(u8 cycle) = 0;
virtual void tick(u8 voice) = 0; virtual void tick(u8 voice) = 0;
void irq_update(es550x_intf &intf, es550x_irq_t &irqv) void irq_update(es550x_intf &intf, es550x_irq_t &irqv)
@ -515,16 +516,12 @@ class es550x_shared_core : public vgsound_emu_core
m_host_access = m_host_access_strobe; m_host_access = m_host_access_strobe;
} }
// Getters
bool host_access() { return m_host_access; }
bool rw() { return m_rw; } public:
u8 m_host_access ; // Host access trigger
private: u8 m_host_access_strobe ; // Host access strobe
u8 m_host_access : 1; // Host access trigger u8 m_rw ; // R/W state
u8 m_host_access_strobe : 1; // Host access strobe u8 m_rw_strobe ; // R/W strobe
u8 m_rw : 1; // R/W state
u8 m_rw_strobe : 1; // R/W strobe
}; };
public: public:

View file

@ -16,13 +16,15 @@ void es550x_shared_core::es550x_voice_t::es550x_alu_t::reset()
m_start = 0; m_start = 0;
m_end = 0; m_end = 0;
m_accum = 0; m_accum = 0;
m_last_accum=0;
m_sample[0] = m_sample[1] = 0; m_sample[0] = m_sample[1] = 0;
} }
bool es550x_shared_core::es550x_voice_t::es550x_alu_t::busy() { return m_cr.stop() == 0; } bool es550x_shared_core::es550x_voice_t::es550x_alu_t::busy() { return !(m_cr.m_stop0 || m_cr.m_stop1); }
bool es550x_shared_core::es550x_voice_t::es550x_alu_t::tick() bool es550x_shared_core::es550x_voice_t::es550x_alu_t::tick()
{ {
m_last_accum = m_accum;
if (m_cr.dir()) if (m_cr.dir())
{ {
m_accum -= m_fc; m_accum -= m_fc;
@ -33,24 +35,19 @@ bool es550x_shared_core::es550x_voice_t::es550x_alu_t::tick()
} }
m_accum &= m_accum_mask; m_accum &= m_accum_mask;
return ((!m_cr.lei()) && return ((!m_cr.m_lei) &&
(((m_cr.dir()) && (m_accum < m_start)) || ((!m_cr.dir()) && (m_accum > m_end)))) (((m_cr.m_dir) && (m_accum < m_start)) || ((!m_cr.m_dir) && (m_accum > m_end))))
? true ? true
: false; : false;
} }
void es550x_shared_core::es550x_voice_t::es550x_alu_t::loop_exec() void es550x_shared_core::es550x_voice_t::es550x_alu_t::loop_exec()
{ {
if (m_cr.irqe()) if (m_cr.m_dir) // Reverse playback
{ // Set IRQ
m_cr.set_irq(true);
}
if (m_cr.dir()) // Reverse playback
{ {
if (m_cr.lpe()) // Loop enable if (m_cr.m_lpe) // Loop enable
{ {
if (m_cr.ble()) // Bidirectional if (m_cr.m_ble) // Bidirectional
{ {
m_cr.set_dir(false); m_cr.set_dir(false);
m_accum = m_start + (m_start - m_accum); m_accum = m_start + (m_start - m_accum);
@ -60,7 +57,7 @@ void es550x_shared_core::es550x_voice_t::es550x_alu_t::loop_exec()
m_accum = m_end - (m_start - m_accum); m_accum = m_end - (m_start - m_accum);
} }
} }
else if (m_cr.ble() && m_transwave) // m_transwave else if (m_cr.m_ble && m_transwave) // m_transwave
{ {
m_cr.set_loop(0); m_cr.set_loop(0);
m_cr.set_lei(true); // Loop end ignore m_cr.set_lei(true); // Loop end ignore
@ -73,9 +70,9 @@ void es550x_shared_core::es550x_voice_t::es550x_alu_t::loop_exec()
} }
else else
{ {
if (m_cr.lpe()) // Loop enable if (m_cr.m_lpe) // Loop enable
{ {
if (m_cr.ble()) // Bidirectional if (m_cr.m_ble) // Bidirectional
{ {
m_cr.set_dir(true); m_cr.set_dir(true);
m_accum = m_end - (m_end - m_accum); m_accum = m_end - (m_end - m_accum);
@ -85,7 +82,7 @@ void es550x_shared_core::es550x_voice_t::es550x_alu_t::loop_exec()
m_accum = (m_accum - m_end) + m_start; m_accum = (m_accum - m_end) + m_start;
} }
} }
else if (m_cr.ble() && m_transwave) // m_transwave else if (m_cr.m_ble && m_transwave) // m_transwave
{ {
m_cr.set_loop(0); m_cr.set_loop(0);
m_cr.set_lei(true); // Loop end ignore m_cr.set_lei(true); // Loop end ignore
@ -101,14 +98,14 @@ void es550x_shared_core::es550x_voice_t::es550x_alu_t::loop_exec()
s32 es550x_shared_core::es550x_voice_t::es550x_alu_t::interpolation() s32 es550x_shared_core::es550x_voice_t::es550x_alu_t::interpolation()
{ {
// SF = S1 + ACCfr * (S2 - S1) // SF = S1 + ACCfr * (S2 - S1)
return m_sample[0] + ((bitfield<s32>(m_accum, std::max<s8>(0, m_fraction - 9), 9) * return m_sample[0] + (((((int)m_accum>>(int)2)&(int)511) *
(m_sample[1] - m_sample[0])) >> (m_sample[1] - m_sample[0])) >>
9); 9);
} }
u32 es550x_shared_core::es550x_voice_t::es550x_alu_t::get_accum_integer() u32 es550x_shared_core::es550x_voice_t::es550x_alu_t::get_accum_integer()
{ {
return bitfield(m_accum, m_fraction, m_integer); return (m_accum>>m_fraction)&((1<<m_integer)-1);
} }
void es550x_shared_core::es550x_voice_t::es550x_alu_t::irq_exec(es550x_intf &intf, void es550x_shared_core::es550x_voice_t::es550x_alu_t::irq_exec(es550x_intf &intf,

View file

@ -8,16 +8,24 @@
#include "es550x.hpp" #include "es550x.hpp"
// Yn = K*(Xn - Yn-1) + Yn-1
#define lp_exec(coeff,in,out) \
m_o[out][1] = m_o[out][0]; \
m_o[out][0] = ((coeff * (m_o[in][0] - m_o[out][0])) / 4096) + m_o[out][0];
// Yn = Xn - Xn-1 + K*Yn-1
#define hp_exec(coeff,in,out) \
m_o[out][1] = m_o[out][0]; \
m_o[out][0] = m_o[in][0] - m_o[in][1] + ((coeff * m_o[out][0]) / 8192) + (m_o[out][0] / 2);
// Filter functions // Filter functions
void es550x_shared_core::es550x_voice_t::es550x_filter_t::reset() void es550x_shared_core::es550x_voice_t::es550x_filter_t::reset()
{ {
m_lp = 0; m_lp = 0;
m_k2 = 0; m_k2 = 0;
m_k1 = 0; m_k1 = 0;
for (std::array<s32, 2> &elem : m_o) memset(m_o,0,2*5*sizeof(s32));
{
std::fill(elem.begin(), elem.end(), 0);
}
} }
void es550x_shared_core::es550x_voice_t::es550x_filter_t::tick(s32 in) void es550x_shared_core::es550x_voice_t::es550x_filter_t::tick(s32 in)
@ -52,21 +60,3 @@ void es550x_shared_core::es550x_voice_t::es550x_filter_t::tick(s32 in)
break; break;
} }
} }
void es550x_shared_core::es550x_voice_t::es550x_filter_t::lp_exec(s32 coeff, s32 in, s32 out)
{
// Store previous filter data
m_o[out][1] = m_o[out][0];
// Yn = K*(Xn - Yn-1) + Yn-1
m_o[out][0] = ((coeff * (m_o[in][0] - m_o[out][0])) / 4096) + m_o[out][0];
}
void es550x_shared_core::es550x_voice_t::es550x_filter_t::hp_exec(s32 coeff, s32 in, s32 out)
{
// Store previous filter data
m_o[out][1] = m_o[out][0];
// Yn = Xn - Xn-1 + K*Yn-1
m_o[out][0] = m_o[in][0] - m_o[in][1] + ((coeff * m_o[out][0]) / 8192) + (m_o[out][0] / 2);
}

View file

@ -8,11 +8,11 @@
#include "k005289.hpp" #include "k005289.hpp"
void k005289_core::tick() void k005289_core::tick(const unsigned int cycles)
{ {
for (timer_t &elem : m_timer) 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() void k005289_core::timer_t::tick(const unsigned int cycles) {
{ m_counter-=cycles;
if (bitfield(++m_counter, 0, 12) == 0) while (m_counter < 0)
{ {
m_addr = bitfield(m_addr + 1, 0, 5); m_addr = bitfield(m_addr + 1, 0, 5);
m_counter = m_freq; m_counter += 0x1000-(m_freq&0xfff);
} }
} }

View file

@ -32,7 +32,7 @@ class k005289_core : public vgsound_emu_core
// internal state // internal state
void reset(); void reset();
void tick(); void tick(const unsigned int cycles=1);
// accessors // accessors
// Replace current frequency to lastest loaded pitch // Replace current frequency to lastest loaded pitch
@ -63,7 +63,7 @@ class k005289_core : public vgsound_emu_core
// internal state // internal state
void reset(); void reset();
void tick(); void tick(const unsigned int cycles);
// accessors // accessors
// TG1/2 pin // TG1/2 pin

View file

@ -1,5 +1,9 @@
# Konami SCC # Konami SCC
## modified
the emulation core has been modified for optimization.
## Summary ## Summary
- 5 voice wavetable - 5 voice wavetable

View file

@ -10,17 +10,17 @@
#include "scc.hpp" #include "scc.hpp"
// shared SCC features // shared SCC features
void scc_core::tick() void scc_core::tick(const int cycles)
{ {
m_out = 0; m_out = 0;
for (auto &elem : m_voice) for (int elem=0; elem<5; elem++)
{ {
elem.tick(); m_voice[elem].tick(cycles);
m_out += elem.out(); m_out += m_voice[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 if (m_pitch >= 9) // or voice is halted
{ {
@ -28,23 +28,27 @@ void scc_core::voice_t::tick()
const u16 temp = m_counter; const u16 temp = m_counter;
if (m_host.m_test.freq_4bit()) // 4 bit frequency mode 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 & ~0x0ff) | (bitfield(bitfield(m_counter, 0, 8) - cycles, 0, 8) << 0);
m_counter = (m_counter & ~0xf00) | (bitfield(bitfield(m_counter, 8, 4) - 1, 0, 4) << 8); m_counter = (m_counter & ~0xf00) | (bitfield(bitfield(m_counter, 8, 4) - cycles, 0, 4) << 8);
} }
else else
{ {
m_counter = bitfield(m_counter - 1, 0, 12); m_counter = bitfield(m_counter - cycles, 0, 12);
} }
// handle counter carry // handle counter carry
const bool carry = m_host.m_test.freq_8bit() const bool carry = (temp<cycles) || (m_host.m_test.freq_8bit()
? (bitfield(temp, 0, 8) == 0) ? (bitfield(temp, 0, 8) == 0)
: (m_host.m_test.freq_4bit() ? (bitfield(temp, 8, 4) == 0) : (m_host.m_test.freq_4bit() ? (bitfield(temp, 8, 4) == 0)
: (bitfield(temp, 0, 12) == 0)); : (bitfield(temp, 0, 12) == 0)));
if (carry) if (carry)
{ {
m_addr = bitfield(m_addr + 1, 0, 5); m_addr = bitfield(m_addr + 1, 0, 5);
m_counter = m_pitch; m_counter = m_pitch - ((temp<cycles)?(cycles-temp-1):0);
while (m_counter>m_pitch) {
m_addr = bitfield(m_addr + 1, 0, 5);
m_counter+=m_pitch-1;
}
} }
} }
// get output // get output
@ -60,19 +64,19 @@ void scc_core::voice_t::tick()
void scc_core::reset() void scc_core::reset()
{ {
for (auto &elem : m_voice) for (int elem=0; elem<5; elem++)
{ {
elem.reset(); m_voice[elem].reset();
} }
m_test.reset(); m_test.reset();
m_out = 0; m_out = 0;
std::fill(m_reg.begin(), m_reg.end(), 0); memset(m_reg,0,256);
} }
void scc_core::voice_t::reset() void scc_core::voice_t::reset()
{ {
std::fill(m_wave.begin(), m_wave.end(), 0); memset(m_wave,0,32);
m_enable = false; m_enable = false;
m_pitch = 0; m_pitch = 0;
m_volume = 0; m_volume = 0;

View file

@ -14,6 +14,7 @@
#include "../core/core.hpp" #include "../core/core.hpp"
#include "../core/util/mem_intf.hpp" #include "../core/util/mem_intf.hpp"
#include <string.h>
using namespace vgsound_emu; using namespace vgsound_emu;
@ -36,12 +37,12 @@ class scc_core : public vgsound_emu_core
, m_counter(0) , m_counter(0)
, m_out(0) , m_out(0)
{ {
m_wave.fill(0); memset(m_wave,0,32);
} }
// internal state // internal state
void reset(); void reset();
void tick(); void tick(const int cycles=1);
// accessors // accessors
inline void reset_addr() { m_addr = 0; } inline void reset_addr() { m_addr = 0; }
@ -69,7 +70,7 @@ class scc_core : public vgsound_emu_core
private: private:
// registers // registers
scc_core &m_host; scc_core &m_host;
std::array<s8, 32> m_wave; // internal waveform s8 m_wave[32]; // internal waveform
bool m_enable = false; // output enable flag bool m_enable = false; // output enable flag
u16 m_pitch : 12; // pitch u16 m_pitch : 12; // pitch
u16 m_volume : 4; // volume u16 m_volume : 4; // volume
@ -139,7 +140,7 @@ class scc_core : public vgsound_emu_core
, m_test(test_t()) , m_test(test_t())
, m_out(0) , m_out(0)
{ {
m_reg.fill(0); memset(m_reg,0,256);
} }
// destructor // destructor
@ -151,7 +152,7 @@ class scc_core : public vgsound_emu_core
// internal state // internal state
virtual void reset(); virtual void reset();
void tick(); void tick(const int cycles=1);
// getters // getters
inline s32 out() { return m_out; } // output to DA0...DA10 pin inline s32 out() { return m_out; } // output to DA0...DA10 pin
@ -168,12 +169,12 @@ class scc_core : public vgsound_emu_core
void freq_vol_enable_w(u8 address, u8 data); void freq_vol_enable_w(u8 address, u8 data);
// internal values // internal values
std::array<voice_t, 5> m_voice; // 5 voices voice_t m_voice[5]; // 5 voices
test_t m_test; // test register test_t m_test; // test register
s32 m_out = 0; // output to DA0...10 s32 m_out = 0; // output to DA0...10
std::array<u8, 256> m_reg; // register pool u8 m_reg[256]; // register pool
}; };
// SCC core // SCC core

View file

@ -11,13 +11,11 @@
void vrcvi_core::tick() void vrcvi_core::tick()
{ {
m_out = 0; m_out = 0;
if (!m_control.halt()) // Halt flag if (!m_control.m_halt) // Halt flag
{ {
// tick per each clock // tick per each clock
for (auto &elem : m_pulse) m_out += m_pulse[0].get_output(); // add 4 bit pulse output
{ m_out += m_pulse[1].get_output();
m_out += elem.get_output(); // add 4 bit pulse output
}
m_out += m_sawtooth.get_output(); // add 5 bit sawtooth output m_out += m_sawtooth.get_output(); // add 5 bit sawtooth output
} }
if (m_timer.tick()) if (m_timer.tick())
@ -28,11 +26,8 @@ void vrcvi_core::tick()
void vrcvi_core::reset() void vrcvi_core::reset()
{ {
for (auto &elem : m_pulse) m_pulse[0].reset();
{ m_pulse[1].reset();
elem.reset();
}
m_sawtooth.reset(); m_sawtooth.reset();
m_timer.reset(); m_timer.reset();
m_control.reset(); m_control.reset();
@ -41,65 +36,71 @@ void vrcvi_core::reset()
bool vrcvi_core::alu_t::tick() bool vrcvi_core::alu_t::tick()
{ {
if (m_divider.enable()) if (m_divider.m_enable)
{ {
const u16 temp = m_counter; const u16 temp = m_counter;
// post decrement // post decrement
if (bitfield(m_host.m_control.shift(), 1)) if (m_host.m_control.m_shift&2)
{ {
m_counter = (m_counter & 0x0ff) | (bitfield(bitfield(m_counter, 8, 4) - 1, 0, 4) << 8); m_counter = (m_counter & 0x0ff) | (bitfield(bitfield(m_counter, 8, 4) - 1, 0, 4) << 8);
m_counter = (m_counter & 0xf00) | (bitfield(bitfield(m_counter, 0, 8) - 1, 0, 8) << 0); m_counter = (m_counter & 0xf00) | (bitfield(bitfield(m_counter, 0, 8) - 1, 0, 8) << 0);
if (bitfield(temp, 8, 4) == 0)
{
m_counter = m_divider.m_divider;
return true;
}
} }
else if (bitfield(m_host.m_control.shift(), 0)) else if (m_host.m_control.m_shift&1)
{ {
m_counter = (m_counter & 0x00f) | (bitfield(bitfield(m_counter, 4, 8) - 1, 0, 8) << 4); m_counter = (m_counter & 0x00f) | (bitfield(bitfield(m_counter, 4, 8) - 1, 0, 8) << 4);
m_counter = (m_counter & 0xff0) | (bitfield(bitfield(m_counter, 0, 4) - 1, 0, 4) << 0); m_counter = (m_counter & 0xff0) | (bitfield(bitfield(m_counter, 0, 4) - 1, 0, 4) << 0);
if (bitfield(temp, 4, 8) == 0)
{
m_counter = m_divider.m_divider;
return true;
}
} }
else else
{ {
m_counter = bitfield(bitfield(m_counter, 0, 12) - 1, 0, 12); m_counter = (m_counter-1)&0xfff; //bitfield(bitfield(m_counter, 0, 12) - 1, 0, 12);
if (!(temp&0xfff)) {
m_counter = m_divider.m_divider;
return true;
}
} }
// carry handling return false;
const bool carry = bitfield(m_host.m_control.shift(), 1)
? (bitfield(temp, 8, 4) == 0)
: (bitfield(m_host.m_control.shift(), 0) ? (bitfield(temp, 4, 8) == 0)
: (bitfield(temp, 0, 12) == 0));
if (carry)
{
m_counter = m_divider.divider();
}
return carry;
} }
return false; return false;
} }
bool vrcvi_core::pulse_t::tick() bool vrcvi_core::pulse_t::tick()
{ {
if (!m_divider.enable()) if (!m_divider.m_enable)
{ {
return false; return false;
} }
if (vrcvi_core::alu_t::tick()) if (vrcvi_core::alu_t::tick())
{ {
m_cycle = bitfield(m_cycle + 1, 0, 4); m_cycle = (m_cycle+1)&15;
} }
return m_control.mode() ? true : ((m_cycle > m_control.duty()) ? true : false); return m_control.m_mode ? true : ((m_cycle > m_control.m_duty) ? true : false);
} }
bool vrcvi_core::sawtooth_t::tick() bool vrcvi_core::sawtooth_t::tick()
{ {
if (!m_divider.enable()) if (!m_divider.m_enable)
{ {
return false; return false;
} }
if (vrcvi_core::alu_t::tick()) if (vrcvi_core::alu_t::tick())
{ {
if (bitfield(m_cycle++, 0)) if ((m_cycle++)&1)
{ // Even step only { // Even step only
m_accum += m_rate; m_accum += m_rate;
} }
@ -109,20 +110,20 @@ bool vrcvi_core::sawtooth_t::tick()
m_cycle = 0; m_cycle = 0;
} }
} }
return (m_accum == 0) ? false : true; return (m_accum != 0);
} }
s8 vrcvi_core::pulse_t::get_output() s8 vrcvi_core::pulse_t::get_output()
{ {
// add 4 bit pulse output // add 4 bit pulse output
m_out = tick() ? m_control.volume() : 0; m_out = tick() ? m_control.m_volume : 0;
return m_out; return m_out;
} }
s8 vrcvi_core::sawtooth_t::get_output() s8 vrcvi_core::sawtooth_t::get_output()
{ {
// add 5 bit sawtooth output // add 5 bit sawtooth output
m_out = tick() ? bitfield(m_accum, 3, 5) : 0; m_out = tick() ? ((m_accum>>3)&31) : 0;
return m_out; return m_out;
} }
@ -209,7 +210,7 @@ void vrcvi_core::pulse_w(u8 voice, u8 address, u8 data)
break; break;
case 0x02: // Pitch MSB, Enable/Disable - 0x9002/0x9001 (Pulse 1), 0xa002/0xa001 (Pulse 2) case 0x02: // Pitch MSB, Enable/Disable - 0x9002/0x9001 (Pulse 1), 0xa002/0xa001 (Pulse 2)
v.divider().write(true, data); v.divider().write(true, data);
if (!v.divider().enable()) if (!v.divider().m_enable)
{ // Reset duty cycle { // Reset duty cycle
v.clear_cycle(); v.clear_cycle();
} }
@ -229,7 +230,7 @@ void vrcvi_core::saw_w(u8 address, u8 data)
break; break;
case 0x02: // Pitch MSB, Enable/Disable - 0xb002/0xb001 (Sawtooth) case 0x02: // Pitch MSB, Enable/Disable - 0xb002/0xb001 (Sawtooth)
m_sawtooth.divider().write(true, data); m_sawtooth.divider().write(true, data);
if (!m_sawtooth.divider().enable()) if (!m_sawtooth.divider().m_enable)
{ // Reset accumulator { // Reset accumulator
m_sawtooth.clear_accum(); m_sawtooth.clear_accum();
} }

View file

@ -58,7 +58,7 @@ class vrcvi_core : public vgsound_emu_core
inline bool enable() { return m_enable; } inline bool enable() { return m_enable; }
private: public:
u16 m_divider : 12; // divider (pitch) u16 m_divider : 12; // divider (pitch)
u16 m_enable : 1; // channel disable flag u16 m_enable : 1; // channel disable flag
}; };
@ -141,7 +141,7 @@ class vrcvi_core : public vgsound_emu_core
inline u8 volume() { return m_volume; } inline u8 volume() { return m_volume; }
private: public:
u8 m_mode : 1; // duty toggle flag u8 m_mode : 1; // duty toggle flag
u8 m_duty : 3; // 3 bit duty cycle u8 m_duty : 3; // 3 bit duty cycle
u8 m_volume : 4; // 4 bit volume u8 m_volume : 4; // 4 bit volume
@ -359,9 +359,9 @@ class vrcvi_core : public vgsound_emu_core
inline u8 shift() { return m_shift; } inline u8 shift() { return m_shift; }
private: public:
u8 m_halt : 1; // halt sound u8 m_halt; // halt sound
u8 m_shift : 2; // 4/8 bit right shift u8 m_shift; // 4/8 bit right shift
}; };
public: public:
@ -398,7 +398,7 @@ class vrcvi_core : public vgsound_emu_core
private: private:
vrcvi_intf &m_intf; vrcvi_intf &m_intf;
std::array<pulse_t, 2> m_pulse; // 2 pulse channels pulse_t m_pulse[2]; // 2 pulse channels
sawtooth_t m_sawtooth; // sawtooth channel sawtooth_t m_sawtooth; // sawtooth channel
timer_t m_timer; // internal timer timer_t m_timer; // internal timer
global_control_t m_control; // control global_control_t m_control; // control

Binary file not shown.

View file

@ -4,3 +4,4 @@ the Furnace user interface is where the job gets done.
- [UI components](components.md) - [UI components](components.md)
- [global keyboard shortcuts](keyboard.md) - [global keyboard shortcuts](keyboard.md)
- [basic mode](basic-mode.md)

View file

@ -0,0 +1,34 @@
# basic mode
Furnace comes with a "basic mode" that can be toggled through the "settings" menu. it disables certain features in Furnace that may look intimidating or confusing for newcomers. if you find that a certain feature of furnace is missing, see if this setting is enabled or not.
among the features that cannot be accessed in this mode are:
* file menu:
* chip manipulation options (add, configure, change, remove chips)
* edit menu:
* paste special&hellip;
* operation masking
* input latch
* find and replace
* speed window:
* virtual tempo
* divider
* song length
* song info window:
* manual system naming
* tuning options
* right-clicking on the pattern window:
* gradient/fade&hellip;
* scale
* randomize
* invert values
* flip selection
* collapse
* expand
* these windows:
* mixer
* grooves
* channels
* pattern manager
* chip manager
* compatibility flags

View file

@ -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. - `EDxx`: delay note by `xx` ticks.
- `EExx`: send external command. - `EExx`: send external command.
- this effect is currently incomplete. - 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. - `F0xx`: change song Hz by BPM value.
- `F1xx`: single tick slide up. - `F1xx`: single tick slide up.
- `F2xx`: single tick slide down. - `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 | | 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 | 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 | 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 | | | | | A | ALG | ALG | ALG | | AutoEnvDen | AutoEnvDen | | | | AutoEnvDen | WaveLoad P | | | Control | | | | |
B | FB | FB | FB | | | Noise AND | | | | | WaveLoad L | | | | | | | | B | FB | FB | FB | | | Noise AND | | | | | WaveLoad L | | | | | | | |
C | FMS | FMS | FMS | | | Noise OR | | | | | WaveLoad T | | | | | | | | 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 | | | | | 5 | | | AMD2 | | | | | | | | | | | EnvRampR | | | | |
6 | | | PMD2 | | | | | | | | | | | EnvRampK1 | | | | | 6 | | | PMD2 | | | | | | | | | | | EnvRampK1 | | | | |
7 | | | LFO2Speed | | | | | | | | | | | EnvRampK2 | | | | | 7 | | | LFO2Speed | | | | | | | | | | | EnvRampK2 | | | | |
8 | | | LFO2Shape | | | | | | | | | | | Env Mode | | | | | 8 | | | LFO2Shape | | | | | | | | | | | Env Mode | | | | |

View file

@ -45,6 +45,16 @@ no plans have been made for TX81Z MIDI passthrough, because:
- `1Bxx`: set attack of operator 2. - `1Bxx`: set attack of operator 2.
- `1Cxx`: set attack of operator 3. - `1Cxx`: set attack of operator 3.
- `1Dxx`: set attack of operator 4. - `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. - `28xy`: set reverb of operator.
- `x` is the operator (1-4). a value of 0 means "all operators". - `x` is the operator (1-4). a value of 0 means "all operators".
- `y` is the value. - `y` is the value.

View file

@ -1,6 +1,6 @@
# Yamaha YM2151 # Yamaha YM2151
the sound chip powering several arcade boards and the Sharp X1/X68000. Eight 4-op FM channels, with overpowered LFO and almost unused noise generator. the sound chip powering several arcade boards, the Sharp X1/X68000 and the Commander X16. Eight 4-op FM channels, with overpowered LFO and almost unused noise generator.
it also was present on several pinball machines and synthesizers of the era, and later surpassed by the YM2414 (OPZ) present in the world-famous TX81Z. it also was present on several pinball machines and synthesizers of the era, and later surpassed by the YM2414 (OPZ) present in the world-famous TX81Z.

View file

@ -20,6 +20,7 @@ writers:
- DeMOSic - DeMOSic
- cam900 - cam900
- host12prog - host12prog
- WindowxDeveloper
other: other:

View file

@ -32,6 +32,12 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
the format versions are: the format versions are:
- 142: Furnace dev142
- 141: Furnace Tournament Edition (for intro tune contest)
- 140: Furnace dev140
- 139: Furnace dev139
- 138: Furnace dev138
- 137: Furnace dev137
- 136: Furnace dev136 - 136: Furnace dev136
- 135: Furnace dev135 - 135: Furnace dev135
- 134: Furnace dev134 - 134: Furnace dev134
@ -280,6 +286,7 @@ size | description
| - 0xc5: YM2610B CSM - 20 channels | - 0xc5: YM2610B CSM - 20 channels
| - 0xc6: K007232 - 2 channels | - 0xc6: K007232 - 2 channels
| - 0xc7: GA20 - 4 channels | - 0xc7: GA20 - 4 channels
| - 0xc8: SM8521 - 3 channels
| - 0xde: YM2610B extended - 19 channels | - 0xde: YM2610B extended - 19 channels
| - 0xe0: QSound - 19 channels | - 0xe0: QSound - 19 channels
| - 0xfc: Pong - 1 channel | - 0xfc: Pong - 1 channel
@ -398,6 +405,17 @@ size | description
4?? | patchbay 4?? | patchbay
| - see next section for more details. | - see next section for more details.
1 | automatic patchbay (>=136) 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 # patchbay
@ -467,6 +485,9 @@ size | description
| - a list of channelCount C strings | - a list of channelCount C strings
S?? | channel short names S?? | channel short names
| - same as above | - 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 # chip flags
@ -1640,6 +1661,10 @@ chips which aren't on this list don't have any flags.
- 1: 16.67MHz - 1: 16.67MHz
- bit 4: stereo (bool) - bit 4: stereo (bool)
## 0xb1: Ensoniq ES5506
- bit 0-4: channels (int)
## 0xb5: tildearrow Sound Unit ## 0xb5: tildearrow Sound Unit
- bit 0: clockSel (int) - bit 0: clockSel (int)

View file

@ -115,6 +115,7 @@ the following instrument types are available:
- 45: K007232 - 45: K007232
- 46: GA20 - 46: GA20
- 47: Pokémon Mini - 47: Pokémon Mini
- 48: SM8521
the following feature codes are recognized: the following feature codes are recognized:

View file

@ -15,17 +15,17 @@
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleLongVersionString</key> <key>CFBundleLongVersionString</key>
<string>0.6pre3</string> <string>0.6pre4</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>Furnace</string> <string>Furnace</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.6pre3</string> <string>0.6pre4</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>0.6pre3</string> <string>0.6pre4</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string></string> <string></string>
<key>NSHighResolutionCapable</key> <key>NSHighResolutionCapable</key>

BIN
res/furnaceword.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

102
res/furnaceword.svg Normal file
View file

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="205.65253mm"
height="85.955002mm"
viewBox="0 0 205.65253 85.955"
version="1.1"
id="svg5"
inkscape:export-filename="/tmp/furnaceword.png"
inkscape:export-xdpi="166.66396"
inkscape:export-ydpi="166.66396"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
sodipodi:docname="furnaceword.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#424242"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:document-units="mm"
showgrid="false"
fit-margin-top="32"
fit-margin-left="32"
fit-margin-right="32"
fit-margin-bottom="32"
inkscape:zoom="1.0935553"
inkscape:cx="414.70239"
inkscape:cy="225.86877"
inkscape:window-width="1920"
inkscape:window-height="1056"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2">
<rect
x="6.4661274"
y="115.09706"
width="584.08398"
height="238.58037"
id="rect1970" />
<rect
x="6.4661274"
y="115.09706"
width="488.38528"
height="165.18983"
id="rect25170" />
<rect
x="6.4661274"
y="115.09706"
width="584.08398"
height="238.58037"
id="rect93388" />
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter93390"
x="-0.053540562"
y="-0.35937083"
width="1.1070811"
height="1.7187417">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="9.1033312"
id="feGaussianBlur93392" />
</filter>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(30.4643,23.150964)">
<text
xml:space="preserve"
transform="matrix(0.26458333,0,0,0.26458333,-2.0565565,-29.517635)"
id="text93386"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:120px;line-height:1.25;font-family:'Averia Libre';-inkscape-font-specification:'Averia Libre Bold';letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect93388);fill:#75aae9;fill-opacity:1;stroke:#006bff;stroke-width:13.22834663;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.46923077;filter:url(#filter93390);opacity:1"><tspan
x="6.4667969"
y="226.09766"
id="tspan109265"><tspan
style="font-family:Xolonium;-inkscape-font-specification:'Xolonium Bold'"
id="tspan109263">Furnace</tspan></tspan></text>
<text
xml:space="preserve"
transform="matrix(0.26458333,0,0,0.26458333,-2.0565565,-29.517635)"
id="text1968"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:120px;line-height:1.25;font-family:'Averia Libre';-inkscape-font-specification:'Averia Libre Bold';letter-spacing:0px;word-spacing:0px;white-space:pre;shape-inside:url(#rect1970);fill:#8cf5ff;fill-opacity:1;stroke:#4dc4ff;stroke-width:3.77953;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.61758202"><tspan
x="6.4667969"
y="226.09766"
id="tspan109269"><tspan
style="font-family:Xolonium;-inkscape-font-specification:'Xolonium Bold'"
id="tspan109267">Furnace</tspan></tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
res/intro.fur Normal file

Binary file not shown.

BIN
res/introbg_1024.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 KiB

BIN
res/introbg_512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

BIN
res/patexcerpt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
res/tachip.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
res/talogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -30,8 +30,9 @@ cp ../../README.md README.txt || exit 1
cp -r ../../papers papers || exit 1 cp -r ../../papers papers || exit 1
cp -r ../../demos demos || exit 1 cp -r ../../demos demos || exit 1
cp -r ../../instruments instruments || exit 1 cp -r ../../instruments instruments || exit 1
cp -r ../../wavetables wavetables || exit 1
zip -r furnace.zip LICENSE.txt furnace.exe README.txt papers demos instruments zip -r furnace.zip LICENSE.txt furnace.exe README.txt papers demos instruments wavetables
furName=$(git describe --tags | sed "s/v0/0/") furName=$(git describe --tags | sed "s/v0/0/")

View file

@ -30,8 +30,9 @@ cp ../../README.md README.txt || exit 1
cp -r ../../papers papers || exit 1 cp -r ../../papers papers || exit 1
cp -r ../../demos demos || exit 1 cp -r ../../demos demos || exit 1
cp -r ../../instruments instruments || exit 1 cp -r ../../instruments instruments || exit 1
cp -r ../../wavetables wavetables || exit 1
zip -r furnace.zip LICENSE.txt furnace.exe README.txt papers demos instruments zip -r furnace.zip LICENSE.txt furnace.exe README.txt papers demos instruments wavetables
furName=$(git describe --tags | sed "s/v0/0/") furName=$(git describe --tags | sed "s/v0/0/")

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * 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) { if (m.type!=TA_MIDI_SYSEX && msg.size()>1) {
memcpy(m.data,msg.data()+1,MIN(msg.size()-1,7)); memcpy(m.data,msg.data()+1,MIN(msg.size()-1,7));
} else if (m.type==TA_MIDI_SYSEX) { } else if (m.type==TA_MIDI_SYSEX) {
m.sysExData.reset(new unsigned char[msg.size()]); m.sysExData=std::shared_ptr<unsigned char>(new unsigned char[msg.size()],std::default_delete<unsigned char[]>());
m.sysExLen=msg.size(); m.sysExLen=msg.size();
logD("got a SysEx of length %ld!",msg.size()); logD("got a SysEx of length %ld!",msg.size());
memcpy(m.sysExData.get(),msg.data(),msg.size()); memcpy(m.sysExData.get(),msg.data(),msg.size());

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -175,7 +175,7 @@ String DivConfig::getString(String key, String fallback) const {
return fallback; return fallback;
} }
bool DivConfig::has(String key) { bool DivConfig::has(String key) const {
try { try {
String test=conf.at(key); String test=conf.at(key);
} catch (std::out_of_range& e) { } catch (std::out_of_range& e) {

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -46,7 +46,7 @@ class DivConfig {
String getString(String key, String fallback) const; String getString(String key, String fallback) const;
// check for existence // check for existence
bool has(String key); bool has(String key) const;
// set a config value // set a config value
void set(String key, bool value); void set(String key, bool value);

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -161,3 +161,7 @@ void DivEngine::setConf(String key, const char* value) {
void DivEngine::setConf(String key, String value) { void DivEngine::setConf(String key, String value) {
conf.set(key,value); conf.set(key,value);
} }
bool DivEngine::hasConf(String key) {
return conf.has(key);
}

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * 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_CHIPS 32
#define DIV_MAX_CHANS 128 #define DIV_MAX_CHANS 128
#define DIV_MAX_PATTERNS 256 #define DIV_MAX_PATTERNS 256
#define DIV_MAX_CHIP_DEFS 256
// in-pattern // in-pattern
#define DIV_MAX_ROWS 256 #define DIV_MAX_ROWS 256

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * 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_AM_DEPTH, // (depth)
DIV_CMD_FM_PM_DEPTH, // (depth) DIV_CMD_FM_PM_DEPTH, // (depth)
DIV_CMD_GENESIS_LFO, // unused? DIV_CMD_FM_LFO2, // (speed)
DIV_CMD_FM_LFO2_WAVE, // (waveform)
DIV_CMD_ARCADE_LFO, // unused?
DIV_CMD_STD_NOISE_FREQ, // (freq) DIV_CMD_STD_NOISE_FREQ, // (freq)
DIV_CMD_STD_NOISE_MODE, // (mode) DIV_CMD_STD_NOISE_MODE, // (mode)
@ -215,6 +214,21 @@ enum DivDispatchCmds {
DIV_CMD_SURROUND_PANNING, // (out, val) DIV_CMD_SURROUND_PANNING, // (out, val)
DIV_CMD_FM_AM2_DEPTH, // (depth)
DIV_CMD_FM_PM2_DEPTH, // (depth)
DIV_CMD_ES5506_FILTER_MODE, // (value)
DIV_CMD_ES5506_FILTER_K1, // (value, mask)
DIV_CMD_ES5506_FILTER_K2, // (value, mask)
DIV_CMD_ES5506_FILTER_K1_SLIDE, // (value, negative)
DIV_CMD_ES5506_FILTER_K2_SLIDE, // (value, negative)
DIV_CMD_ES5506_ENVELOPE_COUNT, // (count)
DIV_CMD_ES5506_ENVELOPE_LVRAMP, // (ramp)
DIV_CMD_ES5506_ENVELOPE_RVRAMP, // (ramp)
DIV_CMD_ES5506_ENVELOPE_K1RAMP, // (ramp, slowdown)
DIV_CMD_ES5506_ENVELOPE_K2RAMP, // (ramp, slowdown)
DIV_CMD_ES5506_PAUSE, // (value)
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
DIV_CMD_MAX DIV_CMD_MAX
@ -632,8 +646,8 @@ class DivDispatch {
#define COLOR_PAL (283.75*15625.0+25.0) #define COLOR_PAL (283.75*15625.0+25.0)
#define CLAMP_VAR(x,xMin,xMax) \ #define CLAMP_VAR(x,xMin,xMax) \
if (x<xMin) x=xMin; \ if ((x)<(xMin)) (x)=(xMin); \
if (x>xMax) x=xMax; if ((x)>(xMax)) (x)=(xMax);
#define NEW_ARP_STRAT (parent->song.linearPitch==2 && !parent->song.oldArpStrategy) #define NEW_ARP_STRAT (parent->song.linearPitch==2 && !parent->song.oldArpStrategy)
#define HACKY_LEGATO_MESS chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode && !NEW_ARP_STRAT #define HACKY_LEGATO_MESS chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode && !NEW_ARP_STRAT

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -67,6 +67,7 @@
#include "platform/vrc6.h" #include "platform/vrc6.h"
#include "platform/fds.h" #include "platform/fds.h"
#include "platform/mmc5.h" #include "platform/mmc5.h"
#include "platform/es5506.h"
#include "platform/scc.h" #include "platform/scc.h"
#include "platform/ymz280b.h" #include "platform/ymz280b.h"
#include "platform/rf5c68.h" #include "platform/rf5c68.h"
@ -74,6 +75,7 @@
#include "platform/vb.h" #include "platform/vb.h"
#include "platform/k007232.h" #include "platform/k007232.h"
#include "platform/ga20.h" #include "platform/ga20.h"
#include "platform/sm8521.h"
#include "platform/pcmdac.h" #include "platform/pcmdac.h"
#include "platform/dummy.h" #include "platform/dummy.h"
#include "../ta-log.h" #include "../ta-log.h"
@ -436,6 +438,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_MMC5: case DIV_SYSTEM_MMC5:
dispatch=new DivPlatformMMC5; dispatch=new DivPlatformMMC5;
break; break;
case DIV_SYSTEM_ES5506:
dispatch=new DivPlatformES5506;
break;
case DIV_SYSTEM_SCC: case DIV_SYSTEM_SCC:
dispatch=new DivPlatformSCC; dispatch=new DivPlatformSCC;
((DivPlatformSCC*)dispatch)->setChipModel(false); ((DivPlatformSCC*)dispatch)->setChipModel(false);
@ -485,6 +490,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_GA20: case DIV_SYSTEM_GA20:
dispatch=new DivPlatformGA20; dispatch=new DivPlatformGA20;
break; break;
case DIV_SYSTEM_SM8521:
dispatch=new DivPlatformSM8521;
break;
case DIV_SYSTEM_PCM_DAC: case DIV_SYSTEM_PCM_DAC:
dispatch=new DivPlatformPCMDAC; dispatch=new DivPlatformPCMDAC;
break; break;

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * 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: case 0x08:
return "08xy: Set panning (x: left; y: right)"; return "08xy: Set panning (x: left; y: right)";
case 0x09: case 0x09:
return "09xx: Set speed 1"; return "09xx: Set groove pattern (speed 1 if no grooves exist)";
case 0x0a: case 0x0a:
return "0Axy: Volume slide (0y: down; x0: up)"; return "0Axy: Volume slide (0y: down; x0: up)";
case 0x0b: case 0x0b:
@ -70,7 +70,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
case 0x0d: case 0x0d:
return "0Dxx: Jump to next pattern"; return "0Dxx: Jump to next pattern";
case 0x0f: case 0x0f:
return "0Fxx: Set speed 2"; return "0Fxx: Set speed (speed 2 if no grooves exist)";
case 0x80: case 0x80:
return "80xx: Set panning (00: left; 80: center; FF: right)"; return "80xx: Set panning (00: left; 80: center; FF: right)";
case 0x81: case 0x81:
@ -110,8 +110,6 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
return "EDxx: Note delay"; return "EDxx: Note delay";
case 0xee: case 0xee:
return "EExx: Send external command"; return "EExx: Send external command";
case 0xef:
return "EFxx: Set global tuning (quirky!)";
case 0xf0: case 0xf0:
return "F0xx: Set tick rate (bpm)"; return "F0xx: Set tick rate (bpm)";
case 0xf1: case 0xf1:
@ -1471,6 +1469,42 @@ void DivEngine::createNew(const char* description, String sysName, bool inBase64
BUSY_END; BUSY_END;
} }
void DivEngine::createNewFromDefaults() {
quitDispatch();
BUSY_BEGIN;
saveLock.lock();
song.unload();
song=DivSong();
changeSong(0);
String preset=getConfString("initialSys2","");
bool oldVol=getConfInt("configVersion",DIV_ENGINE_VERSION)<135;
if (preset.empty()) {
// try loading old preset
preset=decodeSysDesc(getConfString("initialSys",""));
oldVol=false;
}
logD("preset size %ld",preset.size());
if (preset.size()>0 && (preset.size()&3)==0) {
initSongWithDesc(preset.c_str(),true,oldVol);
}
String sysName=getConfString("initialSysName","");
if (sysName=="") {
song.systemName=getSongSystemLegacyName(song,!getConfInt("noMultiSystem",0));
} else {
song.systemName=sysName;
}
recalcChans();
saveLock.unlock();
BUSY_END;
initDispatch();
BUSY_BEGIN;
renderSamples();
reset();
BUSY_END;
}
void DivEngine::swapChannels(int src, int dest) { void DivEngine::swapChannels(int src, int dest) {
logV("swapping channel %d with %d",src,dest); logV("swapping channel %d with %d",src,dest);
if (src==dest) { if (src==dest) {
@ -1961,14 +1995,12 @@ String DivEngine::getPlaybackDebugInfo() {
"cmdsPerSecond: %d\n" "cmdsPerSecond: %d\n"
"globalPitch: %d\n" "globalPitch: %d\n"
"extValue: %d\n" "extValue: %d\n"
"speed1: %d\n"
"speed2: %d\n"
"tempoAccum: %d\n" "tempoAccum: %d\n"
"totalProcessed: %d\n" "totalProcessed: %d\n"
"bufferPos: %d\n", "bufferPos: %d\n",
curOrder,prevOrder,curRow,prevRow,ticks,subticks,totalLoops,lastLoopPos,nextSpeed,divider,cycles,clockDrift, curOrder,prevOrder,curRow,prevRow,ticks,subticks,totalLoops,lastLoopPos,nextSpeed,divider,cycles,clockDrift,
changeOrd,changePos,totalSeconds,totalTicks,totalTicksR,totalCmds,lastCmds,cmdsPerSecond,globalPitch, 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 +2125,8 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
lastLoopPos=-1; lastLoopPos=-1;
} }
endOfSong=false; endOfSong=false;
speedAB=false; // whaaaaa?
curSpeed=0;
playing=true; playing=true;
skipping=true; skipping=true;
memset(walked,0,8192); memset(walked,0,8192);
@ -2441,15 +2474,14 @@ void DivEngine::reset() {
} }
extValue=0; extValue=0;
extValuePresent=0; extValuePresent=0;
speed1=curSubSong->speed1; speeds=curSubSong->speeds;
speed2=curSubSong->speed2;
firstTick=false; firstTick=false;
shallStop=false; shallStop=false;
shallStopSched=false; shallStopSched=false;
pendingMetroTick=0; pendingMetroTick=0;
elapsedBars=0; elapsedBars=0;
elapsedBeats=0; elapsedBeats=0;
nextSpeed=speed1; nextSpeed=speeds.val[0];
divider=60; divider=60;
if (curSubSong->customTempo) { if (curSubSong->customTempo) {
divider=curSubSong->hz; divider=curSubSong->hz;
@ -2519,6 +2551,8 @@ int DivEngine::getEffectiveSampleRate(int rate) {
return (48828*MIN(128,(rate*128/48828)))/128; return (48828*MIN(128,(rate*128/48828)))/128;
case DIV_SYSTEM_X1_010: case DIV_SYSTEM_X1_010:
return (31250*MIN(255,(rate*16/31250)))/16; // TODO: support variable clock case return (31250*MIN(255,(rate*16/31250)))/16; // TODO: support variable clock case
case DIV_SYSTEM_ES5506:
return (31250*MIN(131071,(rate*2048/31250)))/2048; // TODO: support variable clock, channel limit case
default: default:
break; break;
} }
@ -2649,12 +2683,8 @@ size_t DivEngine::getCurrentSubSong() {
return curSubSongIndex; return curSubSongIndex;
} }
unsigned char DivEngine::getSpeed1() { const DivGroovePattern& DivEngine::getSpeeds() {
return speed1; return speeds;
}
unsigned char DivEngine::getSpeed2() {
return speed2;
} }
float DivEngine::getHz() { float DivEngine::getHz() {
@ -3552,14 +3582,14 @@ void DivEngine::delSample(int index) {
BUSY_END; BUSY_END;
} }
void DivEngine::addOrder(bool duplicate, bool where) { void DivEngine::addOrder(int pos, bool duplicate, bool where) {
unsigned char order[DIV_MAX_CHANS]; unsigned char order[DIV_MAX_CHANS];
if (curSubSong->ordersLen>=(DIV_MAX_PATTERNS-1)) return; if (curSubSong->ordersLen>=(DIV_MAX_PATTERNS-1)) return;
memset(order,0,DIV_MAX_CHANS); memset(order,0,DIV_MAX_CHANS);
BUSY_BEGIN_SOFT; BUSY_BEGIN_SOFT;
if (duplicate) { if (duplicate) {
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
order[i]=curOrders->ord[i][curOrder]; order[i]=curOrders->ord[i][pos];
} }
} else { } else {
bool used[DIV_MAX_PATTERNS]; bool used[DIV_MAX_PATTERNS];
@ -3587,14 +3617,14 @@ void DivEngine::addOrder(bool duplicate, bool where) {
} else { // after current order } else { // after current order
saveLock.lock(); saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
for (int j=curSubSong->ordersLen; j>curOrder; j--) { for (int j=curSubSong->ordersLen; j>pos; j--) {
curOrders->ord[i][j]=curOrders->ord[i][j-1]; curOrders->ord[i][j]=curOrders->ord[i][j-1];
} }
curOrders->ord[i][curOrder+1]=order[i]; curOrders->ord[i][pos+1]=order[i];
} }
curSubSong->ordersLen++; curSubSong->ordersLen++;
saveLock.unlock(); saveLock.unlock();
curOrder++; if (pos<=curOrder) curOrder++;
if (playing && !freelance) { if (playing && !freelance) {
playSub(false); playSub(false);
} }
@ -3602,7 +3632,7 @@ void DivEngine::addOrder(bool duplicate, bool where) {
BUSY_END; BUSY_END;
} }
void DivEngine::deepCloneOrder(bool where) { void DivEngine::deepCloneOrder(int pos, bool where) {
unsigned char order[DIV_MAX_CHANS]; unsigned char order[DIV_MAX_CHANS];
if (curSubSong->ordersLen>=(DIV_MAX_PATTERNS-1)) return; if (curSubSong->ordersLen>=(DIV_MAX_PATTERNS-1)) return;
warnings=""; warnings="";
@ -3610,7 +3640,7 @@ void DivEngine::deepCloneOrder(bool where) {
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
bool didNotFind=true; bool didNotFind=true;
logD("channel %d",i); logD("channel %d",i);
order[i]=curOrders->ord[i][curOrder]; order[i]=curOrders->ord[i][pos];
// find free slot // find free slot
for (int j=0; j<DIV_MAX_PATTERNS; j++) { for (int j=0; j<DIV_MAX_PATTERNS; j++) {
logD("finding free slot in %d...",j); logD("finding free slot in %d...",j);
@ -3639,14 +3669,14 @@ void DivEngine::deepCloneOrder(bool where) {
} else { // after current order } else { // after current order
saveLock.lock(); saveLock.lock();
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
for (int j=curSubSong->ordersLen; j>curOrder; j--) { for (int j=curSubSong->ordersLen; j>pos; j--) {
curOrders->ord[i][j]=curOrders->ord[i][j-1]; curOrders->ord[i][j]=curOrders->ord[i][j-1];
} }
curOrders->ord[i][curOrder+1]=order[i]; curOrders->ord[i][pos+1]=order[i];
} }
curSubSong->ordersLen++; curSubSong->ordersLen++;
saveLock.unlock(); saveLock.unlock();
curOrder++; if (pos<=curOrder) curOrder++;
if (playing && !freelance) { if (playing && !freelance) {
playSub(false); playSub(false);
} }
@ -3654,17 +3684,18 @@ void DivEngine::deepCloneOrder(bool where) {
BUSY_END; BUSY_END;
} }
void DivEngine::deleteOrder() { void DivEngine::deleteOrder(int pos) {
if (curSubSong->ordersLen<=1) return; if (curSubSong->ordersLen<=1) return;
BUSY_BEGIN_SOFT; BUSY_BEGIN_SOFT;
saveLock.lock(); saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
for (int j=curOrder; j<curSubSong->ordersLen; j++) { for (int j=pos; j<curSubSong->ordersLen; j++) {
curOrders->ord[i][j]=curOrders->ord[i][j+1]; curOrders->ord[i][j]=curOrders->ord[i][j+1];
} }
} }
curSubSong->ordersLen--; curSubSong->ordersLen--;
saveLock.unlock(); saveLock.unlock();
if (curOrder>pos) curOrder--;
if (curOrder>=curSubSong->ordersLen) curOrder=curSubSong->ordersLen-1; if (curOrder>=curSubSong->ordersLen) curOrder=curSubSong->ordersLen-1;
if (playing && !freelance) { if (playing && !freelance) {
playSub(false); playSub(false);
@ -3672,40 +3703,46 @@ void DivEngine::deleteOrder() {
BUSY_END; BUSY_END;
} }
void DivEngine::moveOrderUp() { void DivEngine::moveOrderUp(int& pos) {
BUSY_BEGIN_SOFT; BUSY_BEGIN_SOFT;
if (curOrder<1) { if (pos<1) {
BUSY_END; BUSY_END;
return; return;
} }
saveLock.lock(); saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1]; curOrders->ord[i][pos]^=curOrders->ord[i][pos-1];
curOrders->ord[i][curOrder-1]^=curOrders->ord[i][curOrder]; curOrders->ord[i][pos-1]^=curOrders->ord[i][pos];
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1]; curOrders->ord[i][pos]^=curOrders->ord[i][pos-1];
} }
saveLock.unlock(); saveLock.unlock();
curOrder--; if (curOrder==pos) {
curOrder--;
}
pos--;
if (playing && !freelance) { if (playing && !freelance) {
playSub(false); playSub(false);
} }
BUSY_END; BUSY_END;
} }
void DivEngine::moveOrderDown() { void DivEngine::moveOrderDown(int& pos) {
BUSY_BEGIN_SOFT; BUSY_BEGIN_SOFT;
if (curOrder>=curSubSong->ordersLen-1) { if (pos>=curSubSong->ordersLen-1) {
BUSY_END; BUSY_END;
return; return;
} }
saveLock.lock(); saveLock.lock();
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1]; curOrders->ord[i][pos]^=curOrders->ord[i][pos+1];
curOrders->ord[i][curOrder+1]^=curOrders->ord[i][curOrder]; curOrders->ord[i][pos+1]^=curOrders->ord[i][pos];
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1]; curOrders->ord[i][pos]^=curOrders->ord[i][pos+1];
} }
saveLock.unlock(); saveLock.unlock();
curOrder++; if (curOrder==pos) {
curOrder++;
}
pos++;
if (playing && !freelance) { if (playing && !freelance) {
playSub(false); playSub(false);
} }
@ -4236,7 +4273,7 @@ void DivEngine::quitDispatch() {
clockDrift=0; clockDrift=0;
chans=0; chans=0;
playing=false; playing=false;
speedAB=false; curSpeed=0;
endOfSong=false; endOfSong=false;
ticks=0; ticks=0;
tempoAccum=0; tempoAccum=0;
@ -4415,6 +4452,8 @@ void DivEngine::preInit() {
String logPath=configPath+DIR_SEPARATOR_STR+"furnace.log"; String logPath=configPath+DIR_SEPARATOR_STR+"furnace.log";
startLogFile(logPath.c_str()); startLogFile(logPath.c_str());
logI("Furnace version " DIV_VERSION ".");
loadConf(); loadConf();
} }
@ -4472,6 +4511,9 @@ bool DivEngine::init() {
for (int i=0; i<64; i++) { for (int i=0; i<64; i++) {
vibTable[i]=127*sin(((double)i/64.0)*(2*M_PI)); 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++) { for (int i=0; i<4096; i++) {
reversePitchTable[i]=round(1024.0*pow(2.0,(2048.0-(double)i)/(12.0*128.0))); 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))); pitchTable[i]=round(1024.0*pow(2.0,((double)i-2048.0)/(12.0*128.0)));

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * 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_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false; #define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev136" #define DIV_VERSION "dev142"
#define DIV_ENGINE_VERSION 136 #define DIV_ENGINE_VERSION 142
// for imports // for imports
#define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02 #define DIV_VERSION_FC 0xff02
@ -100,6 +100,7 @@ struct DivChannelState {
unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR; unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR;
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff; bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp; bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp;
bool wentThroughNote, goneThroughNote;
int midiNote, curMidiNote, midiPitch; int midiNote, curMidiNote, midiPitch;
size_t midiAge; size_t midiAge;
@ -152,6 +153,8 @@ struct DivChannelState {
wasShorthandPorta(false), wasShorthandPorta(false),
noteOnInhibit(false), noteOnInhibit(false),
resetArp(false), resetArp(false),
wentThroughNote(false),
goneThroughNote(false),
midiNote(-1), midiNote(-1),
curMidiNote(-1), curMidiNote(-1),
midiPitch(-1), midiPitch(-1),
@ -337,7 +340,6 @@ class DivEngine {
bool playing; bool playing;
bool freelance; bool freelance;
bool shallStop, shallStopSched; bool shallStop, shallStopSched;
bool speedAB;
bool endOfSong; bool endOfSong;
bool consoleMode; bool consoleMode;
bool extValuePresent; bool extValuePresent;
@ -359,7 +361,7 @@ class DivEngine {
bool midiOutClock; bool midiOutClock;
int midiOutMode; int midiOutMode;
int softLockCount; 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 curSubSongIndex;
size_t bufferPos; size_t bufferPos;
double divider; double divider;
@ -368,7 +370,7 @@ class DivEngine {
int stepPlay; int stepPlay;
int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch; int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch;
unsigned char extValue, pendingMetroTick; unsigned char extValue, pendingMetroTick;
unsigned char speed1, speed2; DivGroovePattern speeds;
short tempoAccum; short tempoAccum;
DivStatusView view; DivStatusView view;
DivHaltPositions haltOn; DivHaltPositions haltOn;
@ -391,9 +393,9 @@ class DivEngine {
std::vector<String> midiOuts; std::vector<String> midiOuts;
std::vector<DivCommand> cmdStream; std::vector<DivCommand> cmdStream;
std::vector<DivInstrumentType> possibleInsTypes; std::vector<DivInstrumentType> possibleInsTypes;
static DivSysDef* sysDefs[256]; static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS];
static DivSystem sysFileMapFur[256]; static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS];
static DivSystem sysFileMapDMF[256]; static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS];
struct SamplePreview { struct SamplePreview {
double rate; double rate;
@ -413,6 +415,7 @@ class DivEngine {
} sPreview; } sPreview;
short vibTable[64]; short vibTable[64];
short tremTable[128];
int reversePitchTable[4096]; int reversePitchTable[4096];
int pitchTable[4096]; int pitchTable[4096];
char c163NameCS[1024]; char c163NameCS[1024];
@ -513,6 +516,7 @@ class DivEngine {
String decodeSysDesc(String desc); String decodeSysDesc(String desc);
// start fresh // start fresh
void createNew(const char* description, String sysName, bool inBase64=true); void createNew(const char* description, String sysName, bool inBase64=true);
void createNewFromDefaults();
// load a file. // load a file.
bool load(unsigned char* f, size_t length); bool load(unsigned char* f, size_t length);
// save as .dmf. // save as .dmf.
@ -524,7 +528,12 @@ class DivEngine {
// specify system to build ROM for. // specify system to build ROM for.
SafeWriter* buildROM(int sys); SafeWriter* buildROM(int sys);
// dump to VGM. // dump to VGM.
SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false); // set trailingTicks to:
// - 0 to add one tick of trailing
// - x to add x+1 ticks of trailing
// - -1 to auto-determine trailing
// - -2 to add a whole loop of trailing
SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false, int trailingTicks=-1);
// dump to ZSM. // dump to ZSM.
SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true); SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true);
// dump command stream. // dump command stream.
@ -581,6 +590,9 @@ class DivEngine {
void setConf(String key, const char* value); void setConf(String key, const char* value);
void setConf(String key, String value); void setConf(String key, String value);
// get whether config value exists
bool hasConf(String key);
// calculate base frequency/period // calculate base frequency/period
double calcBaseFreq(double clock, double divider, int note, bool period); double calcBaseFreq(double clock, double divider, int note, bool period);
@ -729,11 +741,8 @@ class DivEngine {
// get current subsong // get current subsong
size_t getCurrentSubSong(); size_t getCurrentSubSong();
// get speed 1 // get speeds
unsigned char getSpeed1(); const DivGroovePattern& getSpeeds();
// get speed 2
unsigned char getSpeed2();
// get Hz // get Hz
float getHz(); float getHz();
@ -813,19 +822,19 @@ class DivEngine {
void delSample(int index); void delSample(int index);
// add order // add order
void addOrder(bool duplicate, bool where); void addOrder(int pos, bool duplicate, bool where);
// deep clone orders // deep clone orders
void deepCloneOrder(bool where); void deepCloneOrder(int pos, bool where);
// delete order // delete order
void deleteOrder(); void deleteOrder(int pos);
// move order up // move order up
void moveOrderUp(); void moveOrderUp(int& pos);
// move order down // move order down
void moveOrderDown(); void moveOrderDown(int& pos);
// move thing up // move thing up
bool moveInsUp(int which); bool moveInsUp(int which);
@ -1064,7 +1073,6 @@ class DivEngine {
freelance(false), freelance(false),
shallStop(false), shallStop(false),
shallStopSched(false), shallStopSched(false),
speedAB(false),
endOfSong(false), endOfSong(false),
consoleMode(false), consoleMode(false),
extValuePresent(false), extValuePresent(false),
@ -1098,6 +1106,7 @@ class DivEngine {
nextSpeed(3), nextSpeed(3),
elapsedBars(0), elapsedBars(0),
elapsedBeats(0), elapsedBeats(0),
curSpeed(0),
curSubSongIndex(0), curSubSongIndex(0),
bufferPos(0), bufferPos(0),
divider(60), divider(60),
@ -1115,8 +1124,6 @@ class DivEngine {
globalPitch(0), globalPitch(0),
extValue(0), extValue(0),
pendingMetroTick(0), pendingMetroTick(0),
speed1(3),
speed2(3),
tempoAccum(0), tempoAccum(0),
view(DIV_STATUS_NOTHING), view(DIV_STATUS_NOTHING),
haltOn(DIV_HALT_NONE), haltOn(DIV_HALT_NONE),
@ -1158,13 +1165,14 @@ class DivEngine {
memset(dispatchOfChan,0,DIV_MAX_CHANS*sizeof(int)); memset(dispatchOfChan,0,DIV_MAX_CHANS*sizeof(int));
memset(sysOfChan,0,DIV_MAX_CHANS*sizeof(int)); memset(sysOfChan,0,DIV_MAX_CHANS*sizeof(int));
memset(vibTable,0,64*sizeof(short)); memset(vibTable,0,64*sizeof(short));
memset(tremTable,0,128*sizeof(short));
memset(reversePitchTable,0,4096*sizeof(int)); memset(reversePitchTable,0,4096*sizeof(int));
memset(pitchTable,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(walked,0,8192);
memset(oscBuf,0,DIV_MAX_OUTPUTS*(sizeof(float*))); memset(oscBuf,0,DIV_MAX_OUTPUTS*(sizeof(float*)));
for (int i=0; i<256; i++) { for (int i=0; i<DIV_MAX_CHIP_DEFS; i++) {
sysFileMapFur[i]=DIV_SYSTEM_NULL; sysFileMapFur[i]=DIV_SYSTEM_NULL;
sysFileMapDMF[i]=DIV_SYSTEM_NULL; sysFileMapDMF[i]=DIV_SYSTEM_NULL;
} }

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -83,7 +83,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
} }
ds.version=(unsigned char)reader.readC(); ds.version=(unsigned char)reader.readC();
logI("module version %d (0x%.2x)",ds.version,ds.version); logI("module version %d (0x%.2x)",ds.version,ds.version);
if (ds.version>0x1a) { if (ds.version>0x1b) {
logE("this version is not supported by Furnace yet!"); logE("this version is not supported by Furnace yet!");
lastError="this version is not supported by Furnace yet"; lastError="this version is not supported by Furnace yet";
delete[] file; delete[] file;
@ -219,14 +219,15 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
} }
ds.subsong[0]->timeBase=reader.readC(); 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) { 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]->pal=reader.readC();
ds.subsong[0]->hz=(ds.subsong[0]->pal)?60:50; ds.subsong[0]->hz=(ds.subsong[0]->pal)?60:50;
ds.subsong[0]->customTempo=reader.readC(); ds.subsong[0]->customTempo=reader.readC();
} else { } else {
ds.subsong[0]->speed2=ds.subsong[0]->speed1; ds.subsong[0]->speeds.len=1;
} }
if (ds.version>0x0a) { if (ds.version>0x0a) {
String hz=reader.readString(3); String hz=reader.readString(3);
@ -827,6 +828,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
for (int i=0; i<ds.sampleLen; i++) { for (int i=0; i<ds.sampleLen; i++) {
DivSample* sample=new DivSample; DivSample* sample=new DivSample;
int length=reader.readI(); int length=reader.readI();
int cutStart=0;
int cutEnd=length;
int pitch=5; int pitch=5;
int vol=50; int vol=50;
short* data; short* data;
@ -866,6 +869,29 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
sample->depth=DIV_SAMPLE_DEPTH_YMZ_ADPCM; sample->depth=DIV_SAMPLE_DEPTH_YMZ_ADPCM;
} }
} }
if (ds.version>=0x1b) {
// 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 (cutEnd<cutStart) {
logE("cutEnd %d is before cutStart %d. what's going on?",cutEnd,cutStart);
lastError="file is corrupt or unreadable at samples";
delete[] file;
return false;
}
}
if (length>0) { if (length>0) {
if (ds.version>0x08) { if (ds.version>0x08) {
if (ds.version<0x0b) { if (ds.version<0x0b) {
@ -876,6 +902,19 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
data=new short[length]; data=new short[length];
reader.read(data,length*2); reader.read(data,length*2);
} }
if (ds.version>0x1b) {
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 #ifdef TA_BIG_ENDIAN
// convert to big-endian // convert to big-endian
@ -1716,6 +1755,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version<130) { if (ds.version<130) {
ds.oldArpStrategy=true; ds.oldArpStrategy=true;
} }
if (ds.version<138) {
ds.brokenPortaLegato=true;
}
ds.isDMF=false; ds.isDMF=false;
reader.readS(); // reserved reader.readS(); // reserved
@ -1739,8 +1781,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
reader.readI(); reader.readI();
subSong->timeBase=reader.readC(); subSong->timeBase=reader.readC();
subSong->speed1=reader.readC(); subSong->speeds.len=2;
subSong->speed2=reader.readC(); subSong->speeds.val[0]=reader.readC();
subSong->speeds.val[1]=reader.readC();
subSong->arpLen=reader.readC(); subSong->arpLen=reader.readC();
subSong->hz=reader.readF(); subSong->hz=reader.readF();
subSong->pal=(subSong->hz>=53); 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>=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<grooveCount; i++) {
DivGroovePattern gp;
gp.len=reader.readC();
for (int j=0; j<16; j++) {
gp.val[j]=reader.readC();
}
ds.grooves.push_back(gp);
}
}
// read system flags // read system flags
if (ds.version>=119) { if (ds.version>=119) {
logD("reading chip flags..."); logD("reading chip flags...");
@ -2279,8 +2348,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
subSong=ds.subsong[i+1]; subSong=ds.subsong[i+1];
subSong->timeBase=reader.readC(); subSong->timeBase=reader.readC();
subSong->speed1=reader.readC(); subSong->speeds.len=2;
subSong->speed2=reader.readC(); subSong->speeds.val[0]=reader.readC();
subSong->speeds.val[1]=reader.readC();
subSong->arpLen=reader.readC(); subSong->arpLen=reader.readC();
subSong->hz=reader.readF(); subSong->hz=reader.readF();
subSong->pal=(subSong->hz>=53); subSong->pal=(subSong->hz>=53);
@ -2328,6 +2398,13 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
for (int i=0; i<tchans; i++) { for (int i=0; i<tchans; i++) {
subSong->chanShortName[i]=reader.readString(); subSong->chanShortName[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; i<ds.systemLen; i++) {
switch (ds.system[i]) {
case DIV_SYSTEM_YM2612:
case DIV_SYSTEM_YM2612_EXT:
case DIV_SYSTEM_YM2612_DUALPCM:
case DIV_SYSTEM_YM2612_DUALPCM_EXT:
case DIV_SYSTEM_YM2612_CSM:
ds.systemVol[i]/=2.0;
break;
case DIV_SYSTEM_SMS:
case DIV_SYSTEM_T6W28:
case DIV_SYSTEM_OPLL:
case DIV_SYSTEM_OPLL_DRUMS:
ds.systemVol[i]/=1.5;
break;
case DIV_SYSTEM_X1_010:
ds.systemVol[i]/=4.0;
break;
default:
break;
}
}
}
if (active) quitDispatch(); if (active) quitDispatch();
BUSY_BEGIN_SOFT; BUSY_BEGIN_SOFT;
saveLock.lock(); saveLock.lock();
@ -2920,7 +3023,6 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
if (fxVal>0x20 && ds.name!="klisje paa klisje") { if (fxVal>0x20 && ds.name!="klisje paa klisje") {
writeFxCol(0xf0,fxVal); writeFxCol(0xf0,fxVal);
} else { } else {
writeFxCol(0x09,fxVal);
writeFxCol(0x0f,fxVal); writeFxCol(0x0f,fxVal);
} }
break; break;
@ -3399,8 +3501,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
ds.subsong[0]->pal=true; ds.subsong[0]->pal=true;
ds.subsong[0]->customTempo=true; ds.subsong[0]->customTempo=true;
ds.subsong[0]->pat[3].effectCols=3; ds.subsong[0]->pat[3].effectCols=3;
ds.subsong[0]->speed1=3; ds.subsong[0]->speeds.val[0]=3;
ds.subsong[0]->speed2=3; ds.subsong[0]->speeds.len=1;
int lastIns[4]; int lastIns[4];
int lastNote[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; ds.subsong[0]->orders.ord[j][i]=i;
DivPattern* p=ds.subsong[0]->pat[j].getPattern(i,true); DivPattern* p=ds.subsong[0]->pat[j].getPattern(i,true);
if (j==3 && seq[i].speed) { 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][7]=seq[i].speed;
p->data[0][8]=0x0f;
p->data[0][9]=seq[i].speed;
} }
bool ignoreNext=false; bool ignoreNext=false;
@ -4307,8 +4407,9 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeI(0); w->writeI(0);
w->writeC(subSong->timeBase); w->writeC(subSong->timeBase);
w->writeC(subSong->speed1); // these are for compatibility
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->writeC(subSong->arpLen);
w->writeF(subSong->hz); w->writeF(subSong->hz);
w->writeS(subSong->patLen); w->writeS(subSong->patLen);
@ -4489,6 +4590,27 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
} }
w->writeC(song.patchbayAuto); 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(); blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET); w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4); w->writeI(blockEndSeek-blockStartSeek-4);
@ -4503,8 +4625,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeI(0); w->writeI(0);
w->writeC(subSong->timeBase); w->writeC(subSong->timeBase);
w->writeC(subSong->speed1); w->writeC(subSong->speeds.val[0]);
w->writeC(subSong->speed2); w->writeC((subSong->speeds.len>=2)?subSong->speeds.val[1]:subSong->speeds.val[0]);
w->writeC(subSong->arpLen); w->writeC(subSong->arpLen);
w->writeF(subSong->hz); w->writeF(subSong->hz);
w->writeS(subSong->patLen); w->writeS(subSong->patLen);
@ -4543,6 +4665,12 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeString(subSong->chanShortName[i],false); 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(); blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET); w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4); w->writeI(blockEndSeek-blockStartSeek-4);
@ -4798,8 +4926,8 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeC(curSubSong->hilightB); w->writeC(curSubSong->hilightB);
w->writeC(curSubSong->timeBase); w->writeC(curSubSong->timeBase);
w->writeC(curSubSong->speed1); w->writeC(curSubSong->speeds.val[0]);
w->writeC(curSubSong->speed2); w->writeC((curSubSong->speeds.len>=2)?curSubSong->speeds.val[1]:curSubSong->speeds.val[0]);
w->writeC(curSubSong->pal); w->writeC(curSubSong->pal);
w->writeC(curSubSong->customTempo); w->writeC(curSubSong->customTempo);
char customHz[4]; char customHz[4];
@ -4823,6 +4951,14 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
addWarning("only the currently selected subsong will be saved"); 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) { if (curSubSong->virtualTempoD!=curSubSong->virtualTempoN) {
addWarning(".dmf format does not support virtual tempo"); addWarning(".dmf format does not support virtual tempo");
} }

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -924,6 +924,10 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
break; break;
case DIV_INS_POKEMINI: case DIV_INS_POKEMINI:
break; break;
case DIV_INS_SM8521:
checkForWL=true;
if (ws.enabled) featureWS=true;
break;
case DIV_INS_MAX: case DIV_INS_MAX:
break; break;

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -78,6 +78,7 @@ enum DivInstrumentType: unsigned short {
DIV_INS_K007232=45, DIV_INS_K007232=45,
DIV_INS_GA20=46, DIV_INS_GA20=46,
DIV_INS_POKEMINI=47, DIV_INS_POKEMINI=47,
DIV_INS_SM8521=48,
DIV_INS_MAX, DIV_INS_MAX,
DIV_INS_NULL DIV_INS_NULL
}; };

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * 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) { if (delay>0) {
delay--; delay--;
had=false; if (!linger) had=false;
return; return;
} }
if (began && source.delay>0) { if (began && source.delay>0) {
@ -523,4 +523,4 @@ DivMacroStruct* DivMacroInt::structByName(const String& name) {
return NULL; return NULL;
} }
#undef CONSIDER #undef CONSIDER

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -18,6 +18,7 @@
*/ */
#include "../dispatch.h" #include "../dispatch.h"
#include "../../ta-log.h"
void DivDispatch::acquire(short** buf, size_t len) { void DivDispatch::acquire(short** buf, size_t len) {
} }
@ -121,7 +122,8 @@ void DivDispatch::notifyWaveChange(int ins) {
} }
void DivDispatch::notifyInsDeletion(void* ins) { void DivDispatch::notifyInsDeletion(void* ins) {
logE("notifyInsDeletion NOT implemented!");
abort();
} }
void DivDispatch::notifyPlaybackStop() { void DivDispatch::notifyPlaybackStop() {

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -89,7 +89,7 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
chan[i].audSub-=AMIGA_DIVIDER; chan[i].audSub-=AMIGA_DIVIDER;
if (chan[i].audSub<0) { if (chan[i].audSub<0) {
if (chan[i].useWave) { if (chan[i].useWave) {
writeAudDat(chan[i].ws.output[chan[i].audPos++]^0x80); writeAudDat(chan[i].ws.output[(chan[i].audPos++)&255]^0x80);
if (chan[i].audPos>=(unsigned int)(chan[i].audLen<<1)) { if (chan[i].audPos>=(unsigned int)(chan[i].audLen<<1)) {
chan[i].audPos=0; chan[i].audPos=0;
} }

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -152,7 +152,9 @@ void DivPlatformArcade::tick(bool sysTick) {
for (int j=0; j<4; j++) { for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j]; unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (KVS(i,j)) { if (!op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else if (KVS(i,j)) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[i].outVol&0x7f,127)); rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[i].outVol&0x7f,127));
} else { } else {
rWrite(baseAddr+ADDR_TL,op.tl); rWrite(baseAddr+ADDR_TL,op.tl);
@ -235,7 +237,7 @@ void DivPlatformArcade::tick(bool sysTick) {
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) { if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j]; unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isMuted[i]) { if (isMuted[i] || !op.enable) {
rWrite(baseAddr+ADDR_TL,127); rWrite(baseAddr+ADDR_TL,127);
} else { } else {
if (KVS(i,j)) { if (KVS(i,j)) {
@ -296,7 +298,9 @@ void DivPlatformArcade::tick(bool sysTick) {
} }
if (m.tl.had) { if (m.tl.had) {
op.tl=127-m.tl.val; op.tl=127-m.tl.val;
if (KVS(i,j)) { if (!op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else if (KVS(i,j)) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[i].outVol&0x7f,127)); rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[i].outVol&0x7f,127));
} else { } else {
rWrite(baseAddr+ADDR_TL,op.tl); rWrite(baseAddr+ADDR_TL,op.tl);
@ -319,27 +323,6 @@ void DivPlatformArcade::tick(bool sysTick) {
rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); 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++) { for (int i=0; i<256; i++) {
@ -349,6 +332,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++) { for (int i=0; i<8; i++) {
if (chan[i].freqChanged) { if (chan[i].freqChanged) {
chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].pitch2; chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].pitch2;
@ -363,14 +364,37 @@ void DivPlatformArcade::tick(bool sysTick) {
if (chan[i].freq>=(95<<6)) chan[i].freq=(95<<6)-1; if (chan[i].freq>=(95<<6)) chan[i].freq=(95<<6)-1;
immWrite(i+0x28,hScale(chan[i].freq>>6)); immWrite(i+0x28,hScale(chan[i].freq>>6));
immWrite(i+0x30,chan[i].freq<<2); immWrite(i+0x30,chan[i].freq<<2);
hardResetElapsed+=2;
chan[i].freqChanged=false; 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); immWrite(0x08,(chan[i].opMask<<3)|i);
hardResetElapsed++;
chan[i].opMaskChanged=false; chan[i].opMaskChanged=false;
chan[i].keyOn=false; chan[i].keyOn=false;
} }
} }
// hard reset handling
if (mustHardReset) {
for (unsigned int i=hardResetElapsed; i<hardResetCycles; i++) {
immWrite(0x1f,i&0xff);
}
for (int i=0; i<8; i++) {
if ((chan[i].keyOn || chan[i].opMaskChanged) && chan[i].hardReset) {
// restore SL/RR
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
immWrite(0x08,(chan[i].opMask<<3)|i);
chan[i].opMaskChanged=false;
chan[i].keyOn=false;
}
}
}
} }
void DivPlatformArcade::muteChannel(int ch, bool mute) { void DivPlatformArcade::muteChannel(int ch, bool mute) {
@ -382,53 +406,59 @@ void DivPlatformArcade::muteChannel(int ch, bool mute) {
} }
} }
void DivPlatformArcade::commitState(int ch, DivInstrument* ins) {
if (chan[ch].insChanged) {
chan[ch].state=ins->fm;
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 (!op.enable) {
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)|(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) { int DivPlatformArcade::dispatch(DivCommand c) {
switch (c.cmd) { switch (c.cmd) {
case DIV_CMD_NOTE_ON: { case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); 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); chan[c.chan].macroInit(ins);
if (!chan[c.chan].std.vol.will) { if (!chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol; chan[c.chan].outVol=chan[c.chan].vol;
} }
for (int i=0; i<4; i++) { commitState(c.chan,ins);
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));
}
chan[c.chan].insChanged=false; chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
@ -462,7 +492,9 @@ int DivPlatformArcade::dispatch(DivCommand c) {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (KVS(c.chan,i)) { if (!op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else if (KVS(c.chan,i)) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127)); rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else { } else {
rWrite(baseAddr+ADDR_TL,op.tl); rWrite(baseAddr+ADDR_TL,op.tl);
@ -521,6 +553,11 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_LEGATO: { 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].baseFreq=NOTE_LINEAR(c.value);
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
break; break;
@ -559,7 +596,9 @@ int DivPlatformArcade::dispatch(DivCommand c) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.tl=c.value2; op.tl=c.value2;
if (KVS(c.chan,c.value)) { if (!op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else if (KVS(c.chan,c.value)) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127)); rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else { } else {
rWrite(baseAddr+ADDR_TL,op.tl); rWrite(baseAddr+ADDR_TL,op.tl);
@ -766,7 +805,9 @@ void DivPlatformArcade::forceIns() {
for (int j=0; j<4; j++) { for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j]; unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator op=chan[i].state.op[j]; DivInstrumentFM::Operator op=chan[i].state.op[j];
if (KVS(i,j)) { if (!op.enable) {
rWrite(baseAddr+ADDR_TL,127);
} else if (KVS(i,j)) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[i].outVol&0x7f,127)); rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[i].outVol&0x7f,127));
} else { } else {
rWrite(baseAddr+ADDR_TL,op.tl); rWrite(baseAddr+ADDR_TL,op.tl);
@ -800,6 +841,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) { void* DivPlatformArcade::getChanState(int ch) {
return &chan[ch]; return &chan[ch];
} }

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * 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 octave(int freq);
int toFreq(int freq); int toFreq(int freq);
void commitState(int ch, DivInstrument* ins);
void acquire_nuked(short** buf, size_t len); void acquire_nuked(short** buf, size_t len);
void acquire_ymfm(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); void muteChannel(int ch, bool mute);
DivMacroInt* getChanMacroInt(int ch); DivMacroInt* getChanMacroInt(int ch);
void notifyInsChange(int ins); void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
void setFlags(const DivConfig& flags); void setFlags(const DivConfig& flags);
int getOutputCount(); int getOutputCount();
void setYMFM(bool use); void setYMFM(bool use);

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/** /**
* Furnace Tracker - multi-system chiptune tracker * 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 * 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 * it under the terms of the GNU General Public License as published by

Some files were not shown because too many files have changed in this diff Show more