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

This commit is contained in:
Natt Akuma 2023-03-01 12:58:27 +07:00
commit e0803d9bb1
420 changed files with 125299 additions and 7468 deletions

View File

@ -11,7 +11,7 @@ defaults:
shell: bash
env:
BUILD_TYPE: Debug
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,7 @@ 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

View File

@ -70,6 +70,7 @@ option(SUPPORT_XP "Build a Windows XP-compatible binary" OFF)
option(WARNINGS_ARE_ERRORS "Whether warnings in furnace's C++ code should be treated as errors" OFF)
option(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
@ -531,6 +536,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
@ -538,6 +544,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
)
@ -567,6 +574,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
@ -577,12 +585,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
@ -597,7 +617,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
@ -614,6 +636,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
@ -621,6 +644,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
@ -639,8 +663,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)
@ -757,10 +779,10 @@ 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})
elseif(WIN32)
add_executable(furnace WIN32 ${USED_SOURCES})
else()
add_executable(furnace ${USED_SOURCES})
endif()
@ -796,6 +818,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)

View File

@ -47,6 +47,7 @@ check out the [Releases](https://github.com/tildearrow/furnace/releases) page. a
- OKI MSM6258 and MSM6295
- Konami K007232
- Irem GA20
- Ensoniq ES5506
- wavetable chips:
- HuC6280 used in PC Engine
- Konami Bubble System WSG
@ -54,6 +55,7 @@ check out the [Releases](https://github.com/tildearrow/furnace/releases) page. a
- Namco arcade chips (WSG/C15/C30)
- WonderSwan
- Seta/Allumer X1-010
- Sharp SM8521 used in Tiger Game.com
- NES (Ricoh 2A03/2A07), with additional expansion sound support:
- Konami VRC6
- Konami VRC7
@ -78,7 +80,7 @@ check out the [Releases](https://github.com/tildearrow/furnace/releases) page. a
- over 200 ready to use presets from computers, game consoles and arcade boards...
- ...or create your own - up to 32 of them or a total of 128 channels!
- DefleMask compatibility
- loads .dmf modules from all versions (beta 1 to 1.1.5)
- loads .dmf modules from all versions (beta 1 to 1.1.7)
- saves .dmf modules - both modern and legacy
- Furnace doubles as a module downgrader
- loads/saves .dmp instruments and .dmw wavetables as well
@ -214,6 +216,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 +285,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 +300,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 +309,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.

View File

@ -15,8 +15,8 @@ android {
}
minSdkVersion 21
targetSdkVersion 26
versionCode 136
versionName "dev136"
versionCode 143
versionName "0.6pre4"
externalNativeBuild {
cmake {
arguments "-DANDROID_APP_PLATFORM=android-21", "-DANDROID_STL=c++_static"

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.tildearrow.furnace"
android:versionCode="133"
android:versionName="0.6pre3"
android:versionCode="143"
android:versionName="0.6pre4"
android:installLocation="auto">
<!-- OpenGL ES 2.0 -->

BIN
demos/amiga/serendipid.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
demos/opl/e3m2_opl3.fur Normal file

Binary file not shown.

BIN
demos/pc98/CT_maintheme.fur Normal file

Binary file not shown.

BIN
demos/specs2/Tim_Follin.fur Normal file

Binary file not shown.

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 Nuke.YKT
* Copyright (C) 2019-2023 Nuke.YKT
*
* This program is free software; you can redistribute it and/or
* 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) {

3
extern/opn/ym3438.c vendored
View File

@ -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
{

View File

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

View File

@ -19,6 +19,10 @@
#include <string>
#include <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;
}

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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();
voice_tick();
}
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
m_voice_end = true;
m_voice_cycle = 0;
for (int i=0; i<6; i++)
{
m_ch[i].reset();
}
// Update voice
const int total=VGS_CLAMP(m_active,4,31);
for (int i=0; i<=total; i++) {
m_voice[i].tick(i);
// Refresh output
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)
{
elem.reset();
}
for (voice_t &elem : m_voice)
{
const u8 ca = bitfield<u8>(elem.cr().ca(), 0, 3);
if (ca < 6)
{
m_ch[ca] += elem.ch();
}
elem.ch().reset();
}
}
else
{
m_voice_end = false;
}
m_voice_fetch = 0;
}
const u8 ca = m_voice[i].cr().ca()&7;
if (ca < 6)
{
m_ch[ca] += m_voice[i].ch();
}
}
}
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,18 +111,23 @@ 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)
{
// 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)

View File

@ -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

View File

@ -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:

View File

@ -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,

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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

View File

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

View File

@ -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;

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -78,10 +78,10 @@ C2: 31 9 0 11 15 0 0 1 3 0 0
// vgm offset = 00001d22, channels used = 1-------
@:8 Synth Bass 2
LFO: 0 0 0 0 0
CH: 64 5 4 0 0 120 0
M1: 27 10 5 11 7 21 1 1 3 0 0
C1: 31 0 15 11 7 3 0 1 3 0 0
M2: 22 13 13 8 6 21 3 12 3 0 0
C2: 31 15 16 11 8 13 0 2 3 0 0
CH: 64 0 0 0 0 120 0
M1: 22 12 20 8 15 39 3 12 3 0 0
C1: 31 13 11 11 4 25 3 4 3 0 0
M2: 27 0 5 11 7 23 1 1 3 0 0
C2: 31 0 15 11 7 3 0 1 3 0 0

Binary file not shown.

View 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)

View File

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

View File

@ -57,9 +57,6 @@ however, effects are continuous, which means you only need to type it once and t
- `EDxx`: delay note by `xx` ticks.
- `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.

View File

@ -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.

View File

@ -0,0 +1,23 @@
# Sharp SM8521
The SM8521 is the CPU and sound chip of the Game.com, a handheld console released in 1997 as a competitor to the infamous Nintendo Virtual Boy.
Ultimately, most of the games for the Game.com ended up being failiures in the eyes of reviewers, thus giving the Game.com a pretty bad reputation. This was one of the reasons that the Game.com only ended up selling at least 300,000 units. For these reasons and more, the Game.com ended up being discontinued in 2000.
However, for its time, it was a pretty competitively priced system. The Gameboy Color was to be released in a year for $79.95, while the Game.com was released for $69.99, and its later model, the Pocket Pro, was released in mid-1999 for $29.99 due to the Game.com's apparent significant decrease in value.
In fact, most games never used the wavetable/noise mode of the chip. Sonic Jam, for example, uses a sine wave with a software-controlled volume envelope on the DAC channel (see below for more information on the DAC channel).
The sound-related features and quirks of the SM8521 are as follows:
- 2 4-bit wavetable channels
- a noise channel (which can go up to a very high pitch, creating an almost periodic noise sound)
- 5-bit volume
- A low bit-depth output (which means it distorts a lot).
- It phase resets when you switch waves
- 12-bit pitch with a wide frequency range
- A software-controlled D/A register that (potentially) requires all other registers to be stopped to play. Due to this, it is currently, it is not implemented in Furnace as of version 0.6pre4.
## effect commands
- `10xx` Set waveform
- `xx` is a value between 0 and 255, that sets the waveform of the channel you place it on.

View File

@ -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.

View File

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

View File

@ -32,6 +32,11 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
the format versions are:
- 143: Furnace 0.6pre4
- 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
@ -282,6 +287,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
@ -290,10 +296,12 @@ size | description
| - 0xff: reserved for development
| - (compound!) means that the system is composed of two or more chips,
| and has to be flattened.
32 | sound chip volumes
32 | sound chip volumes (<135) or reserved
| - signed char, 64=1.0, 127=~2.0
32 | sound chip panning
| - as of version 135 these fields only exist for compatibility reasons.
32 | sound chip panning (<135) or reserved
| - signed char, -128=left, 127=right
| - as of version 135 these fields only exist for compatibility reasons.
128 | sound chip flag pointers (>=119) or sound chip flags
| - before 118, these were 32-bit flags.
| - for conversion details, see the "converting from old flags" section.
@ -403,6 +411,14 @@ size | description
--- | **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
@ -472,6 +488,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
@ -1645,6 +1664,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)

View File

@ -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:

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

102
res/furnaceword.svg Normal file
View File

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

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
res/intro.fur Normal file

Binary file not shown.

BIN
res/introbg_1024.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 KiB

BIN
res/introbg_512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 565 KiB

After

Width:  |  Height:  |  Size: 554 KiB

BIN
res/patexcerpt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
res/tachip.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
res/talogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -15,9 +15,9 @@ fi
cd win32build
# TODO: potential Arch-ism?
i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Werror" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=ON .. || exit 1
i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Werror" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=ON .. || exit 1
make -j8 || exit 1
i686-w64-mingw32-strip -s furnace.exe || exit 1
#i686-w64-mingw32-strip -s furnace.exe || exit 1
cd ..
@ -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/")

View File

@ -15,9 +15,9 @@ fi
cd winbuild
# TODO: potential Arch-ism?
x86_64-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Werror" .. || exit 1
x86_64-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Wno-deprecated-declarations -Werror" .. || exit 1
make -j8 || exit 1
x86_64-w64-mingw32-strip -s furnace.exe || exit 1
#x86_64-w64-mingw32-strip -s furnace.exe || exit 1
cd ..
@ -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/")

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This 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) {

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This 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);

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This 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);
}

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This 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

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This 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

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -68,6 +68,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"
@ -75,6 +76,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"
@ -440,6 +442,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);
@ -489,6 +494,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;

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This 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,43 @@ 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
logD("trying to load 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 +1996,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 +2126,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 +2475,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 +2552,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 +2684,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 +3583,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 +3618,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 +3633,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 +3641,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 +3670,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 +3685,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 +3704,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();
curOrder--;
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();
curOrder++;
if (curOrder==pos) {
curOrder++;
}
pos++;
if (playing && !freelance) {
playSub(false);
}
@ -4236,7 +4274,7 @@ void DivEngine::quitDispatch() {
clockDrift=0;
chans=0;
playing=false;
speedAB=false;
curSpeed=0;
endOfSong=false;
ticks=0;
tempoAccum=0;
@ -4415,6 +4453,8 @@ void DivEngine::preInit() {
String logPath=configPath+DIR_SEPARATOR_STR+"furnace.log";
startLogFile(logPath.c_str());
logI("Furnace version " DIV_VERSION ".");
loadConf();
}
@ -4429,6 +4469,7 @@ bool DivEngine::init() {
bool oldVol=getConfInt("configVersion",DIV_ENGINE_VERSION)<135;
if (preset.empty()) {
// try loading old preset
logD("trying to load old preset");
preset=decodeSysDesc(getConfString("initialSys",""));
oldVol=false;
}
@ -4472,6 +4513,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)));

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This 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 "dev138"
#define DIV_ENGINE_VERSION 138
#define DIV_VERSION "0.6pre4-hotfix"
#define DIV_ENGINE_VERSION 144
// 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;
}

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This 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) {
@ -876,6 +902,19 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
data=new short[length];
reader.read(data,length*2);
}
if (ds.version>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
@ -1743,8 +1782,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);
@ -2232,6 +2272,25 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
}
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...");
@ -2290,8 +2349,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);
@ -2339,6 +2399,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();
}
}
}
}
@ -2957,7 +3024,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;
@ -3436,8 +3502,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];
@ -3454,10 +3520,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;
@ -4344,8 +4408,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);
@ -4532,6 +4597,21 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
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);
@ -4546,8 +4626,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);
@ -4586,6 +4666,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);
@ -4841,8 +4927,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];
@ -4866,6 +4952,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");
}

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This 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;

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This 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
};

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -56,7 +56,7 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic
}
if (delay>0) {
delay--;
had=false;
if (!linger) had=false;
return;
}
if (began && source.delay>0) {
@ -523,4 +523,4 @@ DivMacroStruct* DivMacroInt::structByName(const String& name) {
return NULL;
}
#undef CONSIDER
#undef CONSIDER

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This 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;
}

View File

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

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