Merge branch 'master' of https://github.com/tildearrow/furnace into ymf289b
11
.github/workflows/build.yml
vendored
|
@ -11,7 +11,7 @@ defaults:
|
|||
shell: bash
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -20,8 +20,8 @@ jobs:
|
|||
config:
|
||||
- { name: 'Windows MSVC x86', os: windows-latest, compiler: msvc, arch: x86 }
|
||||
- { name: 'Windows MSVC x86_64', os: windows-latest, compiler: msvc, arch: x86_64 }
|
||||
- { name: 'Windows MinGW x86', os: ubuntu-20.04, compiler: mingw, arch: x86 }
|
||||
- { name: 'Windows MinGW x86_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 }
|
||||
#- { name: 'Windows MinGW x86', os: ubuntu-20.04, compiler: mingw, arch: x86 }
|
||||
#- { name: 'Windows MinGW x86_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 }
|
||||
- { name: 'macOS x86_64', os: macos-latest, arch: x86_64 }
|
||||
- { name: 'macOS ARM', os: macos-latest, arch: arm64 }
|
||||
- { name: 'Linux x86_64', os: ubuntu-18.04, arch: x86_64 }
|
||||
|
@ -277,7 +277,10 @@ jobs:
|
|||
|
||||
cp -v ../LICENSE LICENSE.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
|
||||
|
||||
popd
|
||||
|
|
|
@ -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(WITH_DEMOS "Install demo songs" ON)
|
||||
option(WITH_INSTRUMENTS "Install instruments" ON)
|
||||
option(WITH_WAVETABLES "Install wavetables" ON)
|
||||
|
||||
set(DEPENDENCIES_INCLUDE_DIRS "")
|
||||
|
||||
|
@ -365,6 +366,7 @@ src/engine/platform/sound/sn76496.cpp
|
|||
src/engine/platform/sound/ay8910.cpp
|
||||
src/engine/platform/sound/saa1099.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/timing.c
|
||||
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__ST.cc
|
||||
|
||||
src/engine/platform/sound/c64_fp/array.cpp
|
||||
src/engine/platform/sound/c64_fp/Dac.cpp
|
||||
src/engine/platform/sound/c64_fp/EnvelopeGenerator.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/sm8521.c
|
||||
|
||||
src/engine/platform/oplAInterface.cpp
|
||||
src/engine/platform/ym2608Interface.cpp
|
||||
src/engine/platform/ym2610Interface.cpp
|
||||
|
@ -530,6 +535,7 @@ src/engine/platform/pokemini.cpp
|
|||
src/engine/platform/pong.cpp
|
||||
src/engine/platform/vic20.cpp
|
||||
src/engine/platform/vrc6.cpp
|
||||
src/engine/platform/es5506.cpp
|
||||
src/engine/platform/scc.cpp
|
||||
src/engine/platform/ymz280b.cpp
|
||||
src/engine/platform/namcowsg.cpp
|
||||
|
@ -537,6 +543,7 @@ src/engine/platform/rf5c68.cpp
|
|||
src/engine/platform/snes.cpp
|
||||
src/engine/platform/k007232.cpp
|
||||
src/engine/platform/ga20.cpp
|
||||
src/engine/platform/sm8521.cpp
|
||||
src/engine/platform/pcmdac.cpp
|
||||
src/engine/platform/dummy.cpp
|
||||
)
|
||||
|
@ -566,6 +573,7 @@ extern/imgui_patched/misc/cpp/imgui_stdlib.cpp
|
|||
extern/igfd/ImGuiFileDialog.cpp
|
||||
|
||||
src/gui/plot_nolerp.cpp
|
||||
|
||||
src/gui/font_exo.cpp
|
||||
src/gui/font_liberationSans.cpp
|
||||
src/gui/font_mononoki.cpp
|
||||
|
@ -576,12 +584,24 @@ src/gui/font_ptMono.cpp
|
|||
src/gui/font_unifont.cpp
|
||||
src/gui/font_icon.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/fileDialog.cpp
|
||||
|
||||
src/gui/intConst.cpp
|
||||
src/gui/guiConst.cpp
|
||||
|
||||
src/gui/introTune.cpp
|
||||
|
||||
src/gui/about.cpp
|
||||
src/gui/channels.cpp
|
||||
src/gui/chanOsc.cpp
|
||||
|
@ -596,7 +616,9 @@ src/gui/editControls.cpp
|
|||
src/gui/effectList.cpp
|
||||
src/gui/findReplace.cpp
|
||||
src/gui/gradient.cpp
|
||||
src/gui/grooves.cpp
|
||||
src/gui/insEdit.cpp
|
||||
src/gui/intro.cpp
|
||||
src/gui/log.cpp
|
||||
src/gui/mixer.cpp
|
||||
src/gui/midiMap.cpp
|
||||
|
@ -613,6 +635,7 @@ src/gui/scaling.cpp
|
|||
src/gui/settings.cpp
|
||||
src/gui/songInfo.cpp
|
||||
src/gui/songNotes.cpp
|
||||
src/gui/speed.cpp
|
||||
src/gui/spoiler.cpp
|
||||
src/gui/stats.cpp
|
||||
src/gui/subSongs.cpp
|
||||
|
@ -620,6 +643,7 @@ src/gui/sysConf.cpp
|
|||
src/gui/sysEx.cpp
|
||||
src/gui/sysManager.cpp
|
||||
src/gui/sysPicker.cpp
|
||||
src/gui/tutorial.cpp
|
||||
src/gui/util.cpp
|
||||
src/gui/waveEdit.cpp
|
||||
src/gui/volMeter.cpp
|
||||
|
@ -638,8 +662,6 @@ if (APPLE)
|
|||
endif()
|
||||
|
||||
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(linux/input.h LINUX_INPUT_FOUND)
|
||||
CHECK_INCLUDE_FILE(linux/kd.h LINUX_KD_FOUND)
|
||||
|
@ -756,9 +778,7 @@ if (WARNINGS_ARE_ERRORS)
|
|||
)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
add_executable(furnace WIN32 ${USED_SOURCES})
|
||||
elseif(ANDROID AND NOT TERMUX)
|
||||
if(ANDROID AND NOT TERMUX)
|
||||
add_library(furnace SHARED ${USED_SOURCES})
|
||||
else()
|
||||
add_executable(furnace ${USED_SOURCES})
|
||||
|
@ -795,6 +815,9 @@ if (NOT ANDROID OR TERMUX)
|
|||
if (WITH_INSTRUMENTS)
|
||||
install(DIRECTORY instruments DESTINATION ${CMAKE_INSTALL_DATADIR}/furnace)
|
||||
endif()
|
||||
if (WITH_WAVETABLES)
|
||||
install(DIRECTORY wavetables DESTINATION ${CMAKE_INSTALL_DATADIR}/furnace)
|
||||
endif()
|
||||
foreach(num 16 32 64 128 256 512)
|
||||
set(res ${num}x${num})
|
||||
install(FILES res/icon.iconset/icon_${res}.png RENAME furnace.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/${res}/apps)
|
||||
|
|
15
README.md
|
@ -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 |
|
||||
| `WITH_DEMOS` | `ON` | Install demo songs on `make install` |
|
||||
| `WITH_INSTRUMENTS` | `ON` | Install demo instruments on `make install` |
|
||||
| `WITH_WAVETABLES` | `ON` | Install wavetables on `make install` |
|
||||
|
||||
## console usage
|
||||
|
||||
|
@ -282,17 +283,13 @@ two possibilities:
|
|||
- the recommended way is by creating the "Sample" type instrument and assigning a sample to it.
|
||||
- otherwise you may employ the DefleMask-compatible method, using `17xx` effect.
|
||||
|
||||
> my .dmf song sounds very odd at a certain point
|
||||
> my .dmf song sounds odd at a certain point
|
||||
|
||||
file a bug report. use the Issues page. it's probably another playback inaccuracy.
|
||||
|
||||
> my .dmf song sounds correct, but it doesn't in DefleMask
|
||||
|
||||
file a bug report **here**. it still is a playback inaccuracy.
|
||||
Furnace's .dmf compatibility isn't perfect and it's mostly because DefleMask does things different.
|
||||
|
||||
> my song sounds terrible after saving as .dmf!
|
||||
|
||||
the DefleMask format has several limitations. save in Furnace song format instead (.fur).
|
||||
you should only save as .dmf if you're really sure, because the DefleMask format has several limitations. save in Furnace song format instead (.fur).
|
||||
|
||||
> how do I solo channels?
|
||||
|
||||
|
@ -301,7 +298,7 @@ right click on the channel name.
|
|||
---
|
||||
# footnotes
|
||||
|
||||
copyright (C) 2021-2022 tildearrow and contributors.
|
||||
copyright (C) 2021-2023 tildearrow and contributors.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
|
||||
|
||||
|
@ -310,4 +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.
|
||||
|
||||
|
||||
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
BIN
demos/arcade/Salamander_Starfield.fur
Normal file
BIN
demos/arcade/iji_tor_segaxboard.fur
Normal file
BIN
demos/genesis/Plok_Beach.fur
Normal file
BIN
demos/misc/Miami_Ice_Sharp_X1.fur
Normal file
BIN
demos/misc/Wicked_Express_X68000.fur
Normal file
BIN
demos/msx/Gyruss_Stage_2.fur
Normal file
BIN
demos/multichip/Boomer Kuwanger.fur
Normal file
BIN
demos/nes/Super_Space_Invaders_Title.fur
Normal file
BIN
demos/opl/e3m2_opl3.fur
Normal file
BIN
demos/snes/amalgam.fur
Normal file
BIN
demos/specs2/Tim_Follin.fur
Normal file
9
extern/Nuked-OPLL/opll.c
vendored
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Nuke.YKT
|
||||
* Copyright (C) 2019-2023 Nuke.YKT
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -17,7 +17,7 @@
|
|||
* siliconpr0n.org(digshadow, John McMaster):
|
||||
* VRC VII decap and die shot.
|
||||
*
|
||||
* version: 1.0.1
|
||||
* version: 1.0.2
|
||||
*/
|
||||
|
||||
#include <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 },{ 0x05, 0x00 },{ 0x00, 0x00 },{ 0x0f, 0x00 },{ 0x08, 0x00 },{ 0x05, 0x00 },{ 0x09, 0x00 } },
|
||||
{ 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0f },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x0d } },
|
||||
{ 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x06 },{ 0x00, 0x08 } },
|
||||
{ 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0d },{ 0x00, 0x08 },{ 0x00, 0x04 },{ 0x00, 0x08 } },
|
||||
{ 0x00, 0x00, 0x00, 0x00,{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x00 },{ 0x00, 0x01 },{ 0x00, 0x00 },{ 0x00, 0x0a },{ 0x00, 0x0a },{ 0x00, 0x05 },{ 0x00, 0x05 } }
|
||||
};
|
||||
|
||||
|
@ -1018,6 +1018,9 @@ static void OPLL_Operator(opll_t *chip) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!(chip->rm_enable & 0x80))
|
||||
routput = 0;
|
||||
|
||||
chip->ch_out = ismod1 ? routput : (output>>3);
|
||||
|
||||
if (!ismod1) {
|
||||
|
|
29
extern/nfd-modified/src/nfd_win.cpp
vendored
|
@ -30,6 +30,9 @@
|
|||
// hack I know
|
||||
#include "../../../src/utfutils.h"
|
||||
|
||||
// hack 2...
|
||||
#include "../../../src/ta-log.h"
|
||||
|
||||
class NFDWinEvents: public IFileDialogEvents {
|
||||
nfdselcallback_t selCallback;
|
||||
size_t refCount;
|
||||
|
@ -38,21 +41,21 @@ class NFDWinEvents: public IFileDialogEvents {
|
|||
}
|
||||
public:
|
||||
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) {
|
||||
printf("QueryInterface called DAMN IT\n");
|
||||
logV("%p: QueryInterface called DAMN IT",(const void*)this);
|
||||
*ppv=NULL;
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG) AddRef() {
|
||||
printf("AddRef() called\n");
|
||||
logV("%p: AddRef() called",(const void*)this);
|
||||
return InterlockedIncrement(&refCount);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG) Release() {
|
||||
printf("Release() called\n");
|
||||
logV("%p: Release() called",(const void*)this);
|
||||
LONG ret=InterlockedDecrement(&refCount);
|
||||
if (ret==0) {
|
||||
printf("Destroying the final object.\n");
|
||||
logV("%p: Destroying the final object.",(const void*)this);
|
||||
delete this;
|
||||
}
|
||||
return ret;
|
||||
|
@ -67,30 +70,40 @@ class NFDWinEvents: public IFileDialogEvents {
|
|||
|
||||
IFACEMETHODIMP OnSelectionChange(IFileDialog* dialog) {
|
||||
// Get the file name
|
||||
logV("%p: OnSelectionChange() called",(const void*)this);
|
||||
::IShellItem *shellItem(NULL);
|
||||
logV("%p: GetCurrentSelection",(const void*)this);
|
||||
HRESULT result = dialog->GetCurrentSelection(&shellItem);
|
||||
if ( !SUCCEEDED(result) )
|
||||
{
|
||||
printf("failure!\n");
|
||||
logV("%p: failure!",(const void*)this);
|
||||
return S_OK;
|
||||
}
|
||||
wchar_t *filePath(NULL);
|
||||
result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath);
|
||||
if ( !SUCCEEDED(result) )
|
||||
{
|
||||
printf("GDN failure!\n");
|
||||
logV("%p: GDN failure!",(const void*)this);
|
||||
shellItem->Release();
|
||||
return S_OK;
|
||||
}
|
||||
std::string utf8FilePath=utf16To8(filePath);
|
||||
if (selCallback!=NULL) selCallback(utf8FilePath.c_str());
|
||||
printf("I got you for a value of %s\n",utf8FilePath.c_str());
|
||||
if (selCallback!=NULL) {
|
||||
logV("%p: calling back.",(const void*)this);
|
||||
selCallback(utf8FilePath.c_str());
|
||||
logV("%p: end of callback",(const void*)this);
|
||||
} else {
|
||||
logV("%p: no callback.",(const void*)this);
|
||||
}
|
||||
logV("%p: I got you for a value of %s",(const void*)this,utf8FilePath.c_str());
|
||||
shellItem->Release();
|
||||
logV("%p: shellItem->Release()",(const void*)this);
|
||||
return S_OK;
|
||||
}
|
||||
NFDWinEvents(nfdselcallback_t callback):
|
||||
selCallback(callback),
|
||||
refCount(1) {
|
||||
logV("%p: CONSTRUCT!",(const void*)this);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
3
extern/opn/ym3438.c
vendored
|
@ -981,6 +981,9 @@ static void OPN2_ChOutput(ym3438_t *chip)
|
|||
{
|
||||
out = (Bit16s)chip->dacdata;
|
||||
out = SIGN_EXTEND(8, out);
|
||||
if (chip->chip_type & ym3438_mode_opn) {
|
||||
out <<=5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
1
extern/vgsound_emu-modified/MODIFIED.md
vendored
|
@ -9,3 +9,4 @@ you can get original software from [here](https://gitlab.com/cam900/vgsound_emu/
|
|||
## Modifier
|
||||
|
||||
- [cam900](https://gitlab.com/cam900)
|
||||
- [tildearrow](https://github.com/tildearrow)
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
#include <string>
|
||||
#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
|
||||
{
|
||||
typedef unsigned char u8;
|
||||
|
@ -43,7 +47,7 @@ namespace vgsound_emu
|
|||
return std::clamp(in, min, max);
|
||||
#else
|
||||
// otherwise, use my own implementation of std::clamp
|
||||
return std::min(std::max(in, min), max);
|
||||
return VGS_CLAMP(in,min,max);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -58,7 +62,7 @@ namespace vgsound_emu
|
|||
template<typename T>
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ void es5504_core::tick()
|
|||
// /CAS low, E low: fetch sample
|
||||
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
|
||||
{
|
||||
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_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_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(m_ha, m_hd);
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ void es5504_core::tick_perf()
|
|||
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);
|
||||
m_voice[m_voice_cycle].fetch(m_voice_fetch);
|
||||
voice_tick();
|
||||
// rising edge
|
||||
m_e.edge().set(true);
|
||||
|
@ -92,7 +92,7 @@ void es5504_core::tick_perf()
|
|||
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);
|
||||
m_voice[m_voice_cycle].fetch(m_voice_fetch);
|
||||
voice_tick();
|
||||
// rising edge
|
||||
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(
|
||||
cycle,
|
||||
m_host.m_intf.read_sample(voice,
|
||||
m_host.m_intf.read_sample(
|
||||
bitfield(m_cr.ca(), 0, 3),
|
||||
bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer)));
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ void es5504_core::voice_t::reset()
|
|||
// Accessors
|
||||
u16 es5504_core::host_r(u8 address)
|
||||
{
|
||||
if (!m_host_intf.host_access())
|
||||
if (!m_host_intf.m_host_access)
|
||||
{
|
||||
m_ha = address;
|
||||
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)
|
||||
{
|
||||
if (!m_host_intf.host_access())
|
||||
if (!m_host_intf.m_host_access)
|
||||
{
|
||||
m_ha = address;
|
||||
m_hd = data;
|
||||
|
|
|
@ -32,7 +32,7 @@ class es5504_core : public es550x_shared_core
|
|||
|
||||
// internal state
|
||||
virtual void reset() override;
|
||||
virtual void fetch(u8 voice, u8 cycle) override;
|
||||
virtual void fetch(u8 cycle) override;
|
||||
virtual void tick(u8 voice) override;
|
||||
|
||||
// setters
|
||||
|
|
|
@ -97,7 +97,7 @@ void es5505_core::tick()
|
|||
// /CAS low, E low: fetch sample
|
||||
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
|
||||
{
|
||||
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_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_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(m_ha, m_hd);
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ void es5505_core::tick_perf()
|
|||
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);
|
||||
m_voice[m_voice_cycle].fetch(m_voice_fetch);
|
||||
voice_tick();
|
||||
// rising edge
|
||||
m_e.edge().set(true);
|
||||
|
@ -170,7 +170,7 @@ void es5505_core::tick_perf()
|
|||
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);
|
||||
m_voice[m_voice_cycle].fetch(m_voice_fetch);
|
||||
voice_tick();
|
||||
// rising edge
|
||||
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(
|
||||
cycle,
|
||||
m_host.m_intf.read_sample(voice,
|
||||
bitfield(m_cr.bs(), 0),
|
||||
m_host.m_intf.read_sample(
|
||||
bitfield(m_cr.m_bs, 0),
|
||||
bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer)));
|
||||
}
|
||||
|
||||
|
@ -301,7 +301,7 @@ void es5505_core::voice_t::reset()
|
|||
// Accessors
|
||||
u16 es5505_core::host_r(u8 address)
|
||||
{
|
||||
if (!m_host_intf.host_access())
|
||||
if (!m_host_intf.m_host_access)
|
||||
{
|
||||
m_ha = address;
|
||||
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)
|
||||
{
|
||||
if (!m_host_intf.host_access())
|
||||
if (!m_host_intf.m_host_access)
|
||||
{
|
||||
m_ha = address;
|
||||
m_hd = data;
|
||||
|
|
|
@ -112,7 +112,7 @@ class es5505_core : public es550x_shared_core
|
|||
|
||||
// internal state
|
||||
virtual void reset() override;
|
||||
virtual void fetch(u8 voice, u8 cycle) override;
|
||||
virtual void fetch(u8 cycle) override;
|
||||
virtual void tick(u8 voice) override;
|
||||
|
||||
// setters
|
||||
|
|
|
@ -9,182 +9,10 @@
|
|||
#include "es5506.hpp"
|
||||
|
||||
// Internal functions
|
||||
|
||||
// DO NOT USE THIS ONE!
|
||||
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
|
||||
|
@ -193,12 +21,18 @@ void es5506_core::tick_perf()
|
|||
// output
|
||||
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)
|
||||
{
|
||||
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
|
||||
// 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();
|
||||
// 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()
|
||||
{
|
||||
// Voice updates every 2 E clock cycle (or 4 BCLK clock cycle)
|
||||
m_voice_update = bitfield(m_voice_fetch++, 0);
|
||||
if (m_voice_update)
|
||||
{
|
||||
// Update voice
|
||||
m_voice[m_voice_cycle].tick(m_voice_cycle);
|
||||
|
||||
// Refresh output
|
||||
if ((++m_voice_cycle) > clamp<u8>(m_active, 4, 31)) // 5 ~ 32 voices
|
||||
{
|
||||
m_voice_end = true;
|
||||
m_voice_cycle = 0;
|
||||
for (output_t &elem : m_ch)
|
||||
for (int i=0; i<6; i++)
|
||||
{
|
||||
elem.reset();
|
||||
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);
|
||||
|
||||
for (voice_t &elem : m_voice)
|
||||
{
|
||||
const u8 ca = bitfield<u8>(elem.cr().ca(), 0, 3);
|
||||
const u8 ca = m_voice[i].cr().ca()&7;
|
||||
if (ca < 6)
|
||||
{
|
||||
m_ch[ca] += elem.ch();
|
||||
m_ch[ca] += m_voice[i].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(
|
||||
cycle,
|
||||
m_host.m_intf.read_sample(voice,
|
||||
m_cr.bs(),
|
||||
bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer)));
|
||||
0,
|
||||
m_host.m_intf.read_sample(m_cr.m_bs,
|
||||
(m_alu.get_accum_integer())&((1<<m_alu.m_integer)-1)));
|
||||
if (m_cr.cmpd())
|
||||
{ // 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)
|
||||
{
|
||||
m_output[0] = m_output[1] = 0;
|
||||
m_ch.reset();
|
||||
|
||||
// Filter execute
|
||||
m_filter.tick(m_alu.interpolation());
|
||||
|
||||
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
|
||||
m_output[0] = volume_calc(m_lvol, sign_ext<s32>(m_filter.o4_1(), 16));
|
||||
m_output[1] = volume_calc(m_rvol, 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] = m_mute ? 0 : volume_calc(m_rvol, (short)m_filter.o4_1());
|
||||
|
||||
m_ch.set_left(m_output[0]);
|
||||
m_ch.set_right(m_output[1]);
|
||||
|
@ -307,6 +111,11 @@ void es5506_core::voice_t::tick(u8 voice)
|
|||
{
|
||||
m_alu.loop_exec();
|
||||
}
|
||||
} else {
|
||||
m_filter.tick(m_alu.interpolation());
|
||||
m_output[0] = m_output[1] = 0;
|
||||
m_ch.reset();
|
||||
|
||||
}
|
||||
// Envelope
|
||||
if (m_ecount != 0)
|
||||
|
@ -314,11 +123,11 @@ void es5506_core::voice_t::tick(u8 voice)
|
|||
// Left and Right volume
|
||||
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)
|
||||
{
|
||||
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
|
||||
|
@ -326,13 +135,13 @@ void es5506_core::voice_t::tick(u8 voice)
|
|||
((m_k1ramp.slow() == 0) || (bitfield(m_filtcount, 0, 3) == 0)))
|
||||
{
|
||||
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) &&
|
||||
((m_k2ramp.slow() == 0) || (bitfield(m_filtcount, 0, 3) == 0)))
|
||||
{
|
||||
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--;
|
||||
|
@ -340,7 +149,7 @@ void es5506_core::voice_t::tick(u8 voice)
|
|||
m_filtcount = bitfield(m_filtcount + 1, 0, 3);
|
||||
|
||||
// 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
|
||||
|
@ -357,9 +166,7 @@ s16 es5506_core::voice_t::decompress(u8 sample)
|
|||
// volume calculation
|
||||
s32 es5506_core::voice_t::volume_calc(u16 volume, s32 in)
|
||||
{
|
||||
u8 exponent = bitfield(volume, 12, 4);
|
||||
u8 mantissa = bitfield(volume, 4, 8);
|
||||
return (in * s32(0x100 | mantissa)) >> (20 - exponent);
|
||||
return (in * s32(0x100 | ((volume>>4)&255))) >> (20 - ((volume>>12)&15));
|
||||
}
|
||||
|
||||
void es5506_core::reset()
|
||||
|
@ -420,36 +227,16 @@ void es5506_core::voice_t::reset()
|
|||
// Accessors
|
||||
u8 es5506_core::host_r(u8 address)
|
||||
{
|
||||
if (!m_host_intf.host_access())
|
||||
{
|
||||
m_ha = address;
|
||||
if (m_e.rising_edge())
|
||||
{ // update directly
|
||||
m_hd = read(m_ha, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_host_intf.set_strobe(true);
|
||||
}
|
||||
}
|
||||
return m_hd;
|
||||
}
|
||||
|
||||
void es5506_core::host_w(u8 address, u8 data)
|
||||
{
|
||||
if (!m_host_intf.host_access())
|
||||
{
|
||||
m_ha = address;
|
||||
m_hd = data;
|
||||
if (m_e.rising_edge())
|
||||
{ // update directly
|
||||
write(m_ha, m_hd);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_host_intf.set_strobe(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u8 es5506_core::read(u8 address, bool cpu_access)
|
||||
|
|
|
@ -27,7 +27,7 @@ class es5506_core : public es550x_shared_core
|
|||
{
|
||||
}
|
||||
|
||||
void reset()
|
||||
inline void reset()
|
||||
{
|
||||
m_left = 0;
|
||||
m_right = 0;
|
||||
|
@ -39,7 +39,7 @@ class es5506_core : public es550x_shared_core
|
|||
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)
|
||||
{
|
||||
|
@ -89,7 +89,7 @@ class es5506_core : public es550x_shared_core
|
|||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
public: // oh my...
|
||||
s32 m_left = 0;
|
||||
s32 m_right = 0;
|
||||
};
|
||||
|
@ -145,15 +145,14 @@ class es5506_core : public es550x_shared_core
|
|||
, m_k2ramp(filter_ramp_t())
|
||||
, m_k1ramp(filter_ramp_t())
|
||||
, m_filtcount(0)
|
||||
, m_ch(output_t())
|
||||
, m_mute(false)
|
||||
, m_output{0,0}
|
||||
{
|
||||
m_output.fill(0);
|
||||
}
|
||||
|
||||
// internal state
|
||||
virtual void reset() override;
|
||||
virtual void fetch(u8 voice, u8 cycle) override;
|
||||
virtual void fetch(u8 cycle) override;
|
||||
virtual void tick(u8 voice) override;
|
||||
|
||||
// Setters
|
||||
|
@ -182,6 +181,8 @@ class es5506_core : public es550x_shared_core
|
|||
|
||||
inline filter_ramp_t &k1ramp() { return m_k1ramp; }
|
||||
|
||||
inline bool muted() { return m_mute; }
|
||||
|
||||
output_t &ch() { return m_ch; }
|
||||
|
||||
// for debug/preview only
|
||||
|
@ -211,7 +212,7 @@ class es5506_core : public es550x_shared_core
|
|||
u8 m_filtcount = 0; // Internal counter for slow mode
|
||||
output_t m_ch; // channel output
|
||||
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
|
||||
|
@ -313,9 +314,9 @@ class es5506_core : public es550x_shared_core
|
|||
inline bool bclk_falling_edge() { return m_bclk.falling_edge(); }
|
||||
|
||||
// 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;
|
||||
|
||||
private:
|
||||
std::array<voice_t, 32> m_voice; // 32 voices
|
||||
voice_t m_voice[32]; // 32 voices
|
||||
|
||||
// Host interfaces
|
||||
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
|
||||
bool m_wclk_lr = false; // WCLK, L/R output select
|
||||
s8 m_output_bit = 0; // Bit position in output
|
||||
std::array<output_t, 6> m_ch; // 6 stereo output channels
|
||||
std::array<output_t, 6> m_output; // Serial outputs
|
||||
std::array<output_t, 6> m_output_temp; // temporary signal for serial output
|
||||
std::array<output_t, 6> m_output_latch; // output latch
|
||||
output_t m_ch[6]; // 6 stereo output channels
|
||||
output_t m_output[8]; // Serial outputs
|
||||
output_t m_output_temp[6]; // temporary signal for serial output
|
||||
output_t m_output_latch[6]; // output latch
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include "../core/core.hpp"
|
||||
#include "../core/util/clock_pulse.hpp"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace vgsound_emu;
|
||||
|
||||
// ES5504/ES5505/ES5506 interface
|
||||
|
@ -39,7 +41,7 @@ class es550x_intf : public vgsound_emu_core
|
|||
|
||||
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
|
||||
|
@ -135,7 +137,7 @@ class es550x_shared_core : public vgsound_emu_core
|
|||
|
||||
inline bool cmpd() { return m_cmpd; }
|
||||
|
||||
protected:
|
||||
public:
|
||||
// Channel assign -
|
||||
// 4 bit (16 channel or Bank) for ES5504
|
||||
// 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")
|
||||
, m_integer(integer)
|
||||
, m_fraction(fraction)
|
||||
, m_fraction_m9(std::max<s8>(0, m_fraction - 9))
|
||||
, m_total_bits(integer + fraction)
|
||||
, m_accum_mask(
|
||||
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_end(0)
|
||||
, m_accum(0)
|
||||
, m_last_accum(0)
|
||||
, m_sample{0,0}
|
||||
{
|
||||
m_sample.fill(0);
|
||||
}
|
||||
|
||||
// configurations
|
||||
const u8 m_integer = 21;
|
||||
const u8 m_fraction = 11;
|
||||
const u8 m_fraction_m9 = 2;
|
||||
const u8 m_total_bits = 32;
|
||||
const u32 m_accum_mask = 0xffffffff;
|
||||
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]; }
|
||||
|
||||
private:
|
||||
public:
|
||||
class es550x_alu_cr_t : public vgsound_emu_core
|
||||
{
|
||||
public:
|
||||
|
@ -343,7 +348,7 @@ class es550x_shared_core : public vgsound_emu_core
|
|||
|
||||
inline u8 loop() { return (m_lpe << 0) | (m_ble << 1); }
|
||||
|
||||
private:
|
||||
public:
|
||||
u8 m_stop0 : 1; // Stop with ALU
|
||||
u8 m_stop1 : 1; // Stop with processor
|
||||
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
|
||||
// 21 integer, 11 fraction for ES5506
|
||||
u32 m_accum = 0;
|
||||
u32 m_last_accum = 0;
|
||||
// Samples
|
||||
std::array<s32, 2> m_sample;
|
||||
s32 m_sample[2];
|
||||
};
|
||||
|
||||
// Filter
|
||||
|
@ -380,10 +386,7 @@ class es550x_shared_core : public vgsound_emu_core
|
|||
, m_k2(0)
|
||||
, m_k1(0)
|
||||
{
|
||||
for (std::array<s32, 2> &elem : m_o)
|
||||
{
|
||||
std::fill(elem.begin(), elem.end(), 0);
|
||||
}
|
||||
memset(m_o,0,2*5*sizeof(s32));
|
||||
}
|
||||
|
||||
void reset();
|
||||
|
@ -428,8 +431,6 @@ class es550x_shared_core : public vgsound_emu_core
|
|||
inline s32 o4_1() { return m_o[4][0]; }
|
||||
|
||||
private:
|
||||
void lp_exec(s32 coeff, s32 in, s32 out);
|
||||
void hp_exec(s32 coeff, s32 in, s32 out);
|
||||
|
||||
// Registers
|
||||
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
|
||||
|
||||
// Filter storage registers
|
||||
std::array<std::array<s32, 2>, 5> m_o;
|
||||
s32 m_o[5][2];
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -455,7 +456,7 @@ class es550x_shared_core : public vgsound_emu_core
|
|||
|
||||
// internal state
|
||||
virtual void reset();
|
||||
virtual void fetch(u8 voice, u8 cycle) = 0;
|
||||
virtual void fetch(u8 cycle) = 0;
|
||||
virtual void tick(u8 voice) = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Getters
|
||||
bool host_access() { return m_host_access; }
|
||||
|
||||
bool rw() { return m_rw; }
|
||||
|
||||
private:
|
||||
u8 m_host_access : 1; // Host access trigger
|
||||
u8 m_host_access_strobe : 1; // Host access strobe
|
||||
u8 m_rw : 1; // R/W state
|
||||
u8 m_rw_strobe : 1; // R/W strobe
|
||||
public:
|
||||
u8 m_host_access ; // Host access trigger
|
||||
u8 m_host_access_strobe ; // Host access strobe
|
||||
u8 m_rw ; // R/W state
|
||||
u8 m_rw_strobe ; // R/W strobe
|
||||
};
|
||||
|
||||
public:
|
||||
|
|
|
@ -16,13 +16,15 @@ void es550x_shared_core::es550x_voice_t::es550x_alu_t::reset()
|
|||
m_start = 0;
|
||||
m_end = 0;
|
||||
m_accum = 0;
|
||||
m_last_accum=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()
|
||||
{
|
||||
m_last_accum = m_accum;
|
||||
if (m_cr.dir())
|
||||
{
|
||||
m_accum -= m_fc;
|
||||
|
@ -33,24 +35,19 @@ bool es550x_shared_core::es550x_voice_t::es550x_alu_t::tick()
|
|||
}
|
||||
|
||||
m_accum &= m_accum_mask;
|
||||
return ((!m_cr.lei()) &&
|
||||
(((m_cr.dir()) && (m_accum < m_start)) || ((!m_cr.dir()) && (m_accum > m_end))))
|
||||
return ((!m_cr.m_lei) &&
|
||||
(((m_cr.m_dir) && (m_accum < m_start)) || ((!m_cr.m_dir) && (m_accum > m_end))))
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
|
||||
void es550x_shared_core::es550x_voice_t::es550x_alu_t::loop_exec()
|
||||
{
|
||||
if (m_cr.irqe())
|
||||
{ // Set IRQ
|
||||
m_cr.set_irq(true);
|
||||
}
|
||||
|
||||
if (m_cr.dir()) // Reverse playback
|
||||
if (m_cr.m_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_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);
|
||||
}
|
||||
}
|
||||
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_lei(true); // Loop end ignore
|
||||
|
@ -73,9 +70,9 @@ void es550x_shared_core::es550x_voice_t::es550x_alu_t::loop_exec()
|
|||
}
|
||||
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_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;
|
||||
}
|
||||
}
|
||||
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_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()
|
||||
{
|
||||
// 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])) >>
|
||||
9);
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
|
@ -8,16 +8,24 @@
|
|||
|
||||
#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
|
||||
void es550x_shared_core::es550x_voice_t::es550x_filter_t::reset()
|
||||
{
|
||||
m_lp = 0;
|
||||
m_k2 = 0;
|
||||
m_k1 = 0;
|
||||
for (std::array<s32, 2> &elem : m_o)
|
||||
{
|
||||
std::fill(elem.begin(), elem.end(), 0);
|
||||
}
|
||||
memset(m_o,0,2*5*sizeof(s32));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
|
||||
#include "k005289.hpp"
|
||||
|
||||
void k005289_core::tick()
|
||||
void k005289_core::tick(const unsigned int cycles)
|
||||
{
|
||||
for (timer_t &elem : m_timer)
|
||||
{
|
||||
elem.tick();
|
||||
elem.tick(cycles);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,12 +24,12 @@ void k005289_core::reset()
|
|||
}
|
||||
}
|
||||
|
||||
void k005289_core::timer_t::tick()
|
||||
{
|
||||
if (bitfield(++m_counter, 0, 12) == 0)
|
||||
void k005289_core::timer_t::tick(const unsigned int cycles) {
|
||||
m_counter-=cycles;
|
||||
while (m_counter < 0)
|
||||
{
|
||||
m_addr = bitfield(m_addr + 1, 0, 5);
|
||||
m_counter = m_freq;
|
||||
m_counter += 0x1000-(m_freq&0xfff);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ class k005289_core : public vgsound_emu_core
|
|||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
void tick(const unsigned int cycles=1);
|
||||
|
||||
// accessors
|
||||
// Replace current frequency to lastest loaded pitch
|
||||
|
@ -63,7 +63,7 @@ class k005289_core : public vgsound_emu_core
|
|||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
void tick(const unsigned int cycles);
|
||||
|
||||
// accessors
|
||||
// TG1/2 pin
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
# Konami SCC
|
||||
|
||||
## modified
|
||||
|
||||
the emulation core has been modified for optimization.
|
||||
|
||||
## Summary
|
||||
|
||||
- 5 voice wavetable
|
||||
|
|
|
@ -10,17 +10,17 @@
|
|||
#include "scc.hpp"
|
||||
|
||||
// shared SCC features
|
||||
void scc_core::tick()
|
||||
void scc_core::tick(const int cycles)
|
||||
{
|
||||
m_out = 0;
|
||||
for (auto &elem : m_voice)
|
||||
for (int elem=0; elem<5; elem++)
|
||||
{
|
||||
elem.tick();
|
||||
m_out += elem.out();
|
||||
m_voice[elem].tick(cycles);
|
||||
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
|
||||
{
|
||||
|
@ -28,23 +28,27 @@ void scc_core::voice_t::tick()
|
|||
const u16 temp = m_counter;
|
||||
if (m_host.m_test.freq_4bit()) // 4 bit frequency mode
|
||||
{
|
||||
m_counter = (m_counter & ~0x0ff) | (bitfield(bitfield(m_counter, 0, 8) - 1, 0, 8) << 0);
|
||||
m_counter = (m_counter & ~0xf00) | (bitfield(bitfield(m_counter, 8, 4) - 1, 0, 4) << 8);
|
||||
m_counter = (m_counter & ~0x0ff) | (bitfield(bitfield(m_counter, 0, 8) - cycles, 0, 8) << 0);
|
||||
m_counter = (m_counter & ~0xf00) | (bitfield(bitfield(m_counter, 8, 4) - cycles, 0, 4) << 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_counter = bitfield(m_counter - 1, 0, 12);
|
||||
m_counter = bitfield(m_counter - cycles, 0, 12);
|
||||
}
|
||||
|
||||
// handle counter carry
|
||||
const bool carry = m_host.m_test.freq_8bit()
|
||||
const bool carry = (temp<cycles) || (m_host.m_test.freq_8bit()
|
||||
? (bitfield(temp, 0, 8) == 0)
|
||||
: (m_host.m_test.freq_4bit() ? (bitfield(temp, 8, 4) == 0)
|
||||
: (bitfield(temp, 0, 12) == 0));
|
||||
: (bitfield(temp, 0, 12) == 0)));
|
||||
if (carry)
|
||||
{
|
||||
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
|
||||
|
@ -60,19 +64,19 @@ void scc_core::voice_t::tick()
|
|||
|
||||
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_out = 0;
|
||||
std::fill(m_reg.begin(), m_reg.end(), 0);
|
||||
memset(m_reg,0,256);
|
||||
}
|
||||
|
||||
void scc_core::voice_t::reset()
|
||||
{
|
||||
std::fill(m_wave.begin(), m_wave.end(), 0);
|
||||
memset(m_wave,0,32);
|
||||
m_enable = false;
|
||||
m_pitch = 0;
|
||||
m_volume = 0;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "../core/core.hpp"
|
||||
#include "../core/util/mem_intf.hpp"
|
||||
#include <string.h>
|
||||
|
||||
using namespace vgsound_emu;
|
||||
|
||||
|
@ -36,12 +37,12 @@ class scc_core : public vgsound_emu_core
|
|||
, m_counter(0)
|
||||
, m_out(0)
|
||||
{
|
||||
m_wave.fill(0);
|
||||
memset(m_wave,0,32);
|
||||
}
|
||||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
void tick(const int cycles=1);
|
||||
|
||||
// accessors
|
||||
inline void reset_addr() { m_addr = 0; }
|
||||
|
@ -69,7 +70,7 @@ class scc_core : public vgsound_emu_core
|
|||
private:
|
||||
// registers
|
||||
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
|
||||
u16 m_pitch : 12; // pitch
|
||||
u16 m_volume : 4; // volume
|
||||
|
@ -139,7 +140,7 @@ class scc_core : public vgsound_emu_core
|
|||
, m_test(test_t())
|
||||
, m_out(0)
|
||||
{
|
||||
m_reg.fill(0);
|
||||
memset(m_reg,0,256);
|
||||
}
|
||||
|
||||
// destructor
|
||||
|
@ -151,7 +152,7 @@ class scc_core : public vgsound_emu_core
|
|||
|
||||
// internal state
|
||||
virtual void reset();
|
||||
void tick();
|
||||
void tick(const int cycles=1);
|
||||
|
||||
// getters
|
||||
inline s32 out() { return m_out; } // output to DA0...DA10 pin
|
||||
|
@ -168,12 +169,12 @@ class scc_core : public vgsound_emu_core
|
|||
void freq_vol_enable_w(u8 address, u8 data);
|
||||
|
||||
// internal values
|
||||
std::array<voice_t, 5> m_voice; // 5 voices
|
||||
voice_t m_voice[5]; // 5 voices
|
||||
|
||||
test_t m_test; // test register
|
||||
s32 m_out = 0; // output to DA0...10
|
||||
|
||||
std::array<u8, 256> m_reg; // register pool
|
||||
u8 m_reg[256]; // register pool
|
||||
};
|
||||
|
||||
// SCC core
|
||||
|
|
|
@ -11,13 +11,11 @@
|
|||
void vrcvi_core::tick()
|
||||
{
|
||||
m_out = 0;
|
||||
if (!m_control.halt()) // Halt flag
|
||||
if (!m_control.m_halt) // Halt flag
|
||||
{
|
||||
// tick per each clock
|
||||
for (auto &elem : m_pulse)
|
||||
{
|
||||
m_out += elem.get_output(); // add 4 bit pulse output
|
||||
}
|
||||
m_out += m_pulse[0].get_output(); // add 4 bit pulse output
|
||||
m_out += m_pulse[1].get_output();
|
||||
m_out += m_sawtooth.get_output(); // add 5 bit sawtooth output
|
||||
}
|
||||
if (m_timer.tick())
|
||||
|
@ -28,11 +26,8 @@ void vrcvi_core::tick()
|
|||
|
||||
void vrcvi_core::reset()
|
||||
{
|
||||
for (auto &elem : m_pulse)
|
||||
{
|
||||
elem.reset();
|
||||
}
|
||||
|
||||
m_pulse[0].reset();
|
||||
m_pulse[1].reset();
|
||||
m_sawtooth.reset();
|
||||
m_timer.reset();
|
||||
m_control.reset();
|
||||
|
@ -41,65 +36,71 @@ void vrcvi_core::reset()
|
|||
|
||||
bool vrcvi_core::alu_t::tick()
|
||||
{
|
||||
if (m_divider.enable())
|
||||
if (m_divider.m_enable)
|
||||
{
|
||||
const u16 temp = m_counter;
|
||||
// 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 & 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 & 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
|
||||
{
|
||||
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
|
||||
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()
|
||||
{
|
||||
if (!m_divider.enable())
|
||||
if (!m_divider.m_enable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if (!m_divider.enable())
|
||||
if (!m_divider.m_enable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vrcvi_core::alu_t::tick())
|
||||
{
|
||||
if (bitfield(m_cycle++, 0))
|
||||
if ((m_cycle++)&1)
|
||||
{ // Even step only
|
||||
m_accum += m_rate;
|
||||
}
|
||||
|
@ -109,20 +110,20 @@ bool vrcvi_core::sawtooth_t::tick()
|
|||
m_cycle = 0;
|
||||
}
|
||||
}
|
||||
return (m_accum == 0) ? false : true;
|
||||
return (m_accum != 0);
|
||||
}
|
||||
|
||||
s8 vrcvi_core::pulse_t::get_output()
|
||||
{
|
||||
// add 4 bit pulse output
|
||||
m_out = tick() ? m_control.volume() : 0;
|
||||
m_out = tick() ? m_control.m_volume : 0;
|
||||
return m_out;
|
||||
}
|
||||
|
||||
s8 vrcvi_core::sawtooth_t::get_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;
|
||||
}
|
||||
|
||||
|
@ -209,7 +210,7 @@ void vrcvi_core::pulse_w(u8 voice, u8 address, u8 data)
|
|||
break;
|
||||
case 0x02: // Pitch MSB, Enable/Disable - 0x9002/0x9001 (Pulse 1), 0xa002/0xa001 (Pulse 2)
|
||||
v.divider().write(true, data);
|
||||
if (!v.divider().enable())
|
||||
if (!v.divider().m_enable)
|
||||
{ // Reset duty cycle
|
||||
v.clear_cycle();
|
||||
}
|
||||
|
@ -229,7 +230,7 @@ void vrcvi_core::saw_w(u8 address, u8 data)
|
|||
break;
|
||||
case 0x02: // Pitch MSB, Enable/Disable - 0xb002/0xb001 (Sawtooth)
|
||||
m_sawtooth.divider().write(true, data);
|
||||
if (!m_sawtooth.divider().enable())
|
||||
if (!m_sawtooth.divider().m_enable)
|
||||
{ // Reset accumulator
|
||||
m_sawtooth.clear_accum();
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ class vrcvi_core : public vgsound_emu_core
|
|||
|
||||
inline bool enable() { return m_enable; }
|
||||
|
||||
private:
|
||||
public:
|
||||
u16 m_divider : 12; // divider (pitch)
|
||||
u16 m_enable : 1; // channel disable flag
|
||||
};
|
||||
|
@ -141,7 +141,7 @@ class vrcvi_core : public vgsound_emu_core
|
|||
|
||||
inline u8 volume() { return m_volume; }
|
||||
|
||||
private:
|
||||
public:
|
||||
u8 m_mode : 1; // duty toggle flag
|
||||
u8 m_duty : 3; // 3 bit duty cycle
|
||||
u8 m_volume : 4; // 4 bit volume
|
||||
|
@ -359,9 +359,9 @@ class vrcvi_core : public vgsound_emu_core
|
|||
|
||||
inline u8 shift() { return m_shift; }
|
||||
|
||||
private:
|
||||
u8 m_halt : 1; // halt sound
|
||||
u8 m_shift : 2; // 4/8 bit right shift
|
||||
public:
|
||||
u8 m_halt; // halt sound
|
||||
u8 m_shift; // 4/8 bit right shift
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -398,7 +398,7 @@ class vrcvi_core : public vgsound_emu_core
|
|||
private:
|
||||
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
|
||||
timer_t m_timer; // internal timer
|
||||
global_control_t m_control; // control
|
||||
|
|
BIN
instruments/OPL/Psuedo-PoKEY Perodic Noise (4OP).fui
Normal file
|
@ -4,3 +4,4 @@ the Furnace user interface is where the job gets done.
|
|||
|
||||
- [UI components](components.md)
|
||||
- [global keyboard shortcuts](keyboard.md)
|
||||
- [basic mode](basic-mode.md)
|
||||
|
|
34
papers/doc/2-interface/basic-mode.md
Normal 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…
|
||||
* 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…
|
||||
* scale
|
||||
* randomize
|
||||
* invert values
|
||||
* flip selection
|
||||
* collapse
|
||||
* expand
|
||||
* these windows:
|
||||
* mixer
|
||||
* grooves
|
||||
* channels
|
||||
* pattern manager
|
||||
* chip manager
|
||||
* compatibility flags
|
|
@ -57,9 +57,6 @@ however, effects are continuous, which means you only need to type it once and t
|
|||
- `EDxx`: delay note by `xx` ticks.
|
||||
- `EExx`: send external command.
|
||||
- this effect is currently incomplete.
|
||||
- `EFxx`: add or subtract global pitch.
|
||||
- this effect is rather weird. use with caution.
|
||||
- `80` is center.
|
||||
- `F0xx`: change song Hz by BPM value.
|
||||
- `F1xx`: single tick slide up.
|
||||
- `F2xx`: single tick slide down.
|
||||
|
@ -135,7 +132,7 @@ ex | FM | OPM | OPZ | OPLL | AY-3-8910 | AY8930 | Lynx
|
|||
W | | LFO Shape | LFO Shape | Patch | Waveform | Waveform | | Waveform | Waveform | Waveform | Waveform | Waveform | Waveform | | | | Waveform | |
|
||||
1 | | AMD | AMD | | | Duty | | FilterMode | Envelope | EnvMode | WaveLen | Mod Depth | Cutoff | Filter K1 | ClockDiv | EchoFeedback | Special | GroupAtk |
|
||||
2 | | PMD | PMD | | Envelope | Envelope | | Resonance | | Envelope | WaveUpdate | Mod Speed | Resonance | Filter K2 | | Echo Length | Gain | GroupDec |
|
||||
3 | | LFO Speed | LFO Speed | | AutoEnvNum | AutoEnvNum | | Special | | AutoEnvNum | WaveLoad W | | Control | Env Count | | | | Noise |
|
||||
3 | LFOSpd | LFO Speed | LFO Speed | | AutoEnvNum | AutoEnvNum | | Special | | AutoEnvNum | WaveLoad W | | Control | Env Count | | | | Noise |
|
||||
A | ALG | ALG | ALG | | AutoEnvDen | AutoEnvDen | | | | AutoEnvDen | WaveLoad P | | | Control | | | | |
|
||||
B | FB | FB | FB | | | Noise AND | | | | | WaveLoad L | | | | | | | |
|
||||
C | FMS | FMS | FMS | | | Noise OR | | | | | WaveLoad T | | | | | | | |
|
||||
|
|
|
@ -45,6 +45,16 @@ no plans have been made for TX81Z MIDI passthrough, because:
|
|||
- `1Bxx`: set attack of operator 2.
|
||||
- `1Cxx`: set attack of operator 3.
|
||||
- `1Dxx`: set attack of operator 4.
|
||||
- `1Exx`: set LFO AM depth.
|
||||
- `1Fxx`: set LFO PM depth.
|
||||
- `24xx`: set LFO 2 speed.
|
||||
- `25xx`: set LFO 2 waveform. `xx` may be one of the following:
|
||||
- `00`: saw
|
||||
- `01`: square
|
||||
- `02`: triangle
|
||||
- `03`: noise
|
||||
- `26xx`: set LFO 2 AM depth.
|
||||
- `27xx`: set LFO 2 PM depth.
|
||||
- `28xy`: set reverb of operator.
|
||||
- `x` is the operator (1-4). a value of 0 means "all operators".
|
||||
- `y` is the value.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 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.
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ writers:
|
|||
- DeMOSic
|
||||
- cam900
|
||||
- host12prog
|
||||
- WindowxDeveloper
|
||||
|
||||
other:
|
||||
|
||||
|
|
|
@ -32,6 +32,12 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
|
|||
|
||||
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
|
||||
- 135: Furnace dev135
|
||||
- 134: Furnace dev134
|
||||
|
@ -280,6 +286,7 @@ size | description
|
|||
| - 0xc5: YM2610B CSM - 20 channels
|
||||
| - 0xc6: K007232 - 2 channels
|
||||
| - 0xc7: GA20 - 4 channels
|
||||
| - 0xc8: SM8521 - 3 channels
|
||||
| - 0xde: YM2610B extended - 19 channels
|
||||
| - 0xe0: QSound - 19 channels
|
||||
| - 0xfc: Pong - 1 channel
|
||||
|
@ -398,6 +405,17 @@ size | description
|
|||
4?? | patchbay
|
||||
| - see next section for more details.
|
||||
1 | automatic patchbay (>=136)
|
||||
--- | **a couple more compat flags** (>=138)
|
||||
1 | broken portamento during legato
|
||||
7 | reserved
|
||||
--- | **speed pattern of first song** (>=139)
|
||||
1 | length of speed pattern (fail if this is lower than 0 or higher than 16)
|
||||
16 | speed pattern (this overrides speed 1 and speed 2 settings)
|
||||
--- | **groove list** (>=139)
|
||||
1 | number of entries
|
||||
??? | groove entries. the format is:
|
||||
| - 1 byte: length of groove
|
||||
| - 16 bytes: groove pattern
|
||||
```
|
||||
|
||||
# patchbay
|
||||
|
@ -467,6 +485,9 @@ size | description
|
|||
| - a list of channelCount C strings
|
||||
S?? | channel short names
|
||||
| - same as above
|
||||
--- | **speed pattern** (>=139)
|
||||
1 | length of speed pattern (fail if this is lower than 0 or higher than 16)
|
||||
16 | speed pattern (this overrides speed 1 and speed 2 settings)
|
||||
```
|
||||
|
||||
# chip flags
|
||||
|
@ -1640,6 +1661,10 @@ chips which aren't on this list don't have any flags.
|
|||
- 1: 16.67MHz
|
||||
- bit 4: stereo (bool)
|
||||
|
||||
## 0xb1: Ensoniq ES5506
|
||||
|
||||
- bit 0-4: channels (int)
|
||||
|
||||
## 0xb5: tildearrow Sound Unit
|
||||
|
||||
- bit 0: clockSel (int)
|
||||
|
|
|
@ -115,6 +115,7 @@ the following instrument types are available:
|
|||
- 45: K007232
|
||||
- 46: GA20
|
||||
- 47: Pokémon Mini
|
||||
- 48: SM8521
|
||||
|
||||
the following feature codes are recognized:
|
||||
|
||||
|
|
|
@ -15,17 +15,17 @@
|
|||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLongVersionString</key>
|
||||
<string>0.6pre3</string>
|
||||
<string>0.6pre4</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Furnace</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.6pre3</string>
|
||||
<string>0.6pre4</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.6pre3</string>
|
||||
<string>0.6pre4</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string></string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
|
|
BIN
res/furnaceword.png
Normal file
After Width: | Height: | Size: 154 KiB |
102
res/furnaceword.svg
Normal 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
BIN
res/introbg_1024.png
Normal file
After Width: | Height: | Size: 513 KiB |
BIN
res/introbg_512.png
Normal file
After Width: | Height: | Size: 181 KiB |
BIN
res/patexcerpt.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
res/tachip.png
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
res/talogo.png
Normal file
After Width: | Height: | Size: 22 KiB |
|
@ -30,8 +30,9 @@ cp ../../README.md README.txt || exit 1
|
|||
cp -r ../../papers papers || exit 1
|
||||
cp -r ../../demos demos || 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/")
|
||||
|
||||
|
|
|
@ -30,8 +30,9 @@ cp ../../README.md README.txt || exit 1
|
|||
cp -r ../../papers papers || exit 1
|
||||
cp -r ../../demos demos || 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/")
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -58,7 +58,7 @@ bool TAMidiInRtMidi::gather() {
|
|||
if (m.type!=TA_MIDI_SYSEX && msg.size()>1) {
|
||||
memcpy(m.data,msg.data()+1,MIN(msg.size()-1,7));
|
||||
} else if (m.type==TA_MIDI_SYSEX) {
|
||||
m.sysExData.reset(new unsigned char[msg.size()]);
|
||||
m.sysExData=std::shared_ptr<unsigned char>(new unsigned char[msg.size()],std::default_delete<unsigned char[]>());
|
||||
m.sysExLen=msg.size();
|
||||
logD("got a SysEx of length %ld!",msg.size());
|
||||
memcpy(m.sysExData.get(),msg.data(),msg.size());
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -175,7 +175,7 @@ String DivConfig::getString(String key, String fallback) const {
|
|||
return fallback;
|
||||
}
|
||||
|
||||
bool DivConfig::has(String key) {
|
||||
bool DivConfig::has(String key) const {
|
||||
try {
|
||||
String test=conf.at(key);
|
||||
} catch (std::out_of_range& e) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -46,7 +46,7 @@ class DivConfig {
|
|||
String getString(String key, String fallback) const;
|
||||
|
||||
// check for existence
|
||||
bool has(String key);
|
||||
bool has(String key) const;
|
||||
|
||||
// set a config value
|
||||
void set(String key, bool value);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -161,3 +161,7 @@ void DivEngine::setConf(String key, const char* value) {
|
|||
void DivEngine::setConf(String key, String value) {
|
||||
conf.set(key,value);
|
||||
}
|
||||
|
||||
bool DivEngine::hasConf(String key) {
|
||||
return conf.has(key);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -24,6 +24,7 @@
|
|||
#define DIV_MAX_CHIPS 32
|
||||
#define DIV_MAX_CHANS 128
|
||||
#define DIV_MAX_PATTERNS 256
|
||||
#define DIV_MAX_CHIP_DEFS 256
|
||||
|
||||
// in-pattern
|
||||
#define DIV_MAX_ROWS 256
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -103,9 +103,8 @@ enum DivDispatchCmds {
|
|||
DIV_CMD_FM_AM_DEPTH, // (depth)
|
||||
DIV_CMD_FM_PM_DEPTH, // (depth)
|
||||
|
||||
DIV_CMD_GENESIS_LFO, // unused?
|
||||
|
||||
DIV_CMD_ARCADE_LFO, // unused?
|
||||
DIV_CMD_FM_LFO2, // (speed)
|
||||
DIV_CMD_FM_LFO2_WAVE, // (waveform)
|
||||
|
||||
DIV_CMD_STD_NOISE_FREQ, // (freq)
|
||||
DIV_CMD_STD_NOISE_MODE, // (mode)
|
||||
|
@ -215,6 +214,21 @@ enum DivDispatchCmds {
|
|||
|
||||
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_CMD_MAX
|
||||
|
@ -632,8 +646,8 @@ class DivDispatch {
|
|||
#define COLOR_PAL (283.75*15625.0+25.0)
|
||||
|
||||
#define CLAMP_VAR(x,xMin,xMax) \
|
||||
if (x<xMin) x=xMin; \
|
||||
if (x>xMax) x=xMax;
|
||||
if ((x)<(xMin)) (x)=(xMin); \
|
||||
if ((x)>(xMax)) (x)=(xMax);
|
||||
|
||||
#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
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -67,6 +67,7 @@
|
|||
#include "platform/vrc6.h"
|
||||
#include "platform/fds.h"
|
||||
#include "platform/mmc5.h"
|
||||
#include "platform/es5506.h"
|
||||
#include "platform/scc.h"
|
||||
#include "platform/ymz280b.h"
|
||||
#include "platform/rf5c68.h"
|
||||
|
@ -74,6 +75,7 @@
|
|||
#include "platform/vb.h"
|
||||
#include "platform/k007232.h"
|
||||
#include "platform/ga20.h"
|
||||
#include "platform/sm8521.h"
|
||||
#include "platform/pcmdac.h"
|
||||
#include "platform/dummy.h"
|
||||
#include "../ta-log.h"
|
||||
|
@ -436,6 +438,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_MMC5:
|
||||
dispatch=new DivPlatformMMC5;
|
||||
break;
|
||||
case DIV_SYSTEM_ES5506:
|
||||
dispatch=new DivPlatformES5506;
|
||||
break;
|
||||
case DIV_SYSTEM_SCC:
|
||||
dispatch=new DivPlatformSCC;
|
||||
((DivPlatformSCC*)dispatch)->setChipModel(false);
|
||||
|
@ -485,6 +490,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_GA20:
|
||||
dispatch=new DivPlatformGA20;
|
||||
break;
|
||||
case DIV_SYSTEM_SM8521:
|
||||
dispatch=new DivPlatformSM8521;
|
||||
break;
|
||||
case DIV_SYSTEM_PCM_DAC:
|
||||
dispatch=new DivPlatformPCMDAC;
|
||||
break;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -60,7 +60,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
|
|||
case 0x08:
|
||||
return "08xy: Set panning (x: left; y: right)";
|
||||
case 0x09:
|
||||
return "09xx: Set speed 1";
|
||||
return "09xx: Set groove pattern (speed 1 if no grooves exist)";
|
||||
case 0x0a:
|
||||
return "0Axy: Volume slide (0y: down; x0: up)";
|
||||
case 0x0b:
|
||||
|
@ -70,7 +70,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
|
|||
case 0x0d:
|
||||
return "0Dxx: Jump to next pattern";
|
||||
case 0x0f:
|
||||
return "0Fxx: Set speed 2";
|
||||
return "0Fxx: Set speed (speed 2 if no grooves exist)";
|
||||
case 0x80:
|
||||
return "80xx: Set panning (00: left; 80: center; FF: right)";
|
||||
case 0x81:
|
||||
|
@ -110,8 +110,6 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
|
|||
return "EDxx: Note delay";
|
||||
case 0xee:
|
||||
return "EExx: Send external command";
|
||||
case 0xef:
|
||||
return "EFxx: Set global tuning (quirky!)";
|
||||
case 0xf0:
|
||||
return "F0xx: Set tick rate (bpm)";
|
||||
case 0xf1:
|
||||
|
@ -1471,6 +1469,42 @@ void DivEngine::createNew(const char* description, String sysName, bool inBase64
|
|||
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) {
|
||||
logV("swapping channel %d with %d",src,dest);
|
||||
if (src==dest) {
|
||||
|
@ -1961,14 +1995,12 @@ String DivEngine::getPlaybackDebugInfo() {
|
|||
"cmdsPerSecond: %d\n"
|
||||
"globalPitch: %d\n"
|
||||
"extValue: %d\n"
|
||||
"speed1: %d\n"
|
||||
"speed2: %d\n"
|
||||
"tempoAccum: %d\n"
|
||||
"totalProcessed: %d\n"
|
||||
"bufferPos: %d\n",
|
||||
curOrder,prevOrder,curRow,prevRow,ticks,subticks,totalLoops,lastLoopPos,nextSpeed,divider,cycles,clockDrift,
|
||||
changeOrd,changePos,totalSeconds,totalTicks,totalTicksR,totalCmds,lastCmds,cmdsPerSecond,globalPitch,
|
||||
(int)extValue,(int)speed1,(int)speed2,(int)tempoAccum,(int)totalProcessed,(int)bufferPos
|
||||
(int)extValue,(int)tempoAccum,(int)totalProcessed,(int)bufferPos
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2093,7 +2125,8 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
|
|||
lastLoopPos=-1;
|
||||
}
|
||||
endOfSong=false;
|
||||
speedAB=false;
|
||||
// whaaaaa?
|
||||
curSpeed=0;
|
||||
playing=true;
|
||||
skipping=true;
|
||||
memset(walked,0,8192);
|
||||
|
@ -2441,15 +2474,14 @@ void DivEngine::reset() {
|
|||
}
|
||||
extValue=0;
|
||||
extValuePresent=0;
|
||||
speed1=curSubSong->speed1;
|
||||
speed2=curSubSong->speed2;
|
||||
speeds=curSubSong->speeds;
|
||||
firstTick=false;
|
||||
shallStop=false;
|
||||
shallStopSched=false;
|
||||
pendingMetroTick=0;
|
||||
elapsedBars=0;
|
||||
elapsedBeats=0;
|
||||
nextSpeed=speed1;
|
||||
nextSpeed=speeds.val[0];
|
||||
divider=60;
|
||||
if (curSubSong->customTempo) {
|
||||
divider=curSubSong->hz;
|
||||
|
@ -2519,6 +2551,8 @@ int DivEngine::getEffectiveSampleRate(int rate) {
|
|||
return (48828*MIN(128,(rate*128/48828)))/128;
|
||||
case DIV_SYSTEM_X1_010:
|
||||
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:
|
||||
break;
|
||||
}
|
||||
|
@ -2649,12 +2683,8 @@ size_t DivEngine::getCurrentSubSong() {
|
|||
return curSubSongIndex;
|
||||
}
|
||||
|
||||
unsigned char DivEngine::getSpeed1() {
|
||||
return speed1;
|
||||
}
|
||||
|
||||
unsigned char DivEngine::getSpeed2() {
|
||||
return speed2;
|
||||
const DivGroovePattern& DivEngine::getSpeeds() {
|
||||
return speeds;
|
||||
}
|
||||
|
||||
float DivEngine::getHz() {
|
||||
|
@ -3552,14 +3582,14 @@ void DivEngine::delSample(int index) {
|
|||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::addOrder(bool duplicate, bool where) {
|
||||
void DivEngine::addOrder(int pos, bool duplicate, bool where) {
|
||||
unsigned char order[DIV_MAX_CHANS];
|
||||
if (curSubSong->ordersLen>=(DIV_MAX_PATTERNS-1)) return;
|
||||
memset(order,0,DIV_MAX_CHANS);
|
||||
BUSY_BEGIN_SOFT;
|
||||
if (duplicate) {
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
order[i]=curOrders->ord[i][curOrder];
|
||||
order[i]=curOrders->ord[i][pos];
|
||||
}
|
||||
} else {
|
||||
bool used[DIV_MAX_PATTERNS];
|
||||
|
@ -3587,14 +3617,14 @@ void DivEngine::addOrder(bool duplicate, bool where) {
|
|||
} else { // after current order
|
||||
saveLock.lock();
|
||||
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][curOrder+1]=order[i];
|
||||
curOrders->ord[i][pos+1]=order[i];
|
||||
}
|
||||
curSubSong->ordersLen++;
|
||||
saveLock.unlock();
|
||||
curOrder++;
|
||||
if (pos<=curOrder) curOrder++;
|
||||
if (playing && !freelance) {
|
||||
playSub(false);
|
||||
}
|
||||
|
@ -3602,7 +3632,7 @@ void DivEngine::addOrder(bool duplicate, bool where) {
|
|||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::deepCloneOrder(bool where) {
|
||||
void DivEngine::deepCloneOrder(int pos, bool where) {
|
||||
unsigned char order[DIV_MAX_CHANS];
|
||||
if (curSubSong->ordersLen>=(DIV_MAX_PATTERNS-1)) return;
|
||||
warnings="";
|
||||
|
@ -3610,7 +3640,7 @@ void DivEngine::deepCloneOrder(bool where) {
|
|||
for (int i=0; i<chans; i++) {
|
||||
bool didNotFind=true;
|
||||
logD("channel %d",i);
|
||||
order[i]=curOrders->ord[i][curOrder];
|
||||
order[i]=curOrders->ord[i][pos];
|
||||
// find free slot
|
||||
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
|
||||
logD("finding free slot in %d...",j);
|
||||
|
@ -3639,14 +3669,14 @@ void DivEngine::deepCloneOrder(bool where) {
|
|||
} else { // after current order
|
||||
saveLock.lock();
|
||||
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][curOrder+1]=order[i];
|
||||
curOrders->ord[i][pos+1]=order[i];
|
||||
}
|
||||
curSubSong->ordersLen++;
|
||||
saveLock.unlock();
|
||||
curOrder++;
|
||||
if (pos<=curOrder) curOrder++;
|
||||
if (playing && !freelance) {
|
||||
playSub(false);
|
||||
}
|
||||
|
@ -3654,17 +3684,18 @@ void DivEngine::deepCloneOrder(bool where) {
|
|||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::deleteOrder() {
|
||||
void DivEngine::deleteOrder(int pos) {
|
||||
if (curSubSong->ordersLen<=1) return;
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
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];
|
||||
}
|
||||
}
|
||||
curSubSong->ordersLen--;
|
||||
saveLock.unlock();
|
||||
if (curOrder>pos) curOrder--;
|
||||
if (curOrder>=curSubSong->ordersLen) curOrder=curSubSong->ordersLen-1;
|
||||
if (playing && !freelance) {
|
||||
playSub(false);
|
||||
|
@ -3672,40 +3703,46 @@ void DivEngine::deleteOrder() {
|
|||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::moveOrderUp() {
|
||||
void DivEngine::moveOrderUp(int& pos) {
|
||||
BUSY_BEGIN_SOFT;
|
||||
if (curOrder<1) {
|
||||
if (pos<1) {
|
||||
BUSY_END;
|
||||
return;
|
||||
}
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1];
|
||||
curOrders->ord[i][curOrder-1]^=curOrders->ord[i][curOrder];
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder-1];
|
||||
curOrders->ord[i][pos]^=curOrders->ord[i][pos-1];
|
||||
curOrders->ord[i][pos-1]^=curOrders->ord[i][pos];
|
||||
curOrders->ord[i][pos]^=curOrders->ord[i][pos-1];
|
||||
}
|
||||
saveLock.unlock();
|
||||
if (curOrder==pos) {
|
||||
curOrder--;
|
||||
}
|
||||
pos--;
|
||||
if (playing && !freelance) {
|
||||
playSub(false);
|
||||
}
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::moveOrderDown() {
|
||||
void DivEngine::moveOrderDown(int& pos) {
|
||||
BUSY_BEGIN_SOFT;
|
||||
if (curOrder>=curSubSong->ordersLen-1) {
|
||||
if (pos>=curSubSong->ordersLen-1) {
|
||||
BUSY_END;
|
||||
return;
|
||||
}
|
||||
saveLock.lock();
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1];
|
||||
curOrders->ord[i][curOrder+1]^=curOrders->ord[i][curOrder];
|
||||
curOrders->ord[i][curOrder]^=curOrders->ord[i][curOrder+1];
|
||||
curOrders->ord[i][pos]^=curOrders->ord[i][pos+1];
|
||||
curOrders->ord[i][pos+1]^=curOrders->ord[i][pos];
|
||||
curOrders->ord[i][pos]^=curOrders->ord[i][pos+1];
|
||||
}
|
||||
saveLock.unlock();
|
||||
if (curOrder==pos) {
|
||||
curOrder++;
|
||||
}
|
||||
pos++;
|
||||
if (playing && !freelance) {
|
||||
playSub(false);
|
||||
}
|
||||
|
@ -4236,7 +4273,7 @@ void DivEngine::quitDispatch() {
|
|||
clockDrift=0;
|
||||
chans=0;
|
||||
playing=false;
|
||||
speedAB=false;
|
||||
curSpeed=0;
|
||||
endOfSong=false;
|
||||
ticks=0;
|
||||
tempoAccum=0;
|
||||
|
@ -4416,6 +4453,8 @@ void DivEngine::preInit() {
|
|||
String logPath=configPath+DIR_SEPARATOR_STR+"furnace.log";
|
||||
startLogFile(logPath.c_str());
|
||||
|
||||
logI("Furnace version " DIV_VERSION ".");
|
||||
|
||||
loadConf();
|
||||
}
|
||||
|
||||
|
@ -4472,6 +4511,9 @@ bool DivEngine::init() {
|
|||
for (int i=0; i<64; i++) {
|
||||
vibTable[i]=127*sin(((double)i/64.0)*(2*M_PI));
|
||||
}
|
||||
for (int i=0; i<128; i++) {
|
||||
tremTable[i]=255*0.5*(1.0-cos(((double)i/128.0)*(2*M_PI)));
|
||||
}
|
||||
for (int i=0; i<4096; i++) {
|
||||
reversePitchTable[i]=round(1024.0*pow(2.0,(2048.0-(double)i)/(12.0*128.0)));
|
||||
pitchTable[i]=round(1024.0*pow(2.0,((double)i-2048.0)/(12.0*128.0)));
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -47,8 +47,8 @@
|
|||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev136"
|
||||
#define DIV_ENGINE_VERSION 136
|
||||
#define DIV_VERSION "dev142"
|
||||
#define DIV_ENGINE_VERSION 142
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
@ -100,6 +100,7 @@ struct DivChannelState {
|
|||
unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR;
|
||||
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
|
||||
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp;
|
||||
bool wentThroughNote, goneThroughNote;
|
||||
|
||||
int midiNote, curMidiNote, midiPitch;
|
||||
size_t midiAge;
|
||||
|
@ -152,6 +153,8 @@ struct DivChannelState {
|
|||
wasShorthandPorta(false),
|
||||
noteOnInhibit(false),
|
||||
resetArp(false),
|
||||
wentThroughNote(false),
|
||||
goneThroughNote(false),
|
||||
midiNote(-1),
|
||||
curMidiNote(-1),
|
||||
midiPitch(-1),
|
||||
|
@ -337,7 +340,6 @@ class DivEngine {
|
|||
bool playing;
|
||||
bool freelance;
|
||||
bool shallStop, shallStopSched;
|
||||
bool speedAB;
|
||||
bool endOfSong;
|
||||
bool consoleMode;
|
||||
bool extValuePresent;
|
||||
|
@ -359,7 +361,7 @@ class DivEngine {
|
|||
bool midiOutClock;
|
||||
int midiOutMode;
|
||||
int softLockCount;
|
||||
int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats;
|
||||
int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats, curSpeed;
|
||||
size_t curSubSongIndex;
|
||||
size_t bufferPos;
|
||||
double divider;
|
||||
|
@ -368,7 +370,7 @@ class DivEngine {
|
|||
int stepPlay;
|
||||
int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch;
|
||||
unsigned char extValue, pendingMetroTick;
|
||||
unsigned char speed1, speed2;
|
||||
DivGroovePattern speeds;
|
||||
short tempoAccum;
|
||||
DivStatusView view;
|
||||
DivHaltPositions haltOn;
|
||||
|
@ -391,9 +393,9 @@ class DivEngine {
|
|||
std::vector<String> midiOuts;
|
||||
std::vector<DivCommand> cmdStream;
|
||||
std::vector<DivInstrumentType> possibleInsTypes;
|
||||
static DivSysDef* sysDefs[256];
|
||||
static DivSystem sysFileMapFur[256];
|
||||
static DivSystem sysFileMapDMF[256];
|
||||
static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS];
|
||||
static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS];
|
||||
static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS];
|
||||
|
||||
struct SamplePreview {
|
||||
double rate;
|
||||
|
@ -413,6 +415,7 @@ class DivEngine {
|
|||
} sPreview;
|
||||
|
||||
short vibTable[64];
|
||||
short tremTable[128];
|
||||
int reversePitchTable[4096];
|
||||
int pitchTable[4096];
|
||||
char c163NameCS[1024];
|
||||
|
@ -513,6 +516,7 @@ class DivEngine {
|
|||
String decodeSysDesc(String desc);
|
||||
// start fresh
|
||||
void createNew(const char* description, String sysName, bool inBase64=true);
|
||||
void createNewFromDefaults();
|
||||
// load a file.
|
||||
bool load(unsigned char* f, size_t length);
|
||||
// save as .dmf.
|
||||
|
@ -524,7 +528,12 @@ class DivEngine {
|
|||
// specify system to build ROM for.
|
||||
SafeWriter* buildROM(int sys);
|
||||
// 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.
|
||||
SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true);
|
||||
// dump command stream.
|
||||
|
@ -581,6 +590,9 @@ class DivEngine {
|
|||
void setConf(String key, const char* value);
|
||||
void setConf(String key, String value);
|
||||
|
||||
// get whether config value exists
|
||||
bool hasConf(String key);
|
||||
|
||||
// calculate base frequency/period
|
||||
double calcBaseFreq(double clock, double divider, int note, bool period);
|
||||
|
||||
|
@ -729,11 +741,8 @@ class DivEngine {
|
|||
// get current subsong
|
||||
size_t getCurrentSubSong();
|
||||
|
||||
// get speed 1
|
||||
unsigned char getSpeed1();
|
||||
|
||||
// get speed 2
|
||||
unsigned char getSpeed2();
|
||||
// get speeds
|
||||
const DivGroovePattern& getSpeeds();
|
||||
|
||||
// get Hz
|
||||
float getHz();
|
||||
|
@ -813,19 +822,19 @@ class DivEngine {
|
|||
void delSample(int index);
|
||||
|
||||
// add order
|
||||
void addOrder(bool duplicate, bool where);
|
||||
void addOrder(int pos, bool duplicate, bool where);
|
||||
|
||||
// deep clone orders
|
||||
void deepCloneOrder(bool where);
|
||||
void deepCloneOrder(int pos, bool where);
|
||||
|
||||
// delete order
|
||||
void deleteOrder();
|
||||
void deleteOrder(int pos);
|
||||
|
||||
// move order up
|
||||
void moveOrderUp();
|
||||
void moveOrderUp(int& pos);
|
||||
|
||||
// move order down
|
||||
void moveOrderDown();
|
||||
void moveOrderDown(int& pos);
|
||||
|
||||
// move thing up
|
||||
bool moveInsUp(int which);
|
||||
|
@ -1064,7 +1073,6 @@ class DivEngine {
|
|||
freelance(false),
|
||||
shallStop(false),
|
||||
shallStopSched(false),
|
||||
speedAB(false),
|
||||
endOfSong(false),
|
||||
consoleMode(false),
|
||||
extValuePresent(false),
|
||||
|
@ -1098,6 +1106,7 @@ class DivEngine {
|
|||
nextSpeed(3),
|
||||
elapsedBars(0),
|
||||
elapsedBeats(0),
|
||||
curSpeed(0),
|
||||
curSubSongIndex(0),
|
||||
bufferPos(0),
|
||||
divider(60),
|
||||
|
@ -1115,8 +1124,6 @@ class DivEngine {
|
|||
globalPitch(0),
|
||||
extValue(0),
|
||||
pendingMetroTick(0),
|
||||
speed1(3),
|
||||
speed2(3),
|
||||
tempoAccum(0),
|
||||
view(DIV_STATUS_NOTHING),
|
||||
haltOn(DIV_HALT_NONE),
|
||||
|
@ -1158,13 +1165,14 @@ class DivEngine {
|
|||
memset(dispatchOfChan,0,DIV_MAX_CHANS*sizeof(int));
|
||||
memset(sysOfChan,0,DIV_MAX_CHANS*sizeof(int));
|
||||
memset(vibTable,0,64*sizeof(short));
|
||||
memset(tremTable,0,128*sizeof(short));
|
||||
memset(reversePitchTable,0,4096*sizeof(int));
|
||||
memset(pitchTable,0,4096*sizeof(int));
|
||||
memset(sysDefs,0,256*sizeof(void*));
|
||||
memset(sysDefs,0,DIV_MAX_CHIP_DEFS*sizeof(void*));
|
||||
memset(walked,0,8192);
|
||||
memset(oscBuf,0,DIV_MAX_OUTPUTS*(sizeof(float*)));
|
||||
|
||||
for (int i=0; i<256; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIP_DEFS; i++) {
|
||||
sysFileMapFur[i]=DIV_SYSTEM_NULL;
|
||||
sysFileMapDMF[i]=DIV_SYSTEM_NULL;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -83,7 +83,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
}
|
||||
ds.version=(unsigned char)reader.readC();
|
||||
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!");
|
||||
lastError="this version is not supported by Furnace yet";
|
||||
delete[] file;
|
||||
|
@ -219,14 +219,15 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
}
|
||||
|
||||
ds.subsong[0]->timeBase=reader.readC();
|
||||
ds.subsong[0]->speed1=reader.readC();
|
||||
ds.subsong[0]->speeds.len=2;
|
||||
ds.subsong[0]->speeds.val[0]=reader.readC();
|
||||
if (ds.version>0x07) {
|
||||
ds.subsong[0]->speed2=reader.readC();
|
||||
ds.subsong[0]->speeds.val[1]=reader.readC();
|
||||
ds.subsong[0]->pal=reader.readC();
|
||||
ds.subsong[0]->hz=(ds.subsong[0]->pal)?60:50;
|
||||
ds.subsong[0]->customTempo=reader.readC();
|
||||
} else {
|
||||
ds.subsong[0]->speed2=ds.subsong[0]->speed1;
|
||||
ds.subsong[0]->speeds.len=1;
|
||||
}
|
||||
if (ds.version>0x0a) {
|
||||
String hz=reader.readString(3);
|
||||
|
@ -827,6 +828,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
for (int i=0; i<ds.sampleLen; i++) {
|
||||
DivSample* sample=new DivSample;
|
||||
int length=reader.readI();
|
||||
int cutStart=0;
|
||||
int cutEnd=length;
|
||||
int pitch=5;
|
||||
int vol=50;
|
||||
short* data;
|
||||
|
@ -866,6 +869,29 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
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 (ds.version>0x08) {
|
||||
if (ds.version<0x0b) {
|
||||
|
@ -877,6 +903,19 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
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
|
||||
// convert to big-endian
|
||||
for (int pos=0; pos<length; pos++) {
|
||||
|
@ -1716,6 +1755,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
if (ds.version<130) {
|
||||
ds.oldArpStrategy=true;
|
||||
}
|
||||
if (ds.version<138) {
|
||||
ds.brokenPortaLegato=true;
|
||||
}
|
||||
ds.isDMF=false;
|
||||
|
||||
reader.readS(); // reserved
|
||||
|
@ -1739,8 +1781,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
reader.readI();
|
||||
|
||||
subSong->timeBase=reader.readC();
|
||||
subSong->speed1=reader.readC();
|
||||
subSong->speed2=reader.readC();
|
||||
subSong->speeds.len=2;
|
||||
subSong->speeds.val[0]=reader.readC();
|
||||
subSong->speeds.val[1]=reader.readC();
|
||||
subSong->arpLen=reader.readC();
|
||||
subSong->hz=reader.readF();
|
||||
subSong->pal=(subSong->hz>=53);
|
||||
|
@ -2221,6 +2264,32 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
|
||||
if (ds.version>=136) song.patchbayAuto=reader.readC();
|
||||
|
||||
if (ds.version>=138) {
|
||||
ds.brokenPortaLegato=reader.readC();
|
||||
for (int i=0; i<7; i++) {
|
||||
reader.readC();
|
||||
}
|
||||
}
|
||||
|
||||
if (ds.version>=139) {
|
||||
subSong->speeds.len=reader.readC();
|
||||
for (int i=0; i<16; i++) {
|
||||
subSong->speeds.val[i]=reader.readC();
|
||||
}
|
||||
|
||||
// grooves
|
||||
unsigned char grooveCount=reader.readC();
|
||||
for (int i=0; i<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
|
||||
if (ds.version>=119) {
|
||||
logD("reading chip flags...");
|
||||
|
@ -2279,8 +2348,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
|
||||
subSong=ds.subsong[i+1];
|
||||
subSong->timeBase=reader.readC();
|
||||
subSong->speed1=reader.readC();
|
||||
subSong->speed2=reader.readC();
|
||||
subSong->speeds.len=2;
|
||||
subSong->speeds.val[0]=reader.readC();
|
||||
subSong->speeds.val[1]=reader.readC();
|
||||
subSong->arpLen=reader.readC();
|
||||
subSong->hz=reader.readF();
|
||||
subSong->pal=(subSong->hz>=53);
|
||||
|
@ -2328,6 +2398,13 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
for (int i=0; i<tchans; i++) {
|
||||
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();
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
|
@ -2920,7 +3023,6 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
if (fxVal>0x20 && ds.name!="klisje paa klisje") {
|
||||
writeFxCol(0xf0,fxVal);
|
||||
} else {
|
||||
writeFxCol(0x09,fxVal);
|
||||
writeFxCol(0x0f,fxVal);
|
||||
}
|
||||
break;
|
||||
|
@ -3399,8 +3501,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
|||
ds.subsong[0]->pal=true;
|
||||
ds.subsong[0]->customTempo=true;
|
||||
ds.subsong[0]->pat[3].effectCols=3;
|
||||
ds.subsong[0]->speed1=3;
|
||||
ds.subsong[0]->speed2=3;
|
||||
ds.subsong[0]->speeds.val[0]=3;
|
||||
ds.subsong[0]->speeds.len=1;
|
||||
|
||||
int lastIns[4];
|
||||
int lastNote[4];
|
||||
|
@ -3417,10 +3519,8 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
|||
ds.subsong[0]->orders.ord[j][i]=i;
|
||||
DivPattern* p=ds.subsong[0]->pat[j].getPattern(i,true);
|
||||
if (j==3 && seq[i].speed) {
|
||||
p->data[0][6]=0x09;
|
||||
p->data[0][6]=0x0f;
|
||||
p->data[0][7]=seq[i].speed;
|
||||
p->data[0][8]=0x0f;
|
||||
p->data[0][9]=seq[i].speed;
|
||||
}
|
||||
|
||||
bool ignoreNext=false;
|
||||
|
@ -4307,8 +4407,9 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeI(0);
|
||||
|
||||
w->writeC(subSong->timeBase);
|
||||
w->writeC(subSong->speed1);
|
||||
w->writeC(subSong->speed2);
|
||||
// these are for compatibility
|
||||
w->writeC(subSong->speeds.val[0]);
|
||||
w->writeC((subSong->speeds.len>=2)?subSong->speeds.val[1]:subSong->speeds.val[0]);
|
||||
w->writeC(subSong->arpLen);
|
||||
w->writeF(subSong->hz);
|
||||
w->writeS(subSong->patLen);
|
||||
|
@ -4489,6 +4590,27 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
}
|
||||
w->writeC(song.patchbayAuto);
|
||||
|
||||
// even more compat flags
|
||||
w->writeC(song.brokenPortaLegato);
|
||||
for (int i=0; i<7; i++) {
|
||||
w->writeC(0);
|
||||
}
|
||||
|
||||
// speeds of first song
|
||||
w->writeC(subSong->speeds.len);
|
||||
for (int i=0; i<16; i++) {
|
||||
w->writeC(subSong->speeds.val[i]);
|
||||
}
|
||||
|
||||
// groove list
|
||||
w->writeC((unsigned char)song.grooves.size());
|
||||
for (const DivGroovePattern& i: song.grooves) {
|
||||
w->writeC(i.len);
|
||||
for (int j=0; j<16; j++) {
|
||||
w->writeC(i.val[j]);
|
||||
}
|
||||
}
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||
|
@ -4503,8 +4625,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeI(0);
|
||||
|
||||
w->writeC(subSong->timeBase);
|
||||
w->writeC(subSong->speed1);
|
||||
w->writeC(subSong->speed2);
|
||||
w->writeC(subSong->speeds.val[0]);
|
||||
w->writeC((subSong->speeds.len>=2)?subSong->speeds.val[1]:subSong->speeds.val[0]);
|
||||
w->writeC(subSong->arpLen);
|
||||
w->writeF(subSong->hz);
|
||||
w->writeS(subSong->patLen);
|
||||
|
@ -4543,6 +4665,12 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeString(subSong->chanShortName[i],false);
|
||||
}
|
||||
|
||||
// speeds
|
||||
w->writeC(subSong->speeds.len);
|
||||
for (int i=0; i<16; i++) {
|
||||
w->writeC(subSong->speeds.val[i]);
|
||||
}
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||
|
@ -4798,8 +4926,8 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
w->writeC(curSubSong->hilightB);
|
||||
|
||||
w->writeC(curSubSong->timeBase);
|
||||
w->writeC(curSubSong->speed1);
|
||||
w->writeC(curSubSong->speed2);
|
||||
w->writeC(curSubSong->speeds.val[0]);
|
||||
w->writeC((curSubSong->speeds.len>=2)?curSubSong->speeds.val[1]:curSubSong->speeds.val[0]);
|
||||
w->writeC(curSubSong->pal);
|
||||
w->writeC(curSubSong->customTempo);
|
||||
char customHz[4];
|
||||
|
@ -4823,6 +4951,14 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
addWarning("only the currently selected subsong will be saved");
|
||||
}
|
||||
|
||||
if (!song.grooves.empty()) {
|
||||
addWarning("grooves will not be saved");
|
||||
}
|
||||
|
||||
if (curSubSong->speeds.len>2) {
|
||||
addWarning("only the first two speeds will be effective");
|
||||
}
|
||||
|
||||
if (curSubSong->virtualTempoD!=curSubSong->virtualTempoN) {
|
||||
addWarning(".dmf format does not support virtual tempo");
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -924,6 +924,10 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
|
|||
break;
|
||||
case DIV_INS_POKEMINI:
|
||||
break;
|
||||
case DIV_INS_SM8521:
|
||||
checkForWL=true;
|
||||
if (ws.enabled) featureWS=true;
|
||||
break;
|
||||
|
||||
case DIV_INS_MAX:
|
||||
break;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -78,6 +78,7 @@ enum DivInstrumentType: unsigned short {
|
|||
DIV_INS_K007232=45,
|
||||
DIV_INS_GA20=46,
|
||||
DIV_INS_POKEMINI=47,
|
||||
DIV_INS_SM8521=48,
|
||||
DIV_INS_MAX,
|
||||
DIV_INS_NULL
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -56,7 +56,7 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic
|
|||
}
|
||||
if (delay>0) {
|
||||
delay--;
|
||||
had=false;
|
||||
if (!linger) had=false;
|
||||
return;
|
||||
}
|
||||
if (began && source.delay>0) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include "../../ta-log.h"
|
||||
|
||||
void DivDispatch::acquire(short** buf, size_t len) {
|
||||
}
|
||||
|
@ -121,7 +122,8 @@ void DivDispatch::notifyWaveChange(int ins) {
|
|||
}
|
||||
|
||||
void DivDispatch::notifyInsDeletion(void* ins) {
|
||||
|
||||
logE("notifyInsDeletion NOT implemented!");
|
||||
abort();
|
||||
}
|
||||
|
||||
void DivDispatch::notifyPlaybackStop() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -89,7 +89,7 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
|
|||
chan[i].audSub-=AMIGA_DIVIDER;
|
||||
if (chan[i].audSub<0) {
|
||||
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)) {
|
||||
chan[i].audPos=0;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -152,7 +152,9 @@ void DivPlatformArcade::tick(bool sysTick) {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[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));
|
||||
} else {
|
||||
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++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (isMuted[i]) {
|
||||
if (isMuted[i] || !op.enable) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (KVS(i,j)) {
|
||||
|
@ -296,7 +298,9 @@ void DivPlatformArcade::tick(bool sysTick) {
|
|||
}
|
||||
if (m.tl.had) {
|
||||
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));
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
}
|
||||
if (chan[i].keyOn || chan[i].keyOff) {
|
||||
if (chan[i].hardReset && chan[i].keyOn) {
|
||||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
immWrite(baseAddr+ADDR_SL_RR,0x0f);
|
||||
immWrite(baseAddr+ADDR_TL,0x7f);
|
||||
oldWrites[baseAddr+ADDR_SL_RR]=-1;
|
||||
oldWrites[baseAddr+ADDR_TL]=-1;
|
||||
}
|
||||
}
|
||||
immWrite(0x08,i);
|
||||
if (chan[i].hardReset && chan[i].keyOn) {
|
||||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
for (int k=0; k<9; k++) {
|
||||
immWrite(baseAddr+ADDR_SL_RR,0x0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
chan[i].keyOff=false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<256; i++) {
|
||||
|
@ -349,6 +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++) {
|
||||
if (chan[i].freqChanged) {
|
||||
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;
|
||||
immWrite(i+0x28,hScale(chan[i].freq>>6));
|
||||
immWrite(i+0x30,chan[i].freq<<2);
|
||||
hardResetElapsed+=2;
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
if (chan[i].keyOn || chan[i].opMaskChanged) {
|
||||
if ((chan[i].keyOn || chan[i].opMaskChanged) && !chan[i].hardReset) {
|
||||
immWrite(0x08,(chan[i].opMask<<3)|i);
|
||||
hardResetElapsed++;
|
||||
chan[i].opMaskChanged=false;
|
||||
chan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
|
||||
// hard reset handling
|
||||
if (mustHardReset) {
|
||||
for (unsigned int i=hardResetElapsed; 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) {
|
||||
|
@ -382,38 +406,31 @@ void DivPlatformArcade::muteChannel(int ch, bool mute) {
|
|||
}
|
||||
}
|
||||
|
||||
int DivPlatformArcade::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
|
||||
|
||||
if (chan[c.chan].insChanged) {
|
||||
chan[c.chan].state=ins->fm;
|
||||
chan[c.chan].opMask=
|
||||
(chan[c.chan].state.op[0].enable?1:0)|
|
||||
(chan[c.chan].state.op[2].enable?2:0)|
|
||||
(chan[c.chan].state.op[1].enable?4:0)|
|
||||
(chan[c.chan].state.op[3].enable?8:0);
|
||||
}
|
||||
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
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[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));
|
||||
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[c.chan].insChanged) {
|
||||
if (chan[ch].insChanged) {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
}
|
||||
}
|
||||
if (chan[c.chan].insChanged) {
|
||||
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));
|
||||
|
@ -421,14 +438,27 @@ int DivPlatformArcade::dispatch(DivCommand c) {
|
|||
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));
|
||||
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[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[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[c.chan]+ADDR_FMS_AMS,((chan[c.chan].state.fms&7)<<4)|(chan[c.chan].state.ams&3));
|
||||
rWrite(chanOffs[ch]+ADDR_FMS_AMS,((chan[ch].state.fms&7)<<4)|(chan[ch].state.ams&3));
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformArcade::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
|
||||
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
}
|
||||
|
||||
commitState(c.chan,ins);
|
||||
chan[c.chan].insChanged=false;
|
||||
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
|
@ -462,7 +492,9 @@ int DivPlatformArcade::dispatch(DivCommand c) {
|
|||
for (int i=0; i<4; i++) {
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
if (KVS(c.chan,i)) {
|
||||
if (!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));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
@ -521,6 +553,11 @@ int DivPlatformArcade::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
if (chan[c.chan].insChanged) {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPM);
|
||||
commitState(c.chan,ins);
|
||||
chan[c.chan].insChanged=false;
|
||||
}
|
||||
chan[c.chan].baseFreq=NOTE_LINEAR(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
|
@ -559,7 +596,9 @@ int DivPlatformArcade::dispatch(DivCommand c) {
|
|||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
|
||||
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));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
|
@ -766,7 +805,9 @@ void DivPlatformArcade::forceIns() {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[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));
|
||||
} else {
|
||||
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) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -57,6 +57,7 @@ class DivPlatformArcade: public DivPlatformOPM {
|
|||
|
||||
int octave(int freq);
|
||||
int toFreq(int freq);
|
||||
void commitState(int ch, DivInstrument* ins);
|
||||
|
||||
void acquire_nuked(short** buf, size_t len);
|
||||
void acquire_ymfm(short** buf, size_t len);
|
||||
|
@ -76,6 +77,7 @@ class DivPlatformArcade: public DivPlatformOPM {
|
|||
void muteChannel(int ch, bool mute);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
void notifyInsChange(int ins);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void setFlags(const DivConfig& flags);
|
||||
int getOutputCount();
|
||||
void setYMFM(bool use);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
* Copyright (C) 2021-2023 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|