From 9c56c3d01b8e4d83bc4abe8d105ecda1d4774066 Mon Sep 17 00:00:00 2001
From: cam900
Date: Sun, 2 Apr 2023 14:32:47 +0900
Subject: [PATCH 001/454] [WIP] K063260 skeleton
---
CMakeLists.txt | 1 +
.../vgsound_emu/src/k053260/k053260.cpp | 101 ++--
.../vgsound_emu/src/k053260/k053260.hpp | 73 ++-
src/engine/dispatchContainer.cpp | 4 +
src/engine/instrument.cpp | 4 +
src/engine/instrument.h | 1 +
src/engine/platform/k053260.cpp | 475 ++++++++++++++++++
src/engine/platform/k053260.h | 96 ++++
src/engine/song.h | 3 +-
src/engine/sysDef.cpp | 10 +
src/engine/vgmOps.cpp | 42 +-
src/gui/dataList.cpp | 4 +
src/gui/debug.cpp | 20 +
src/gui/doAction.cpp | 3 +-
src/gui/gui.h | 1 +
src/gui/guiConst.cpp | 4 +
src/gui/insEdit.cpp | 17 +-
src/gui/presets.cpp | 41 ++
src/gui/settings.cpp | 1 +
src/gui/sysConf.cpp | 20 +
20 files changed, 837 insertions(+), 84 deletions(-)
create mode 100644 src/engine/platform/k053260.cpp
create mode 100644 src/engine/platform/k053260.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 78a68aa83..7105990b1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -551,6 +551,7 @@ src/engine/platform/k007232.cpp
src/engine/platform/ga20.cpp
src/engine/platform/sm8521.cpp
src/engine/platform/pv1000.cpp
+src/engine/platform/k053260.cpp
src/engine/platform/pcmdac.cpp
src/engine/platform/dummy.cpp
diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp
index bf056b816..2ee4efac3 100644
--- a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp
+++ b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp
@@ -8,18 +8,19 @@
#include "k053260.hpp"
-void k053260_core::tick()
+void k053260_core::tick(u32 cycle)
{
m_out[0] = m_out[1] = 0;
if (m_ctrl.sound_en())
{
for (int i = 0; i < 4; i++)
{
- m_voice[i].tick();
+ m_voice[i].tick(cycle);
m_out[0] += m_voice[i].out(0);
m_out[1] += m_voice[i].out(1);
}
}
+ /*
// dac clock (YM3012 format)
u8 dac_clock = m_dac.clock();
if (bitfield(++dac_clock, 0, 4) == 0)
@@ -34,62 +35,59 @@ void k053260_core::tick()
m_dac.set_state(bitfield(dac_state, 0, 2));
}
m_dac.set_clock(bitfield(dac_clock, 0, 4));
+ */
}
-void k053260_core::voice_t::tick()
+void k053260_core::voice_t::tick(u32 cycle)
{
if (m_enable && m_busy)
{
bool update = false;
// update counter
- if (bitfield(++m_counter, 0, 12) == 0)
+ m_counter += cycle;
+ if (m_counter >= 0x1000)
{
if (m_bitpos < 8)
{
m_bitpos += 8;
m_addr = bitfield(m_addr + 1, 0, 21);
m_remain--;
+ if (m_remain < 0) // check end flag
+ {
+ if (m_loop)
+ {
+ m_addr = m_start;
+ m_remain = m_length;
+ m_output = 0;
+ }
+ else
+ {
+ m_busy = false;
+ }
+ }
}
+ m_data = m_host.m_intf.read_sample(bitfield(m_addr, 0, 21)); // fetch ROM
if (m_adpcm)
{
- m_bitpos -= 4;
- update = true;
+ m_bitpos -= 4;
+ const u8 nibble = bitfield(m_data, m_bitpos & 4, 4); // get nibble from ROM
+ if (nibble)
+ {
+ m_output += m_host.adpcm_lut(nibble);
+ }
}
else
{
m_bitpos -= 8;
}
- m_counter = bitfield(m_pitch, 0, 12);
- }
- m_data = m_host.m_intf.read_sample(bitfield(m_addr, 0, 21)); // fetch ROM
- if (update)
- {
- const u8 nibble = bitfield(m_data, m_bitpos & 4, 4); // get nibble from ROM
- if (nibble)
- {
- m_adpcm_buf += bitfield(nibble, 3) ? s8(0x80 >> bitfield(nibble, 0, 3))
- : (1 << bitfield(nibble - 1, 0, 3));
- }
+ m_counter = 0x1000 - bitfield(m_pitch, 0, 12);
}
- if (m_remain < 0) // check end flag
- {
- if (m_loop)
- {
- m_addr = m_start;
- m_remain = m_length;
- m_adpcm_buf = 0;
- }
- else
- {
- m_busy = false;
- }
- }
// calculate output
- s32 output = m_adpcm ? m_adpcm_buf : sign_ext(m_data, 8) * s32(m_volume);
+ s32 output = (m_adpcm ? m_output : sign_ext(m_data, 8)) * s32(m_volume);
// use math for now; actually fomula unknown
- m_out[0] = (m_pan >= 0) ? s32(output * cos(f64(m_pan) * PI / 180)) : 0;
- m_out[1] = (m_pan >= 0) ? s32(output * sin(f64(m_pan) * PI / 180)) : 0;
+ m_out[0] = (output * m_host.pan_lut(m_pan, 0)) >> 7;
+ m_out[1] = (output * m_host.pan_lut(m_pan, 1)) >> 7;
}
else
{
@@ -244,7 +242,8 @@ void k053260_core::voice_t::keyon()
m_addr = m_start;
m_remain = m_length;
m_bitpos = 4;
- m_adpcm_buf = 0;
+ m_data = 0;
+ m_output = 0;
std::fill(m_out.begin(), m_out.end(), 0);
}
@@ -259,12 +258,12 @@ void k053260_core::reset()
elem.reset();
}
- m_intf.write_int(0);
+ //m_intf.write_int(0);
std::fill(m_host2snd.begin(), m_host2snd.end(), 0);
std::fill(m_snd2host.begin(), m_snd2host.end(), 0);
m_ctrl.reset();
- m_dac.reset();
+ //m_dac.reset();
std::fill(m_reg.begin(), m_reg.end(), 0);
std::fill(m_out.begin(), m_out.end(), 0);
@@ -273,20 +272,20 @@ void k053260_core::reset()
// reset voice
void k053260_core::voice_t::reset()
{
- m_enable = 0;
- m_busy = 0;
- m_loop = 0;
- m_adpcm = 0;
- m_pitch = 0;
- m_start = 0;
- m_length = 0;
- m_volume = 0;
- m_pan = -1;
- m_counter = 0;
- m_addr = 0;
- m_remain = 0;
- m_bitpos = 4;
- m_data = 0;
- m_adpcm_buf = 0;
+ m_enable = 0;
+ m_busy = 0;
+ m_loop = 0;
+ m_adpcm = 0;
+ m_pitch = 0;
+ m_start = 0;
+ m_length = 0;
+ m_volume = 0;
+ m_pan = 4;
+ m_counter = 0;
+ m_addr = 0;
+ m_remain = 0;
+ m_bitpos = 4;
+ m_data = 0;
+ m_output = 0;
m_out[0] = m_out[1] = 0;
}
diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp
index a8668a0df..feaffeac5 100644
--- a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp
+++ b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp
@@ -25,7 +25,7 @@ class k053260_intf : public vgsound_emu_core
virtual u8 read_sample(u32 address) { return 0; } // sample fetch
- virtual void write_int(u8 out) {} // timer interrupt
+ //virtual void write_int(u8 out) {} // timer interrupt
};
class k053260_core : public vgsound_emu_core
@@ -33,7 +33,19 @@ class k053260_core : public vgsound_emu_core
friend class k053260_intf; // k053260 specific interface
private:
- const int pan_dir[8] = {-1, 0, 24, 35, 45, 55, 66, 90}; // pan direction
+ const s32 m_pan_lut[8][2] = {
+ {0x00, 0x00},
+ {0x7f, 0x00},
+ {0x74, 0x34},
+ {0x68, 0x49},
+ {0x5a, 0x5a},
+ {0x49, 0x68},
+ {0x34, 0x74},
+ {0x00, 0x7f}
+ }; // pan LUT
+
+ const s8 m_adpcm_lut[16] =
+ {0, 1, 2, 4, 8, 16, 32, 64, -128, -64, -32, -16, -8, -4, -2, -1}; // ADPCM LUT
class voice_t : public vgsound_emu_core
{
@@ -50,20 +62,20 @@ class k053260_core : public vgsound_emu_core
, m_start(0)
, m_length(0)
, m_volume(0)
- , m_pan(-1)
+ , m_pan(4)
, m_counter(0)
, m_addr(0)
, m_remain(0)
, m_bitpos(4)
, m_data(0)
- , m_adpcm_buf(0)
+ , m_output(0)
{
m_out.fill(0);
}
// internal state
void reset();
- void tick();
+ void tick(u32 cycle);
// accessors
void write(u8 address, u8 data);
@@ -81,7 +93,7 @@ class k053260_core : public vgsound_emu_core
inline void length_inc() { m_length = (m_length + 1) & 0xffff; }
- inline void set_pan(u8 pan) { m_pan = m_host.pan_dir[pan & 7]; }
+ inline void set_pan(u8 pan) { m_pan = pan & 7; }
// getters
inline bool enable() { return m_enable; }
@@ -97,21 +109,21 @@ class k053260_core : public vgsound_emu_core
private:
// registers
k053260_core &m_host;
- u16 m_enable : 1; // enable flag
- u16 m_busy : 1; // busy status
- u16 m_loop : 1; // loop flag
- u16 m_adpcm : 1; // ADPCM flag
- u16 m_pitch : 12; // pitch
- u32 m_start = 0; // start position
- u16 m_length = 0; // source length
- u8 m_volume = 0; // master volume
- int m_pan = -1; // master pan
- u16 m_counter = 0; // frequency counter
- u32 m_addr = 0; // current address
- s32 m_remain = 0; // remain for end sample
- u8 m_bitpos = 4; // bit position for ADPCM decoding
- u8 m_data = 0; // current data
- s8 m_adpcm_buf = 0; // ADPCM buffer
+ u16 m_enable : 1; // enable flag
+ u16 m_busy : 1; // busy status
+ u16 m_loop : 1; // loop flag
+ u16 m_adpcm : 1; // ADPCM flag
+ u16 m_pitch : 12; // pitch
+ u32 m_start = 0; // start position
+ u16 m_length = 0; // source length
+ u8 m_volume = 0; // master volume
+ int m_pan = -1; // master pan
+ u16 m_counter = 0; // frequency counter
+ u32 m_addr = 0; // current address
+ s32 m_remain = 0; // remain for end sample
+ u8 m_bitpos = 4; // bit position for ADPCM decoding
+ u8 m_data = 0; // current data
+ s8 m_output = 0; // ADPCM buffer
std::array m_out; // current output
};
@@ -152,6 +164,7 @@ class k053260_core : public vgsound_emu_core
u8 m_input_en : 2; // Input enable
};
+ /*
class ym3012_t
{
public:
@@ -177,7 +190,9 @@ class k053260_core : public vgsound_emu_core
std::array m_in;
std::array m_out;
};
+ */
+ /*
class dac_t
{
public:
@@ -205,6 +220,7 @@ class k053260_core : public vgsound_emu_core
u8 m_clock : 4; // DAC clock (16 clock)
u8 m_state : 2; // DAC state (4 state - SAM1, SAM2)
};
+ */
public:
// constructor
@@ -213,8 +229,8 @@ class k053260_core : public vgsound_emu_core
, m_voice{*this, *this, *this, *this}
, m_intf(intf)
, m_ctrl(ctrl_t())
- , m_ym3012(ym3012_t())
- , m_dac(dac_t())
+ //, m_ym3012(ym3012_t())
+ //, m_dac(dac_t())
{
m_host2snd.fill(0);
m_snd2host.fill(0);
@@ -233,7 +249,7 @@ class k053260_core : public vgsound_emu_core
// internal state
void reset();
- void tick();
+ void tick(u32 cycle);
// getters for debug, trackers, etc
inline s32 output(u8 ch) { return m_out[ch & 1]; } // output for each channels
@@ -245,6 +261,11 @@ class k053260_core : public vgsound_emu_core
return (voice < 4) ? m_voice[voice].out(ch & 1) : 0;
}
+ protected:
+ inline s32 pan_lut(const u8 pan, const u8 out) { return m_pan_lut[pan][out]; }
+
+ inline s32 adpcm_lut(const u8 nibble) { return m_adpcm_lut[nibble]; }
+
private:
std::array m_voice;
k053260_intf &m_intf; // common memory interface
@@ -254,8 +275,8 @@ class k053260_core : public vgsound_emu_core
ctrl_t m_ctrl; // chip control
- ym3012_t m_ym3012; // YM3012 output
- dac_t m_dac; // YM3012 interface
+ //ym3012_t m_ym3012; // YM3012 output
+ //dac_t m_dac; // YM3012 interface
std::array m_reg; // register pool
std::array m_out; // stereo output
diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp
index c30d7f231..319630d6e 100644
--- a/src/engine/dispatchContainer.cpp
+++ b/src/engine/dispatchContainer.cpp
@@ -78,6 +78,7 @@
#include "platform/ga20.h"
#include "platform/sm8521.h"
#include "platform/pv1000.h"
+#include "platform/k053260.h"
#include "platform/pcmdac.h"
#include "platform/dummy.h"
#include "../ta-log.h"
@@ -501,6 +502,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_PV1000:
dispatch=new DivPlatformPV1000;
break;
+ case DIV_SYSTEM_K053260:
+ dispatch=new DivPlatformK053260;
+ break;
case DIV_SYSTEM_PCM_DAC:
dispatch=new DivPlatformPCMDAC;
break;
diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp
index faa9f86c3..c51805ab5 100644
--- a/src/engine/instrument.cpp
+++ b/src/engine/instrument.cpp
@@ -930,6 +930,10 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
break;
case DIV_INS_PV1000:
break;
+ case DIV_INS_K053260:
+ featureSM=true;
+ featureSL=true;
+ break;
case DIV_INS_MAX:
break;
diff --git a/src/engine/instrument.h b/src/engine/instrument.h
index c53688ec6..932d44ffb 100644
--- a/src/engine/instrument.h
+++ b/src/engine/instrument.h
@@ -80,6 +80,7 @@ enum DivInstrumentType: unsigned short {
DIV_INS_POKEMINI=47,
DIV_INS_SM8521=48,
DIV_INS_PV1000=49,
+ DIV_INS_K053260=50,
DIV_INS_MAX,
DIV_INS_NULL
};
diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp
new file mode 100644
index 000000000..47e7636d1
--- /dev/null
+++ b/src/engine/platform/k053260.cpp
@@ -0,0 +1,475 @@
+/**
+ * Furnace Tracker - multi-system chiptune tracker
+ * Copyright (C) 2021-2023 tildearrow and contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "k053260.h"
+#include "../engine.h"
+#include "../../ta-log.h"
+#include
+
+#define rWrite(a,v) {if(!skipRegisterWrites) {k053260.write(a,v); regPool[a]=v; if(dumpWrites) addWrite(a,v);}}
+
+#define CHIP_DIVIDER 64
+#define TICK_DIVIDER 4
+
+const char* regCheatSheetK053260[]={
+ "FreqL", "0",
+ "FreqH", "1",
+ "LengthL", "2",
+ "LengthH", "3",
+ "StartL", "4",
+ "StartM", "5",
+ "StartH", "6",
+ "Volume", "7",
+ NULL
+};
+
+const char** DivPlatformK053260::getRegisterSheet() {
+ return regCheatSheetK053260;
+}
+
+inline void DivPlatformK053260::chWrite(unsigned char ch, unsigned int addr, unsigned char val) {
+ if (!skipRegisterWrites) {
+ rWrite(8+((ch<<3)|(addr&7)),val);
+ }
+}
+
+// TODO: this code is weird
+// make sure newDispatch didn't break it up
+void DivPlatformK053260::acquire(short** buf, size_t len) {
+ for (int i=0; i32767) lout=32767;
+ if (lout<-32768) lout=-32768;
+ if (rout>32767) rout=32767;
+ if (rout<-32768) rout=-32768;
+ buf[0][i]=lout;
+ buf[1][i]=rout;
+
+ for (int i=0; i<4; i++) {
+ oscBuf[i]->data[oscBuf[i]->needle++]=(k053260.voice_out(i,0)+k053260.voice_out(i,1))>>1;
+ }
+ }
+}
+
+void DivPlatformK053260::tick(bool sysTick) {
+ unsigned char panMask=0;
+ for (int i=0; i<4; i++) {
+ chan[i].std.next();
+ if (chan[i].std.vol.had) {
+ chan[i].outVol=((chan[i].vol&0x7f)*MIN(chan[i].macroVolMul,chan[i].std.vol.val))/chan[i].macroVolMul;
+ chWrite(i,7,chan[i].outVol);
+ }
+ if (NEW_ARP_STRAT) {
+ chan[i].handleArp();
+ } else if (chan[i].std.arp.had) {
+ if (!chan[i].inPorta) {
+ chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
+ }
+ chan[i].freqChanged=true;
+ }
+ if (chan[i].std.pitch.had) {
+ if (chan[i].std.pitch.mode) {
+ chan[i].pitch2+=chan[i].std.pitch.val;
+ CLAMP_VAR(chan[i].pitch2,-32768,32767);
+ } else {
+ chan[i].pitch2=chan[i].std.pitch.val;
+ }
+ chan[i].freqChanged=true;
+ }
+ if (chan[i].std.panL.had) { // panning
+ chan[i].panning=4+chan[i].std.panL.val;
+ if (!isMuted[i]) {
+ panMask|=1<=0 && samplesong.sampleLen) {
+ DivSample* s=parent->getSample(sample);
+ if (s->centerRate<1) {
+ off=1.0;
+ } else {
+ off=8363.0/s->centerRate;
+ }
+ }
+ DivSample* s=parent->getSample(chan[i].sample);
+ chan[i].freq=0x1000-(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER));
+ if (chan[i].freq>4095) chan[i].freq=4095;
+ if (chan[i].freq<0) chan[i].freq=0;
+ if (chan[i].keyOn) {
+ unsigned int start=0;
+ unsigned int length=0;
+ if (chan[i].sample>=0 && chan[i].samplesong.sampleLen) {
+ start=sampleOffK053260[chan[i].sample];
+ length=start+s->length8;
+ }
+ if (chan[i].audPos>0) {
+ start=start+MIN(chan[i].audPos,s->length8);
+ }
+ start=MIN(start,getSampleMemCapacity()-31);
+ length=MIN(length,getSampleMemCapacity()-31);
+ rWrite(0x28,keyoff); // force keyoff first
+ rWrite(0x2a,loopoff);
+ chWrite(i,2,length&0xff);
+ chWrite(i,3,length>>8);
+ chWrite(i,4,start&0xff);
+ chWrite(i,5,start>>8);
+ chWrite(i,6,start>>16);
+ if (!chan[i].std.vol.had) {
+ chan[i].outVol=chan[i].vol;
+ chWrite(i,7,chan[i].outVol);
+ }
+ rWrite(0x28,keyon);
+ if (s->isLoopable()) {
+ rWrite(0x2a,loopon);
+ }
+ chan[i].keyOn=false;
+ }
+ if (chan[i].keyOff) {
+ rWrite(0x28,keyoff);
+ rWrite(0x2a,loopoff);
+ chan[i].keyOff=false;
+ }
+ if (chan[i].freqChanged) {
+ chWrite(i,0,chan[i].freq&0xff);
+ chWrite(i,1,chan[i].freq>>8);
+ chan[i].freqChanged=false;
+ }
+ }
+ }
+ if (panMask) {
+ updatePanning(panMask);
+ }
+}
+
+void DivPlatformK053260::updatePanning(unsigned char mask) {
+ if (mask&3) {
+ rWrite(0x2c,
+ (isMuted[0]?0:chan[0].panning)|
+ (isMuted[1]?0:chan[1].panning<<3));
+ }
+ if (mask&0xc) {
+ rWrite(0x2d,
+ (isMuted[2]?0:chan[2].panning)|
+ (isMuted[3]?0:chan[3].panning<<3));
+ }
+}
+
+int DivPlatformK053260::dispatch(DivCommand c) {
+ switch (c.cmd) {
+ case DIV_CMD_NOTE_ON: {
+ DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
+ chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127;
+ if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
+ if (c.value!=DIV_NOTE_NULL) {
+ chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
+ }
+ if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
+ chan[c.chan].sample=-1;
+ }
+ if (c.value!=DIV_NOTE_NULL) {
+ chan[c.chan].freqChanged=true;
+ chan[c.chan].note=c.value;
+ }
+ chan[c.chan].active=true;
+ chan[c.chan].keyOn=true;
+ chan[c.chan].macroInit(ins);
+ if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
+ chan[c.chan].outVol=chan[c.chan].vol;
+ }
+ break;
+ }
+ case DIV_CMD_NOTE_OFF:
+ chan[c.chan].sample=-1;
+ chan[c.chan].active=false;
+ chan[c.chan].keyOff=true;
+ chan[c.chan].macroInit(NULL);
+ break;
+ case DIV_CMD_NOTE_OFF_ENV:
+ case DIV_CMD_ENV_RELEASE:
+ chan[c.chan].std.release();
+ break;
+ case DIV_CMD_INSTRUMENT:
+ if (chan[c.chan].ins!=c.value || c.value2==1) {
+ chan[c.chan].ins=c.value;
+ }
+ break;
+ case DIV_CMD_VOLUME:
+ if (chan[c.chan].vol!=c.value) {
+ chan[c.chan].vol=c.value;
+ if (!chan[c.chan].std.vol.has) {
+ chan[c.chan].outVol=c.value;
+ chWrite(c.chan,7,chan[c.chan].outVol);
+ }
+ }
+ break;
+ case DIV_CMD_GET_VOLUME:
+ if (chan[c.chan].std.vol.has) {
+ return chan[c.chan].vol;
+ }
+ return chan[c.chan].outVol;
+ break;
+ case DIV_CMD_PANNING:
+ chan[c.chan].panning=MIN(parent->convertPanSplitToLinearLR(c.value,c.value2,7)+1,7);
+ if (!isMuted[c.chan]) {
+ updatePanning(1<chan[c.chan].baseFreq) {
+ chan[c.chan].baseFreq+=c.value;
+ if (chan[c.chan].baseFreq>=destFreq) {
+ chan[c.chan].baseFreq=destFreq;
+ return2=true;
+ }
+ } else {
+ chan[c.chan].baseFreq-=c.value;
+ if (chan[c.chan].baseFreq<=destFreq) {
+ chan[c.chan].baseFreq=destFreq;
+ return2=true;
+ }
+ }
+ chan[c.chan].freqChanged=true;
+ if (return2) {
+ chan[c.chan].inPorta=false;
+ return 2;
+ }
+ break;
+ }
+ case DIV_CMD_LEGATO: {
+ chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val-12):(0)));
+ chan[c.chan].freqChanged=true;
+ chan[c.chan].note=c.value;
+ break;
+ }
+ case DIV_CMD_PRE_PORTA:
+ if (chan[c.chan].active && c.value2) {
+ if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
+ }
+ if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
+ chan[c.chan].inPorta=c.value;
+ break;
+ case DIV_CMD_SAMPLE_POS:
+ chan[c.chan].audPos=c.value;
+ chan[c.chan].setPos=true;
+ break;
+ case DIV_CMD_GET_VOLMAX:
+ return 127;
+ break;
+ case DIV_CMD_MACRO_OFF:
+ chan[c.chan].std.mask(c.value,true);
+ break;
+ case DIV_CMD_MACRO_ON:
+ chan[c.chan].std.mask(c.value,false);
+ break;
+ case DIV_ALWAYS_SET_VOLUME:
+ return 1;
+ break;
+ default:
+ break;
+ }
+ return 1;
+}
+
+void DivPlatformK053260::muteChannel(int ch, bool mute) {
+ isMuted[ch]=mute;
+ updatePanning(1<rate=rate;
+ }
+}
+
+void DivPlatformK053260::poke(unsigned int addr, unsigned short val) {
+ rWrite(addr&0x0f,val);
+}
+
+void DivPlatformK053260::poke(std::vector& wlist) {
+ for (DivRegWrite& i: wlist) rWrite(i.addr&0x0f,i.val);
+}
+
+unsigned char* DivPlatformK053260::getRegisterPool() {
+ return regPool;
+}
+
+int DivPlatformK053260::getRegisterPoolSize() {
+ return 48;
+}
+
+const void* DivPlatformK053260::getSampleMem(int index) {
+ return index == 0 ? sampleMem : NULL;
+}
+
+size_t DivPlatformK053260::getSampleMemCapacity(int index) {
+ return index == 0 ? 2097152 : 0;
+}
+
+size_t DivPlatformK053260::getSampleMemUsage(int index) {
+ return index == 0 ? sampleMemLen : 0;
+}
+
+bool DivPlatformK053260::isSampleLoaded(int index, int sample) {
+ if (index!=0) return false;
+ if (sample<0 || sample>255) return false;
+ return sampleLoaded[sample];
+}
+
+void DivPlatformK053260::renderSamples(int sysID) {
+ memset(sampleMem,0,getSampleMemCapacity());
+ memset(sampleOffK053260,0,256*sizeof(unsigned int));
+ memset(sampleLoaded,0,256*sizeof(bool));
+
+ size_t memPos=1; // for avoid silence
+ for (int i=0; isong.sampleLen; i++) {
+ DivSample* s=parent->song.sample[i];
+ if (!s->renderOn[0][sysID]) {
+ sampleOffK053260[i]=0;
+ continue;
+ }
+
+ int length=MIN(65535,s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT));
+ int actualLength=MIN((int)(getSampleMemCapacity()-memPos),length);
+ if (actualLength>0) {
+ sampleOffK053260[i]=memPos-1;
+ for (int j=0; jdata8[j];
+ }
+ }
+ if (actualLength
+#include "vgsound_emu/src/k053260/k053260.hpp"
+
+class DivPlatformK053260: public DivDispatch, public k053260_intf {
+ struct Channel: public SharedChannel {
+ unsigned int audPos;
+ int sample, wave;
+ int panning;
+ bool setPos;
+ int macroVolMul;
+ Channel():
+ SharedChannel(127),
+ audPos(0),
+ sample(-1),
+ wave(-1),
+ panning(4),
+ setPos(false),
+ macroVolMul(64) {}
+ };
+ Channel chan[4];
+ DivDispatchOscBuffer* oscBuf[4];
+ bool isMuted[4];
+ int chipType;
+ unsigned char curChan;
+ unsigned int sampleOffK053260[256];
+ bool sampleLoaded[256];
+
+ unsigned char* sampleMem;
+ size_t sampleMemLen;
+ k053260_core k053260;
+ unsigned char regPool[48];
+ void updatePanning(unsigned char mask);
+
+ friend void putDispatchChip(void*,int);
+ friend void putDispatchChan(void*,int,int);
+
+ public:
+ void acquire(short** buf, size_t len);
+ int dispatch(DivCommand c);
+ void* getChanState(int chan);
+ DivMacroInt* getChanMacroInt(int ch);
+ DivDispatchOscBuffer* getOscBuffer(int chan);
+ unsigned char* getRegisterPool();
+ int getRegisterPoolSize();
+ void reset();
+ void forceIns();
+ void tick(bool sysTick=true);
+ void muteChannel(int ch, bool mute);
+ int getOutputCount();
+ void setChipModel(int type);
+ void notifyInsChange(int ins);
+ void notifyWaveChange(int wave);
+ void notifyInsDeletion(void* ins);
+ void setFlags(const DivConfig& flags);
+ void poke(unsigned int addr, unsigned short val);
+ void poke(std::vector& wlist);
+ const char** getRegisterSheet();
+ const void* getSampleMem(int index = 0);
+ size_t getSampleMemCapacity(int index = 0);
+ size_t getSampleMemUsage(int index = 0);
+ bool isSampleLoaded(int index, int sample);
+ void renderSamples(int chipID);
+ int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
+ void quit();
+ DivPlatformK053260():
+ DivDispatch(),
+ k053260_intf(),
+ k053260(*this) {}
+ private:
+ void chWrite(unsigned char ch, unsigned int addr, unsigned char val);
+};
+
+#endif
diff --git a/src/engine/song.h b/src/engine/song.h
index 8b6da13b2..bc2dc7501 100644
--- a/src/engine/song.h
+++ b/src/engine/song.h
@@ -127,7 +127,8 @@ enum DivSystem {
DIV_SYSTEM_YM2203_CSM,
DIV_SYSTEM_YM2608_CSM,
DIV_SYSTEM_SM8521,
- DIV_SYSTEM_PV1000
+ DIV_SYSTEM_PV1000,
+ DIV_SYSTEM_K053260
};
struct DivGroovePattern {
diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp
index fbb718cb5..f236fcd2c 100644
--- a/src/engine/sysDef.cpp
+++ b/src/engine/sysDef.cpp
@@ -1855,6 +1855,16 @@ void DivEngine::registerSystems() {
}
);
+ sysDefs[DIV_SYSTEM_K053260]=new DivSysDef(
+ "Konami K053260", NULL, 0xfe/*placeholder*/, 0, 4, false, true, 0x161, false, 1U<writeC(0xff);
break;
case DIV_SYSTEM_GA20:
- for (int i=0; i<3; i++) {
+ for (int i=0; i<4; i++) {
w->writeC(0xbf); // mute
w->writeC((baseAddr2|5)+(i*8));
w->writeC(0);
@@ -573,6 +573,16 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(0);
}
break;
+ case DIV_SYSTEM_K053260:
+ for (int i=0; i<4; i++) {
+ w->writeC(0xba); // mute
+ w->writeC(baseAddr2|0x2f);
+ w->writeC(0);
+ w->writeC(0xba); // keyoff
+ w->writeC(baseAddr2|0x28);
+ w->writeC(0);
+ }
+ break;
default:
break;
}
@@ -928,6 +938,11 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(baseAddr2|(write.addr&0x7f));
w->writeC(write.val);
break;
+ case DIV_SYSTEM_K053260:
+ w->writeC(0xba);
+ w->writeC(baseAddr2|(write.addr&0x3f));
+ w->writeC(write.val&0xff);
+ break;
default:
logW("write not handled!");
break;
@@ -1093,6 +1108,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
DivDispatch* writeRF5C68[2]={NULL,NULL};
DivDispatch* writeMSM6295[2]={NULL,NULL};
DivDispatch* writeGA20[2]={NULL,NULL};
+ DivDispatch* writeK053260[2]={NULL,NULL};
for (int i=0; ichipClock;
+ CHIP_VOL(40,0.4);
+ willExport[i]=true;
+ writeK053260[0]=disCont[i].dispatch;
+ } else if (!(hasK053260&0x40000000)) {
+ isSecond[i]=true;
+ CHIP_VOL_SECOND(40,0.4);
+ willExport[i]=true;
+ writeK053260[1]=disCont[i].dispatch;
+ hasK053260|=0x40000000;
+ howManyChips++;
+ }
+ break;
case DIV_SYSTEM_T6W28:
if (!hasSN) {
hasSN=0xc0000000|disCont[i].dispatch->chipClock;
@@ -1964,6 +1995,15 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
w->writeI(0);
w->write(writeGA20[i]->getSampleMem(),writeGA20[i]->getSampleMemUsage());
}
+ if (writeK053260[i]!=NULL && writeK053260[i]->getSampleMemUsage()>0) {
+ w->writeC(0x67);
+ w->writeC(0x66);
+ w->writeC(0x8e);
+ w->writeI((writeK053260[i]->getSampleMemUsage()+8)|(i*0x80000000));
+ w->writeI(writeK053260[i]->getSampleMemCapacity());
+ w->writeI(0);
+ w->write(writeK053260[i]->getSampleMem(),writeK053260[i]->getSampleMemUsage());
+ }
}
// TODO
diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp
index 2a37c7e8e..859a0d0dc 100644
--- a/src/gui/dataList.cpp
+++ b/src/gui/dataList.cpp
@@ -438,6 +438,10 @@ void FurnaceGUI::drawInsList(bool asChild) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PV1000]);
name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
break;
+ case DIV_INS_K053260:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_K053260]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
default:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]);
name=fmt::sprintf(ICON_FA_QUESTION "##_INS%d",i);
diff --git a/src/gui/debug.cpp b/src/gui/debug.cpp
index 49d568b3c..7a01edf8e 100644
--- a/src/gui/debug.cpp
+++ b/src/gui/debug.cpp
@@ -53,6 +53,7 @@
#include "../engine/platform/ga20.h"
#include "../engine/platform/sm8521.h"
#include "../engine/platform/pv1000.h"
+#include "../engine/platform/k053260.h"
#include "../engine/platform/dummy.h"
#define COMMON_CHIP_DEBUG \
@@ -545,6 +546,13 @@ void putDispatchChip(void* data, int type) {
COMMON_CHIP_DEBUG_BOOL;
break;
}
+ case DIV_SYSTEM_K053260: {
+ DivPlatformK053260* ch=(DivPlatformK053260*)data;
+ ImGui::Text("> K053260");
+ COMMON_CHIP_DEBUG;
+ COMMON_CHIP_DEBUG_BOOL;
+ break;
+ }
default:
ImGui::Text("Unimplemented chip! Help!");
break;
@@ -1083,6 +1091,18 @@ void putDispatchChan(void* data, int chanNum, int type) {
COMMON_CHAN_DEBUG_BOOL;
break;
}
+ case DIV_SYSTEM_K053260: {
+ DivPlatformK053260::Channel* ch=(DivPlatformK053260::Channel*)data;
+ ImGui::Text("> K053260");
+ COMMON_CHAN_DEBUG;
+ ImGui::Text("* Sample: %d",ch->sample);
+ ImGui::Text(" - pos: %d",ch->audPos);
+ ImGui::Text("- panning: %d",ch->panning);
+ ImGui::Text("- macroVolMul: %.2x",ch->macroVolMul);
+ COMMON_CHAN_DEBUG_BOOL;
+ ImGui::TextColored(ch->setPos?colorOn:colorOff,">> SetPos");
+ break;
+ }
default:
ImGui::Text("Unimplemented chip! Help!");
break;
diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp
index 4e6cdec5b..475798643 100644
--- a/src/gui/doAction.cpp
+++ b/src/gui/doAction.cpp
@@ -1344,7 +1344,8 @@ void FurnaceGUI::doAction(int what) {
i==DIV_INS_SNES ||
i==DIV_INS_ES5506 ||
i==DIV_INS_K007232 ||
- i==DIV_INS_GA20) {
+ i==DIV_INS_GA20 ||
+ i==DIV_INS_K053260) {
makeInsTypeList.push_back(i);
}
}
diff --git a/src/gui/gui.h b/src/gui/gui.h
index 7dddb53e7..9fcabf6c7 100644
--- a/src/gui/gui.h
+++ b/src/gui/gui.h
@@ -193,6 +193,7 @@ enum FurnaceGUIColors {
GUI_COLOR_INSTR_POKEMINI,
GUI_COLOR_INSTR_SM8521,
GUI_COLOR_INSTR_PV1000,
+ GUI_COLOR_INSTR_K053260,
GUI_COLOR_INSTR_UNKNOWN,
GUI_COLOR_CHANNEL_BG,
diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp
index 214eb4688..15b4e8c7f 100644
--- a/src/gui/guiConst.cpp
+++ b/src/gui/guiConst.cpp
@@ -131,6 +131,7 @@ const char* insTypes[DIV_INS_MAX+1]={
"Pokémon Mini/QuadTone",
"SM8521",
"PV-1000",
+ "K053260",
NULL
};
@@ -822,6 +823,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_INSTR_POKEMINI,"",ImVec4(1.0f,1.0f,0.3f,1.0f)),
D(GUI_COLOR_INSTR_SM8521,"",ImVec4(0.5f,0.55f,0.6f,1.0f)),
D(GUI_COLOR_INSTR_PV1000,"",ImVec4(0.4f,0.6f,0.7f,1.0f)),
+ D(GUI_COLOR_INSTR_K053260,"",ImVec4(1.0f,0.8f,0.1f,1.0f)),
D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)),
D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)),
@@ -1005,6 +1007,7 @@ const int availableSystems[]={
DIV_SYSTEM_GA20,
DIV_SYSTEM_SM8521,
DIV_SYSTEM_PV1000,
+ DIV_SYSTEM_K053260,
DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_PONG,
0 // don't remove this last one!
@@ -1113,6 +1116,7 @@ const int chipsSample[]={
DIV_SYSTEM_GA20,
DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_ES5506,
+ DIV_SYSTEM_K053260,
0 // don't remove this last one!
};
diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp
index abac70c1a..92692ce5d 100644
--- a/src/gui/insEdit.cpp
+++ b/src/gui/insEdit.cpp
@@ -4338,7 +4338,8 @@ void FurnaceGUI::drawInsEdit() {
ins->type==DIV_INS_SNES ||
ins->type==DIV_INS_ES5506 ||
ins->type==DIV_INS_K007232 ||
- ins->type==DIV_INS_GA20) {
+ ins->type==DIV_INS_GA20 ||
+ ins->type==DIV_INS_K053260) {
if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) {
String sName;
if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) {
@@ -5078,7 +5079,8 @@ void FurnaceGUI::drawInsEdit() {
}
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_SEGAPCM || ins->type==DIV_INS_MIKEY ||
ins->type==DIV_INS_MULTIPCM || ins->type==DIV_INS_SU || ins->type==DIV_INS_OPZ ||
- ins->type==DIV_INS_OPM || ins->type==DIV_INS_SNES || ins->type==DIV_INS_MSM5232) {
+ ins->type==DIV_INS_OPM || ins->type==DIV_INS_SNES || ins->type==DIV_INS_MSM5232 ||
+ ins->type==DIV_INS_K053260) {
volMax=127;
}
if (ins->type==DIV_INS_GB) {
@@ -5167,7 +5169,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_TIA || ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SCC ||
ins->type==DIV_INS_PET || ins->type==DIV_INS_SEGAPCM ||
ins->type==DIV_INS_FM || ins->type==DIV_INS_K007232 || ins->type==DIV_INS_GA20 ||
- ins->type==DIV_INS_SM8521 || ins->type==DIV_INS_PV1000) {
+ ins->type==DIV_INS_SM8521 || ins->type==DIV_INS_PV1000 || ins->type==DIV_INS_K053260) {
dutyMax=0;
}
if (ins->type==DIV_INS_VBOY) {
@@ -5267,6 +5269,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_SEGAPCM) waveMax=0;
if (ins->type==DIV_INS_K007232) waveMax=0;
if (ins->type==DIV_INS_GA20) waveMax=0;
+ if (ins->type==DIV_INS_K053260) waveMax=0;
if (ins->type==DIV_INS_POKEMINI) waveMax=0;
if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7;
if (ins->type==DIV_INS_PET) {
@@ -5385,6 +5388,11 @@ void FurnaceGUI::drawInsEdit() {
panMax=7;
panSingleNoBit=true;
}
+ if (ins->type==DIV_INS_K053260) {
+ panMin=-3;
+ panMax=3;
+ panSingleNoBit=true;
+ }
if (ins->type==DIV_INS_SU) {
panMin=-127;
panMax=127;
@@ -5475,7 +5483,8 @@ void FurnaceGUI::drawInsEdit() {
ins->type==DIV_INS_VBOY ||
(ins->type==DIV_INS_X1_010 && ins->amiga.useSample) ||
ins->type==DIV_INS_K007232 ||
- ins->type==DIV_INS_GA20) {
+ ins->type==DIV_INS_GA20 ||
+ ins->type==DIV_INS_K053260) {
macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
}
if (ex1Max>0) {
diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp
index 3354914c1..33924b846 100644
--- a/src/gui/presets.cpp
+++ b/src/gui/presets.cpp
@@ -1245,6 +1245,42 @@ void FurnaceGUI::initSystemPresets() {
CH(DIV_SYSTEM_K007232, 1.0f, 0, "") // ""
}
);
+ ENTRY(
+ "Konami Rollergames", {
+ CH(DIV_SYSTEM_OPL2, 1.0f, 0, ""), // 3.58MHz
+ CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // ""
+ }
+ );
+ ENTRY(
+ "Konami Rollergames (drums mode)", {
+ CH(DIV_SYSTEM_OPL2_DRUMS, 1.0f, 0, ""), // 3.58MHz
+ CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // ""
+ }
+ );
+ ENTRY(
+ "Konami Golfing Greats", {
+ CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // 3.58MHz
+ }
+ );
+ ENTRY(
+ "Konami Lightning Fighters", {
+ CH(DIV_SYSTEM_YM2151, 1.0f, 0, ""), // 3.58MHz
+ CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // ""
+ }
+ );
+ ENTRY(
+ "Konami Over Drive", {
+ CH(DIV_SYSTEM_YM2151, 1.0f, 0, ""), // 3.58MHz
+ CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // ""
+ CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // ""
+ }
+ );
+ ENTRY(
+ "Konami Asterix", {
+ CH(DIV_SYSTEM_YM2151, 1.0f, 0, "clockSel=2"), // 4MHz
+ CH(DIV_SYSTEM_K053260, 1.0f, 0, "clockSel=1"), // ""
+ }
+ );
ENTRY(
"Konami Hexion", {
CH(DIV_SYSTEM_SCC, 1.0f, 0, "clockSel=2"), // 1.5MHz (3MHz input)
@@ -2449,6 +2485,11 @@ void FurnaceGUI::initSystemPresets() {
CH(DIV_SYSTEM_ES5506, 1.0f, 0, "channels=31")
}
);
+ ENTRY(
+ "Konami K053260", {
+ CH(DIV_SYSTEM_K053260, 1.0f, 0, "")
+ }
+ );
CATEGORY_END;
CATEGORY_BEGIN("Wavetable","chips which use user-specified waveforms to generate sound.");
diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp
index 207c8838b..7edaeb0fa 100644
--- a/src/gui/settings.cpp
+++ b/src/gui/settings.cpp
@@ -1954,6 +1954,7 @@ void FurnaceGUI::drawSettings() {
UI_COLOR_CONFIG(GUI_COLOR_INSTR_POKEMINI,"Pokémon Mini");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_SM8521,"SM8521");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_PV1000,"PV-1000");
+ UI_COLOR_CONFIG(GUI_COLOR_INSTR_K053260,"K053260");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown");
ImGui::TreePop();
}
diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp
index 77bc6c726..31f66e0a7 100644
--- a/src/gui/sysConf.cpp
+++ b/src/gui/sysConf.cpp
@@ -1784,6 +1784,26 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
}
break;
}*/
+ case DIV_SYSTEM_K053260: {
+ int clockSel=flags.getInt("clockSel",0);
+
+ ImGui::Text("Clock rate:");
+ if (ImGui::RadioButton("3.58MHz (NTSC)",clockSel==0)) {
+ clockSel=0;
+ altered=true;
+ }
+ if (ImGui::RadioButton("4MHz",clockSel==1)) {
+ clockSel=1;
+ altered=true;
+ }
+
+ if (altered) {
+ e->lockSave([&]() {
+ flags.set("clockSel",clockSel);
+ });
+ }
+ break;
+ }
case DIV_SYSTEM_SWAN:
case DIV_SYSTEM_BUBSYS_WSG:
case DIV_SYSTEM_PET:
From 09726e6290a4ccdbd5360dcb80339e6472dd3d00 Mon Sep 17 00:00:00 2001
From: cam900
Date: Sun, 2 Apr 2023 14:34:10 +0900
Subject: [PATCH 002/454] Fix pitch
---
extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp
index 2ee4efac3..9c8d9434b 100644
--- a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp
+++ b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp
@@ -80,7 +80,7 @@ void k053260_core::voice_t::tick(u32 cycle)
{
m_bitpos -= 8;
}
- m_counter = 0x1000 - bitfield(m_pitch, 0, 12);
+ m_counter = (m_counter - 0x1000) + bitfield(m_pitch, 0, 12);
}
// calculate output
From dee28d218e302fabede1282aefae0634e8612f7e Mon Sep 17 00:00:00 2001
From: cam900
Date: Sun, 2 Apr 2023 14:55:10 +0900
Subject: [PATCH 003/454] Implement sample playback Fix register viewer Minor
optimize
---
.../vgsound_emu/src/k053260/k053260.cpp | 10 ++--
.../vgsound_emu/src/k053260/k053260.hpp | 22 ++++-----
src/engine/platform/k053260.cpp | 49 +++++++++++++------
src/engine/platform/k053260.h | 1 +
4 files changed, 51 insertions(+), 31 deletions(-)
diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp
index 9c8d9434b..990c1fc36 100644
--- a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp
+++ b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp
@@ -244,7 +244,7 @@ void k053260_core::voice_t::keyon()
m_bitpos = 4;
m_data = 0;
m_output = 0;
- std::fill(m_out.begin(), m_out.end(), 0);
+ std::fill_n(m_out, 2, 0);
}
// key off trigger
@@ -260,13 +260,13 @@ void k053260_core::reset()
//m_intf.write_int(0);
- std::fill(m_host2snd.begin(), m_host2snd.end(), 0);
- std::fill(m_snd2host.begin(), m_snd2host.end(), 0);
+ std::fill_n(m_host2snd, 2, 0);
+ std::fill_n(m_snd2host, 2, 0);
m_ctrl.reset();
//m_dac.reset();
- std::fill(m_reg.begin(), m_reg.end(), 0);
- std::fill(m_out.begin(), m_out.end(), 0);
+ std::fill_n(m_reg, 64, 0);
+ std::fill_n(m_out, 2, 0);
}
// reset voice
diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp
index feaffeac5..bfb7ea002 100644
--- a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp
+++ b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp
@@ -70,7 +70,7 @@ class k053260_core : public vgsound_emu_core
, m_data(0)
, m_output(0)
{
- m_out.fill(0);
+ std::fill_n(m_out, 2, 0);
}
// internal state
@@ -124,7 +124,7 @@ class k053260_core : public vgsound_emu_core
u8 m_bitpos = 4; // bit position for ADPCM decoding
u8 m_data = 0; // current data
s8 m_output = 0; // ADPCM buffer
- std::array m_out; // current output
+ s32 m_out[2]; // current output
};
class ctrl_t
@@ -232,10 +232,10 @@ class k053260_core : public vgsound_emu_core
//, m_ym3012(ym3012_t())
//, m_dac(dac_t())
{
- m_host2snd.fill(0);
- m_snd2host.fill(0);
- m_reg.fill(0);
- m_out.fill(0);
+ std::fill_n(m_host2snd, 2, 0);
+ std::fill_n(m_snd2host, 2, 0);
+ std::fill_n(m_reg, 64, 0);
+ std::fill_n(m_out, 2, 0);
}
// communications
@@ -267,19 +267,19 @@ class k053260_core : public vgsound_emu_core
inline s32 adpcm_lut(const u8 nibble) { return m_adpcm_lut[nibble]; }
private:
- std::array m_voice;
+ voice_t m_voice[4];
k053260_intf &m_intf; // common memory interface
- std::array m_host2snd;
- std::array m_snd2host;
+ u8 m_host2snd[2];
+ u8 m_snd2host[2];
ctrl_t m_ctrl; // chip control
//ym3012_t m_ym3012; // YM3012 output
//dac_t m_dac; // YM3012 interface
- std::array m_reg; // register pool
- std::array m_out; // stereo output
+ u8 m_reg[64]; // register pool
+ s32 m_out[2]; // stereo output
};
#endif
diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp
index 47e7636d1..f42a532dc 100644
--- a/src/engine/platform/k053260.cpp
+++ b/src/engine/platform/k053260.cpp
@@ -22,20 +22,32 @@
#include "../../ta-log.h"
#include
-#define rWrite(a,v) {if(!skipRegisterWrites) {k053260.write(a,v); regPool[a]=v; if(dumpWrites) addWrite(a,v);}}
+#define rWrite(a,v) {if(!skipRegisterWrites && a<0x30) {k053260.write(a,v); regPool[a]=v; if(dumpWrites) addWrite(a,v);}}
-#define CHIP_DIVIDER 64
-#define TICK_DIVIDER 4
+#define CHIP_DIVIDER 16
+#define TICK_DIVIDER 64 // for match to YM3012 output rate
const char* regCheatSheetK053260[]={
- "FreqL", "0",
- "FreqH", "1",
- "LengthL", "2",
- "LengthH", "3",
- "StartL", "4",
- "StartM", "5",
- "StartH", "6",
- "Volume", "7",
+ "MainToSub0", "00",
+ "MainToSub1", "01",
+ "SubToMain0", "02",
+ "SubToMain1", "03",
+ "CHx_FreqL", "08+x*8",
+ "CHx_FreqH", "09+x*8",
+ "CHx_LengthL", "0A+x*8",
+ "CHx_LengthH", "0B+x*8",
+ "CHx_StartL", "0C+x*8",
+ "CHx_StartM", "0D+x*8",
+ "CHx_StartH", "0E+x*8",
+ "CHx_Volume", "0F+x*8",
+ "KeyOn", "28",
+ "Status", "29",
+ "LoopFormat", "2A",
+ "Test", "2B",
+ "CH01_Pan", "2C",
+ "CH23_Pan", "2D",
+ "ROMReadback", "2E",
+ "Control", "2F",
NULL
};
@@ -49,8 +61,14 @@ inline void DivPlatformK053260::chWrite(unsigned char ch, unsigned int addr, uns
}
}
-// TODO: this code is weird
-// make sure newDispatch didn't break it up
+u8 DivPlatformK053260::read_sample(u32 address) {
+ if ((sampleMem!=NULL) && (address& wlist) {
- for (DivRegWrite& i: wlist) rWrite(i.addr&0x0f,i.val);
+ for (DivRegWrite& i: wlist) rWrite(i.addr&0x3f,i.val);
}
unsigned char* DivPlatformK053260::getRegisterPool() {
+ regPool[0x29]=k053260.read(0x29); // dynamically updated
return regPool;
}
diff --git a/src/engine/platform/k053260.h b/src/engine/platform/k053260.h
index c449206f6..3b21cb4dc 100644
--- a/src/engine/platform/k053260.h
+++ b/src/engine/platform/k053260.h
@@ -58,6 +58,7 @@ class DivPlatformK053260: public DivDispatch, public k053260_intf {
friend void putDispatchChan(void*,int,int);
public:
+ virtual u8 read_sample(u32 address) override;
void acquire(short** buf, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
From 8f783e604a4cd2c0bd2ae870b90a9e462a8490a1 Mon Sep 17 00:00:00 2001
From: cam900
Date: Sun, 2 Apr 2023 15:48:30 +0900
Subject: [PATCH 004/454] Minor fix
---
src/engine/platform/k053260.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp
index f42a532dc..801325f86 100644
--- a/src/engine/platform/k053260.cpp
+++ b/src/engine/platform/k053260.cpp
@@ -159,6 +159,7 @@ void DivPlatformK053260::tick(bool sysTick) {
}
if (chan[i].audPos>0) {
start=start+MIN(chan[i].audPos,s->length8);
+ length=MAX(1,length-chan[i].audPos);
}
start=MIN(start,getSampleMemCapacity()-31);
length=MIN(length,getSampleMemCapacity()-31);
From f56e09606a741ab0408bf892c390c8202f026dc6 Mon Sep 17 00:00:00 2001
From: cam900
Date: Sun, 2 Apr 2023 15:49:12 +0900
Subject: [PATCH 005/454] More fixes
---
src/engine/platform/k053260.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp
index 801325f86..dd8364fa1 100644
--- a/src/engine/platform/k053260.cpp
+++ b/src/engine/platform/k053260.cpp
@@ -161,8 +161,8 @@ void DivPlatformK053260::tick(bool sysTick) {
start=start+MIN(chan[i].audPos,s->length8);
length=MAX(1,length-chan[i].audPos);
}
- start=MIN(start,getSampleMemCapacity()-31);
- length=MIN(length,getSampleMemCapacity()-31);
+ start=MIN(start,getSampleMemCapacity());
+ length=MIN(65535,MIN(length,getSampleMemCapacity()));
rWrite(0x28,keyoff); // force keyoff first
rWrite(0x2a,loopoff);
chWrite(i,2,length&0xff);
From f7768dafe3937dc0b95829cb2659959502ff0cbb Mon Sep 17 00:00:00 2001
From: cam900
Date: Sun, 2 Apr 2023 23:01:57 +0900
Subject: [PATCH 006/454] Fix K053260 VGM output
---
src/engine/vgmOps.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp
index 4445ef93b..d68302b5d 100644
--- a/src/engine/vgmOps.cpp
+++ b/src/engine/vgmOps.cpp
@@ -1627,12 +1627,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_K053260:
if (!hasK053260) {
hasK053260=disCont[i].dispatch->chipClock;
- CHIP_VOL(40,0.4);
+ CHIP_VOL(29,0.4);
willExport[i]=true;
writeK053260[0]=disCont[i].dispatch;
} else if (!(hasK053260&0x40000000)) {
isSecond[i]=true;
- CHIP_VOL_SECOND(40,0.4);
+ CHIP_VOL_SECOND(29,0.4);
willExport[i]=true;
writeK053260[1]=disCont[i].dispatch;
hasK053260|=0x40000000;
From 9b877764c41db500bb05cf7f0c40a1b80d1a1d8a Mon Sep 17 00:00:00 2001
From: cam900
Date: Sun, 2 Apr 2023 23:33:34 +0900
Subject: [PATCH 007/454] Fix build
---
extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp
index 990c1fc36..b1c56a8d7 100644
--- a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp
+++ b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp
@@ -42,7 +42,6 @@ void k053260_core::voice_t::tick(u32 cycle)
{
if (m_enable && m_busy)
{
- bool update = false;
// update counter
m_counter += cycle;
if (m_counter >= 0x1000)
From 048728b496017cdf344e675e55d6514e549efe00 Mon Sep 17 00:00:00 2001
From: cam900
Date: Mon, 3 Apr 2023 00:34:09 +0900
Subject: [PATCH 008/454] Add reverse playback support
---
.../vgsound_emu/src/k053260/k053260.cpp | 6 ++++--
.../vgsound_emu/src/k053260/k053260.hpp | 9 +++++++-
src/engine/platform/k053260.cpp | 21 ++++++++++++++++---
src/engine/platform/k053260.h | 3 ++-
src/engine/sysDef.cpp | 5 ++++-
src/gui/debug.cpp | 1 +
6 files changed, 37 insertions(+), 8 deletions(-)
diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp
index b1c56a8d7..59d8af8f7 100644
--- a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp
+++ b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.cpp
@@ -49,7 +49,7 @@ void k053260_core::voice_t::tick(u32 cycle)
if (m_bitpos < 8)
{
m_bitpos += 8;
- m_addr = bitfield(m_addr + 1, 0, 21);
+ m_addr = m_reverse ? bitfield(m_addr - 1, 0, 21) : bitfield(m_addr + 1, 0, 21);
m_remain--;
if (m_remain < 0) // check end flag
{
@@ -69,7 +69,7 @@ void k053260_core::voice_t::tick(u32 cycle)
if (m_adpcm)
{
m_bitpos -= 4;
- const u8 nibble = bitfield(m_data, m_bitpos & 4, 4); // get nibble from ROM
+ const u8 nibble = bitfield(m_data, m_reverse ? (~m_bitpos & 4) : (m_bitpos & 4), 4); // get nibble from ROM
if (nibble)
{
m_output += m_host.adpcm_lut(nibble);
@@ -169,6 +169,7 @@ void k053260_core::write(u8 address, u8 data)
case 0x28: // keyon/off toggle
for (int i = 0; i < 4; i++)
{
+ m_voice[i].set_reverse(bitfield(data, 4 + i));
if (bitfield(data, i) && (!m_voice[i].enable()))
{ // rising edge (keyon)
m_voice[i].keyon();
@@ -276,6 +277,7 @@ void k053260_core::voice_t::reset()
m_loop = 0;
m_adpcm = 0;
m_pitch = 0;
+ m_reverse = 0;
m_start = 0;
m_length = 0;
m_volume = 0;
diff --git a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp
index bfb7ea002..9eb81363d 100644
--- a/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp
+++ b/extern/vgsound_emu-modified/vgsound_emu/src/k053260/k053260.hpp
@@ -59,6 +59,7 @@ class k053260_core : public vgsound_emu_core
, m_loop(0)
, m_adpcm(0)
, m_pitch(0)
+ , m_reverse(0)
, m_start(0)
, m_length(0)
, m_volume(0)
@@ -91,6 +92,11 @@ class k053260_core : public vgsound_emu_core
inline void set_adpcm(bool adpcm) { m_adpcm = adpcm ? 1 : 0; }
+ inline void set_reverse(const bool reverse)
+ {
+ m_reverse = reverse ? 1 : 0;
+ }
+
inline void length_inc() { m_length = (m_length + 1) & 0xffff; }
inline void set_pan(u8 pan) { m_pan = pan & 7; }
@@ -114,10 +120,11 @@ class k053260_core : public vgsound_emu_core
u16 m_loop : 1; // loop flag
u16 m_adpcm : 1; // ADPCM flag
u16 m_pitch : 12; // pitch
+ u8 m_reverse : 1; // reverse playback
u32 m_start = 0; // start position
u16 m_length = 0; // source length
u8 m_volume = 0; // master volume
- int m_pan = -1; // master pan
+ s32 m_pan = 4; // master pan
u16 m_counter = 0; // frequency counter
u32 m_addr = 0; // current address
s32 m_remain = 0; // remain for end sample
diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp
index dd8364fa1..b35bc5a27 100644
--- a/src/engine/platform/k053260.cpp
+++ b/src/engine/platform/k053260.cpp
@@ -133,7 +133,7 @@ void DivPlatformK053260::tick(bool sysTick) {
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
unsigned char keyon=regPool[0x28]|(1<=0 && chan[i].samplesong.sampleLen) {
start=sampleOffK053260[chan[i].sample];
- length=start+s->length8;
+ length=s->length8;
+ if (chan[i].reverse) {
+ start+=length;
+ keyon|=(16<0) {
- start=start+MIN(chan[i].audPos,s->length8);
+ if (chan[i].reverse) {
+ start=start-MIN(chan[i].audPos,s->length8);
+ }
+ else {
+ start=start+MIN(chan[i].audPos,s->length8);
+ }
length=MAX(1,length-chan[i].audPos);
}
start=MIN(start,getSampleMemCapacity());
@@ -314,6 +323,12 @@ int DivPlatformK053260::dispatch(DivCommand c) {
chan[c.chan].audPos=c.value;
chan[c.chan].setPos=true;
break;
+ case DIV_CMD_SAMPLE_DIR: {
+ if (chan[c.chan].reverse!=(bool)(c.value&1)) {
+ chan[c.chan].reverse=c.value&1;
+ }
+ break;
+ }
case DIV_CMD_GET_VOLMAX:
return 127;
break;
diff --git a/src/engine/platform/k053260.h b/src/engine/platform/k053260.h
index 3b21cb4dc..42ae0565a 100644
--- a/src/engine/platform/k053260.h
+++ b/src/engine/platform/k053260.h
@@ -29,7 +29,7 @@ class DivPlatformK053260: public DivDispatch, public k053260_intf {
unsigned int audPos;
int sample, wave;
int panning;
- bool setPos;
+ bool setPos, reverse;
int macroVolMul;
Channel():
SharedChannel(127),
@@ -38,6 +38,7 @@ class DivPlatformK053260: public DivDispatch, public k053260_intf {
wave(-1),
panning(4),
setPos(false),
+ reverse(false),
macroVolMul(64) {}
};
Channel chan[4];
diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp
index f236fcd2c..0ce88965e 100644
--- a/src/engine/sysDef.cpp
+++ b/src/engine/sysDef.cpp
@@ -1862,7 +1862,10 @@ void DivEngine::registerSystems() {
{"CH1", "CH2", "CH3", "CH4"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
{DIV_INS_K053260, DIV_INS_K053260, DIV_INS_K053260, DIV_INS_K053260},
- {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}
+ {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA},
+ {
+ {0xdf, {DIV_CMD_SAMPLE_DIR, "DFxx: Set sample playback direction (0: normal; 1: reverse)"}}
+ }
);
sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(
diff --git a/src/gui/debug.cpp b/src/gui/debug.cpp
index 7a01edf8e..b1cdb2920 100644
--- a/src/gui/debug.cpp
+++ b/src/gui/debug.cpp
@@ -1101,6 +1101,7 @@ void putDispatchChan(void* data, int chanNum, int type) {
ImGui::Text("- macroVolMul: %.2x",ch->macroVolMul);
COMMON_CHAN_DEBUG_BOOL;
ImGui::TextColored(ch->setPos?colorOn:colorOff,">> SetPos");
+ ImGui::TextColored(ch->reverse?colorOn:colorOff,">> Reverse");
break;
}
default:
From 40f3455357fc9f8c2e4f7fd45a41559752a97e3a Mon Sep 17 00:00:00 2001
From: cam900
Date: Mon, 3 Apr 2023 00:35:02 +0900
Subject: [PATCH 009/454] Fix build
---
src/engine/platform/k053260.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp
index b35bc5a27..1705f1568 100644
--- a/src/engine/platform/k053260.cpp
+++ b/src/engine/platform/k053260.cpp
@@ -22,7 +22,7 @@
#include "../../ta-log.h"
#include
-#define rWrite(a,v) {if(!skipRegisterWrites && a<0x30) {k053260.write(a,v); regPool[a]=v; if(dumpWrites) addWrite(a,v);}}
+#define rWrite(a,v) {if((!skipRegisterWrites) && (a<0x30)) {k053260.write(a,v); regPool[a]=v; if(dumpWrites) addWrite(a,v);}}
#define CHIP_DIVIDER 16
#define TICK_DIVIDER 64 // for match to YM3012 output rate
From 04f208c535677985d203b3301cad9c2a0767b580 Mon Sep 17 00:00:00 2001
From: cam900
Date: Mon, 3 Apr 2023 06:34:05 +0900
Subject: [PATCH 010/454] Fix build again
---
src/engine/platform/k053260.cpp | 6 +++---
src/engine/platform/k053260.h | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp
index 1705f1568..04cec849d 100644
--- a/src/engine/platform/k053260.cpp
+++ b/src/engine/platform/k053260.cpp
@@ -22,7 +22,7 @@
#include "../../ta-log.h"
#include
-#define rWrite(a,v) {if((!skipRegisterWrites) && (a<0x30)) {k053260.write(a,v); regPool[a]=v; if(dumpWrites) addWrite(a,v);}}
+#define rWrite(a,v) {if(!skipRegisterWrites) {k053260.write(a,v); regPool[a]=v; if(dumpWrites) addWrite(a,v);}}
#define CHIP_DIVIDER 16
#define TICK_DIVIDER 64 // for match to YM3012 output rate
@@ -374,7 +374,7 @@ DivDispatchOscBuffer* DivPlatformK053260::getOscBuffer(int ch) {
}
void DivPlatformK053260::reset() {
- memset(regPool,0,48);
+ memset(regPool,0,64);
k053260.reset();
rWrite(0x28,0); // keyoff all channels
for (int i=0; i<4; i++) {
@@ -434,7 +434,7 @@ unsigned char* DivPlatformK053260::getRegisterPool() {
}
int DivPlatformK053260::getRegisterPoolSize() {
- return 48;
+ return 64;
}
const void* DivPlatformK053260::getSampleMem(int index) {
diff --git a/src/engine/platform/k053260.h b/src/engine/platform/k053260.h
index 42ae0565a..c0c3f5a97 100644
--- a/src/engine/platform/k053260.h
+++ b/src/engine/platform/k053260.h
@@ -52,7 +52,7 @@ class DivPlatformK053260: public DivDispatch, public k053260_intf {
unsigned char* sampleMem;
size_t sampleMemLen;
k053260_core k053260;
- unsigned char regPool[48];
+ unsigned char regPool[64];
void updatePanning(unsigned char mask);
friend void putDispatchChip(void*,int);
From d9c64e7c8c0e9db253f8f6f6b38cd9a357bbfcf1 Mon Sep 17 00:00:00 2001
From: cam900
Date: Mon, 3 Apr 2023 06:42:37 +0900
Subject: [PATCH 011/454] Fix typecasting
---
src/engine/platform/k053260.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp
index 04cec849d..a082be6d1 100644
--- a/src/engine/platform/k053260.cpp
+++ b/src/engine/platform/k053260.cpp
@@ -70,7 +70,7 @@ u8 DivPlatformK053260::read_sample(u32 address) {
}
void DivPlatformK053260::acquire(short** buf, size_t len) {
- for (int i=0; i
Date: Mon, 3 Apr 2023 06:44:00 +0900
Subject: [PATCH 012/454] Minor optimize
---
src/engine/platform/k053260.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp
index a082be6d1..7005f18d3 100644
--- a/src/engine/platform/k053260.cpp
+++ b/src/engine/platform/k053260.cpp
@@ -63,8 +63,7 @@ inline void DivPlatformK053260::chWrite(unsigned char ch, unsigned int addr, uns
u8 DivPlatformK053260::read_sample(u32 address) {
if ((sampleMem!=NULL) && (address
Date: Mon, 3 Apr 2023 07:02:56 +0900
Subject: [PATCH 013/454] Fix Mac compile
---
src/engine/platform/k053260.h | 53 +++++++++++++++++------------------
1 file changed, 26 insertions(+), 27 deletions(-)
diff --git a/src/engine/platform/k053260.h b/src/engine/platform/k053260.h
index c0c3f5a97..ce531f3d5 100644
--- a/src/engine/platform/k053260.h
+++ b/src/engine/platform/k053260.h
@@ -60,33 +60,32 @@ class DivPlatformK053260: public DivDispatch, public k053260_intf {
public:
virtual u8 read_sample(u32 address) override;
- void acquire(short** buf, size_t len);
- int dispatch(DivCommand c);
- void* getChanState(int chan);
- DivMacroInt* getChanMacroInt(int ch);
- DivDispatchOscBuffer* getOscBuffer(int chan);
- unsigned char* getRegisterPool();
- int getRegisterPoolSize();
- void reset();
- void forceIns();
- void tick(bool sysTick=true);
- void muteChannel(int ch, bool mute);
- int getOutputCount();
- void setChipModel(int type);
- void notifyInsChange(int ins);
- void notifyWaveChange(int wave);
- void notifyInsDeletion(void* ins);
- void setFlags(const DivConfig& flags);
- void poke(unsigned int addr, unsigned short val);
- void poke(std::vector& wlist);
- const char** getRegisterSheet();
- const void* getSampleMem(int index = 0);
- size_t getSampleMemCapacity(int index = 0);
- size_t getSampleMemUsage(int index = 0);
- bool isSampleLoaded(int index, int sample);
- void renderSamples(int chipID);
- int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
- void quit();
+ virtual void acquire(short** buf, size_t len) override;
+ virtual int dispatch(DivCommand c) override;
+ virtual void* getChanState(int chan) override;
+ virtual DivMacroInt* getChanMacroInt(int ch) override;
+ virtual DivDispatchOscBuffer* getOscBuffer(int chan) override;
+ virtual unsigned char* getRegisterPool() override;
+ virtual int getRegisterPoolSize() override;
+ virtual void reset() override;
+ virtual void forceIns() override;
+ virtual void tick(bool sysTick=true) override;
+ virtual void muteChannel(int ch, bool mute) override;
+ virtual int getOutputCount() override;
+ virtual void notifyInsChange(int ins) override;
+ virtual void notifyWaveChange(int wave) override;
+ virtual void notifyInsDeletion(void* ins) override;
+ virtual void setFlags(const DivConfig& flags) override;
+ virtual void poke(unsigned int addr, unsigned short val) override;
+ virtual void poke(std::vector& wlist) override;
+ virtual const char** getRegisterSheet() override;
+ virtual const void* getSampleMem(int index = 0) override;
+ virtual size_t getSampleMemCapacity(int index = 0) override;
+ virtual size_t getSampleMemUsage(int index = 0) override;
+ virtual bool isSampleLoaded(int index, int sample) override;
+ virtual void renderSamples(int chipID) override;
+ virtual int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) override;
+ virtual void quit() override;
DivPlatformK053260():
DivDispatch(),
k053260_intf(),
From 74958c59fa2368e2c8f078807745a9a147d1469d Mon Sep 17 00:00:00 2001
From: cam900
Date: Sat, 8 Apr 2023 08:52:16 +0900
Subject: [PATCH 014/454] Sync with master
---
src/engine/platform/k053260.cpp | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp
index 7005f18d3..ec5f26cd5 100644
--- a/src/engine/platform/k053260.cpp
+++ b/src/engine/platform/k053260.cpp
@@ -223,7 +223,10 @@ int DivPlatformK053260::dispatch(DivCommand c) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127;
- if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
+ if (c.value!=DIV_NOTE_NULL) {
+ chan[c.chan].sample=ins->amiga.getSample(c.value);
+ c.value=ins->amiga.getFreq(c.value);
+ }
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
}
From 0e072b62b3cc883785be966a2ff7e4e0caf7ea79 Mon Sep 17 00:00:00 2001
From: cam900
Date: Tue, 2 May 2023 15:14:46 +0900
Subject: [PATCH 015/454] Fix sample cutoff with looping
---
src/engine/platform/k053260.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/engine/platform/k053260.cpp b/src/engine/platform/k053260.cpp
index ec5f26cd5..396316d62 100644
--- a/src/engine/platform/k053260.cpp
+++ b/src/engine/platform/k053260.cpp
@@ -470,7 +470,7 @@ void DivPlatformK053260::renderSamples(int sysID) {
continue;
}
- int length=MIN(65535,s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT));
+ int length=MIN(65535,s->getEndPosition(DIV_SAMPLE_DEPTH_8BIT));
int actualLength=MIN((int)(getSampleMemCapacity()-memPos),length);
if (actualLength>0) {
sampleOffK053260[i]=memPos-1;
From e1b58427280647b1e63d775989e745e9cc6127de Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Mon, 15 May 2023 01:36:02 -0500
Subject: [PATCH 016/454] asset directories, part 1
---
src/gui/dataList.cpp | 651 ++++++++++++++++++++++++-------------------
src/gui/doAction.cpp | 9 +
src/gui/gui.cpp | 13 +
src/gui/gui.h | 9 +
src/gui/guiConst.cpp | 3 +
src/gui/settings.cpp | 3 +
6 files changed, 399 insertions(+), 289 deletions(-)
diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp
index 51f6f43e3..14a1ccc93 100644
--- a/src/gui/dataList.cpp
+++ b/src/gui/dataList.cpp
@@ -30,6 +30,282 @@ const char* sampleNote[12]={
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
};
+void FurnaceGUI::insListItem(int i) {
+ ImGui::PushID(i);
+ String name=ICON_FA_CIRCLE_O;
+ const char* insType="Bug!";
+ if (i>=0) {
+ DivInstrument* ins=e->song.ins[i];
+ insType=(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type];
+ if (ins->type==DIV_INS_N163) insType=settings.c163Name.c_str();
+ switch (ins->type) {
+ case DIV_INS_FM:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FM]);
+ name=fmt::sprintf(ICON_FA_AREA_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_STD:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_STD]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_GB:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GB]);
+ name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
+ break;
+ case DIV_INS_C64:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_C64]);
+ name=fmt::sprintf(ICON_FA_KEYBOARD_O "##_INS%d",i);
+ break;
+ case DIV_INS_AMIGA:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AMIGA]);
+ name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
+ break;
+ case DIV_INS_PCE:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PCE]);
+ name=fmt::sprintf(ICON_FA_ID_BADGE "##_INS%d",i);
+ break;
+ case DIV_INS_AY:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_AY8930:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY8930]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_TIA:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_TIA]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_SAA1099:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SAA1099]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_VIC:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VIC]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_PET:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PET]);
+ name=fmt::sprintf(ICON_FA_SQUARE "##_INS%d",i);
+ break;
+ case DIV_INS_VRC6:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_VRC6_SAW:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6_SAW]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_OPLL:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPLL]);
+ name=fmt::sprintf(ICON_FA_AREA_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_OPL:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL]);
+ name=fmt::sprintf(ICON_FA_AREA_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_FDS:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FDS]);
+ name=fmt::sprintf(ICON_FA_FLOPPY_O "##_INS%d",i);
+ break;
+ case DIV_INS_VBOY:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VBOY]);
+ name=fmt::sprintf(ICON_FA_BINOCULARS "##_INS%d",i);
+ break;
+ case DIV_INS_N163:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_N163]);
+ name=fmt::sprintf(ICON_FA_CALCULATOR "##_INS%d",i);
+ break;
+ case DIV_INS_SCC:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SCC]);
+ name=fmt::sprintf(ICON_FA_CALCULATOR "##_INS%d",i);
+ break;
+ case DIV_INS_OPZ:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPZ]);
+ name=fmt::sprintf(ICON_FA_AREA_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_POKEY:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEY]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_BEEPER:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_BEEPER]);
+ name=fmt::sprintf(ICON_FA_SQUARE "##_INS%d",i);
+ break;
+ case DIV_INS_SWAN:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SWAN]);
+ name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
+ break;
+ case DIV_INS_MIKEY:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MIKEY]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_VERA:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VERA]);
+ name=fmt::sprintf(ICON_FA_KEYBOARD_O "##_INS%d",i);
+ break;
+ case DIV_INS_X1_010:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_X1_010]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_ES5506:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_ES5506]);
+ name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
+ break;
+ case DIV_INS_MULTIPCM:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MULTIPCM]);
+ name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
+ break;
+ case DIV_INS_SNES:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SNES]);
+ name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
+ break;
+ case DIV_INS_SU:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SU]);
+ name=fmt::sprintf(ICON_FA_MICROCHIP "##_INS%d",i);
+ break;
+ case DIV_INS_NAMCO:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_NAMCO]);
+ name=fmt::sprintf(ICON_FA_PIE_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_OPL_DRUMS:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL_DRUMS]);
+ name=fmt::sprintf(ICON_FA_COFFEE "##_INS%d",i);
+ break;
+ case DIV_INS_OPM:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPM]);
+ name=fmt::sprintf(ICON_FA_AREA_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_NES:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_NES]);
+ name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
+ break;
+ case DIV_INS_MSM6258:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MSM6258]);
+ name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
+ break;
+ case DIV_INS_MSM6295:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MSM6295]);
+ name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
+ break;
+ case DIV_INS_ADPCMA:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_ADPCMA]);
+ name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
+ break;
+ case DIV_INS_ADPCMB:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_ADPCMB]);
+ name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
+ break;
+ case DIV_INS_SEGAPCM:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SEGAPCM]);
+ name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
+ break;
+ case DIV_INS_QSOUND:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_QSOUND]);
+ name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
+ break;
+ case DIV_INS_YMZ280B:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_YMZ280B]);
+ name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
+ break;
+ case DIV_INS_RF5C68:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_RF5C68]);
+ name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
+ break;
+ case DIV_INS_MSM5232:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MSM5232]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_T6W28:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_T6W28]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_K007232:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_K007232]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_GA20:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GA20]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_POKEMINI:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEMINI]);
+ name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
+ break;
+ case DIV_INS_SM8521:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SM8521]);
+ name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
+ break;
+ case DIV_INS_PV1000:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PV1000]);
+ name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
+ break;
+ default:
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]);
+ name=fmt::sprintf(ICON_FA_QUESTION "##_INS%d",i);
+ break;
+ }
+ } else {
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
+ }
+ if (ImGui::Selectable(name.c_str(),(i==-1)?(curIns<0 || curIns>=e->song.insLen):(curIns==i))) {
+ curIns=i;
+ wavePreviewInit=true;
+ updateFMPreview=true;
+ }
+ if (wantScrollList && curIns==i) ImGui::SetScrollHereY();
+ if (settings.insFocusesPattern && patternOpen && ImGui::IsItemActivated()) {
+ nextWindow=GUI_WINDOW_PATTERN;
+ curIns=i;
+ wavePreviewInit=true;
+ updateFMPreview=true;
+ }
+ if (ImGui::IsItemHovered() && i>=0 && !mobileUI) {
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
+ ImGui::SetTooltip("%s",insType);
+ ImGui::PopStyleColor();
+ if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
+ insEditOpen=true;
+ nextWindow=GUI_WINDOW_INS_EDIT;
+ }
+ }
+ if (i>=0) {
+ if (ImGui::BeginPopupContextItem("InsRightMenu")) {
+ curIns=i;
+ updateFMPreview=true;
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
+ if (ImGui::MenuItem("replace...")) {
+ doAction((curIns>=0 && curIns<(int)e->song.ins.size())?GUI_ACTION_INS_LIST_OPEN_REPLACE:GUI_ACTION_INS_LIST_OPEN);
+ }
+ if (ImGui::MenuItem("save")) {
+ doAction(GUI_ACTION_INS_LIST_SAVE);
+ }
+ if (ImGui::MenuItem("save (legacy .fui)")) {
+ doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
+ }
+ if (ImGui::MenuItem("save (.dmp)")) {
+ doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
+ }
+ if (ImGui::MenuItem("delete")) {
+ doAction(GUI_ACTION_INS_LIST_DELETE);
+ }
+ ImGui::PopStyleColor();
+ ImGui::EndPopup();
+ }
+ }
+ if (i>=0) {
+ if (i<(int)e->song.ins.size()) {
+ DivInstrument* ins=e->song.ins[i];
+ ImGui::SameLine();
+ ImGui::Text("%.2X: %s",i,ins->name.c_str());
+ }
+ } else {
+ ImGui::SameLine();
+ ImGui::Text("- None -");
+ }
+ ImGui::PopID();
+ ImGui::PopStyleColor();
+}
+
void FurnaceGUI::drawInsList(bool asChild) {
if (nextWindow==GUI_WINDOW_INS_LIST) {
insListOpen=true;
@@ -176,18 +452,50 @@ void FurnaceGUI::drawInsList(bool asChild) {
ImGui::EndPopup();
}
ImGui::SameLine();
- if (ImGui::ArrowButton("InsUp",ImGuiDir_Up)) {
- doAction(GUI_ACTION_INS_LIST_MOVE_UP);
+ pushToggleColors(insListDir);
+ if (ImGui::Button(ICON_FA_SITEMAP "##DirMode")) {
+ doAction(GUI_ACTION_INS_LIST_DIR_VIEW);
}
+ popToggleColors();
if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip("Move up");
+ ImGui::SetTooltip("Toggle folders/standard view");
}
- ImGui::SameLine();
- if (ImGui::ArrowButton("InsDown",ImGuiDir_Down)) {
- doAction(GUI_ACTION_INS_LIST_MOVE_DOWN);
- }
- if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip("Move down");
+ if (!insListDir) {
+ ImGui::SameLine();
+ if (ImGui::ArrowButton("InsUp",ImGuiDir_Up)) {
+ doAction(GUI_ACTION_INS_LIST_MOVE_UP);
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("Move up");
+ }
+ ImGui::SameLine();
+ if (ImGui::ArrowButton("InsDown",ImGuiDir_Down)) {
+ doAction(GUI_ACTION_INS_LIST_MOVE_DOWN);
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("Move down");
+ }
+ } else {
+ ImGui::SameLine();
+ if (ImGui::Button(ICON_FA_FOLDER "##InsFolder")) {
+ folderString="";
+ }
+ if (ImGui::BeginPopupContextItem("NewInsFolder",ImGuiMouseButton_Left)) {
+ ImGui::InputText("##FolderName",&folderString);
+ ImGui::SameLine();
+ ImGui::BeginDisabled(folderString.empty());
+ if (ImGui::Button("Create")) {
+ e->lockEngine([this]() {
+ e->song.insDir.push_back(DivAssetDir(folderString));
+ });
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::EndDisabled();
+ ImGui::EndPopup();
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("New folder");
+ }
}
}
ImGui::SameLine();
@@ -217,6 +525,7 @@ void FurnaceGUI::drawInsList(bool asChild) {
int columns=settings.horizontalDataView?(int)(ceil((double)(e->song.ins.size()+1)/(double)availableRows)):1;
if (columns<1) columns=1;
if (columns>64) columns=64;
+ if (insListDir) columns=1;
if (ImGui::BeginTable("InsListScroll",columns,(settings.horizontalDataView?ImGuiTableFlags_ScrollX:0)|ImGuiTableFlags_ScrollY)) {
if (settings.unifiedDataView) {
ImGui::TableNextRow();
@@ -225,294 +534,40 @@ void FurnaceGUI::drawInsList(bool asChild) {
ImGui::Indent();
}
- if (settings.horizontalDataView) {
+ if (settings.horizontalDataView && !insListDir) {
ImGui::TableNextRow();
}
- int curRow=0;
- for (int i=-1; i<(int)e->song.ins.size(); i++) {
- ImGui::PushID(i);
- String name=ICON_FA_CIRCLE_O;
- const char* insType="Bug!";
- if (i>=0) {
- DivInstrument* ins=e->song.ins[i];
- insType=(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type];
- if (ins->type==DIV_INS_N163) insType=settings.c163Name.c_str();
- switch (ins->type) {
- case DIV_INS_FM:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FM]);
- name=fmt::sprintf(ICON_FA_AREA_CHART "##_INS%d",i);
- break;
- case DIV_INS_STD:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_STD]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_GB:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GB]);
- name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
- break;
- case DIV_INS_C64:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_C64]);
- name=fmt::sprintf(ICON_FA_KEYBOARD_O "##_INS%d",i);
- break;
- case DIV_INS_AMIGA:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AMIGA]);
- name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
- break;
- case DIV_INS_PCE:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PCE]);
- name=fmt::sprintf(ICON_FA_ID_BADGE "##_INS%d",i);
- break;
- case DIV_INS_AY:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_AY8930:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY8930]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_TIA:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_TIA]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_SAA1099:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SAA1099]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_VIC:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VIC]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_PET:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PET]);
- name=fmt::sprintf(ICON_FA_SQUARE "##_INS%d",i);
- break;
- case DIV_INS_VRC6:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_VRC6_SAW:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6_SAW]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_OPLL:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPLL]);
- name=fmt::sprintf(ICON_FA_AREA_CHART "##_INS%d",i);
- break;
- case DIV_INS_OPL:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL]);
- name=fmt::sprintf(ICON_FA_AREA_CHART "##_INS%d",i);
- break;
- case DIV_INS_FDS:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FDS]);
- name=fmt::sprintf(ICON_FA_FLOPPY_O "##_INS%d",i);
- break;
- case DIV_INS_VBOY:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VBOY]);
- name=fmt::sprintf(ICON_FA_BINOCULARS "##_INS%d",i);
- break;
- case DIV_INS_N163:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_N163]);
- name=fmt::sprintf(ICON_FA_CALCULATOR "##_INS%d",i);
- break;
- case DIV_INS_SCC:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SCC]);
- name=fmt::sprintf(ICON_FA_CALCULATOR "##_INS%d",i);
- break;
- case DIV_INS_OPZ:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPZ]);
- name=fmt::sprintf(ICON_FA_AREA_CHART "##_INS%d",i);
- break;
- case DIV_INS_POKEY:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEY]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_BEEPER:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_BEEPER]);
- name=fmt::sprintf(ICON_FA_SQUARE "##_INS%d",i);
- break;
- case DIV_INS_SWAN:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SWAN]);
- name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
- break;
- case DIV_INS_MIKEY:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MIKEY]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_VERA:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VERA]);
- name=fmt::sprintf(ICON_FA_KEYBOARD_O "##_INS%d",i);
- break;
- case DIV_INS_X1_010:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_X1_010]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_ES5506:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_ES5506]);
- name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
- break;
- case DIV_INS_MULTIPCM:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MULTIPCM]);
- name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
- break;
- case DIV_INS_SNES:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SNES]);
- name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
- break;
- case DIV_INS_SU:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SU]);
- name=fmt::sprintf(ICON_FA_MICROCHIP "##_INS%d",i);
- break;
- case DIV_INS_NAMCO:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_NAMCO]);
- name=fmt::sprintf(ICON_FA_PIE_CHART "##_INS%d",i);
- break;
- case DIV_INS_OPL_DRUMS:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL_DRUMS]);
- name=fmt::sprintf(ICON_FA_COFFEE "##_INS%d",i);
- break;
- case DIV_INS_OPM:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPM]);
- name=fmt::sprintf(ICON_FA_AREA_CHART "##_INS%d",i);
- break;
- case DIV_INS_NES:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_NES]);
- name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
- break;
- case DIV_INS_MSM6258:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MSM6258]);
- name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
- break;
- case DIV_INS_MSM6295:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MSM6295]);
- name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
- break;
- case DIV_INS_ADPCMA:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_ADPCMA]);
- name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
- break;
- case DIV_INS_ADPCMB:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_ADPCMB]);
- name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
- break;
- case DIV_INS_SEGAPCM:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SEGAPCM]);
- name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
- break;
- case DIV_INS_QSOUND:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_QSOUND]);
- name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
- break;
- case DIV_INS_YMZ280B:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_YMZ280B]);
- name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
- break;
- case DIV_INS_RF5C68:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_RF5C68]);
- name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
- break;
- case DIV_INS_MSM5232:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MSM5232]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_T6W28:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_T6W28]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_K007232:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_K007232]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_GA20:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GA20]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_POKEMINI:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEMINI]);
- name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
- break;
- case DIV_INS_SM8521:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SM8521]);
- name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
- break;
- case DIV_INS_PV1000:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PV1000]);
- name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
- break;
- default:
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]);
- name=fmt::sprintf(ICON_FA_QUESTION "##_INS%d",i);
- break;
+ if (insListDir) {
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ insListItem(-1);
+ for (DivAssetDir& i: e->song.insDir) {
+ if (!i.name.empty()) {
+ ImGui::Text(ICON_FA_FOLDER_OPEN " %s",i.name.c_str());
+ ImGui::Indent();
}
- } else {
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
- }
- if (!settings.horizontalDataView) {
- ImGui::TableNextRow();
- ImGui::TableNextColumn();
- } else if (curRow==0) {
- ImGui::TableNextColumn();
- }
- if (ImGui::Selectable(name.c_str(),(i==-1)?(curIns<0 || curIns>=e->song.insLen):(curIns==i))) {
- curIns=i;
- wavePreviewInit=true;
- updateFMPreview=true;
- }
- if (wantScrollList && curIns==i) ImGui::SetScrollHereY();
- if (settings.insFocusesPattern && patternOpen && ImGui::IsItemActivated()) {
- nextWindow=GUI_WINDOW_PATTERN;
- curIns=i;
- wavePreviewInit=true;
- updateFMPreview=true;
- }
- if (ImGui::IsItemHovered() && i>=0 && !mobileUI) {
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
- ImGui::SetTooltip("%s",insType);
- ImGui::PopStyleColor();
- if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
- insEditOpen=true;
- nextWindow=GUI_WINDOW_INS_EDIT;
+ for (int j: i.entries) {
+ insListItem(j);
+ }
+ if (!i.name.empty()) {
+ ImGui::Unindent();
}
}
- if (i>=0) {
- if (ImGui::BeginPopupContextItem("InsRightMenu")) {
- curIns=i;
- updateFMPreview=true;
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
- if (ImGui::MenuItem("replace...")) {
- doAction((curIns>=0 && curIns<(int)e->song.ins.size())?GUI_ACTION_INS_LIST_OPEN_REPLACE:GUI_ACTION_INS_LIST_OPEN);
- }
- if (ImGui::MenuItem("save")) {
- doAction(GUI_ACTION_INS_LIST_SAVE);
- }
- if (ImGui::MenuItem("save (legacy .fui)")) {
- doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
- }
- if (ImGui::MenuItem("save (.dmp)")) {
- doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
- }
- if (ImGui::MenuItem("delete")) {
- doAction(GUI_ACTION_INS_LIST_DELETE);
- }
- ImGui::PopStyleColor();
- ImGui::EndPopup();
+ } else {
+ int curRow=0;
+ for (int i=-1; i<(int)e->song.ins.size(); i++) {
+ if (!settings.horizontalDataView) {
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ } else if (curRow==0) {
+ ImGui::TableNextColumn();
+ }
+ insListItem(i);
+ if (settings.horizontalDataView) {
+ if (++curRow>=availableRows) curRow=0;
}
}
- if (i>=0) {
- if (i<(int)e->song.ins.size()) {
- DivInstrument* ins=e->song.ins[i];
- ImGui::SameLine();
- ImGui::Text("%.2X: %s",i,ins->name.c_str());
- }
- } else {
- ImGui::SameLine();
- ImGui::Text("- None -");
- }
- ImGui::PopStyleColor();
- if (settings.horizontalDataView) {
- if (++curRow>=availableRows) curRow=0;
- }
- ImGui::PopID();
}
if (settings.unifiedDataView) {
@@ -608,6 +663,15 @@ void FurnaceGUI::drawWaveList(bool asChild) {
}
}
ImGui::SameLine();
+ pushToggleColors(waveListDir);
+ if (ImGui::Button(ICON_FA_SITEMAP "##WaveDirMode")) {
+ doAction(GUI_ACTION_WAVE_LIST_DIR_VIEW);
+ }
+ popToggleColors();
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("Toggle folders/standard view");
+ }
+ ImGui::SameLine();
if (ImGui::ArrowButton("WaveUp",ImGuiDir_Up)) {
doAction(GUI_ACTION_WAVE_LIST_MOVE_UP);
}
@@ -716,6 +780,15 @@ void FurnaceGUI::drawSampleList(bool asChild) {
ImGui::EndPopup();
}
ImGui::SameLine();
+ pushToggleColors(sampleListDir);
+ if (ImGui::Button(ICON_FA_SITEMAP "##SampleDirMode")) {
+ doAction(GUI_ACTION_SAMPLE_LIST_DIR_VIEW);
+ }
+ popToggleColors();
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("Toggle folders/standard view");
+ }
+ ImGui::SameLine();
if (ImGui::ArrowButton("SampleUp",ImGuiDir_Up)) {
doAction(GUI_ACTION_SAMPLE_LIST_MOVE_UP);
}
diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp
index 1929d7c0f..e6a244139 100644
--- a/src/gui/doAction.cpp
+++ b/src/gui/doAction.cpp
@@ -684,6 +684,9 @@ void FurnaceGUI::doAction(int what) {
wavePreviewInit=true;
updateFMPreview=true;
break;
+ case GUI_ACTION_INS_LIST_DIR_VIEW:
+ insListDir=!insListDir;
+ break;
case GUI_ACTION_WAVE_LIST_ADD:
curWave=e->addWave();
@@ -757,6 +760,9 @@ void FurnaceGUI::doAction(int what) {
if (++curWave>=(int)e->song.wave.size()) curWave=((int)e->song.wave.size())-1;
wantScrollList=true;
break;
+ case GUI_ACTION_WAVE_LIST_DIR_VIEW:
+ waveListDir=!waveListDir;
+ break;
case GUI_ACTION_SAMPLE_LIST_ADD:
curSample=e->addSample();
@@ -860,6 +866,9 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW:
e->stopSamplePreview();
break;
+ case GUI_ACTION_SAMPLE_LIST_DIR_VIEW:
+ sampleListDir=!sampleListDir;
+ break;
case GUI_ACTION_SAMPLE_SELECT:
if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp
index e3f4b3d54..3e0399fcc 100644
--- a/src/gui/gui.cpp
+++ b/src/gui/gui.cpp
@@ -5882,6 +5882,10 @@ bool FurnaceGUI::init() {
basicMode=true;
}
+ insListDir=e->getConfBool("insListDir",false);
+ waveListDir=e->getConfBool("waveListDir",false);
+ sampleListDir=e->getConfBool("sampleListDir",false);
+
tempoView=e->getConfBool("tempoView",true);
waveHex=e->getConfBool("waveHex",false);
waveSigned=e->getConfBool("waveSigned",false);
@@ -6309,6 +6313,11 @@ void FurnaceGUI::commitState() {
e->setConf("spoilerOpen",spoilerOpen);
e->setConf("basicMode",basicMode);
+ // commit dir state
+ e->setConf("insListDir",insListDir);
+ e->setConf("waveListDir",waveListDir);
+ e->setConf("sampleListDir",sampleListDir);
+
// commit last window size
e->setConf("lastWindowWidth",scrConfW);
e->setConf("lastWindowHeight",scrConfH);
@@ -6560,6 +6569,10 @@ FurnaceGUI::FurnaceGUI():
groovesOpen(false),
introMonOpen(false),
basicMode(true),
+ shortIntro(false),
+ insListDir(false),
+ waveListDir(false),
+ sampleListDir(false),
clockShowReal(true),
clockShowRow(true),
clockShowBeat(true),
diff --git a/src/gui/gui.h b/src/gui/gui.h
index d50c810b1..d4462413c 100644
--- a/src/gui/gui.h
+++ b/src/gui/gui.h
@@ -566,6 +566,7 @@ enum FurnaceGUIActions {
GUI_ACTION_INS_LIST_EDIT,
GUI_ACTION_INS_LIST_UP,
GUI_ACTION_INS_LIST_DOWN,
+ GUI_ACTION_INS_LIST_DIR_VIEW,
GUI_ACTION_INS_LIST_MAX,
GUI_ACTION_WAVE_LIST_MIN,
@@ -582,6 +583,7 @@ enum FurnaceGUIActions {
GUI_ACTION_WAVE_LIST_EDIT,
GUI_ACTION_WAVE_LIST_UP,
GUI_ACTION_WAVE_LIST_DOWN,
+ GUI_ACTION_WAVE_LIST_DIR_VIEW,
GUI_ACTION_WAVE_LIST_MAX,
GUI_ACTION_SAMPLE_LIST_MIN,
@@ -601,6 +603,7 @@ enum FurnaceGUIActions {
GUI_ACTION_SAMPLE_LIST_DOWN,
GUI_ACTION_SAMPLE_LIST_PREVIEW,
GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW,
+ GUI_ACTION_SAMPLE_LIST_DIR_VIEW,
GUI_ACTION_SAMPLE_LIST_MAX,
GUI_ACTION_SAMPLE_MIN,
@@ -1207,6 +1210,7 @@ class FurnaceGUI {
String workingDirLayout, workingDirROM, workingDirTest;
String mmlString[32];
String mmlStringW, mmlStringSNES, grooveString, grooveListString, mmlStringModTable;
+ String folderString;
std::vector sysSearchResults;
std::vector newSongSearchResults;
@@ -1614,6 +1618,7 @@ class FurnaceGUI {
bool groovesOpen, introMonOpen;
bool basicMode, shortIntro;
+ bool insListDir, waveListDir, sampleListDir;
bool clockShowReal, clockShowRow, clockShowBeat, clockShowMetro, clockShowTime;
float clockMetroTick[16];
@@ -1995,6 +2000,10 @@ class FurnaceGUI {
void actualWaveList();
void actualSampleList();
+ void insListItem(int index);
+ void waveListItem(int index);
+ void sampleListItem(int index);
+
void toggleMobileUI(bool enable, bool force=false);
void pushToggleColors(bool status);
diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp
index 985382b86..dc52def97 100644
--- a/src/gui/guiConst.cpp
+++ b/src/gui/guiConst.cpp
@@ -610,6 +610,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("INS_LIST_EDIT", "Edit", FURKMOD_SHIFT|SDLK_RETURN),
D("INS_LIST_UP", "Cursor up", SDLK_UP),
D("INS_LIST_DOWN", "Cursor down", SDLK_DOWN),
+ D("INS_LIST_DIR_VIEW", "Toggle folders/standard view", FURKMOD_CMD|SDLK_v),
D("INS_LIST_MAX", "", NOT_AN_ACTION),
D("WAVE_LIST_MIN", "---Wavetable list", NOT_AN_ACTION),
@@ -626,6 +627,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("WAVE_LIST_EDIT", "Edit", FURKMOD_SHIFT|SDLK_RETURN),
D("WAVE_LIST_UP", "Cursor up", SDLK_UP),
D("WAVE_LIST_DOWN", "Cursor down", SDLK_DOWN),
+ D("WAVE_LIST_DIR_VIEW", "Toggle folders/standard view", FURKMOD_CMD|SDLK_v),
D("WAVE_LIST_MAX", "", NOT_AN_ACTION),
D("SAMPLE_LIST_MIN", "---Sample list", NOT_AN_ACTION),
@@ -645,6 +647,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("SAMPLE_LIST_DOWN", "Cursor down", SDLK_DOWN),
D("SAMPLE_LIST_PREVIEW", "Preview", 0),
D("SAMPLE_LIST_STOP_PREVIEW", "Stop preview", 0),
+ D("SAMPLE_LIST_DIR_VIEW", "Toggle folders/standard view", FURKMOD_CMD|SDLK_v),
D("SAMPLE_LIST_MAX", "", NOT_AN_ACTION),
D("SAMPLE_MIN", "---Sample editor", NOT_AN_ACTION),
diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp
index 2605a0673..678593ea8 100644
--- a/src/gui/settings.cpp
+++ b/src/gui/settings.cpp
@@ -2351,6 +2351,7 @@ void FurnaceGUI::drawSettings() {
UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_EDIT);
UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_UP);
UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_DOWN);
+ UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_DIR_VIEW);
KEYBIND_CONFIG_END;
ImGui::TreePop();
@@ -2368,6 +2369,7 @@ void FurnaceGUI::drawSettings() {
UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_EDIT);
UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_UP);
UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_DOWN);
+ UI_KEYBIND_CONFIG(GUI_ACTION_WAVE_LIST_DIR_VIEW);
KEYBIND_CONFIG_END;
ImGui::TreePop();
@@ -2387,6 +2389,7 @@ void FurnaceGUI::drawSettings() {
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_DOWN);
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_PREVIEW);
UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW);
+ UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_LIST_DIR_VIEW);
KEYBIND_CONFIG_END;
ImGui::TreePop();
From 51b44927464c1d76c02ff5a29ca6d8c9877cb11a Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Mon, 15 May 2023 04:25:23 -0500
Subject: [PATCH 017/454] asset directories, part 2
---
src/gui/dataList.cpp | 37 +++++++++++++++++++++++++++----------
1 file changed, 27 insertions(+), 10 deletions(-)
diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp
index 14a1ccc93..168a87ca8 100644
--- a/src/gui/dataList.cpp
+++ b/src/gui/dataList.cpp
@@ -254,7 +254,7 @@ void FurnaceGUI::insListItem(int i) {
}
if (wantScrollList && curIns==i) ImGui::SetScrollHereY();
if (settings.insFocusesPattern && patternOpen && ImGui::IsItemActivated()) {
- nextWindow=GUI_WINDOW_PATTERN;
+ if (!insListDir) nextWindow=GUI_WINDOW_PATTERN;
curIns=i;
wavePreviewInit=true;
updateFMPreview=true;
@@ -269,6 +269,27 @@ void FurnaceGUI::insListItem(int i) {
}
}
if (i>=0) {
+ if (insListDir) {
+ if (ImGui::BeginDragDropSource()) {
+ chanToMove=i;
+ ImGui::SetDragDropPayload("FUR_DIR",NULL,0,ImGuiCond_Once);
+ //ImGui::Button(ICON_FA_ARROWS "##ChanDrag");
+ ImGui::EndDragDropSource();
+ }
+ if (ImGui::BeginDragDropTarget()) {
+ const ImGuiPayload* dragItem=ImGui::AcceptDragDropPayload("FUR_DIR");
+ if (dragItem!=NULL) {
+ if (dragItem->IsDataType("FUR_DIR")) {
+ if (chanToMove!=i && chanToMove>=0) {
+ }
+ logV("TO %d",i);
+ chanToMove=-1;
+ }
+ }
+ ImGui::EndDragDropTarget();
+ }
+ }
+
if (ImGui::BeginPopupContextItem("InsRightMenu")) {
curIns=i;
updateFMPreview=true;
@@ -543,15 +564,11 @@ void FurnaceGUI::drawInsList(bool asChild) {
ImGui::TableNextColumn();
insListItem(-1);
for (DivAssetDir& i: e->song.insDir) {
- if (!i.name.empty()) {
- ImGui::Text(ICON_FA_FOLDER_OPEN " %s",i.name.c_str());
- ImGui::Indent();
- }
- for (int j: i.entries) {
- insListItem(j);
- }
- if (!i.name.empty()) {
- ImGui::Unindent();
+ if (ImGui::TreeNode(i.name.empty()?"":i.name.c_str())) {
+ for (int j: i.entries) {
+ insListItem(j);
+ }
+ ImGui::TreePop();
}
}
} else {
From c523d80fc35ea111e12f11b440b973daed278683 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Mon, 15 May 2023 17:43:27 -0500
Subject: [PATCH 018/454] asset directories, part 3 - DO NOT USE
IT CRASHES
---
src/gui/dataList.cpp | 63 ++++++++++++++++++++++++++++----------------
src/gui/doAction.cpp | 12 ++++-----
src/gui/gui.cpp | 13 +++++++--
src/gui/gui.h | 9 ++++---
4 files changed, 63 insertions(+), 34 deletions(-)
diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp
index 168a87ca8..b973c434f 100644
--- a/src/gui/dataList.cpp
+++ b/src/gui/dataList.cpp
@@ -30,7 +30,36 @@ const char* sampleNote[12]={
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
};
-void FurnaceGUI::insListItem(int i) {
+#define DRAG_SOURCE(_d,_a) \
+ if (ImGui::BeginDragDropSource()) { \
+ dirToMove=_d; \
+ assetToMove=_a; \
+ ImGui::SetDragDropPayload("FUR_DIR",NULL,0,ImGuiCond_Once); \
+ ImGui::Button(ICON_FA_ARROWS "##AssetDrag"); \
+ ImGui::EndDragDropSource(); \
+ }
+
+#define DRAG_TARGET(_d,_a,_type) \
+ if (ImGui::BeginDragDropTarget()) { \
+ const ImGuiPayload* dragItem=ImGui::AcceptDragDropPayload("FUR_DIR"); \
+ if (dragItem!=NULL) { \
+ if (dragItem->IsDataType("FUR_DIR")) { \
+ if (dirToMove!=_d || assetToMove!=_a) { \
+ logV("%d/%d -> %d/%d",dirToMove,assetToMove,_d,_a); \
+ e->lockEngine([&]() { \
+ int val=e->song.insDir[dirToMove].entries[assetToMove]; \
+ e->song.insDir[dirToMove].entries.erase(e->song.insDir[dirToMove].entries.begin()+assetToMove); \
+ e->song.insDir[_d].entries.insert(e->song.insDir[_d].entries.begin()+_a,val); \
+ }); \
+ } \
+ dirToMove=-1; \
+ assetToMove=-1; \
+ } \
+ } \
+ ImGui::EndDragDropTarget(); \
+ }
+
+void FurnaceGUI::insListItem(int i, int dir, int asset) {
ImGui::PushID(i);
String name=ICON_FA_CIRCLE_O;
const char* insType="Bug!";
@@ -270,24 +299,8 @@ void FurnaceGUI::insListItem(int i) {
}
if (i>=0) {
if (insListDir) {
- if (ImGui::BeginDragDropSource()) {
- chanToMove=i;
- ImGui::SetDragDropPayload("FUR_DIR",NULL,0,ImGuiCond_Once);
- //ImGui::Button(ICON_FA_ARROWS "##ChanDrag");
- ImGui::EndDragDropSource();
- }
- if (ImGui::BeginDragDropTarget()) {
- const ImGuiPayload* dragItem=ImGui::AcceptDragDropPayload("FUR_DIR");
- if (dragItem!=NULL) {
- if (dragItem->IsDataType("FUR_DIR")) {
- if (chanToMove!=i && chanToMove>=0) {
- }
- logV("TO %d",i);
- chanToMove=-1;
- }
- }
- ImGui::EndDragDropTarget();
- }
+ DRAG_SOURCE(dir,asset);
+ DRAG_TARGET(dir,asset,e->song.insDir);
}
if (ImGui::BeginPopupContextItem("InsRightMenu")) {
@@ -562,14 +575,20 @@ void FurnaceGUI::drawInsList(bool asChild) {
if (insListDir) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
- insListItem(-1);
+ insListItem(-1,-1,-1);
+ int dirIndex=0;
for (DivAssetDir& i: e->song.insDir) {
if (ImGui::TreeNode(i.name.empty()?"":i.name.c_str())) {
+ int assetIndex=0;
for (int j: i.entries) {
- insListItem(j);
+ insListItem(j,dirIndex,assetIndex);
+ assetIndex++;
}
ImGui::TreePop();
}
+ DRAG_SOURCE(dirIndex,-1);
+ DRAG_TARGET(dirIndex,-1,e->song.insDir);
+ dirIndex++;
}
} else {
int curRow=0;
@@ -580,7 +599,7 @@ void FurnaceGUI::drawInsList(bool asChild) {
} else if (curRow==0) {
ImGui::TableNextColumn();
}
- insListItem(i);
+ insListItem(i,-1,-1);
if (settings.horizontalDataView) {
if (++curRow>=availableRows) curRow=0;
}
diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp
index e6a244139..7520162f7 100644
--- a/src/gui/doAction.cpp
+++ b/src/gui/doAction.cpp
@@ -561,17 +561,17 @@ void FurnaceGUI::doAction(int what) {
doFlip();
break;
case GUI_ACTION_PAT_COLLAPSE_ROWS:
- doCollapse(2,selStart,selEnd);
+ doCollapse(collapseAmount,selStart,selEnd);
break;
case GUI_ACTION_PAT_EXPAND_ROWS:
- doExpand(2,selStart,selEnd);
+ doExpand(collapseAmount,selStart,selEnd);
break;
case GUI_ACTION_PAT_COLLAPSE_PAT: {
SelectionPoint selEndPat;
selEndPat.xCoarse=e->getTotalChannelCount()-1;
selEndPat.xFine=2+e->curPat[selEndPat.xCoarse].effectCols*2;
selEndPat.y=e->curSubSong->patLen-1;
- doCollapse(2,SelectionPoint(0,0,0),selEndPat);
+ doCollapse(collapseAmount,SelectionPoint(0,0,0),selEndPat);
break;
}
case GUI_ACTION_PAT_EXPAND_PAT: {
@@ -579,14 +579,14 @@ void FurnaceGUI::doAction(int what) {
selEndPat.xCoarse=e->getTotalChannelCount()-1;
selEndPat.xFine=2+e->curPat[selEndPat.xCoarse].effectCols*2;
selEndPat.y=e->curSubSong->patLen-1;
- doExpand(2,SelectionPoint(0,0,0),selEndPat);
+ doExpand(collapseAmount,SelectionPoint(0,0,0),selEndPat);
break;
}
case GUI_ACTION_PAT_COLLAPSE_SONG:
- doCollapseSong(2);
+ doCollapseSong(collapseAmount);
break;
case GUI_ACTION_PAT_EXPAND_SONG:
- doExpandSong(2);
+ doExpandSong(collapseAmount);
break;
case GUI_ACTION_PAT_LATCH: // TODO
break;
diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp
index 3e0399fcc..7b0a26e67 100644
--- a/src/gui/gui.cpp
+++ b/src/gui/gui.cpp
@@ -2852,8 +2852,14 @@ void FurnaceGUI::editOptions(bool topMenu) {
ImGui::Separator();
if (ImGui::MenuItem("flip selection",BIND_FOR(GUI_ACTION_PAT_FLIP_SELECTION))) doFlip();
- if (ImGui::MenuItem("collapse",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_ROWS))) doCollapse(2,selStart,selEnd);
- if (ImGui::MenuItem("expand",BIND_FOR(GUI_ACTION_PAT_EXPAND_ROWS))) doExpand(2,selStart,selEnd);
+
+ ImGui::SetNextItemWidth(120.0f*dpiScale);
+ if (ImGui::InputInt("collapse/expand amount##CollapseAmount",&collapseAmount,1,1)) {
+ if (collapseAmount<2) collapseAmount=2;
+ if (collapseAmount>256) collapseAmount=256;
+ }
+ if (ImGui::MenuItem("collapse",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_ROWS))) doCollapse(collapseAmount,selStart,selEnd);
+ if (ImGui::MenuItem("expand",BIND_FOR(GUI_ACTION_PAT_EXPAND_ROWS))) doExpand(collapseAmount,selStart,selEnd);
if (topMenu) {
ImGui::Separator();
@@ -6720,11 +6726,14 @@ FurnaceGUI::FurnaceGUI():
sysToMove(-1),
sysToDelete(-1),
opToMove(-1),
+ assetToMove(-1),
+ dirToMove(-1),
transposeAmount(0),
randomizeMin(0),
randomizeMax(255),
fadeMin(0),
fadeMax(255),
+ collapseAmount(2),
scaleMax(100.0f),
fadeMode(false),
randomMode(false),
diff --git a/src/gui/gui.h b/src/gui/gui.h
index d4462413c..7efc8c927 100644
--- a/src/gui/gui.h
+++ b/src/gui/gui.h
@@ -1792,6 +1792,7 @@ class FurnaceGUI {
std::map images;
int chanToMove, sysToMove, sysToDelete, opToMove;
+ int assetToMove, dirToMove;
ImVec2 patWindowPos, patWindowSize;
@@ -1800,7 +1801,7 @@ class FurnaceGUI {
ImVec2 noteCellSize, insCellSize, volCellSize, effectCellSize, effectValCellSize;
SelectionPoint sel1, sel2;
int dummyRows, demandX;
- int transposeAmount, randomizeMin, randomizeMax, fadeMin, fadeMax;
+ int transposeAmount, randomizeMin, randomizeMax, fadeMin, fadeMax, collapseAmount;
float scaleMax;
bool fadeMode, randomMode, haveHitBounds, pendingStepUpdate;
@@ -2000,9 +2001,9 @@ class FurnaceGUI {
void actualWaveList();
void actualSampleList();
- void insListItem(int index);
- void waveListItem(int index);
- void sampleListItem(int index);
+ void insListItem(int index, int dir, int asset);
+ void waveListItem(int index, int dir, int asset);
+ void sampleListItem(int index, int dir, int asset);
void toggleMobileUI(bool enable, bool force=false);
From 1f5b08e09ec53c09c236cf900a7e6939d9c61fdd Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Mon, 15 May 2023 19:11:50 -0500
Subject: [PATCH 019/454] asset directories, part 4
partially working
---
src/gui/dataList.cpp | 32 ++++++++++++++++++++++----------
1 file changed, 22 insertions(+), 10 deletions(-)
diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp
index b973c434f..dceeca59f 100644
--- a/src/gui/dataList.cpp
+++ b/src/gui/dataList.cpp
@@ -25,6 +25,7 @@
#include "../ta-log.h"
#include
#include
+#include
const char* sampleNote[12]={
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
@@ -44,13 +45,23 @@ const char* sampleNote[12]={
const ImGuiPayload* dragItem=ImGui::AcceptDragDropPayload("FUR_DIR"); \
if (dragItem!=NULL) { \
if (dragItem->IsDataType("FUR_DIR")) { \
- if (dirToMove!=_d || assetToMove!=_a) { \
- logV("%d/%d -> %d/%d",dirToMove,assetToMove,_d,_a); \
- e->lockEngine([&]() { \
- int val=e->song.insDir[dirToMove].entries[assetToMove]; \
- e->song.insDir[dirToMove].entries.erase(e->song.insDir[dirToMove].entries.begin()+assetToMove); \
- e->song.insDir[_d].entries.insert(e->song.insDir[_d].entries.begin()+_a,val); \
- }); \
+ if (assetToMove==-1) { \
+ if (dirToMove!=_d && _a==-1) { \
+ e->lockEngine([&]() { \
+ DivAssetDir val=_type[dirToMove]; \
+ _type.erase(_type.begin()+dirToMove); \
+ _type.insert(_type.begin()+_d,val); \
+ }); \
+ } \
+ } else { \
+ if (dirToMove!=_d || assetToMove!=_a) { \
+ logV("%d/%d -> %d/%d",dirToMove,assetToMove,_d,_a); \
+ e->lockEngine([&]() { \
+ int val=_type[dirToMove].entries[assetToMove]; \
+ _type[dirToMove].entries.erase(_type[dirToMove].entries.begin()+assetToMove); \
+ _type[_d].entries.insert(_type[_d].entries.begin()+MAX(_a,0),val); \
+ }); \
+ } \
} \
dirToMove=-1; \
assetToMove=-1; \
@@ -578,7 +589,10 @@ void FurnaceGUI::drawInsList(bool asChild) {
insListItem(-1,-1,-1);
int dirIndex=0;
for (DivAssetDir& i: e->song.insDir) {
- if (ImGui::TreeNode(i.name.empty()?"":i.name.c_str())) {
+ bool treeNode=ImGui::TreeNode(i.name.empty()?"":i.name.c_str());
+ DRAG_SOURCE(dirIndex,-1);
+ DRAG_TARGET(dirIndex,-1,e->song.insDir);
+ if (treeNode) {
int assetIndex=0;
for (int j: i.entries) {
insListItem(j,dirIndex,assetIndex);
@@ -586,8 +600,6 @@ void FurnaceGUI::drawInsList(bool asChild) {
}
ImGui::TreePop();
}
- DRAG_SOURCE(dirIndex,-1);
- DRAG_TARGET(dirIndex,-1,e->song.insDir);
dirIndex++;
}
} else {
From 442ccd0e0910f7cfdf79f6e9bb8788e70e610617 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Mon, 15 May 2023 23:27:45 -0500
Subject: [PATCH 020/454] asset directories, part 5
---
src/engine/engine.cpp | 27 +++++++++++++++++++++++++++
src/engine/engine.h | 3 +++
src/gui/dataList.cpp | 5 ++++-
3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
index 60c46341d..744d7274d 100644
--- a/src/engine/engine.cpp
+++ b/src/engine/engine.cpp
@@ -1572,6 +1572,21 @@ void DivEngine::changeSong(size_t songIndex) {
prevRow=0;
}
+void DivEngine::removeAsset(std::vector& dir, int entry) {
+ if (entry<0) return;
+ for (DivAssetDir& i: dir) {
+ for (size_t j=0; jentry) {
+ i.entries[j]--;
+ }
+ }
+ }
+}
+
void DivEngine::checkAssetDir(std::vector& dir, size_t entries) {
bool* inAssetDir=new bool[entries];
memset(inAssetDir,0,entries*sizeof(bool));
@@ -3032,6 +3047,7 @@ int DivEngine::addInstrument(int refChan, DivInstrumentType fallbackType) {
saveLock.lock();
song.ins.push_back(ins);
song.insLen=insCount+1;
+ checkAssetDir(song.insDir,song.ins.size());
saveLock.unlock();
BUSY_END;
return insCount;
@@ -3046,6 +3062,7 @@ int DivEngine::addInstrumentPtr(DivInstrument* which) {
saveLock.lock();
song.ins.push_back(which);
song.insLen=song.ins.size();
+ checkAssetDir(song.insDir,song.ins.size());
saveLock.unlock();
BUSY_END;
return song.insLen;
@@ -3082,6 +3099,8 @@ void DivEngine::delInstrument(int index) {
}
}
}
+ removeAsset(song.insDir,index);
+ checkAssetDir(song.insDir,song.ins.size());
}
saveLock.unlock();
BUSY_END;
@@ -3098,6 +3117,7 @@ int DivEngine::addWave() {
int waveCount=(int)song.wave.size();
song.wave.push_back(wave);
song.waveLen=waveCount+1;
+ checkAssetDir(song.waveDir,song.wave.size());
saveLock.unlock();
BUSY_END;
return waveCount;
@@ -3114,6 +3134,7 @@ int DivEngine::addWavePtr(DivWavetable* which) {
int waveCount=(int)song.wave.size();
song.wave.push_back(which);
song.waveLen=waveCount+1;
+ checkAssetDir(song.waveDir,song.wave.size());
saveLock.unlock();
BUSY_END;
return song.waveLen;
@@ -3268,6 +3289,8 @@ void DivEngine::delWave(int index) {
delete song.wave[index];
song.wave.erase(song.wave.begin()+index);
song.waveLen=song.wave.size();
+ removeAsset(song.waveDir,index);
+ checkAssetDir(song.waveDir,song.wave.size());
}
saveLock.unlock();
BUSY_END;
@@ -3288,6 +3311,7 @@ int DivEngine::addSample() {
sPreview.sample=-1;
sPreview.pos=0;
sPreview.dir=false;
+ checkAssetDir(song.sampleDir,song.sample.size());
saveLock.unlock();
renderSamples();
BUSY_END;
@@ -3305,6 +3329,7 @@ int DivEngine::addSamplePtr(DivSample* which) {
saveLock.lock();
song.sample.push_back(which);
song.sampleLen=sampleCount+1;
+ checkAssetDir(song.sampleDir,song.sample.size());
saveLock.unlock();
renderSamples();
BUSY_END;
@@ -3774,6 +3799,8 @@ void DivEngine::delSample(int index) {
delete song.sample[index];
song.sample.erase(song.sample.begin()+index);
song.sampleLen=song.sample.size();
+ removeAsset(song.sampleDir,index);
+ checkAssetDir(song.sampleDir,song.sample.size());
renderSamples();
}
saveLock.unlock();
diff --git a/src/engine/engine.h b/src/engine/engine.h
index 21b1c2728..20cec077c 100644
--- a/src/engine/engine.h
+++ b/src/engine/engine.h
@@ -517,6 +517,9 @@ class DivEngine {
// change song (UNSAFE)
void changeSong(size_t songIndex);
+ // remove an asset
+ void removeAsset(std::vector& dir, int entry);
+
// check whether an asset directory is complete
void checkAssetDir(std::vector& dir, size_t entries);
diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp
index dceeca59f..733b1a6b4 100644
--- a/src/gui/dataList.cpp
+++ b/src/gui/dataList.cpp
@@ -74,7 +74,7 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
ImGui::PushID(i);
String name=ICON_FA_CIRCLE_O;
const char* insType="Bug!";
- if (i>=0) {
+ if (i>=0 && isong.insLen) {
DivInstrument* ins=e->song.ins[i];
insType=(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type];
if (ins->type==DIV_INS_N163) insType=settings.c163Name.c_str();
@@ -342,6 +342,9 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
DivInstrument* ins=e->song.ins[i];
ImGui::SameLine();
ImGui::Text("%.2X: %s",i,ins->name.c_str());
+ } else {
+ ImGui::SameLine();
+ ImGui::Text("%.2X: ",i);
}
} else {
ImGui::SameLine();
From 22638d51995a10d36a7518a2f7f8a447de42cf0b Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Tue, 16 May 2023 00:04:26 -0500
Subject: [PATCH 021/454] asset directories, part 6
---
src/engine/engine.cpp | 7 +++++++
src/gui/dataList.cpp | 5 +++--
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
index 744d7274d..edf153fba 100644
--- a/src/engine/engine.cpp
+++ b/src/engine/engine.cpp
@@ -1599,6 +1599,13 @@ void DivEngine::checkAssetDir(std::vector& dir, size_t entries) {
j--;
continue;
}
+
+ // erase duplicate entry
+ if (inAssetDir[j]) {
+ i.entries.erase(i.entries.begin()+j);
+ j--;
+ continue;
+ }
// mark entry as present
inAssetDir[j]=true;
diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp
index 733b1a6b4..19fca6235 100644
--- a/src/gui/dataList.cpp
+++ b/src/gui/dataList.cpp
@@ -59,7 +59,7 @@ const char* sampleNote[12]={
e->lockEngine([&]() { \
int val=_type[dirToMove].entries[assetToMove]; \
_type[dirToMove].entries.erase(_type[dirToMove].entries.begin()+assetToMove); \
- _type[_d].entries.insert(_type[_d].entries.begin()+MAX(_a,0),val); \
+ _type[_d].entries.insert((_a<0)?(_type[_d].entries.end()):(_type[_d].entries.begin()+_a),val); \
}); \
} \
} \
@@ -592,7 +592,8 @@ void FurnaceGUI::drawInsList(bool asChild) {
insListItem(-1,-1,-1);
int dirIndex=0;
for (DivAssetDir& i: e->song.insDir) {
- bool treeNode=ImGui::TreeNode(i.name.empty()?"":i.name.c_str());
+ String nodeName=fmt::sprintf("%s %s##_AD%d",i.name.empty()?ICON_FA_FOLDER_O:ICON_FA_FOLDER,i.name.empty()?"":i.name,i.name.empty()?-1:dirIndex);
+ bool treeNode=ImGui::TreeNodeEx(nodeName.c_str(),ImGuiTreeNodeFlags_SpanAvailWidth|(i.name.empty()?ImGuiTreeNodeFlags_DefaultOpen:0));
DRAG_SOURCE(dirIndex,-1);
DRAG_TARGET(dirIndex,-1,e->song.insDir);
if (treeNode) {
From 160753243d7ebfd03eef151717a09d1d03855adb Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Tue, 16 May 2023 02:44:46 -0500
Subject: [PATCH 022/454] asset directories, part 7
---
papers/format.md | 21 +++++++
src/engine/engine.cpp | 4 +-
src/engine/engine.h | 8 ++-
src/engine/fileOps.cpp | 133 ++++++++++++++++++++++++++++++++++++++++-
4 files changed, 161 insertions(+), 5 deletions(-)
diff --git a/papers/format.md b/papers/format.md
index 1b2e30c8f..2c98ecb99 100644
--- a/papers/format.md
+++ b/papers/format.md
@@ -32,6 +32,7 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
the format versions are:
+- 156: Furnace dev156
- 155: Furnace dev155
- 154: Furnace dev154
- 153: Furnace dev153
@@ -435,6 +436,10 @@ size | description
??? | groove entries. the format is:
| - 1 byte: length of groove
| - 16 bytes: groove pattern
+ --- | **pointers to asset directories** (>=156)
+ 4 | instrument directories
+ 4 | wavetable directories
+ 4 | sample directories
```
# patchbay
@@ -526,6 +531,22 @@ clock=4000000
stereo=true
```
+# asset directories (>=156)
+
+also known as "folder" in the user interface.
+
+```
+size | description
+-----|------------------------------------
+ 4 | "ADIR" block ID
+ 4 | size of this block
+ 4 | number of directories
+ --- | **asset directory** (×numberOfDirs)
+ STR | name (if empty, this is the uncategorized directory)
+ 2 | number of assets
+ 1?? | assets in this directory
+```
+
# instrument (>=127)
Furnace dev127 and higher use the new instrument format.
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
index edf153fba..5a7ec1db2 100644
--- a/src/engine/engine.cpp
+++ b/src/engine/engine.cpp
@@ -1601,14 +1601,14 @@ void DivEngine::checkAssetDir(std::vector& dir, size_t entries) {
}
// erase duplicate entry
- if (inAssetDir[j]) {
+ if (inAssetDir[i.entries[j]]) {
i.entries.erase(i.entries.begin()+j);
j--;
continue;
}
// mark entry as present
- inAssetDir[j]=true;
+ inAssetDir[i.entries[j]]=true;
}
}
diff --git a/src/engine/engine.h b/src/engine/engine.h
index 20cec077c..07fd53507 100644
--- a/src/engine/engine.h
+++ b/src/engine/engine.h
@@ -53,8 +53,8 @@
#define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock();
#define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false;
-#define DIV_VERSION "dev155"
-#define DIV_ENGINE_VERSION 155
+#define DIV_VERSION "dev156"
+#define DIV_ENGINE_VERSION 156
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02
@@ -523,6 +523,10 @@ class DivEngine {
// check whether an asset directory is complete
void checkAssetDir(std::vector& dir, size_t entries);
+ // read/write asset dir
+ void putAssetDirData(SafeWriter* w, std::vector& dir);
+ DivDataErrors readAssetDirData(SafeReader& reader, std::vector& dir);
+
// add every export method here
friend class DivROMExport;
friend class DivExportAmigaValidation;
diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp
index 6ea0c59b7..dacaa14cc 100644
--- a/src/engine/fileOps.cpp
+++ b/src/engine/fileOps.cpp
@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include "dataErrors.h"
#include "engine.h"
#include "../ta-log.h"
#include "instrument.h"
@@ -1655,6 +1656,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
unsigned int samplePtr[256];
unsigned int subSongPtr[256];
unsigned int sysFlagsPtr[DIV_MAX_CHIPS];
+ unsigned int assetDirPtr[3];
std::vector patPtr;
int numberOfSubSongs=0;
char magic[5];
@@ -2332,6 +2334,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
}
+ if (ds.version>=156) {
+ assetDirPtr[0]=reader.readI();
+ assetDirPtr[1]=reader.readI();
+ assetDirPtr[2]=reader.readI();
+ }
+
// read system flags
if (ds.version>=119) {
logD("reading chip flags...");
@@ -2366,6 +2374,53 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
}
+ // read asset directories
+ if (ds.version>=156) {
+ logD("reading asset directories...");
+
+ if (!reader.seek(assetDirPtr[0],SEEK_SET)) {
+ logE("couldn't seek to ins dir!");
+ lastError=fmt::sprintf("couldn't read instrument directory");
+ ds.unload();
+ delete[] file;
+ return false;
+ }
+ if (readAssetDirData(reader,ds.insDir)!=DIV_DATA_SUCCESS) {
+ lastError="invalid instrument directory data!";
+ ds.unload();
+ delete[] file;
+ return false;
+ }
+
+ if (!reader.seek(assetDirPtr[1],SEEK_SET)) {
+ logE("couldn't seek to wave dir!");
+ lastError=fmt::sprintf("couldn't read wavetable directory");
+ ds.unload();
+ delete[] file;
+ return false;
+ }
+ if (readAssetDirData(reader,ds.waveDir)!=DIV_DATA_SUCCESS) {
+ lastError="invalid wavetable directory data!";
+ ds.unload();
+ delete[] file;
+ return false;
+ }
+
+ if (!reader.seek(assetDirPtr[2],SEEK_SET)) {
+ logE("couldn't seek to sample dir!");
+ lastError=fmt::sprintf("couldn't read sample directory");
+ ds.unload();
+ delete[] file;
+ return false;
+ }
+ if (readAssetDirData(reader,ds.sampleDir)!=DIV_DATA_SUCCESS) {
+ lastError="invalid sample directory data!";
+ ds.unload();
+ delete[] file;
+ return false;
+ }
+ }
+
// read subsongs
if (ds.version>=95) {
for (int i=0; i& dir) {
+ size_t blockStartSeek, blockEndSeek;
+
+ w->write("ADIR",4);
+ blockStartSeek=w->tell();
+ w->writeI(0);
+
+ w->writeI(dir.size());
+
+ for (DivAssetDir& i: dir) {
+ w->writeString(i.name,false);
+ w->writeS(i.entries.size());
+ for (int j: i.entries) {
+ w->writeC(j);
+ }
+ }
+
+ blockEndSeek=w->tell();
+ w->seek(blockStartSeek,SEEK_SET);
+ w->writeI(blockEndSeek-blockStartSeek-4);
+ w->seek(0,SEEK_END);
+}
+
+DivDataErrors DivEngine::readAssetDirData(SafeReader& reader, std::vector& dir) {
+ char magic[4];
+ reader.read(magic,4);
+ if (memcmp(magic,"ADIR",4)!=0) {
+ logV("header is invalid: %c%c%c%c",magic[0],magic[1],magic[2],magic[3]);
+ return DIV_DATA_INVALID_HEADER;
+ }
+
+ logV("reading");
+
+ reader.readI(); // reserved
+
+ unsigned int numDirs=reader.readI();
+
+ for (unsigned int i=0; i subSongPtr;
@@ -4851,7 +4961,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
std::vector wavePtr;
std::vector samplePtr;
std::vector patPtr;
- size_t ptrSeek, subSongPtrSeek, sysFlagsPtrSeek, blockStartSeek, blockEndSeek;
+ int assetDirPtr[3];
+ size_t ptrSeek, subSongPtrSeek, sysFlagsPtrSeek, blockStartSeek, blockEndSeek, assetDirPtrSeek;
size_t subSongIndex=0;
DivSubSong* subSong=song.subsong[subSongIndex];
warnings="";
@@ -5150,6 +5261,12 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
}
}
+ // asset dir pointers (we'll seek here later)
+ assetDirPtrSeek=w->tell();
+ w->writeI(0);
+ w->writeI(0);
+ w->writeI(0);
+
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4);
@@ -5237,6 +5354,14 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->seek(0,SEEK_END);
}
+ /// ASSET DIRECTORIES
+ assetDirPtr[0]=w->tell();
+ putAssetDirData(w,song.insDir);
+ assetDirPtr[1]=w->tell();
+ putAssetDirData(w,song.waveDir);
+ assetDirPtr[2]=w->tell();
+ putAssetDirData(w,song.sampleDir);
+
/// INSTRUMENT
for (int i=0; iwriteI(sysFlagsPtr[i]);
}
+ // asset dir pointers
+ w->seek(assetDirPtrSeek,SEEK_SET);
+ for (size_t i=0; i<3; i++) {
+ w->writeI(assetDirPtr[i]);
+ }
+
saveLock.unlock();
return w;
}
From 92cf966a36f57d9767a8eb0aa78d3df63430e05a Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Tue, 16 May 2023 03:04:16 -0500
Subject: [PATCH 023/454] asset directories, part 8
---
src/engine/engine.cpp | 20 ++++++++++++++++++++
src/engine/engine.h | 3 +++
src/engine/fileOps.cpp | 5 -----
3 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
index 5a7ec1db2..348e8bff6 100644
--- a/src/engine/engine.cpp
+++ b/src/engine/engine.cpp
@@ -1572,6 +1572,20 @@ void DivEngine::changeSong(size_t songIndex) {
prevRow=0;
}
+void DivEngine::moveAsset(std::vector& dir, int before, int after) {
+ if (before<0 || after<0) return;
+ for (DivAssetDir& i: dir) {
+ for (size_t j=0; j& dir, int entry) {
if (entry<0) return;
for (DivAssetDir& i: dir) {
@@ -4006,6 +4020,7 @@ bool DivEngine::moveInsUp(int which) {
saveLock.lock();
song.ins[which]=song.ins[which-1];
song.ins[which-1]=prev;
+ moveAsset(song.insDir,which,which-1);
exchangeIns(which,which-1);
saveLock.unlock();
BUSY_END;
@@ -4019,6 +4034,7 @@ bool DivEngine::moveWaveUp(int which) {
saveLock.lock();
song.wave[which]=song.wave[which-1];
song.wave[which-1]=prev;
+ moveAsset(song.waveDir,which,which-1);
saveLock.unlock();
BUSY_END;
return true;
@@ -4034,6 +4050,7 @@ bool DivEngine::moveSampleUp(int which) {
saveLock.lock();
song.sample[which]=song.sample[which-1];
song.sample[which-1]=prev;
+ moveAsset(song.sampleDir,which,which-1);
saveLock.unlock();
renderSamples();
BUSY_END;
@@ -4048,6 +4065,7 @@ bool DivEngine::moveInsDown(int which) {
song.ins[which]=song.ins[which+1];
song.ins[which+1]=prev;
exchangeIns(which,which+1);
+ moveAsset(song.insDir,which,which+1);
saveLock.unlock();
BUSY_END;
return true;
@@ -4060,6 +4078,7 @@ bool DivEngine::moveWaveDown(int which) {
saveLock.lock();
song.wave[which]=song.wave[which+1];
song.wave[which+1]=prev;
+ moveAsset(song.waveDir,which,which+1);
saveLock.unlock();
BUSY_END;
return true;
@@ -4075,6 +4094,7 @@ bool DivEngine::moveSampleDown(int which) {
saveLock.lock();
song.sample[which]=song.sample[which+1];
song.sample[which+1]=prev;
+ moveAsset(song.sampleDir,which,which+1);
saveLock.unlock();
renderSamples();
BUSY_END;
diff --git a/src/engine/engine.h b/src/engine/engine.h
index 07fd53507..711e3591e 100644
--- a/src/engine/engine.h
+++ b/src/engine/engine.h
@@ -517,6 +517,9 @@ class DivEngine {
// change song (UNSAFE)
void changeSong(size_t songIndex);
+ // move an asset
+ void moveAsset(std::vector& dir, int before, int after);
+
// remove an asset
void removeAsset(std::vector& dir, int entry);
diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp
index dacaa14cc..a30095108 100644
--- a/src/engine/fileOps.cpp
+++ b/src/engine/fileOps.cpp
@@ -4928,9 +4928,6 @@ DivDataErrors DivEngine::readAssetDirData(SafeReader& reader, std::vector
Date: Tue, 16 May 2023 04:29:26 -0500
Subject: [PATCH 024/454] asset directories, part 9
---
src/engine/engine.h | 5 ++---
src/gui/dataList.cpp | 17 +++++++++++++++++
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/src/engine/engine.h b/src/engine/engine.h
index 711e3591e..6dcf3eb5f 100644
--- a/src/engine/engine.h
+++ b/src/engine/engine.h
@@ -523,9 +523,6 @@ class DivEngine {
// remove an asset
void removeAsset(std::vector& dir, int entry);
- // check whether an asset directory is complete
- void checkAssetDir(std::vector& dir, size_t entries);
-
// read/write asset dir
void putAssetDirData(SafeWriter* w, std::vector& dir);
DivDataErrors readAssetDirData(SafeReader& reader, std::vector& dir);
@@ -607,6 +604,8 @@ class DivEngine {
// convert old flags
static void convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivSystem sys);
+ // check whether an asset directory is complete (UNSAFE)
+ void checkAssetDir(std::vector& dir, size_t entries);
// benchmark (returns time in seconds)
double benchmarkPlayback();
diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp
index 19fca6235..79a3a821c 100644
--- a/src/gui/dataList.cpp
+++ b/src/gui/dataList.cpp
@@ -591,11 +591,22 @@ void FurnaceGUI::drawInsList(bool asChild) {
ImGui::TableNextColumn();
insListItem(-1,-1,-1);
int dirIndex=0;
+ int dirToDelete=-1;
for (DivAssetDir& i: e->song.insDir) {
String nodeName=fmt::sprintf("%s %s##_AD%d",i.name.empty()?ICON_FA_FOLDER_O:ICON_FA_FOLDER,i.name.empty()?"":i.name,i.name.empty()?-1:dirIndex);
+ String popupID=fmt::sprintf("DirRightMenu%d",dirIndex);
bool treeNode=ImGui::TreeNodeEx(nodeName.c_str(),ImGuiTreeNodeFlags_SpanAvailWidth|(i.name.empty()?ImGuiTreeNodeFlags_DefaultOpen:0));
DRAG_SOURCE(dirIndex,-1);
DRAG_TARGET(dirIndex,-1,e->song.insDir);
+ if (ImGui::BeginPopupContextItem(popupID.c_str())) {
+ if (ImGui::MenuItem("rename...")) {
+ ImGui::OpenPopup("NewInsFolder");
+ }
+ if (ImGui::MenuItem("delete")) {
+ dirToDelete=dirIndex;
+ }
+ ImGui::EndPopup();
+ }
if (treeNode) {
int assetIndex=0;
for (int j: i.entries) {
@@ -606,6 +617,12 @@ void FurnaceGUI::drawInsList(bool asChild) {
}
dirIndex++;
}
+ if (dirToDelete!=-1) {
+ e->lockEngine([this,dirToDelete]() {
+ e->song.insDir.erase(e->song.insDir.begin()+dirToDelete);
+ e->checkAssetDir(e->song.insDir,e->song.ins.size());
+ });
+ }
} else {
int curRow=0;
for (int i=-1; i<(int)e->song.ins.size(); i++) {
From 419504628320cfa87f55bdb4ebc2d9a28aebc641 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Tue, 16 May 2023 13:41:08 -0500
Subject: [PATCH 025/454] asset directories, part 10
---
src/gui/dataList.cpp | 128 +++++++++++++++++++++++--------------------
src/gui/gui.cpp | 34 +++++++++++-
src/gui/gui.h | 10 ++--
src/gui/pattern.cpp | 25 +--------
4 files changed, 109 insertions(+), 88 deletions(-)
diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp
index 79a3a821c..cac8314c2 100644
--- a/src/gui/dataList.cpp
+++ b/src/gui/dataList.cpp
@@ -354,6 +354,71 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
ImGui::PopStyleColor();
}
+void FurnaceGUI::waveListItem(int i, float* wavePreview, int dir, int asset) {
+ DivWavetable* wave=e->song.wave[i];
+ for (int i=0; ilen; i++) {
+ wavePreview[i]=wave->data[i];
+ }
+ if (wave->len>0) wavePreview[wave->len]=wave->data[wave->len-1];
+ if (ImGui::Selectable(fmt::sprintf("%d##_WAVE%d\n",i,i).c_str(),curWave==i)) {
+ curWave=i;
+ }
+ if (wantScrollList && curWave==i) ImGui::SetScrollHereY();
+ if (ImGui::IsItemHovered()) {
+ if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
+ waveEditOpen=true;
+ nextWindow=GUI_WINDOW_WAVE_EDIT;
+ }
+ }
+ ImGui::SameLine();
+ PlotNoLerp(fmt::sprintf("##_WAVEP%d",i).c_str(),wavePreview,wave->len+1,0,NULL,0,wave->max);
+}
+
+void FurnaceGUI::sampleListItem(int i, int dir, int asset) {
+ bool memWarning=false;
+
+ DivSample* sample=e->song.sample[i];
+ for (int j=0; jsong.systemLen; j++) {
+ DivDispatch* dispatch=e->getDispatch(j);
+ if (dispatch==NULL) continue;
+
+ for (int k=0; kgetSampleMemCapacity(k)==0) continue;
+ if (!dispatch->isSampleLoaded(k,i) && sample->renderOn[k][j]) {
+ memWarning=true;
+ break;
+ }
+ }
+ if (memWarning) break;
+ }
+ if (memWarning) ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_SAMPLE_CHIP_WARNING]);
+ if (ImGui::Selectable(fmt::sprintf("%d: %s##_SAM%d",i,sample->name,i).c_str(),curSample==i)) {
+ curSample=i;
+ samplePos=0;
+ updateSampleTex=true;
+ }
+ if (ImGui::IsItemHovered() && !mobileUI) {
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
+ ImGui::SetTooltip("Bank %d: %s",i/12,sampleNote[i%12]);
+ if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
+ sampleEditOpen=true;
+ nextWindow=GUI_WINDOW_SAMPLE_EDIT;
+ }
+ ImGui::PopStyleColor();
+ }
+ if (memWarning) {
+ ImGui::SameLine();
+ ImGui::Text(ICON_FA_EXCLAMATION_TRIANGLE);
+ if (ImGui::IsItemHovered() && !mobileUI) {
+ ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
+ ImGui::SetTooltip("out of memory for this sample!");
+ ImGui::PopStyleColor();
+ }
+ ImGui::PopStyleColor();
+ }
+ if (wantScrollList && curSample==i) ImGui::SetScrollHereY();
+}
+
void FurnaceGUI::drawInsList(bool asChild) {
if (nextWindow==GUI_WINDOW_INS_LIST) {
insListOpen=true;
@@ -600,7 +665,7 @@ void FurnaceGUI::drawInsList(bool asChild) {
DRAG_TARGET(dirIndex,-1,e->song.insDir);
if (ImGui::BeginPopupContextItem(popupID.c_str())) {
if (ImGui::MenuItem("rename...")) {
- ImGui::OpenPopup("NewInsFolder");
+ editStr(&i.name);
}
if (ImGui::MenuItem("delete")) {
dirToDelete=dirIndex;
@@ -910,73 +975,16 @@ void FurnaceGUI::drawSampleList(bool asChild) {
void FurnaceGUI::actualWaveList() {
float wavePreview[257];
for (int i=0; i<(int)e->song.wave.size(); i++) {
- DivWavetable* wave=e->song.wave[i];
- for (int i=0; ilen; i++) {
- wavePreview[i]=wave->data[i];
- }
- if (wave->len>0) wavePreview[wave->len]=wave->data[wave->len-1];
ImGui::TableNextRow();
ImGui::TableNextColumn();
- if (ImGui::Selectable(fmt::sprintf("%d##_WAVE%d\n",i,i).c_str(),curWave==i)) {
- curWave=i;
- }
- if (wantScrollList && curWave==i) ImGui::SetScrollHereY();
- if (ImGui::IsItemHovered()) {
- if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
- waveEditOpen=true;
- nextWindow=GUI_WINDOW_WAVE_EDIT;
- }
- }
- ImGui::SameLine();
- PlotNoLerp(fmt::sprintf("##_WAVEP%d",i).c_str(),wavePreview,wave->len+1,0,NULL,0,wave->max);
+ waveListItem(i,wavePreview,-1,-1);
}
}
void FurnaceGUI::actualSampleList() {
for (int i=0; i<(int)e->song.sample.size(); i++) {
- bool memWarning=false;
-
- DivSample* sample=e->song.sample[i];
ImGui::TableNextRow();
ImGui::TableNextColumn();
- for (int j=0; jsong.systemLen; j++) {
- DivDispatch* dispatch=e->getDispatch(j);
- if (dispatch==NULL) continue;
-
- for (int k=0; kgetSampleMemCapacity(k)==0) continue;
- if (!dispatch->isSampleLoaded(k,i) && sample->renderOn[k][j]) {
- memWarning=true;
- break;
- }
- }
- if (memWarning) break;
- }
- if (memWarning) ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_SAMPLE_CHIP_WARNING]);
- if (ImGui::Selectable(fmt::sprintf("%d: %s##_SAM%d",i,sample->name,i).c_str(),curSample==i)) {
- curSample=i;
- samplePos=0;
- updateSampleTex=true;
- }
- if (ImGui::IsItemHovered() && !mobileUI) {
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
- ImGui::SetTooltip("Bank %d: %s",i/12,sampleNote[i%12]);
- if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
- sampleEditOpen=true;
- nextWindow=GUI_WINDOW_SAMPLE_EDIT;
- }
- ImGui::PopStyleColor();
- }
- if (memWarning) {
- ImGui::SameLine();
- ImGui::Text(ICON_FA_EXCLAMATION_TRIANGLE);
- if (ImGui::IsItemHovered() && !mobileUI) {
- ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
- ImGui::SetTooltip("out of memory for this sample!");
- ImGui::PopStyleColor();
- }
- ImGui::PopStyleColor();
- }
- if (wantScrollList && curSample==i) ImGui::SetScrollHereY();
+ sampleListItem(i,-1,-1);
}
}
diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp
index 7b0a26e67..75cc02584 100644
--- a/src/gui/gui.cpp
+++ b/src/gui/gui.cpp
@@ -2272,6 +2272,11 @@ void FurnaceGUI::exportAudio(String path, DivAudioExportModes mode) {
displayExporting=true;
}
+void FurnaceGUI::editStr(String* which) {
+ editString=which;
+ displayEditString=true;
+}
+
void FurnaceGUI::showWarning(String what, FurnaceGUIWarnings type) {
warnString=what;
warnAction=type;
@@ -5053,6 +5058,10 @@ bool FurnaceGUI::loop() {
ImGui::OpenPopup("New Song");
}
+ if (displayEditString) {
+ ImGui::OpenPopup("EditString");
+ }
+
if (nextWindow==GUI_WINDOW_ABOUT) {
aboutOpen=true;
nextWindow=GUI_WINDOW_NOTHING;
@@ -5592,6 +5601,27 @@ bool FurnaceGUI::loop() {
ImGui::EndPopup();
}
+ if (ImGui::BeginPopup("EditString",ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) {
+ if (editString==NULL) {
+ ImGui::Text("Error! No string provided!");
+ } else {
+ if (displayEditString) {
+ ImGui::SetItemDefaultFocus();
+ ImGui::SetKeyboardFocusHere();
+ }
+ ImGui::InputText("##StringVal",editString);
+ }
+ displayEditString=false;
+ ImGui::SameLine();
+ if (ImGui::Button("OK") || ImGui::IsKeyPressed(ImGuiKey_Enter,false)) {
+ editString=NULL;
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::EndPopup();
+ } else {
+ editString=NULL;
+ }
+
MEASURE_END(popup);
if (!tutorial.introPlayed || settings.alwaysPlayIntro!=0) {
@@ -6452,6 +6482,7 @@ FurnaceGUI::FurnaceGUI():
displayPendingRawSample(false),
snesFilterHex(false),
modTableHex(false),
+ displayEditString(false),
mobileEdit(false),
vgmExportVersion(0x171),
vgmExportTrailingTicks(-1),
@@ -6471,6 +6502,7 @@ FurnaceGUI::FurnaceGUI():
fmPreviewOn(false),
fmPreviewPaused(false),
fmPreviewOPN(NULL),
+ editString(NULL),
pendingRawSampleDepth(8),
pendingRawSampleChannels(1),
pendingRawSampleUnsigned(false),
@@ -6525,7 +6557,6 @@ FurnaceGUI::FurnaceGUI():
loopEnd(-1),
isClipping(0),
extraChannelButtons(0),
- patNameTarget(-1),
newSongCategory(0),
latchTarget(0),
wheelX(0),
@@ -6596,7 +6627,6 @@ FurnaceGUI::FurnaceGUI():
collapseWindow(false),
demandScrollX(false),
fancyPattern(false),
- wantPatName(false),
firstFrame(true),
tempoView(true),
waveHex(false),
diff --git a/src/gui/gui.h b/src/gui/gui.h
index 7efc8c927..739e280db 100644
--- a/src/gui/gui.h
+++ b/src/gui/gui.h
@@ -1223,7 +1223,7 @@ class FurnaceGUI {
bool portrait, injectBackUp, mobileMenuOpen;
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
- bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex;
+ bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString;
bool mobileEdit;
bool willExport[DIV_MAX_CHIPS];
int vgmExportVersion;
@@ -1241,6 +1241,7 @@ class FurnaceGUI {
short fmPreview[FM_PREVIEW_SIZE];
bool updateFMPreview, fmPreviewOn, fmPreviewPaused;
void* fmPreviewOPN;
+ String* editString;
String pendingRawSample;
int pendingRawSampleDepth, pendingRawSampleChannels;
@@ -1602,7 +1603,7 @@ class FurnaceGUI {
DivInstrument* prevInsData;
int curIns, curWave, curSample, curOctave, curOrder, prevIns, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan,orderEditMode, orderCursor;
- int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory, latchTarget;
+ int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, newSongCategory, latchTarget;
int wheelX, wheelY, dragSourceX, dragSourceXFine, dragSourceY, dragDestinationX, dragDestinationXFine, dragDestinationY, oldBeat, oldBar;
int curGroove;
float soloTimeout;
@@ -1625,7 +1626,7 @@ class FurnaceGUI {
SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd;
bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI;
- bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
+ bool collapseWindow, demandScrollX, fancyPattern, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
bool keepLoopAlive, keepGrooveAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu, dragMobileEditButton, wantGrooveListFocus;
FurnaceGUIWindows curWindow, nextWindow, curWindowLast;
std::atomic curWindowThreadSafe;
@@ -2002,7 +2003,7 @@ class FurnaceGUI {
void actualSampleList();
void insListItem(int index, int dir, int asset);
- void waveListItem(int index, int dir, int asset);
+ void waveListItem(int index, float* wavePreview, int dir, int asset);
void sampleListItem(int index, int dir, int asset);
void toggleMobileUI(bool enable, bool force=false);
@@ -2165,6 +2166,7 @@ class FurnaceGUI {
const char* getSystemName(DivSystem which);
public:
+ void editStr(String* which);
void showWarning(String what, FurnaceGUIWarnings type);
void showError(String what);
String getLastError();
diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp
index 33de4d4d3..554984043 100644
--- a/src/gui/pattern.cpp
+++ b/src/gui/pattern.cpp
@@ -834,28 +834,9 @@ void FurnaceGUI::drawPattern() {
if (extraChannelButtons==2) {
DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][ord],true);
ImGui::PushFont(mainFont);
- if (patNameTarget==i) {
- snprintf(chanID,2048,"##PatNameI%d_%d",i,ord);
- ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(8.0f*dpiScale));
- ImGui::SetCursorPosX(ImGui::GetCursorPosX()+4.0f*dpiScale);
- ImGui::InputText(chanID,&pat->name);
- if (wantPatName) {
- wantPatName=false;
- ImGui::SetItemDefaultFocus();
- ImGui::SetKeyboardFocusHere(-1);
- } else {
- if (!ImGui::IsItemActive()) {
- patNameTarget=-1;
- }
- }
- } else {
- snprintf(chanID,2048," %s##PatName%d",pat->name.c_str(),i);
- if (ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
- patNameTarget=i;
- wantPatName=true;
- snprintf(chanID,2048,"##PatNameI%d_%d",i,ord);
- ImGui::SetActiveID(ImGui::GetID(chanID),ImGui::GetCurrentWindow());
- }
+ snprintf(chanID,2048," %s##PatName%d",pat->name.c_str(),i);
+ if (ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
+ editStr(&pat->name);
}
ImGui::PopFont();
} else if (extraChannelButtons==1) {
From bd8e8fbaffdb58dff1414e81b3ee119d5f7101a1 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Tue, 16 May 2023 19:00:05 -0500
Subject: [PATCH 026/454] asset directories, part 11
---
src/engine/engine.cpp | 11 +-
src/gui/dataList.cpp | 537 +++++++++++++++++++++++++++---------------
src/gui/gui.cpp | 1 +
src/gui/gui.h | 1 +
4 files changed, 358 insertions(+), 192 deletions(-)
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
index 348e8bff6..0a00babd8 100644
--- a/src/engine/engine.cpp
+++ b/src/engine/engine.cpp
@@ -1635,15 +1635,14 @@ void DivEngine::checkAssetDir(std::vector& dir, size_t entries) {
}
}
- // create unsorted directory if it doesn't exist
- if (unsortedDir==NULL) {
- dir.push_back(DivAssetDir(""));
- unsortedDir=&(*dir.rbegin());
- }
-
// add missing items to unsorted directory
for (size_t i=0; ientries.push_back(i);
}
}
diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp
index cac8314c2..01f91be6c 100644
--- a/src/gui/dataList.cpp
+++ b/src/gui/dataList.cpp
@@ -291,6 +291,8 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
curIns=i;
wavePreviewInit=true;
updateFMPreview=true;
+ lastAssetType=0;
+ if (insListDir) nextWindow=GUI_WINDOW_PATTERN;
}
if (wantScrollList && curIns==i) ImGui::SetScrollHereY();
if (settings.insFocusesPattern && patternOpen && ImGui::IsItemActivated()) {
@@ -298,6 +300,7 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
curIns=i;
wavePreviewInit=true;
updateFMPreview=true;
+ lastAssetType=0;
}
if (ImGui::IsItemHovered() && i>=0 && !mobileUI) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
@@ -362,6 +365,7 @@ void FurnaceGUI::waveListItem(int i, float* wavePreview, int dir, int asset) {
if (wave->len>0) wavePreview[wave->len]=wave->data[wave->len-1];
if (ImGui::Selectable(fmt::sprintf("%d##_WAVE%d\n",i,i).c_str(),curWave==i)) {
curWave=i;
+ lastAssetType=1;
}
if (wantScrollList && curWave==i) ImGui::SetScrollHereY();
if (ImGui::IsItemHovered()) {
@@ -370,6 +374,10 @@ void FurnaceGUI::waveListItem(int i, float* wavePreview, int dir, int asset) {
nextWindow=GUI_WINDOW_WAVE_EDIT;
}
}
+ if (waveListDir || (settings.unifiedDataView && insListDir)) {
+ DRAG_SOURCE(dir,asset);
+ DRAG_TARGET(dir,asset,e->song.waveDir);
+ }
ImGui::SameLine();
PlotNoLerp(fmt::sprintf("##_WAVEP%d",i).c_str(),wavePreview,wave->len+1,0,NULL,0,wave->max);
}
@@ -396,16 +404,21 @@ void FurnaceGUI::sampleListItem(int i, int dir, int asset) {
curSample=i;
samplePos=0;
updateSampleTex=true;
+ lastAssetType=2;
}
if (ImGui::IsItemHovered() && !mobileUI) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
- ImGui::SetTooltip("Bank %d: %s",i/12,sampleNote[i%12]);
+ ImGui::SetTooltip("(legacy bank %d: %s)",i/12,sampleNote[i%12]);
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
sampleEditOpen=true;
nextWindow=GUI_WINDOW_SAMPLE_EDIT;
}
ImGui::PopStyleColor();
}
+ if (sampleListDir || (settings.unifiedDataView && insListDir)) {
+ DRAG_SOURCE(dir,asset);
+ DRAG_TARGET(dir,asset,e->song.sampleDir);
+ }
if (memWarning) {
ImGui::SameLine();
ImGui::Text(ICON_FA_EXCLAMATION_TRIANGLE);
@@ -435,203 +448,226 @@ void FurnaceGUI::drawInsList(bool asChild) {
if (began) {
if (settings.unifiedDataView) settings.horizontalDataView=0;
if (ImGui::Button(ICON_FA_PLUS "##InsAdd")) {
- if (!settings.unifiedDataView) doAction(GUI_ACTION_INS_LIST_ADD);
+ if (settings.unifiedDataView) {
+ switch (lastAssetType) {
+ case 0:
+ doAction(GUI_ACTION_INS_LIST_ADD);
+ break;
+ case 1:
+ doAction(GUI_ACTION_WAVE_LIST_ADD);
+ break;
+ case 2:
+ doAction(GUI_ACTION_SAMPLE_LIST_ADD);
+ break;
+ }
+ } else {
+ doAction(GUI_ACTION_INS_LIST_ADD);
+ }
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Add");
}
- if (settings.unifiedDataView) {
- if (ImGui::BeginPopupContextItem("UnifiedAdd",ImGuiMouseButton_Left)) {
- if (ImGui::MenuItem("instrument")) {
- doAction(GUI_ACTION_INS_LIST_ADD);
- }
- if (ImGui::MenuItem("wavetable")) {
- doAction(GUI_ACTION_WAVE_LIST_ADD);
- }
- if (ImGui::MenuItem("sample (create)")) {
- doAction(GUI_ACTION_SAMPLE_LIST_ADD);
- }
- ImGui::EndPopup();
- }
- } else {
- if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
- displayInsTypeList=true;
- displayInsTypeListMakeInsSample=-1;
- }
+ if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
+ displayInsTypeList=true;
+ displayInsTypeListMakeInsSample=-1;
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FILES_O "##InsClone")) {
- if (!settings.unifiedDataView) doAction(GUI_ACTION_INS_LIST_DUPLICATE);
+ if (settings.unifiedDataView) {
+ switch (lastAssetType) {
+ case 0:
+ doAction(GUI_ACTION_INS_LIST_DUPLICATE);
+ break;
+ case 1:
+ doAction(GUI_ACTION_WAVE_LIST_DUPLICATE);
+ break;
+ case 2:
+ doAction(GUI_ACTION_SAMPLE_LIST_DUPLICATE);
+ break;
+ }
+ } else {
+ doAction(GUI_ACTION_INS_LIST_DUPLICATE);
+ }
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Duplicate");
}
- if (settings.unifiedDataView) {
- if (ImGui::BeginPopupContextItem("UnifiedClone",ImGuiMouseButton_Left)) {
- if (ImGui::MenuItem("instrument")) {
- doAction(GUI_ACTION_INS_LIST_DUPLICATE);
- }
- if (ImGui::MenuItem("wavetable")) {
- doAction(GUI_ACTION_WAVE_LIST_DUPLICATE);
- }
- if (ImGui::MenuItem("sample")) {
- doAction(GUI_ACTION_SAMPLE_LIST_DUPLICATE);
- }
- ImGui::EndPopup();
- }
- }
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FOLDER_OPEN "##InsLoad")) {
- if (!settings.unifiedDataView) doAction(GUI_ACTION_INS_LIST_OPEN);
+ if (settings.unifiedDataView) {
+ switch (lastAssetType) {
+ case 0:
+ doAction(GUI_ACTION_INS_LIST_OPEN);
+ break;
+ case 1:
+ doAction(GUI_ACTION_WAVE_LIST_OPEN);
+ break;
+ case 2:
+ doAction(GUI_ACTION_SAMPLE_LIST_OPEN);
+ break;
+ }
+ } else {
+ doAction(GUI_ACTION_INS_LIST_OPEN);
+ }
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Open");
}
- if (settings.unifiedDataView) {
- if (ImGui::BeginPopupContextItem("UnifiedLoad",ImGuiMouseButton_Left)) {
- if (ImGui::MenuItem("instrument")) {
- doAction(GUI_ACTION_INS_LIST_OPEN);
- }
- if (ImGui::MenuItem("instrument (replace...)")) {
- doAction((curIns>=0 && curIns<(int)e->song.ins.size())?GUI_ACTION_INS_LIST_OPEN_REPLACE:GUI_ACTION_INS_LIST_OPEN);
- }
- if (ImGui::MenuItem("wavetable")) {
- doAction(GUI_ACTION_WAVE_LIST_OPEN);
- }
- if (ImGui::MenuItem("sample")) {
- doAction(GUI_ACTION_SAMPLE_LIST_OPEN);
- }
- ImGui::Separator();
- if (ImGui::MenuItem("instrument from TX81Z")) {
- doAction(GUI_ACTION_TX81Z_REQUEST);
- }
- ImGui::EndPopup();
+ if (ImGui::BeginPopupContextItem("InsOpenOpt")) {
+ if (ImGui::MenuItem("replace...")) {
+ doAction((curIns>=0 && curIns<(int)e->song.ins.size())?GUI_ACTION_INS_LIST_OPEN_REPLACE:GUI_ACTION_INS_LIST_OPEN);
}
- } else {
- if (ImGui::BeginPopupContextItem("InsOpenOpt")) {
- if (ImGui::MenuItem("replace...")) {
- doAction((curIns>=0 && curIns<(int)e->song.ins.size())?GUI_ACTION_INS_LIST_OPEN_REPLACE:GUI_ACTION_INS_LIST_OPEN);
- }
- ImGui::Separator();
- if (ImGui::MenuItem("load from TX81Z")) {
- doAction(GUI_ACTION_TX81Z_REQUEST);
- }
- ImGui::EndPopup();
- }
- if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip("Open (insert; right-click to replace)");
+ ImGui::Separator();
+ if (ImGui::MenuItem("load from TX81Z")) {
+ doAction(GUI_ACTION_TX81Z_REQUEST);
}
+ ImGui::EndPopup();
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("Open (insert; right-click to replace)");
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FLOPPY_O "##InsSave")) {
- if (!settings.unifiedDataView) doAction(GUI_ACTION_INS_LIST_SAVE);
+ if (settings.unifiedDataView) {
+ switch (lastAssetType) {
+ case 0:
+ doAction(GUI_ACTION_INS_LIST_SAVE);
+ break;
+ case 1:
+ doAction(GUI_ACTION_WAVE_LIST_SAVE);
+ break;
+ case 2:
+ doAction(GUI_ACTION_SAMPLE_LIST_SAVE);
+ break;
+ }
+ } else {
+ doAction(GUI_ACTION_INS_LIST_SAVE);
+ }
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Save");
}
- if (settings.unifiedDataView) {
- if (ImGui::BeginPopupContextItem("UnifiedSave",ImGuiMouseButton_Left)) {
- if (ImGui::MenuItem("instrument")) {
- doAction(GUI_ACTION_INS_LIST_SAVE);
- }
- if (ImGui::MenuItem("instrument (legacy .fui)")) {
- doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
- }
- if (ImGui::MenuItem("instrument (.dmp)")) {
- doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
- }
- if (ImGui::MenuItem("wavetable")) {
- doAction(GUI_ACTION_WAVE_LIST_SAVE);
- }
- if (ImGui::MenuItem("wavetable (.dmw)")) {
- doAction(GUI_ACTION_WAVE_LIST_SAVE_DMW);
- }
- if (ImGui::MenuItem("wavetable (raw)")) {
- doAction(GUI_ACTION_WAVE_LIST_SAVE_RAW);
- }
- if (ImGui::MenuItem("sample")) {
- doAction(GUI_ACTION_SAMPLE_LIST_SAVE);
- }
- ImGui::EndPopup();
- }
- } else {
- if (ImGui::BeginPopupContextItem("InsSaveFormats",ImGuiMouseButton_Right)) {
- if (ImGui::MenuItem("save in legacy format...")) {
- doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
- }
- if (ImGui::MenuItem("save as .dmp...")) {
- doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
- }
- ImGui::EndPopup();
+ if (ImGui::BeginPopupContextItem("InsSaveFormats",ImGuiMouseButton_Right)) {
+ if (ImGui::MenuItem("save as .dmp...")) {
+ doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
}
+ ImGui::EndPopup();
+ }
+ ImGui::SameLine();
+ pushToggleColors(insListDir);
+ if (ImGui::Button(ICON_FA_SITEMAP "##DirMode")) {
+ doAction(GUI_ACTION_INS_LIST_DIR_VIEW);
+ }
+ popToggleColors();
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("Toggle folders/standard view");
+ }
+ if (!insListDir) {
ImGui::SameLine();
- pushToggleColors(insListDir);
- if (ImGui::Button(ICON_FA_SITEMAP "##DirMode")) {
- doAction(GUI_ACTION_INS_LIST_DIR_VIEW);
- }
- popToggleColors();
- if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip("Toggle folders/standard view");
- }
- if (!insListDir) {
- ImGui::SameLine();
- if (ImGui::ArrowButton("InsUp",ImGuiDir_Up)) {
+ if (ImGui::ArrowButton("InsUp",ImGuiDir_Up)) {
+ if (settings.unifiedDataView) {
+ switch (lastAssetType) {
+ case 0:
+ doAction(GUI_ACTION_INS_LIST_MOVE_UP);
+ break;
+ case 1:
+ doAction(GUI_ACTION_WAVE_LIST_MOVE_UP);
+ break;
+ case 2:
+ doAction(GUI_ACTION_SAMPLE_LIST_MOVE_UP);
+ break;
+ }
+ } else {
doAction(GUI_ACTION_INS_LIST_MOVE_UP);
}
- if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip("Move up");
- }
- ImGui::SameLine();
- if (ImGui::ArrowButton("InsDown",ImGuiDir_Down)) {
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("Move up");
+ }
+ ImGui::SameLine();
+ if (ImGui::ArrowButton("InsDown",ImGuiDir_Down)) {
+ if (settings.unifiedDataView) {
+ switch (lastAssetType) {
+ case 0:
+ doAction(GUI_ACTION_INS_LIST_MOVE_DOWN);
+ break;
+ case 1:
+ doAction(GUI_ACTION_WAVE_LIST_MOVE_DOWN);
+ break;
+ case 2:
+ doAction(GUI_ACTION_SAMPLE_LIST_MOVE_DOWN);
+ break;
+ }
+ } else {
doAction(GUI_ACTION_INS_LIST_MOVE_DOWN);
}
- if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip("Move down");
- }
- } else {
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("Move down");
+ }
+ } else {
+ ImGui::SameLine();
+ if (ImGui::Button(ICON_FA_FOLDER "##InsFolder")) {
+ folderString="";
+ }
+ if (ImGui::BeginPopupContextItem("NewInsFolder",ImGuiMouseButton_Left)) {
+ ImGui::InputText("##FolderName",&folderString);
ImGui::SameLine();
- if (ImGui::Button(ICON_FA_FOLDER "##InsFolder")) {
- folderString="";
- }
- if (ImGui::BeginPopupContextItem("NewInsFolder",ImGuiMouseButton_Left)) {
- ImGui::InputText("##FolderName",&folderString);
- ImGui::SameLine();
- ImGui::BeginDisabled(folderString.empty());
- if (ImGui::Button("Create")) {
+ ImGui::BeginDisabled(folderString.empty());
+ if (ImGui::Button("Create")) {
+ if (settings.unifiedDataView) {
+ switch (lastAssetType) {
+ case 0:
+ e->lockEngine([this]() {
+ e->song.insDir.push_back(DivAssetDir(folderString));
+ });
+ break;
+ case 1:
+ e->lockEngine([this]() {
+ e->song.waveDir.push_back(DivAssetDir(folderString));
+ });
+ break;
+ case 2:
+ e->lockEngine([this]() {
+ e->song.sampleDir.push_back(DivAssetDir(folderString));
+ });
+ break;
+ }
+ } else {
e->lockEngine([this]() {
e->song.insDir.push_back(DivAssetDir(folderString));
});
- ImGui::CloseCurrentPopup();
}
- ImGui::EndDisabled();
- ImGui::EndPopup();
- }
- if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip("New folder");
+ ImGui::CloseCurrentPopup();
}
+ ImGui::EndDisabled();
+ ImGui::EndPopup();
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("New folder");
}
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_TIMES "##InsDelete")) {
- if (!settings.unifiedDataView) doAction(GUI_ACTION_INS_LIST_DELETE);
+ if (settings.unifiedDataView) {
+ switch (lastAssetType) {
+ case 0:
+ doAction(GUI_ACTION_INS_LIST_DELETE);
+ break;
+ case 1:
+ doAction(GUI_ACTION_WAVE_LIST_DELETE);
+ break;
+ case 2:
+ doAction(GUI_ACTION_SAMPLE_LIST_DELETE);
+ break;
+ }
+ } else {
+ doAction(GUI_ACTION_INS_LIST_DELETE);
+ }
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Delete");
}
- if (settings.unifiedDataView) {
- if (ImGui::BeginPopupContextItem("UnifiedDelete",ImGuiMouseButton_Left)) {
- if (ImGui::MenuItem("instrument")) {
- doAction(GUI_ACTION_INS_LIST_DELETE);
- }
- if (ImGui::MenuItem("wavetable")) {
- doAction(GUI_ACTION_WAVE_LIST_DELETE);
- }
- if (ImGui::MenuItem("sample")) {
- doAction(GUI_ACTION_SAMPLE_LIST_DELETE);
- }
- ImGui::EndPopup();
- }
- }
ImGui::Separator();
int availableRows=ImGui::GetContentRegionAvail().y/ImGui::GetFrameHeight();
if (availableRows<1) availableRows=1;
@@ -643,7 +679,9 @@ void FurnaceGUI::drawInsList(bool asChild) {
if (settings.unifiedDataView) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
- ImGui::Text(ICON_FA_TASKS " Instruments");
+ if (ImGui::Selectable(ICON_FA_TASKS " Instruments",lastAssetType==0)) {
+ lastAssetType=0;
+ }
ImGui::Indent();
}
@@ -658,7 +696,7 @@ void FurnaceGUI::drawInsList(bool asChild) {
int dirIndex=0;
int dirToDelete=-1;
for (DivAssetDir& i: e->song.insDir) {
- String nodeName=fmt::sprintf("%s %s##_AD%d",i.name.empty()?ICON_FA_FOLDER_O:ICON_FA_FOLDER,i.name.empty()?"":i.name,i.name.empty()?-1:dirIndex);
+ String nodeName=fmt::sprintf("%s %s##_ADI%d",i.name.empty()?ICON_FA_FOLDER_O:ICON_FA_FOLDER,i.name.empty()?"":i.name,i.name.empty()?-1:dirIndex);
String popupID=fmt::sprintf("DirRightMenu%d",dirIndex);
bool treeNode=ImGui::TreeNodeEx(nodeName.c_str(),ImGuiTreeNodeFlags_SpanAvailWidth|(i.name.empty()?ImGuiTreeNodeFlags_DefaultOpen:0));
DRAG_SOURCE(dirIndex,-1);
@@ -709,14 +747,18 @@ void FurnaceGUI::drawInsList(bool asChild) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
- ImGui::Text(ICON_FA_AREA_CHART " Wavetables");
+ if (ImGui::Selectable(ICON_FA_AREA_CHART " Wavetables",lastAssetType==1)) {
+ lastAssetType=1;
+ }
ImGui::Indent();
actualWaveList();
ImGui::Unindent();
ImGui::TableNextRow();
ImGui::TableNextColumn();
- ImGui::Text(ICON_FA_VOLUME_UP " Samples");
+ if (ImGui::Selectable(ICON_FA_VOLUME_UP " Samples",lastAssetType==2)) {
+ lastAssetType=2;
+ }
ImGui::Indent();
actualSampleList();
ImGui::Unindent();
@@ -805,19 +847,42 @@ void FurnaceGUI::drawWaveList(bool asChild) {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Toggle folders/standard view");
}
- ImGui::SameLine();
- if (ImGui::ArrowButton("WaveUp",ImGuiDir_Up)) {
- doAction(GUI_ACTION_WAVE_LIST_MOVE_UP);
- }
- if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip("Move up");
- }
- ImGui::SameLine();
- if (ImGui::ArrowButton("WaveDown",ImGuiDir_Down)) {
- doAction(GUI_ACTION_WAVE_LIST_MOVE_DOWN);
- }
- if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip("Move down");
+ if (!waveListDir) {
+ ImGui::SameLine();
+ if (ImGui::ArrowButton("WaveUp",ImGuiDir_Up)) {
+ doAction(GUI_ACTION_WAVE_LIST_MOVE_UP);
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("Move up");
+ }
+ ImGui::SameLine();
+ if (ImGui::ArrowButton("WaveDown",ImGuiDir_Down)) {
+ doAction(GUI_ACTION_WAVE_LIST_MOVE_DOWN);
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("Move down");
+ }
+ } else {
+ ImGui::SameLine();
+ if (ImGui::Button(ICON_FA_FOLDER "##WaveFolder")) {
+ folderString="";
+ }
+ if (ImGui::BeginPopupContextItem("NewWaveFolder",ImGuiMouseButton_Left)) {
+ ImGui::InputText("##FolderName",&folderString);
+ ImGui::SameLine();
+ ImGui::BeginDisabled(folderString.empty());
+ if (ImGui::Button("Create")) {
+ e->lockEngine([this]() {
+ e->song.waveDir.push_back(DivAssetDir(folderString));
+ });
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::EndDisabled();
+ ImGui::EndPopup();
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("New folder");
+ }
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_TIMES "##WaveDelete")) {
@@ -922,19 +987,42 @@ void FurnaceGUI::drawSampleList(bool asChild) {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Toggle folders/standard view");
}
- ImGui::SameLine();
- if (ImGui::ArrowButton("SampleUp",ImGuiDir_Up)) {
- doAction(GUI_ACTION_SAMPLE_LIST_MOVE_UP);
- }
- if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip("Move up");
- }
- ImGui::SameLine();
- if (ImGui::ArrowButton("SampleDown",ImGuiDir_Down)) {
- doAction(GUI_ACTION_SAMPLE_LIST_MOVE_DOWN);
- }
- if (ImGui::IsItemHovered()) {
- ImGui::SetTooltip("Move down");
+ if (!sampleListDir) {
+ ImGui::SameLine();
+ if (ImGui::ArrowButton("SampleUp",ImGuiDir_Up)) {
+ doAction(GUI_ACTION_SAMPLE_LIST_MOVE_UP);
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("Move up");
+ }
+ ImGui::SameLine();
+ if (ImGui::ArrowButton("SampleDown",ImGuiDir_Down)) {
+ doAction(GUI_ACTION_SAMPLE_LIST_MOVE_DOWN);
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("Move down");
+ }
+ } else {
+ ImGui::SameLine();
+ if (ImGui::Button(ICON_FA_FOLDER "##SampleFolder")) {
+ folderString="";
+ }
+ if (ImGui::BeginPopupContextItem("NewSampleFolder",ImGuiMouseButton_Left)) {
+ ImGui::InputText("##FolderName",&folderString);
+ ImGui::SameLine();
+ ImGui::BeginDisabled(folderString.empty());
+ if (ImGui::Button("Create")) {
+ e->lockEngine([this]() {
+ e->song.sampleDir.push_back(DivAssetDir(folderString));
+ });
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::EndDisabled();
+ ImGui::EndPopup();
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("New folder");
+ }
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_TIMES "##SampleDelete")) {
@@ -974,17 +1062,94 @@ void FurnaceGUI::drawSampleList(bool asChild) {
void FurnaceGUI::actualWaveList() {
float wavePreview[257];
- for (int i=0; i<(int)e->song.wave.size(); i++) {
+
+ if (waveListDir || (settings.unifiedDataView && insListDir)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
- waveListItem(i,wavePreview,-1,-1);
+ int dirIndex=0;
+ int dirToDelete=-1;
+ for (DivAssetDir& i: e->song.waveDir) {
+ String nodeName=fmt::sprintf("%s %s##_ADW%d",i.name.empty()?ICON_FA_FOLDER_O:ICON_FA_FOLDER,i.name.empty()?"":i.name,i.name.empty()?-1:dirIndex);
+ String popupID=fmt::sprintf("DirRightMenu%d",dirIndex);
+ bool treeNode=ImGui::TreeNodeEx(nodeName.c_str(),ImGuiTreeNodeFlags_SpanAvailWidth|(i.name.empty()?ImGuiTreeNodeFlags_DefaultOpen:0));
+ DRAG_SOURCE(dirIndex,-1);
+ DRAG_TARGET(dirIndex,-1,e->song.waveDir);
+ if (ImGui::BeginPopupContextItem(popupID.c_str())) {
+ if (ImGui::MenuItem("rename...")) {
+ editStr(&i.name);
+ }
+ if (ImGui::MenuItem("delete")) {
+ dirToDelete=dirIndex;
+ }
+ ImGui::EndPopup();
+ }
+ if (treeNode) {
+ int assetIndex=0;
+ for (int j: i.entries) {
+ waveListItem(j,wavePreview,dirIndex,assetIndex);
+ assetIndex++;
+ }
+ ImGui::TreePop();
+ }
+ dirIndex++;
+ }
+ if (dirToDelete!=-1) {
+ e->lockEngine([this,dirToDelete]() {
+ e->song.waveDir.erase(e->song.waveDir.begin()+dirToDelete);
+ e->checkAssetDir(e->song.waveDir,e->song.wave.size());
+ });
+ }
+ } else {
+ for (int i=0; i<(int)e->song.wave.size(); i++) {
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ waveListItem(i,wavePreview,-1,-1);
+ }
}
}
void FurnaceGUI::actualSampleList() {
- for (int i=0; i<(int)e->song.sample.size(); i++) {
+ if (sampleListDir || (settings.unifiedDataView && insListDir)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
- sampleListItem(i,-1,-1);
+ int dirIndex=0;
+ int dirToDelete=-1;
+ for (DivAssetDir& i: e->song.sampleDir) {
+ String nodeName=fmt::sprintf("%s %s##_ADS%d",i.name.empty()?ICON_FA_FOLDER_O:ICON_FA_FOLDER,i.name.empty()?"":i.name,i.name.empty()?-1:dirIndex);
+ String popupID=fmt::sprintf("DirRightMenu%d",dirIndex);
+ bool treeNode=ImGui::TreeNodeEx(nodeName.c_str(),ImGuiTreeNodeFlags_SpanAvailWidth|(i.name.empty()?ImGuiTreeNodeFlags_DefaultOpen:0));
+ DRAG_SOURCE(dirIndex,-1);
+ DRAG_TARGET(dirIndex,-1,e->song.sampleDir);
+ if (ImGui::BeginPopupContextItem(popupID.c_str())) {
+ if (ImGui::MenuItem("rename...")) {
+ editStr(&i.name);
+ }
+ if (ImGui::MenuItem("delete")) {
+ dirToDelete=dirIndex;
+ }
+ ImGui::EndPopup();
+ }
+ if (treeNode) {
+ int assetIndex=0;
+ for (int j: i.entries) {
+ sampleListItem(j,dirIndex,assetIndex);
+ assetIndex++;
+ }
+ ImGui::TreePop();
+ }
+ dirIndex++;
+ }
+ if (dirToDelete!=-1) {
+ e->lockEngine([this,dirToDelete]() {
+ e->song.sampleDir.erase(e->song.sampleDir.begin()+dirToDelete);
+ e->checkAssetDir(e->song.sampleDir,e->song.sample.size());
+ });
+ }
+ } else {
+ for (int i=0; i<(int)e->song.sample.size(); i++) {
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ sampleListItem(i,-1,-1);
+ }
}
}
diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp
index 75cc02584..8d8ad721b 100644
--- a/src/gui/gui.cpp
+++ b/src/gui/gui.cpp
@@ -6643,6 +6643,7 @@ FurnaceGUI::FurnaceGUI():
dragMobileMenu(false),
dragMobileEditButton(false),
wantGrooveListFocus(false),
+ lastAssetType(0),
curWindow(GUI_WINDOW_NOTHING),
nextWindow(GUI_WINDOW_NOTHING),
curWindowLast(GUI_WINDOW_NOTHING),
diff --git a/src/gui/gui.h b/src/gui/gui.h
index 739e280db..5a9ce6cb2 100644
--- a/src/gui/gui.h
+++ b/src/gui/gui.h
@@ -1628,6 +1628,7 @@ class FurnaceGUI {
bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI;
bool collapseWindow, demandScrollX, fancyPattern, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
bool keepLoopAlive, keepGrooveAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu, dragMobileEditButton, wantGrooveListFocus;
+ unsigned char lastAssetType;
FurnaceGUIWindows curWindow, nextWindow, curWindowLast;
std::atomic curWindowThreadSafe;
float peak[DIV_MAX_OUTPUTS];
From 1c3ad94b59792abac4303d80d7b9a9e94443afe7 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 17 May 2023 04:08:17 -0500
Subject: [PATCH 027/454] asset directories, part 12
---
src/gui/dataList.cpp | 65 ++++++++++++++++++++++++++++++++++++++------
1 file changed, 57 insertions(+), 8 deletions(-)
diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp
index 01f91be6c..c2ab876eb 100644
--- a/src/gui/dataList.cpp
+++ b/src/gui/dataList.cpp
@@ -468,6 +468,7 @@ void FurnaceGUI::drawInsList(bool asChild) {
ImGui::SetTooltip("Add");
}
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
+ makeInsTypeList=e->getPossibleInsTypes();
displayInsTypeList=true;
displayInsTypeListMakeInsSample=-1;
}
@@ -514,12 +515,39 @@ void FurnaceGUI::drawInsList(bool asChild) {
ImGui::SetTooltip("Open");
}
if (ImGui::BeginPopupContextItem("InsOpenOpt")) {
- if (ImGui::MenuItem("replace...")) {
- doAction((curIns>=0 && curIns<(int)e->song.ins.size())?GUI_ACTION_INS_LIST_OPEN_REPLACE:GUI_ACTION_INS_LIST_OPEN);
- }
- ImGui::Separator();
- if (ImGui::MenuItem("load from TX81Z")) {
- doAction(GUI_ACTION_TX81Z_REQUEST);
+ if (settings.unifiedDataView) {
+ if (ImGui::MenuItem("replace instrument...")) {
+ doAction((curIns>=0 && curIns<(int)e->song.ins.size())?GUI_ACTION_INS_LIST_OPEN_REPLACE:GUI_ACTION_INS_LIST_OPEN);
+ }
+ if (ImGui::MenuItem("load instrument from TX81Z")) {
+ doAction(GUI_ACTION_TX81Z_REQUEST);
+ }
+
+ ImGui::Separator();
+
+ if (ImGui::MenuItem("replace wavetable...")) {
+ doAction((curWave>=0 && curWave<(int)e->song.wave.size())?GUI_ACTION_WAVE_LIST_OPEN_REPLACE:GUI_ACTION_WAVE_LIST_OPEN);
+ }
+
+ ImGui::Separator();
+
+ if (ImGui::MenuItem("replace sample...")) {
+ doAction((curSample>=0 && curSample<(int)e->song.sample.size())?GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE:GUI_ACTION_SAMPLE_LIST_OPEN);
+ }
+ if (ImGui::MenuItem("import raw sample...")) {
+ doAction(GUI_ACTION_SAMPLE_LIST_OPEN_RAW);
+ }
+ if (ImGui::MenuItem("import raw sample (replace)...")) {
+ doAction((curSample>=0 && curSample<(int)e->song.sample.size())?GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE_RAW:GUI_ACTION_SAMPLE_LIST_OPEN_RAW);
+ }
+ } else {
+ if (ImGui::MenuItem("replace...")) {
+ doAction((curIns>=0 && curIns<(int)e->song.ins.size())?GUI_ACTION_INS_LIST_OPEN_REPLACE:GUI_ACTION_INS_LIST_OPEN);
+ }
+ ImGui::Separator();
+ if (ImGui::MenuItem("load from TX81Z")) {
+ doAction(GUI_ACTION_TX81Z_REQUEST);
+ }
}
ImGui::EndPopup();
}
@@ -548,8 +576,29 @@ void FurnaceGUI::drawInsList(bool asChild) {
ImGui::SetTooltip("Save");
}
if (ImGui::BeginPopupContextItem("InsSaveFormats",ImGuiMouseButton_Right)) {
- if (ImGui::MenuItem("save as .dmp...")) {
- doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
+ if (settings.unifiedDataView) {
+ if (ImGui::MenuItem("save instrument as .dmp...")) {
+ doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
+ }
+
+ ImGui::Separator();
+
+ if (ImGui::MenuItem("save wavetable as .dmw...")) {
+ doAction(GUI_ACTION_WAVE_LIST_SAVE_DMW);
+ }
+ if (ImGui::MenuItem("save raw wavetable...")) {
+ doAction(GUI_ACTION_WAVE_LIST_SAVE_RAW);
+ }
+
+ ImGui::Separator();
+
+ if (ImGui::MenuItem("save raw sample...")) {
+ doAction(GUI_ACTION_SAMPLE_LIST_SAVE_RAW);
+ }
+ } else {
+ if (ImGui::MenuItem("save as .dmp...")) {
+ doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
+ }
}
ImGui::EndPopup();
}
From fba17436559191c3437b1c54241a1b021a9c6f75 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 17 May 2023 05:45:22 -0500
Subject: [PATCH 029/454] maybe uninitialized
---
src/engine/fileOps.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp
index a30095108..62716f2b9 100644
--- a/src/engine/fileOps.cpp
+++ b/src/engine/fileOps.cpp
@@ -1663,6 +1663,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
memset(magic,0,5);
SafeReader reader=SafeReader(file,len);
warnings="";
+ assetDirPtr[0]=0;
+ assetDirPtr[1]=0;
+ assetDirPtr[2]=0;
try {
DivSong ds;
DivSubSong* subSong=ds.subsong[0];
From c05557b59d84b17f644277c28d8b6bfedd1754f9 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 17 May 2023 14:37:43 -0500
Subject: [PATCH 030/454] OPLL: fix compilation
---
src/engine/platform/opll.cpp | 4 ----
1 file changed, 4 deletions(-)
diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp
index 4f3d12098..8f30a9dd0 100644
--- a/src/engine/platform/opll.cpp
+++ b/src/engine/platform/opll.cpp
@@ -409,10 +409,6 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
case 8: case 9:
chan[c.chan].fixedFreq=(chan[c.chan].state.tomTopFreq&511)<<(chan[c.chan].state.tomTopFreq>>9);
break;
- default:
- chan[c.chan].fixedFreq=0;
- chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
- break;
}
} else {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
From dae87a4980e68922c689e417729241b2eeced6c2 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Thu, 18 May 2023 02:34:19 -0500
Subject: [PATCH 031/454] GUI: prevent unified view from bricking mobile
---
src/gui/editControls.cpp | 342 ++++++++++++++++++++-------------------
1 file changed, 173 insertions(+), 169 deletions(-)
diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp
index 2193ef609..e3e843481 100644
--- a/src/gui/editControls.cpp
+++ b/src/gui/editControls.cpp
@@ -463,181 +463,185 @@ void FurnaceGUI::drawMobileControls() {
ImGui::Separator();
- if (settings.unifiedDataView) {
- drawInsList(true);
- } else {
- switch (mobScene) {
- case GUI_SCENE_PATTERN:
- case GUI_SCENE_ORDERS:
- case GUI_SCENE_INSTRUMENT:
+ switch (mobScene) {
+ case GUI_SCENE_PATTERN:
+ case GUI_SCENE_ORDERS:
+ case GUI_SCENE_INSTRUMENT:
+ drawInsList(true);
+ break;
+ case GUI_SCENE_WAVETABLE:
+ if (settings.unifiedDataView) {
drawInsList(true);
- break;
- case GUI_SCENE_WAVETABLE:
+ } else {
drawWaveList(true);
- break;
- case GUI_SCENE_SAMPLE:
+ }
+ break;
+ case GUI_SCENE_SAMPLE:
+ if (settings.unifiedDataView) {
+ drawInsList(true);
+ } else {
drawSampleList(true);
- break;
- case GUI_SCENE_SONG: {
- if (ImGui::Button("New")) {
- mobileMenuOpen=false;
- //doAction(GUI_ACTION_NEW);
- if (modified) {
- showWarning("Unsaved changes! Save changes before creating a new song?",GUI_WARN_NEW);
- } else {
- displayNew=true;
- }
- }
- ImGui::SameLine();
- if (ImGui::Button("Open")) {
- mobileMenuOpen=false;
- doAction(GUI_ACTION_OPEN);
- }
- ImGui::SameLine();
- if (ImGui::Button("Save")) {
- mobileMenuOpen=false;
- doAction(GUI_ACTION_SAVE);
- }
- ImGui::SameLine();
- if (ImGui::Button("Save as...")) {
- mobileMenuOpen=false;
- doAction(GUI_ACTION_SAVE_AS);
- }
-
- ImGui::Button("1.1+ .dmf");
- ImGui::SameLine();
- ImGui::Button("Legacy .dmf");
- ImGui::SameLine();
- if (ImGui::Button("Export Audio")) {
- openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
- }
- ImGui::SameLine();
- if (ImGui::Button("Export VGM")) {
- openFileDialog(GUI_FILE_EXPORT_VGM);
- }
-
- if (ImGui::Button("CmdStream")) {
- openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY);
- }
- ImGui::SameLine();
- if (ImGui::Button("CmdStream Text")) {
- openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
- }
-
- if (ImGui::Button("Restore Backup")) {
- mobileMenuOpen=false;
- doAction(GUI_ACTION_OPEN_BACKUP);
- }
-
- ImGui::Separator();
-
- if (ImGui::BeginTabBar("MobileSong")) {
- if (ImGui::BeginTabItem("Song Info")) {
- drawSongInfo(true);
- ImGui::EndTabItem();
- }
- if (ImGui::BeginTabItem("Subsongs")) {
- drawSubSongs(true);
- ImGui::EndTabItem();
- }
- if (ImGui::BeginTabItem("Speed")) {
- drawSpeed(true);
- ImGui::EndTabItem();
- }
- ImGui::EndTabBar();
- }
- break;
}
- case GUI_SCENE_CHANNELS:
- ImGui::Text("Channels here...");
- break;
- case GUI_SCENE_CHIPS:
- ImGui::Text("Chips here...");
- break;
- case GUI_SCENE_MIXER:
- ImGui::Text("What the hell...");
- break;
- case GUI_SCENE_OTHER: {
- if (ImGui::Button("Osc")) {
- oscOpen=!oscOpen;
+ break;
+ case GUI_SCENE_SONG: {
+ if (ImGui::Button("New")) {
+ mobileMenuOpen=false;
+ //doAction(GUI_ACTION_NEW);
+ if (modified) {
+ showWarning("Unsaved changes! Save changes before creating a new song?",GUI_WARN_NEW);
+ } else {
+ displayNew=true;
}
- ImGui::SameLine();
- if (ImGui::Button("ChanOsc")) {
- chanOscOpen=!chanOscOpen;
- }
- ImGui::SameLine();
- if (ImGui::Button("RegView")) {
- regViewOpen=!regViewOpen;
- }
- ImGui::SameLine();
- if (ImGui::Button("Stats")) {
- statsOpen=!statsOpen;
- }
- if (ImGui::Button("Compat Flags")) {
- compatFlagsOpen=!compatFlagsOpen;
- }
-
- ImGui::Separator();
-
- ImGui::Button("Panic");
- ImGui::SameLine();
- if (ImGui::Button("Settings")) {
- mobileMenuOpen=false;
- settingsOpen=true;
- }
- ImGui::SameLine();
- if (ImGui::Button("Log")) {
- logOpen=!logOpen;
- }
- ImGui::SameLine();
- if (ImGui::Button("Debug")) {
- debugOpen=!debugOpen;
- }
- ImGui::SameLine();
- if (ImGui::Button("About")) {
- mobileMenuOpen=false;
- mobileMenuPos=0.0f;
- aboutOpen=true;
- }
- if (ImGui::Button("Switch to Desktop Mode")) {
- toggleMobileUI(!mobileUI);
- }
-
- int numAmiga=0;
- for (int i=0; isong.systemLen; i++) {
- if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++;
- }
-
- if (numAmiga) {
- ImGui::Text(
- "this is NOT ROM export! only use for making sure the\n"
- "Furnace Amiga emulator is working properly by\n"
- "comparing it with real Amiga output."
- );
- ImGui::Text("Directory");
- ImGui::SameLine();
- ImGui::InputText("##AVDPath",&workingDirROMExport);
- if (ImGui::Button("Bake Data")) {
- std::vector out=e->buildROM(DIV_ROM_AMIGA_VALIDATION);
- if (workingDirROMExport.size()>0) {
- if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR;
- }
- for (DivROMExportOutput& i: out) {
- String path=workingDirROMExport+i.name;
- FILE* outFile=ps_fopen(path.c_str(),"wb");
- if (outFile!=NULL) {
- fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile);
- fclose(outFile);
- }
- i.data->finish();
- delete i.data;
- }
- showError(fmt::sprintf("Done! Baked %d files.",(int)out.size()));
- }
- }
-
- break;
}
+ ImGui::SameLine();
+ if (ImGui::Button("Open")) {
+ mobileMenuOpen=false;
+ doAction(GUI_ACTION_OPEN);
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Save")) {
+ mobileMenuOpen=false;
+ doAction(GUI_ACTION_SAVE);
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Save as...")) {
+ mobileMenuOpen=false;
+ doAction(GUI_ACTION_SAVE_AS);
+ }
+
+ ImGui::Button("1.1+ .dmf");
+ ImGui::SameLine();
+ ImGui::Button("Legacy .dmf");
+ ImGui::SameLine();
+ if (ImGui::Button("Export Audio")) {
+ openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Export VGM")) {
+ openFileDialog(GUI_FILE_EXPORT_VGM);
+ }
+
+ if (ImGui::Button("CmdStream")) {
+ openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY);
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("CmdStream Text")) {
+ openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
+ }
+
+ if (ImGui::Button("Restore Backup")) {
+ mobileMenuOpen=false;
+ doAction(GUI_ACTION_OPEN_BACKUP);
+ }
+
+ ImGui::Separator();
+
+ if (ImGui::BeginTabBar("MobileSong")) {
+ if (ImGui::BeginTabItem("Song Info")) {
+ drawSongInfo(true);
+ ImGui::EndTabItem();
+ }
+ if (ImGui::BeginTabItem("Subsongs")) {
+ drawSubSongs(true);
+ ImGui::EndTabItem();
+ }
+ if (ImGui::BeginTabItem("Speed")) {
+ drawSpeed(true);
+ ImGui::EndTabItem();
+ }
+ ImGui::EndTabBar();
+ }
+ break;
+ }
+ case GUI_SCENE_CHANNELS:
+ ImGui::Text("Channels here...");
+ break;
+ case GUI_SCENE_CHIPS:
+ ImGui::Text("Chips here...");
+ break;
+ case GUI_SCENE_MIXER:
+ ImGui::Text("What the hell...");
+ break;
+ case GUI_SCENE_OTHER: {
+ if (ImGui::Button("Osc")) {
+ oscOpen=!oscOpen;
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("ChanOsc")) {
+ chanOscOpen=!chanOscOpen;
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("RegView")) {
+ regViewOpen=!regViewOpen;
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Stats")) {
+ statsOpen=!statsOpen;
+ }
+ if (ImGui::Button("Compat Flags")) {
+ compatFlagsOpen=!compatFlagsOpen;
+ }
+
+ ImGui::Separator();
+
+ ImGui::Button("Panic");
+ ImGui::SameLine();
+ if (ImGui::Button("Settings")) {
+ mobileMenuOpen=false;
+ settingsOpen=true;
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Log")) {
+ logOpen=!logOpen;
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Debug")) {
+ debugOpen=!debugOpen;
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("About")) {
+ mobileMenuOpen=false;
+ mobileMenuPos=0.0f;
+ aboutOpen=true;
+ }
+ if (ImGui::Button("Switch to Desktop Mode")) {
+ toggleMobileUI(!mobileUI);
+ }
+
+ int numAmiga=0;
+ for (int i=0; isong.systemLen; i++) {
+ if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++;
+ }
+
+ if (numAmiga) {
+ ImGui::Text(
+ "this is NOT ROM export! only use for making sure the\n"
+ "Furnace Amiga emulator is working properly by\n"
+ "comparing it with real Amiga output."
+ );
+ ImGui::Text("Directory");
+ ImGui::SameLine();
+ ImGui::InputText("##AVDPath",&workingDirROMExport);
+ if (ImGui::Button("Bake Data")) {
+ std::vector out=e->buildROM(DIV_ROM_AMIGA_VALIDATION);
+ if (workingDirROMExport.size()>0) {
+ if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR;
+ }
+ for (DivROMExportOutput& i: out) {
+ String path=workingDirROMExport+i.name;
+ FILE* outFile=ps_fopen(path.c_str(),"wb");
+ if (outFile!=NULL) {
+ fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile);
+ fclose(outFile);
+ }
+ i.data->finish();
+ delete i.data;
+ }
+ showError(fmt::sprintf("Done! Baked %d files.",(int)out.size()));
+ }
+ }
+
+ break;
}
}
}
From 347d35be196ba8324712ff6c18ac52b9fd0081e2 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Thu, 18 May 2023 15:52:48 -0500
Subject: [PATCH 032/454] GUI: add option to toggle save compression
---
src/gui/gui.cpp | 92 ++++++++++++++++++++++----------------------
src/gui/gui.h | 2 +
src/gui/settings.cpp | 12 ++++++
3 files changed, 59 insertions(+), 47 deletions(-)
diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp
index 8d8ad721b..1f2d48794 100644
--- a/src/gui/gui.cpp
+++ b/src/gui/gui.cpp
@@ -1968,8 +1968,6 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
//ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_NavEnableKeyboard;
}
-#define FURNACE_ZLIB_COMPRESS
-
int FurnaceGUI::save(String path, int dmfVersion) {
SafeWriter* w;
logD("saving file...");
@@ -1992,35 +1990,56 @@ int FurnaceGUI::save(String path, int dmfVersion) {
w->finish();
return 1;
}
-#ifdef FURNACE_ZLIB_COMPRESS
- unsigned char zbuf[131072];
- int ret;
- z_stream zl;
- memset(&zl,0,sizeof(z_stream));
- ret=deflateInit(&zl,Z_DEFAULT_COMPRESSION);
- if (ret!=Z_OK) {
- logE("zlib error!");
- lastError="compression error";
- fclose(outFile);
- w->finish();
- return 2;
- }
- zl.avail_in=w->size();
- zl.next_in=w->getFinalBuf();
- while (zl.avail_in>0) {
+ if (settings.compress) {
+ unsigned char zbuf[131072];
+ int ret;
+ z_stream zl;
+ memset(&zl,0,sizeof(z_stream));
+ ret=deflateInit(&zl,Z_DEFAULT_COMPRESSION);
+ if (ret!=Z_OK) {
+ logE("zlib error!");
+ lastError="compression error";
+ fclose(outFile);
+ w->finish();
+ return 2;
+ }
+ zl.avail_in=w->size();
+ zl.next_in=w->getFinalBuf();
+ while (zl.avail_in>0) {
+ zl.avail_out=131072;
+ zl.next_out=zbuf;
+ if ((ret=deflate(&zl,Z_NO_FLUSH))==Z_STREAM_ERROR) {
+ logE("zlib stream error!");
+ lastError="zlib stream error";
+ deflateEnd(&zl);
+ fclose(outFile);
+ w->finish();
+ return 2;
+ }
+ size_t amount=131072-zl.avail_out;
+ if (amount>0) {
+ if (fwrite(zbuf,1,amount,outFile)!=amount) {
+ logE("did not write entirely: %s!",strerror(errno));
+ lastError=strerror(errno);
+ deflateEnd(&zl);
+ fclose(outFile);
+ w->finish();
+ return 1;
+ }
+ }
+ }
zl.avail_out=131072;
zl.next_out=zbuf;
- if ((ret=deflate(&zl,Z_NO_FLUSH))==Z_STREAM_ERROR) {
- logE("zlib stream error!");
- lastError="zlib stream error";
+ if ((ret=deflate(&zl,Z_FINISH))==Z_STREAM_ERROR) {
+ logE("zlib finish stream error!");
+ lastError="zlib finish stream error";
deflateEnd(&zl);
fclose(outFile);
w->finish();
return 2;
}
- size_t amount=131072-zl.avail_out;
- if (amount>0) {
- if (fwrite(zbuf,1,amount,outFile)!=amount) {
+ if (131072-zl.avail_out>0) {
+ if (fwrite(zbuf,1,131072-zl.avail_out,outFile)!=(131072-zl.avail_out)) {
logE("did not write entirely: %s!",strerror(errno));
lastError=strerror(errno);
deflateEnd(&zl);
@@ -2029,37 +2048,16 @@ int FurnaceGUI::save(String path, int dmfVersion) {
return 1;
}
}
- }
- zl.avail_out=131072;
- zl.next_out=zbuf;
- if ((ret=deflate(&zl,Z_FINISH))==Z_STREAM_ERROR) {
- logE("zlib finish stream error!");
- lastError="zlib finish stream error";
deflateEnd(&zl);
- fclose(outFile);
- w->finish();
- return 2;
- }
- if (131072-zl.avail_out>0) {
- if (fwrite(zbuf,1,131072-zl.avail_out,outFile)!=(131072-zl.avail_out)) {
+ } else {
+ if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
logE("did not write entirely: %s!",strerror(errno));
lastError=strerror(errno);
- deflateEnd(&zl);
fclose(outFile);
w->finish();
return 1;
}
}
- deflateEnd(&zl);
-#else
- if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
- logE("did not write entirely: %s!",strerror(errno));
- lastError=strerror(errno);
- fclose(outFile);
- w->finish();
- return 1;
- }
-#endif
fclose(outFile);
w->finish();
backupLock.lock();
diff --git a/src/gui/gui.h b/src/gui/gui.h
index 5a9ce6cb2..880748645 100644
--- a/src/gui/gui.h
+++ b/src/gui/gui.h
@@ -1424,6 +1424,7 @@ class FurnaceGUI {
int alwaysPlayIntro;
int iCannotWait;
int orderButtonPos;
+ int compress;
unsigned int maxUndoSteps;
String mainFontPath;
String patFontPath;
@@ -1569,6 +1570,7 @@ class FurnaceGUI {
alwaysPlayIntro(0),
iCannotWait(0),
orderButtonPos(2),
+ compress(1),
maxUndoSteps(100),
mainFontPath(""),
patFontPath(""),
diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp
index 678593ea8..85b287662 100644
--- a/src/gui/settings.cpp
+++ b/src/gui/settings.cpp
@@ -654,6 +654,15 @@ void FurnaceGUI::drawSettings() {
settings.saveUnusedPatterns=saveUnusedPatternsB;
}
+ bool compressB=settings.compress;
+ if (ImGui::Checkbox("Compress when saving",&compressB)) {
+ settings.compress=compressB;
+ }
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("use zlib to compress saved songs.");
+ }
+
+
bool cursorFollowsOrderB=settings.cursorFollowsOrder;
if (ImGui::Checkbox("Cursor follows current order when moving it",&cursorFollowsOrderB)) {
settings.cursorFollowsOrder=cursorFollowsOrderB;
@@ -2673,6 +2682,7 @@ void FurnaceGUI::syncSettings() {
settings.cursorFollowsOrder=e->getConfInt("cursorFollowsOrder",1);
settings.iCannotWait=e->getConfInt("iCannotWait",0);
settings.orderButtonPos=e->getConfInt("orderButtonPos",2);
+ settings.compress=e->getConfInt("compress",1);
clampSetting(settings.mainFontSize,2,96);
clampSetting(settings.patFontSize,2,96);
@@ -2793,6 +2803,7 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.cursorFollowsOrder,0,1);
clampSetting(settings.iCannotWait,0,1);
clampSetting(settings.orderButtonPos,0,2);
+ clampSetting(settings.compress,0,1);
if (settings.exportLoops<0.0) settings.exportLoops=0.0;
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
@@ -3008,6 +3019,7 @@ void FurnaceGUI::commitSettings() {
e->setConf("cursorFollowsOrder",settings.cursorFollowsOrder);
e->setConf("iCannotWait",settings.iCannotWait);
e->setConf("orderButtonPos",settings.orderButtonPos);
+ e->setConf("compress",settings.compress);
// colors
for (int i=0; i
Date: Thu, 18 May 2023 17:53:13 -0500
Subject: [PATCH 033/454] prepare for a new pattern format
---
papers/format.md | 57 +++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 54 insertions(+), 3 deletions(-)
diff --git a/papers/format.md b/papers/format.md
index 2c98ecb99..498a8fe92 100644
--- a/papers/format.md
+++ b/papers/format.md
@@ -1278,7 +1278,58 @@ size | description
| - version>=58 size is length
```
-# pattern
+# pattern (>=157)
+
+```
+size | description
+-----|------------------------------------
+ 4 | "PATN" block ID
+ 4 | size of this block
+ 1 | subsong
+ 1 | channel
+ 2 | pattern index
+ STR | pattern name (>=51)
+ ??? | pattern data
+ | read a byte per row.
+ | if bit 7 is set, then read bit 0-6 as "skip N rows".
+ | if it is 0xff, end of pattern.
+ | if bit 7 is clear, then:
+ | - bit 0: note present
+ | - bit 1: ins present
+ | - bit 2: volume present
+ | - bit 3: effect 0 present
+ | - bit 4: effect value 0 present
+ | - bit 5: other effects present
+ | - bit 6: other effect values present
+ | if bit 5 is set, read another byte:
+ | - bit 0: effect 0 present
+ | - bit 1: effect 1 present
+ | - bit 2: effect 2 present
+ | - bit 3: effect 3 present
+ | - bit 4: effect 4 present
+ | - bit 5: effect 5 present
+ | - bit 6: effect 6 present
+ | - bit 7: effect 7 present
+ | if bit 6 is set, read another byte:
+ | - bit 0: effect value 0 present
+ | - bit 1: effect value 1 present
+ | - bit 2: effect value 2 present
+ | - bit 3: effect value 3 present
+ | - bit 4: effect value 4 present
+ | - bit 5: effect value 5 present
+ | - bit 6: effect value 6 present
+ | - bit 7: effect value 7 present
+ | then read note, ins, volume, effects and effect values depending on what is present.
+ | for note:
+ | - 0 is C-(-5)
+ | - 179 is B-9
+ | - 180 is note off
+ | - 181 is note release
+ | - 182 is macro release
+```
+
+
+# old pattern (<157)
```
size | description
@@ -1308,8 +1359,8 @@ size | description
| - 12: C (of next octave)
| - this is actually a leftover of the .dmf format.
| - 100: note off
- | - 100: note release
- | - 100: macro release
+ | - 101: note release
+ | - 102: macro release
| - octave
| - this is an signed char stored in a short.
| - therefore octave value 255 is actually octave -1.
From 90d1fd60d856ab3107d77c73b37428f93e880e05 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Thu, 18 May 2023 19:50:36 -0500
Subject: [PATCH 034/454] dev157 - new pattern format
---
papers/format.md | 73 ++++-----
src/engine/engine.h | 4 +-
src/engine/fileOps.cpp | 326 +++++++++++++++++++++++++++++++++++------
3 files changed, 317 insertions(+), 86 deletions(-)
diff --git a/papers/format.md b/papers/format.md
index 498a8fe92..2a0a045cf 100644
--- a/papers/format.md
+++ b/papers/format.md
@@ -32,6 +32,7 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
the format versions are:
+- 157: Furnace dev157
- 156: Furnace dev156
- 155: Furnace dev155
- 154: Furnace dev154
@@ -1290,42 +1291,42 @@ size | description
2 | pattern index
STR | pattern name (>=51)
??? | pattern data
- | read a byte per row.
- | if bit 7 is set, then read bit 0-6 as "skip N rows".
- | if it is 0xff, end of pattern.
- | if bit 7 is clear, then:
- | - bit 0: note present
- | - bit 1: ins present
- | - bit 2: volume present
- | - bit 3: effect 0 present
- | - bit 4: effect value 0 present
- | - bit 5: other effects present
- | - bit 6: other effect values present
- | if bit 5 is set, read another byte:
- | - bit 0: effect 0 present
- | - bit 1: effect 1 present
- | - bit 2: effect 2 present
- | - bit 3: effect 3 present
- | - bit 4: effect 4 present
- | - bit 5: effect 5 present
- | - bit 6: effect 6 present
- | - bit 7: effect 7 present
- | if bit 6 is set, read another byte:
- | - bit 0: effect value 0 present
- | - bit 1: effect value 1 present
- | - bit 2: effect value 2 present
- | - bit 3: effect value 3 present
- | - bit 4: effect value 4 present
- | - bit 5: effect value 5 present
- | - bit 6: effect value 6 present
- | - bit 7: effect value 7 present
- | then read note, ins, volume, effects and effect values depending on what is present.
- | for note:
- | - 0 is C-(-5)
- | - 179 is B-9
- | - 180 is note off
- | - 181 is note release
- | - 182 is macro release
+ | - read a byte per row.
+ | - if it is 0xff, end of pattern.
+ | - if bit 7 is set, then read bit 0-6 as "skip N+2 rows".
+ | - if bit 7 is clear, then:
+ | - bit 0: note present
+ | - bit 1: ins present
+ | - bit 2: volume present
+ | - bit 3: effect 0 present
+ | - bit 4: effect value 0 present
+ | - bit 5: other effects (0-3) present
+ | - bit 6: other effects (4-7) present
+ | - if bit 5 is set, read another byte:
+ | - bit 0: effect 0 present
+ | - bit 1: effect value 0 present
+ | - bit 2: effect 1 present
+ | - bit 3: effect value 1 present
+ | - bit 4: effect 2 present
+ | - bit 5: effect value 2 present
+ | - bit 6: effect 3 present
+ | - bit 7: effect value 3 present
+ | - if bit 6 is set, read another byte:
+ | - bit 0: effect 4 present
+ | - bit 1: effect value 4 present
+ | - bit 2: effect 5 present
+ | - bit 3: effect value 5 present
+ | - bit 4: effect 6 present
+ | - bit 5: effect value 6 present
+ | - bit 6: effect 7 present
+ | - bit 7: effect value 7 present
+ | - then read note, ins, volume, effects and effect values depending on what is present.
+ | - for note:
+ | - 0 is C-(-5)
+ | - 179 is B-9
+ | - 180 is note off
+ | - 181 is note release
+ | - 182 is macro release
```
diff --git a/src/engine/engine.h b/src/engine/engine.h
index 6dcf3eb5f..3c7d39a4d 100644
--- a/src/engine/engine.h
+++ b/src/engine/engine.h
@@ -53,8 +53,8 @@
#define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock();
#define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false;
-#define DIV_VERSION "dev156"
-#define DIV_ENGINE_VERSION 156
+#define DIV_VERSION "dev157"
+#define DIV_ENGINE_VERSION 157
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02
diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp
index 62716f2b9..fde806126 100644
--- a/src/engine/fileOps.cpp
+++ b/src/engine/fileOps.cpp
@@ -1650,6 +1650,42 @@ void DivEngine::convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivS
}
}
+short newFormatNotes[180]={
+ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // -5
+ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // -4
+ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // -3
+ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // -2
+ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // -1
+ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 0
+ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 1
+ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 2
+ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 3
+ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 4
+ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 5
+ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 6
+ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 7
+ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 8
+ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 // 9
+};
+
+short newFormatOctaves[180]={
+ 250, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, // -5
+ 251, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, // -4
+ 252, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, // -3
+ 253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, // -2
+ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // -1
+ 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1
+ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 2
+ 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 3
+ 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 4
+ 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 5
+ 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 6
+ 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 7
+ 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 8
+ 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 9
+};
+
bool DivEngine::loadFur(unsigned char* file, size_t len) {
unsigned int insPtr[256];
unsigned int wavePtr[256];
@@ -2582,6 +2618,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
// read patterns
for (int i: patPtr) {
+ bool isNewFormat=false;
if (!reader.seek(i,SEEK_SET)) {
logE("couldn't seek to pattern in %x!",i);
lastError=fmt::sprintf("couldn't seek to pattern in %x!",i);
@@ -2592,62 +2629,151 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
reader.read(magic,4);
logD("reading pattern in %x...",i);
if (strcmp(magic,"PATR")!=0) {
- logE("%x: invalid pattern header!",i);
- lastError="invalid pattern header!";
- ds.unload();
- delete[] file;
- return false;
+ if (strcmp(magic,"PATN")!=0 || ds.version<157) {
+ logE("%x: invalid pattern header!",i);
+ lastError="invalid pattern header!";
+ ds.unload();
+ delete[] file;
+ return false;
+ } else {
+ isNewFormat=true;
+ }
}
reader.readI();
- int chan=reader.readS();
- int index=reader.readS();
- int subs=0;
- if (ds.version>=95) {
- subs=reader.readS();
- } else {
- reader.readS();
- }
- reader.readS();
+ if (isNewFormat) {
+ int subs=(unsigned char)reader.readC();
+ int chan=(unsigned char)reader.readC();
+ int index=reader.readS();
- logD("- %d, %d, %d",subs,chan,index);
+ logD("- %d, %d, %d (new)",subs,chan,index);
- if (chan<0 || chan>=tchans) {
- logE("pattern channel out of range!",i);
- lastError="pattern channel out of range!";
- ds.unload();
- delete[] file;
- return false;
- }
- if (index<0 || index>(DIV_MAX_PATTERNS-1)) {
- logE("pattern index out of range!",i);
- lastError="pattern index out of range!";
- ds.unload();
- delete[] file;
- return false;
- }
- if (subs<0 || subs>=(int)ds.subsong.size()) {
- logE("pattern subsong out of range!",i);
- lastError="pattern subsong out of range!";
- ds.unload();
- delete[] file;
- return false;
- }
-
- DivPattern* pat=ds.subsong[subs]->pat[chan].getPattern(index,true);
- for (int j=0; jpatLen; j++) {
- pat->data[j][0]=reader.readS();
- pat->data[j][1]=reader.readS();
- pat->data[j][2]=reader.readS();
- pat->data[j][3]=reader.readS();
- for (int k=0; kpat[chan].effectCols; k++) {
- pat->data[j][4+(k<<1)]=reader.readS();
- pat->data[j][5+(k<<1)]=reader.readS();
+ if (chan<0 || chan>=tchans) {
+ logE("pattern channel out of range!",i);
+ lastError="pattern channel out of range!";
+ ds.unload();
+ delete[] file;
+ return false;
+ }
+ if (index<0 || index>(DIV_MAX_PATTERNS-1)) {
+ logE("pattern index out of range!",i);
+ lastError="pattern index out of range!";
+ ds.unload();
+ delete[] file;
+ return false;
+ }
+ if (subs<0 || subs>=(int)ds.subsong.size()) {
+ logE("pattern subsong out of range!",i);
+ lastError="pattern subsong out of range!";
+ ds.unload();
+ delete[] file;
+ return false;
}
- }
- if (ds.version>=51) {
+ DivPattern* pat=ds.subsong[subs]->pat[chan].getPattern(index,true);
pat->name=reader.readString();
+
+ // read new pattern
+ for (int j=0; jpatLen; j++) {
+ unsigned char mask=reader.readC();
+ unsigned short effectMask=0;
+
+ if (mask==0xff) break;
+ if (mask&128) {
+ j+=(mask&127)+1;
+ continue;
+ }
+
+ if (mask&32) {
+ effectMask|=(unsigned char)reader.readC();
+ }
+ if (mask&64) {
+ effectMask|=((unsigned short)reader.readC()&0xff)<<8;
+ }
+ if (mask&8) effectMask|=1;
+ if (mask&16) effectMask|=2;
+
+ if (mask&1) { // note
+ unsigned char note=reader.readC();
+ if (note==180) {
+ pat->data[j][0]=100;
+ pat->data[j][1]=0;
+ } else if (note==181) {
+ pat->data[j][0]=101;
+ pat->data[j][1]=0;
+ } else if (note==182) {
+ pat->data[j][0]=102;
+ pat->data[j][1]=0;
+ } else if (note<180) {
+ pat->data[j][0]=newFormatNotes[note];
+ pat->data[j][1]=newFormatOctaves[note];
+ } else {
+ pat->data[j][0]=0;
+ pat->data[j][1]=0;
+ }
+ }
+ if (mask&2) { // instrument
+ pat->data[j][2]=(unsigned char)reader.readC();
+ }
+ if (mask&4) { // volume
+ pat->data[j][3]=(unsigned char)reader.readC();
+ }
+ for (unsigned char k=0; k<16; k++) {
+ if (effectMask&(1<data[j][4+k]=(unsigned char)reader.readC();
+ }
+ }
+ }
+ } else {
+ int chan=reader.readS();
+ int index=reader.readS();
+ int subs=0;
+ if (ds.version>=95) {
+ subs=reader.readS();
+ } else {
+ reader.readS();
+ }
+ reader.readS();
+
+ logD("- %d, %d, %d (old)",subs,chan,index);
+
+ if (chan<0 || chan>=tchans) {
+ logE("pattern channel out of range!",i);
+ lastError="pattern channel out of range!";
+ ds.unload();
+ delete[] file;
+ return false;
+ }
+ if (index<0 || index>(DIV_MAX_PATTERNS-1)) {
+ logE("pattern index out of range!",i);
+ lastError="pattern index out of range!";
+ ds.unload();
+ delete[] file;
+ return false;
+ }
+ if (subs<0 || subs>=(int)ds.subsong.size()) {
+ logE("pattern subsong out of range!",i);
+ lastError="pattern subsong out of range!";
+ ds.unload();
+ delete[] file;
+ return false;
+ }
+
+ DivPattern* pat=ds.subsong[subs]->pat[chan].getPattern(index,true);
+ for (int j=0; jpatLen; j++) {
+ pat->data[j][0]=reader.readS();
+ pat->data[j][1]=reader.readS();
+ pat->data[j][2]=reader.readS();
+ pat->data[j][3]=reader.readS();
+ for (int k=0; kpat[chan].effectCols; k++) {
+ pat->data[j][4+(k<<1)]=reader.readS();
+ pat->data[j][5+(k<<1)]=reader.readS();
+ }
+ }
+
+ if (ds.version>=51) {
+ pat->name=reader.readString();
+ }
}
}
@@ -4951,6 +5077,8 @@ DivDataErrors DivEngine::readAssetDirData(SafeReader& reader, std::vector subSongPtr;
@@ -5385,6 +5513,107 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
for (PatToWrite& i: patsToWrite) {
DivPattern* pat=song.subsong[i.subsong]->pat[i.chan].getPattern(i.pat,false);
patPtr.push_back(w->tell());
+
+#ifdef NEW_PATTERN_FORMAT
+ w->write("PATN",4);
+ blockStartSeek=w->tell();
+ w->writeI(0);
+
+ w->writeC(i.subsong);
+ w->writeC(i.chan);
+ w->writeS(i.pat);
+ w->writeString(pat->name,false);
+
+ unsigned char emptyRows=0;
+
+ for (int j=0; jpatLen; j++) {
+ unsigned char mask=0;
+ unsigned char finalNote=255;
+ unsigned short effectMask=0;
+
+ if (pat->data[j][0]==100) {
+ finalNote=180;
+ } else if (pat->data[j][0]==101) { // note release
+ finalNote=181;
+ } else if (pat->data[j][0]==102) { // macro release
+ finalNote=182;
+ } else if (pat->data[j][1]==0 && pat->data[j][0]==0) {
+ finalNote=255;
+ } else {
+ int seek=(pat->data[j][0]+(signed char)pat->data[j][1]*12)+60;
+ if (seek<0 || seek>=180) {
+ finalNote=255;
+ } else {
+ finalNote=seek;
+ }
+ }
+
+ if (finalNote!=255) mask|=1; // note
+ if (pat->data[j][2]!=-1) mask|=2; // instrument
+ if (pat->data[j][3]!=-1) mask|=4; // volume
+ for (int k=0; kpat[i.chan].effectCols*2; k+=2) {
+ if (k==0) {
+ if (pat->data[j][4+k]!=-1) mask|=8;
+ if (pat->data[j][5+k]!=-1) mask|=16;
+ } else if (k<8) {
+ if (pat->data[j][4+k]!=-1 || pat->data[j][5+k]!=-1) mask|=32;
+ } else {
+ if (pat->data[j][4+k]!=-1 || pat->data[j][5+k]!=-1) mask|=64;
+ }
+
+ if (pat->data[j][4+k]!=-1) effectMask|=(1<data[j][5+k]!=-1) effectMask|=(2<127) {
+ w->writeC(128|(emptyRows-2));
+ emptyRows=0;
+ }
+ } else {
+ if (emptyRows>1) {
+ w->writeC(128|(emptyRows-2));
+ emptyRows=0;
+ } else if (emptyRows) {
+ w->writeC(0);
+ emptyRows=0;
+ }
+
+ w->writeC(mask);
+
+ if (mask&32) w->writeC(effectMask&0xff);
+ if (mask&64) w->writeC((effectMask>>8)&0xff);
+
+ if (mask&1) w->writeC(finalNote);
+ if (mask&2) w->writeC(pat->data[j][2]);
+ if (mask&4) w->writeC(pat->data[j][3]);
+ if (mask&8) w->writeC(pat->data[j][4]);
+ if (mask&16) w->writeC(pat->data[j][5]);
+ if (mask&32) {
+ if (effectMask&4) w->writeC(pat->data[j][6]);
+ if (effectMask&8) w->writeC(pat->data[j][7]);
+ if (effectMask&16) w->writeC(pat->data[j][8]);
+ if (effectMask&32) w->writeC(pat->data[j][9]);
+ if (effectMask&64) w->writeC(pat->data[j][10]);
+ if (effectMask&128) w->writeC(pat->data[j][11]);
+ }
+ if (mask&64) {
+ if (effectMask&256) w->writeC(pat->data[j][12]);
+ if (effectMask&512) w->writeC(pat->data[j][13]);
+ if (effectMask&1024) w->writeC(pat->data[j][14]);
+ if (effectMask&2048) w->writeC(pat->data[j][15]);
+ if (effectMask&4096) w->writeC(pat->data[j][16]);
+ if (effectMask&8192) w->writeC(pat->data[j][17]);
+ if (effectMask&16384) w->writeC(pat->data[j][18]);
+ if (effectMask&32768) w->writeC(pat->data[j][19]);
+ }
+ }
+ }
+
+ // stop
+ w->writeC(0xff);
+#else
w->write("PATR",4);
blockStartSeek=w->tell();
w->writeI(0);
@@ -5410,6 +5639,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
}
w->writeString(pat->name,false);
+#endif
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
From eaa82d49eb0de33ae7f56a4965f3cdb44edd0d73 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Fri, 19 May 2023 00:49:05 -0500
Subject: [PATCH 035/454] add a -subsong parameter
---
src/main.cpp | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/src/main.cpp b/src/main.cpp
index 8ba66f7e7..574c1dd7a 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -59,6 +59,7 @@ String zsmOutName;
String cmdOutName;
int loops=1;
int benchMode=0;
+int subsong=-1;
DivAudioExportModes outMode=DIV_EXPORT_MODE_ONE;
#ifdef HAVE_GUI
@@ -237,6 +238,21 @@ TAParamResult pLoops(String val) {
return TA_PARAM_SUCCESS;
}
+TAParamResult pSubSong(String val) {
+ try {
+ int v=std::stoi(val);
+ if (v<0) {
+ logE("sub-song shall be 0 or higher.");
+ return TA_PARAM_ERROR;
+ }
+ subsong=v;
+ } catch (std::exception& e) {
+ logE("sub-song shall be a number.");
+ return TA_PARAM_ERROR;
+ }
+ return TA_PARAM_SUCCESS;
+}
+
TAParamResult pOutMode(String val) {
if (val=="one") {
outMode=DIV_EXPORT_MODE_ONE;
@@ -312,6 +328,7 @@ void initParams() {
params.push_back(TAParam("c","console",false,pConsole,"","enable console mode"));
params.push_back(TAParam("l","loops",true,pLoops,"","set number of loops (-1 means loop forever)"));
+ params.push_back(TAParam("s","subsong",true,pSubSong,"","set sub-song"));
params.push_back(TAParam("o","outmode",true,pOutMode,"one|persys|perchan","set file output mode"));
params.push_back(TAParam("B","benchmark",true,pBenchmark,"render|seek","run performance test"));
@@ -569,6 +586,10 @@ int main(int argc, char** argv) {
return 0;
}
+ if (subsong!=-1) {
+ e.changeSongP(subsong);
+ }
+
if (consoleMode) {
bool cliSuccess=false;
cli.bindEngine(&e);
From 31a4e6efdce0bd1faabc53c1723666253af6916e Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Fri, 19 May 2023 13:46:24 -0500
Subject: [PATCH 036/454] GUI: fix crash in asset dir
when moving incompatible types
---
src/gui/dataList.cpp | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp
index c2ab876eb..72269988a 100644
--- a/src/gui/dataList.cpp
+++ b/src/gui/dataList.cpp
@@ -31,20 +31,20 @@ const char* sampleNote[12]={
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
};
-#define DRAG_SOURCE(_d,_a) \
+#define DRAG_SOURCE(_d,_a,_c) \
if (ImGui::BeginDragDropSource()) { \
dirToMove=_d; \
assetToMove=_a; \
- ImGui::SetDragDropPayload("FUR_DIR",NULL,0,ImGuiCond_Once); \
+ ImGui::SetDragDropPayload(_c,NULL,0,ImGuiCond_Once); \
ImGui::Button(ICON_FA_ARROWS "##AssetDrag"); \
ImGui::EndDragDropSource(); \
}
-#define DRAG_TARGET(_d,_a,_type) \
+#define DRAG_TARGET(_d,_a,_type,_c) \
if (ImGui::BeginDragDropTarget()) { \
- const ImGuiPayload* dragItem=ImGui::AcceptDragDropPayload("FUR_DIR"); \
+ const ImGuiPayload* dragItem=ImGui::AcceptDragDropPayload(_c); \
if (dragItem!=NULL) { \
- if (dragItem->IsDataType("FUR_DIR")) { \
+ if (dragItem->IsDataType(_c)) { \
if (assetToMove==-1) { \
if (dirToMove!=_d && _a==-1) { \
e->lockEngine([&]() { \
@@ -313,8 +313,8 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
}
if (i>=0) {
if (insListDir) {
- DRAG_SOURCE(dir,asset);
- DRAG_TARGET(dir,asset,e->song.insDir);
+ DRAG_SOURCE(dir,asset,"FUR_INSDIR");
+ DRAG_TARGET(dir,asset,e->song.insDir,"FUR_INSDIR");
}
if (ImGui::BeginPopupContextItem("InsRightMenu")) {
@@ -375,8 +375,8 @@ void FurnaceGUI::waveListItem(int i, float* wavePreview, int dir, int asset) {
}
}
if (waveListDir || (settings.unifiedDataView && insListDir)) {
- DRAG_SOURCE(dir,asset);
- DRAG_TARGET(dir,asset,e->song.waveDir);
+ DRAG_SOURCE(dir,asset,"FUR_WAVEDIR");
+ DRAG_TARGET(dir,asset,e->song.waveDir,"FUR_WAVEDIR");
}
ImGui::SameLine();
PlotNoLerp(fmt::sprintf("##_WAVEP%d",i).c_str(),wavePreview,wave->len+1,0,NULL,0,wave->max);
@@ -416,8 +416,8 @@ void FurnaceGUI::sampleListItem(int i, int dir, int asset) {
ImGui::PopStyleColor();
}
if (sampleListDir || (settings.unifiedDataView && insListDir)) {
- DRAG_SOURCE(dir,asset);
- DRAG_TARGET(dir,asset,e->song.sampleDir);
+ DRAG_SOURCE(dir,asset,"FUR_SDIR");
+ DRAG_TARGET(dir,asset,e->song.sampleDir,"FUR_SDIR");
}
if (memWarning) {
ImGui::SameLine();
@@ -748,8 +748,8 @@ void FurnaceGUI::drawInsList(bool asChild) {
String nodeName=fmt::sprintf("%s %s##_ADI%d",i.name.empty()?ICON_FA_FOLDER_O:ICON_FA_FOLDER,i.name.empty()?"":i.name,i.name.empty()?-1:dirIndex);
String popupID=fmt::sprintf("DirRightMenu%d",dirIndex);
bool treeNode=ImGui::TreeNodeEx(nodeName.c_str(),ImGuiTreeNodeFlags_SpanAvailWidth|(i.name.empty()?ImGuiTreeNodeFlags_DefaultOpen:0));
- DRAG_SOURCE(dirIndex,-1);
- DRAG_TARGET(dirIndex,-1,e->song.insDir);
+ DRAG_SOURCE(dirIndex,-1,"FUR_INSDIR");
+ DRAG_TARGET(dirIndex,-1,e->song.insDir,"FUR_INSDIR");
if (ImGui::BeginPopupContextItem(popupID.c_str())) {
if (ImGui::MenuItem("rename...")) {
editStr(&i.name);
@@ -1121,8 +1121,8 @@ void FurnaceGUI::actualWaveList() {
String nodeName=fmt::sprintf("%s %s##_ADW%d",i.name.empty()?ICON_FA_FOLDER_O:ICON_FA_FOLDER,i.name.empty()?"":i.name,i.name.empty()?-1:dirIndex);
String popupID=fmt::sprintf("DirRightMenu%d",dirIndex);
bool treeNode=ImGui::TreeNodeEx(nodeName.c_str(),ImGuiTreeNodeFlags_SpanAvailWidth|(i.name.empty()?ImGuiTreeNodeFlags_DefaultOpen:0));
- DRAG_SOURCE(dirIndex,-1);
- DRAG_TARGET(dirIndex,-1,e->song.waveDir);
+ DRAG_SOURCE(dirIndex,-1,"FUR_WAVEDIR");
+ DRAG_TARGET(dirIndex,-1,e->song.waveDir,"FUR_WAVEDIR");
if (ImGui::BeginPopupContextItem(popupID.c_str())) {
if (ImGui::MenuItem("rename...")) {
editStr(&i.name);
@@ -1167,8 +1167,8 @@ void FurnaceGUI::actualSampleList() {
String nodeName=fmt::sprintf("%s %s##_ADS%d",i.name.empty()?ICON_FA_FOLDER_O:ICON_FA_FOLDER,i.name.empty()?"":i.name,i.name.empty()?-1:dirIndex);
String popupID=fmt::sprintf("DirRightMenu%d",dirIndex);
bool treeNode=ImGui::TreeNodeEx(nodeName.c_str(),ImGuiTreeNodeFlags_SpanAvailWidth|(i.name.empty()?ImGuiTreeNodeFlags_DefaultOpen:0));
- DRAG_SOURCE(dirIndex,-1);
- DRAG_TARGET(dirIndex,-1,e->song.sampleDir);
+ DRAG_SOURCE(dirIndex,-1,"FUR_SDIR");
+ DRAG_TARGET(dirIndex,-1,e->song.sampleDir,"FUR_SDIR");
if (ImGui::BeginPopupContextItem(popupID.c_str())) {
if (ImGui::MenuItem("rename...")) {
editStr(&i.name);
From 61a15835dbab9789f37ae89aa40c620f62bd67ff Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Sat, 20 May 2023 14:58:57 -0500
Subject: [PATCH 037/454] pattern pointers shall be unsigned
---
src/engine/fileOps.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp
index fde806126..7642a021b 100644
--- a/src/engine/fileOps.cpp
+++ b/src/engine/fileOps.cpp
@@ -1693,7 +1693,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
unsigned int subSongPtr[256];
unsigned int sysFlagsPtr[DIV_MAX_CHIPS];
unsigned int assetDirPtr[3];
- std::vector patPtr;
+ std::vector patPtr;
int numberOfSubSongs=0;
char magic[5];
memset(magic,0,5);
@@ -2617,7 +2617,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
// read patterns
- for (int i: patPtr) {
+ for (unsigned int i: patPtr) {
bool isNewFormat=false;
if (!reader.seek(i,SEEK_SET)) {
logE("couldn't seek to pattern in %x!",i);
From d6441892013cbc48c06e9bf7b96c3ac3ff06a135 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Sat, 20 May 2023 15:19:45 -0500
Subject: [PATCH 038/454] C64: prevent multiple write to filter reg
issue #1114
---
src/engine/platform/c64.cpp | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp
index 139b1525b..977e39511 100644
--- a/src/engine/platform/c64.cpp
+++ b/src/engine/platform/c64.cpp
@@ -105,6 +105,7 @@ void DivPlatformC64::updateFilter() {
}
void DivPlatformC64::tick(bool sysTick) {
+ bool willUpdateFilter=false;
for (int i=0; i<3; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
@@ -117,10 +118,10 @@ void DivPlatformC64::tick(bool sysTick) {
if (filtCut>2047) filtCut=2047;
if (filtCut<0) filtCut=0;
}
- updateFilter();
+ willUpdateFilter=true;
} else {
vol=MIN(15,chan[i].std.vol.val);
- updateFilter();
+ willUpdateFilter=true;
}
}
if (NEW_ARP_STRAT) {
@@ -156,11 +157,11 @@ void DivPlatformC64::tick(bool sysTick) {
}
if (chan[i].std.ex1.had) {
filtControl=chan[i].std.ex1.val&15;
- updateFilter();
+ willUpdateFilter=true;
}
if (chan[i].std.ex2.had) {
filtRes=chan[i].std.ex2.val&15;
- updateFilter();
+ willUpdateFilter=true;
}
if (chan[i].std.ex3.had) {
chan[i].sync=chan[i].std.ex3.val&1;
@@ -207,6 +208,7 @@ void DivPlatformC64::tick(bool sysTick) {
chan[i].freqChanged=false;
}
}
+ if (willUpdateFilter) updateFilter();
}
int DivPlatformC64::dispatch(DivCommand c) {
From 5c7b46722380fadd28e3a7fc88d24ac25340a59a Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Sat, 20 May 2023 17:21:37 -0500
Subject: [PATCH 039/454] prepare a format for patchbay effects
---
papers/format.md | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/papers/format.md b/papers/format.md
index 2a0a045cf..b22d7b2c7 100644
--- a/papers/format.md
+++ b/papers/format.md
@@ -32,6 +32,7 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
the format versions are:
+- 158: Furnace dev158
- 157: Furnace dev157
- 156: Furnace dev156
- 155: Furnace dev155
@@ -441,6 +442,9 @@ size | description
4 | instrument directories
4 | wavetable directories
4 | sample directories
+ --- | **patchbay effects** (>=158)
+ 2 | number of effects
+ 4?? | pointers to effects
```
# patchbay
@@ -548,6 +552,31 @@ size | description
1?? | assets in this directory
```
+# patchbay effect (>=158)
+
+these effects sit on the patchbay.
+
+```
+size | description
+-----|------------------------------------
+ 4 | "EFFE" block ID
+ 4 | size of this block
+ 2 | slot
+ | - must be between 32 and 4092.
+ | - the other slots are reserved for chip/system portsets.
+ 2 | effect ID
+ | - 0x00: dummy
+ | - 0x01: external (plugin bridge)
+ | - not implemented yet
+ | - 0x02: volume
+ | - 0x03: filter (circuit)
+ 4f | dry/wet balance
+ 2 | reserved
+ 2 | storage version
+ 4 | storage length
+ ??? | storage data
+```
+
# instrument (>=127)
Furnace dev127 and higher use the new instrument format.
From 49b4ff9c5d959b6117e705fc6106fcaabf5cdb7d Mon Sep 17 00:00:00 2001
From: polluks
Date: Sun, 21 May 2023 00:21:51 +0200
Subject: [PATCH 040/454] Fixed typos
---
papers/doc/4-instrument/c64.md | 2 +-
papers/doc/4-instrument/fm.md | 8 ++++----
papers/doc/6-sample/README.md | 2 +-
papers/doc/7-systems/genesis.md | 2 +-
papers/doc/7-systems/opl.md | 2 +-
papers/doc/7-systems/opll.md | 2 +-
papers/doc/7-systems/opz.md | 2 +-
papers/doc/7-systems/sm8521.md | 2 +-
papers/doc/7-systems/sms.md | 2 +-
papers/doc/7-systems/ym2151.md | 2 +-
papers/doc/7-systems/ym2203.md | 2 +-
papers/doc/7-systems/ym2608.md | 2 +-
papers/doc/7-systems/ym2610.md | 2 +-
papers/doc/7-systems/ym2610b.md | 2 +-
papers/doc/7-systems/ym2612.md | 2 +-
15 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/papers/doc/4-instrument/c64.md b/papers/doc/4-instrument/c64.md
index 93eb261ab..9d612f478 100644
--- a/papers/doc/4-instrument/c64.md
+++ b/papers/doc/4-instrument/c64.md
@@ -25,6 +25,6 @@ C64 instrument editor consists of two tabs: one controlling various parameters o
- [Arpeggio] - pitch sequence
- [Duty cycle] - pulse duty cycle sequence
- [Waveform] - select the waveform used by instrument
-- [Filter mode] - select the filter mode/squence
+- [Filter mode] - select the filter mode/sequence
- [Resonance] - filter resonance sequence
- [Special] - ring and oscillator sync selector
diff --git a/papers/doc/4-instrument/fm.md b/papers/doc/4-instrument/fm.md
index aea2bc6b0..68c8f32d9 100644
--- a/papers/doc/4-instrument/fm.md
+++ b/papers/doc/4-instrument/fm.md
@@ -8,7 +8,7 @@ FM editor is divided into 7 tabs:
- [Macros (OP2)] - for macros controlling FM paramets of operator 2
- [Macros (OP3)] - for macros controlling FM paramets of operator 3
- [Macros (OP4)] - for macros controlling FM paramets of operator 4
-- [Macros] - for miscellanous macros controlling volume, argeggio and YM2151 noise generator.
+- [Macros] - for miscellaneous macros controlling volume, argeggio and YM2151 noise generator.
## FM
@@ -21,7 +21,7 @@ FM synthesizers Furnace supports are for-operator, meaning it takes four oscilla
- [Sustain Level(SL)] - Determines the point at which the sound ceases to decay and changes to a sound having a constant level. The sustain level is expressed as a fraction of the maximum level. (0-15 range)
- [Total Level (TL)] - Represents the envelope’s highest amplitude, with 0 being the largest and 127 (decimal) the smallest. A change of one unit is about 0.75 dB.
- [Envelope Scale (KSR)] - A parameter that determines the degree to which the envelope execution speed increases according to the pitch. (0-3 range)
-- [Frequency Multiplier (MULT)] - Determines the operator frequncy in relation to the pitch. (0-15 range)
+- [Frequency Multiplier (MULT)] - Determines the operator frequency in relation to the pitch. (0-15 range)
- [Fine Detune (DT)] - Shifts the pitch a little (0-7 range)
- [Coarse Detune (DT2)] - Shifts the pitch by tens of cents (0-3 range) WARNING: this parameter affects only YM2151 sound source!!!
- [Hardware Envelope Generator (SSG-EG)] - Executes the built-in envelope, inherited from AY-3-8910 PSG. Speed of execution is controlled via Decay Rate. WARNING: this parameter affects only YM2610/YM2612 sound source!!!
@@ -33,14 +33,14 @@ FM synthesizers Furnace supports are for-operator, meaning it takes four oscilla
## Macros
-Macros define the squence of values passed to the given parameter. Via macro, aside previously mentioned parameters, the following can be controlled:
+Macros define the sequence of values passed to the given parameter. Via macro, aside previously mentioned parameters, the following can be controlled:
- LFO Frequency
- LFO waveform selection WARNING: this parameter affects only YM2151 sound source!!!
- Amplitude Modulation Depth WARNING: this parameter affects only YM2151 sound source!!!
- Frequency Modulation Depth WARNING: this parameter affects only YM2151 sound source!!!
- Arpeggio Macro: pitch change sequence in semitones. Two modes are available:
-Absolute (defult) - Executes the pitch with absolute change based on the pitch of the actual note.
+Absolute (default) - Executes the pitch with absolute change based on the pitch of the actual note.
Fixed - Executes at the pitch specified in the sequence regardless of the note pitch.
- Noise Frequency: specifies the noise frequency in noise mode of YM2151's Channel 8 Operator 4 special mode.
diff --git a/papers/doc/6-sample/README.md b/papers/doc/6-sample/README.md
index ec6f230a6..05891a49a 100644
--- a/papers/doc/6-sample/README.md
+++ b/papers/doc/6-sample/README.md
@@ -64,4 +64,4 @@ In there, you can modify certain data pertaining to your sample, such as the:
- what frequencies to filter, along with filter level/sweep and resonance options (much like the C64)
- and many more.
-The changes you make will be applied as soon as you've committed them to your sample, but they can be undoed and redoed, just like text.
+The changes you make will be applied as soon as you've committed them to your sample, but they can be undone and redoed, just like text.
diff --git a/papers/doc/7-systems/genesis.md b/papers/doc/7-systems/genesis.md
index ceebf88aa..335466d0a 100644
--- a/papers/doc/7-systems/genesis.md
+++ b/papers/doc/7-systems/genesis.md
@@ -16,7 +16,7 @@ this console is powered by two sound chips: the [Yamaha YM2612](ym2612.md) and [
- `15xx`: set operator 4 level.
- `16xy`: set multiplier of operator.
- `x` is the operator (1-4).
- - `y` is the mutliplier.
+ - `y` is the multiplier.
- `17xx`: enable PCM channel.
- this only works on channel 6.
- **this effect is there for compatibility reasons** - it is otherwise recommended to use Sample type instruments (which automatically enable PCM mode when used).
diff --git a/papers/doc/7-systems/opl.md b/papers/doc/7-systems/opl.md
index 33bf627e5..b4a86e6db 100644
--- a/papers/doc/7-systems/opl.md
+++ b/papers/doc/7-systems/opl.md
@@ -29,7 +29,7 @@ afterwards everyone moved to Windows and software mixed PCM streaming...
- only in 4-op mode (OPL3).
- `16xy`: set multiplier of operator.
- `x` is the operator (1-4; last 2 operators only in 4-op mode).
- - `y` is the mutliplier.
+ - `y` is the multiplier.
- 17xx: set vibrato depth. the following values are accepted:
- 0: normal
- 1: double
diff --git a/papers/doc/7-systems/opll.md b/papers/doc/7-systems/opll.md
index 0f9c0f9d8..8b16e8881 100644
--- a/papers/doc/7-systems/opll.md
+++ b/papers/doc/7-systems/opll.md
@@ -30,7 +30,7 @@ the YM2413 is equipped with the following features:
- `13xx`: set operator 2 level.
- `16xy`: set multiplier of operator.
- `x` is the operator (1 or 2).
- - `y` is the mutliplier.
+ - `y` is the multiplier.
- `18xx`: toggle drums mode.
- 0 disables it and 1 enables it.
- only in drums chip.
diff --git a/papers/doc/7-systems/opz.md b/papers/doc/7-systems/opz.md
index 555df814f..507e4ab1d 100644
--- a/papers/doc/7-systems/opz.md
+++ b/papers/doc/7-systems/opz.md
@@ -33,7 +33,7 @@ no plans have been made for TX81Z MIDI passthrough, because:
- `15xx`: set operator 4 level.
- `16xy`: set multiplier of operator.
- `x` is the operator (1-4).
- - `y` is the mutliplier.
+ - `y` is the multiplier.
- `17xx`: set LFO speed.
- `18xx`: set LFO waveform. `xx` may be one of the following:
- `00`: saw
diff --git a/papers/doc/7-systems/sm8521.md b/papers/doc/7-systems/sm8521.md
index c14c91873..e68461987 100644
--- a/papers/doc/7-systems/sm8521.md
+++ b/papers/doc/7-systems/sm8521.md
@@ -2,7 +2,7 @@
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.
+Ultimately, most of the games for the Game.com ended up being failures 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.
diff --git a/papers/doc/7-systems/sms.md b/papers/doc/7-systems/sms.md
index 86e045396..37097ccdc 100644
--- a/papers/doc/7-systems/sms.md
+++ b/papers/doc/7-systems/sms.md
@@ -2,7 +2,7 @@
a relatively simple sound chip made by Texas Instruments. a derivative of it is used in Sega's Master System, the predecessor to Genesis.
-the original iteration of the SN76489 used in the TI-99/4A computers was clocked at 447 KHz, being able to play as low as 13.670 Hz (A -1). consequentially, pitch accuracy for higher notes is compromised.
+the original iteration of the SN76489 used in the TI-99/4A computers was clocked at 447 KHz, being able to play as low as 13.670 Hz (A -1). consequently, pitch accuracy for higher notes is compromised.
on the other hand, the chip was clocked at a much higher speed on Master System and Genesis, which makes it rather poor in the bass range.
diff --git a/papers/doc/7-systems/ym2151.md b/papers/doc/7-systems/ym2151.md
index 1923a18ad..cef663c8d 100644
--- a/papers/doc/7-systems/ym2151.md
+++ b/papers/doc/7-systems/ym2151.md
@@ -16,7 +16,7 @@ in most arcade boards the chip was used in combination with a PCM chip, like [Se
- `15xx`: set operator 4 level.
- `16xy`: set multiplier of operator.
- `x` is the operator (1-4).
- - `y` is the mutliplier.
+ - `y` is the multiplier.
- `17xx`: set LFO speed.
- `18xx`: set LFO waveform. `xx` may be one of the following:
- `00`: saw
diff --git a/papers/doc/7-systems/ym2203.md b/papers/doc/7-systems/ym2203.md
index 2c1f53b40..b9f76b8ec 100644
--- a/papers/doc/7-systems/ym2203.md
+++ b/papers/doc/7-systems/ym2203.md
@@ -18,7 +18,7 @@ several variants of this chip were released as well, with more features.
- `15xx`: set operator 4 level.
- `16xy`: set multiplier of operator.
- `x` is the operator (1-4).
- - `y` is the mutliplier.
+ - `y` is the multiplier.
- `18xx`: toggle extended channel 3 mode.
- 0 disables it and 1 enables it.
- only in extended channel 3 chip.
diff --git a/papers/doc/7-systems/ym2608.md b/papers/doc/7-systems/ym2608.md
index 1581189ef..5eda41c1e 100644
--- a/papers/doc/7-systems/ym2608.md
+++ b/papers/doc/7-systems/ym2608.md
@@ -18,7 +18,7 @@ the YM2610 (OPNB) and YM2610B chips are very similar to this one, but the built-
- `15xx`: set operator 4 level.
- `16xy`: set multiplier of operator.
- `x` is the operator (1-4).
- - `y` is the mutliplier.
+ - `y` is the multiplier.
- `18xx`: toggle extended channel 3 mode.
- 0 disables it and 1 enables it.
- only in extended channel 3 chip.
diff --git a/papers/doc/7-systems/ym2610.md b/papers/doc/7-systems/ym2610.md
index 22df25d87..a18888e60 100644
--- a/papers/doc/7-systems/ym2610.md
+++ b/papers/doc/7-systems/ym2610.md
@@ -16,7 +16,7 @@ its soundchip is a 4-in-1: 4ch 4-op FM, YM2149 (AY-3-8910 clone) and 2 different
- `15xx`: set operator 4 level.
- `16xy`: set multiplier of operator.
- `x` is the operator (1-4).
- - `y` is the mutliplier.
+ - `y` is the multiplier.
- `18xx`: toggle extended channel 2 mode.
- 0 disables it and 1 enables it.
- only in extended channel 2 chip.
diff --git a/papers/doc/7-systems/ym2610b.md b/papers/doc/7-systems/ym2610b.md
index 241366f44..f20e82ed5 100644
--- a/papers/doc/7-systems/ym2610b.md
+++ b/papers/doc/7-systems/ym2610b.md
@@ -15,7 +15,7 @@ it is backward compatible with the original chip.
- `15xx`: set operator 4 level.
- `16xy`: set multiplier of operator.
- `x` is the operator (1-4).
- - `y` is the mutliplier.
+ - `y` is the multiplier.
- `18xx`: toggle extended channel 3 mode.
- 0 disables it and 1 enables it.
- only in extended channel 3 chip.
diff --git a/papers/doc/7-systems/ym2612.md b/papers/doc/7-systems/ym2612.md
index ac17cedc3..1eeb148e2 100644
--- a/papers/doc/7-systems/ym2612.md
+++ b/papers/doc/7-systems/ym2612.md
@@ -14,7 +14,7 @@ one of two chips that powered the Sega Genesis. It is a six-channel, four-operat
- `15xx`: set operator 4 level.
- `16xy`: set multiplier of operator.
- `x` is the operator (1-4).
- - `y` is the mutliplier.
+ - `y` is the multiplier.
- `17xx`: enable PCM channel.
- this only works on channel 6.
- `18xx`: toggle extended channel 3 mode.
From 73986db1ffcf6dfd8260b404640558ae4ae85843 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Sat, 20 May 2023 19:12:46 -0500
Subject: [PATCH 041/454] no more AppImage (on next release)
---
.github/workflows/build.yml | 2 +-
res/furnace.appdata.xml | 5 +++
scripts/release-linux-AppImage.sh | 51 +++++++++++++++++++++++++++++++
scripts/release-linux.sh | 50 ++++++++++++++++++------------
4 files changed, 87 insertions(+), 21 deletions(-)
create mode 100755 scripts/release-linux-AppImage.sh
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 20e267370..7563ac6cf 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -307,7 +307,7 @@ jobs:
pushd furnace.AppDir
cp -v usr/share/{icons/hicolor/1024x1024/apps/furnace.png,applications/furnace.desktop} ./
ln -s furnace.png .DirIcon
- mv -v usr/share/metainfo/{furnace.appdata,org.tildearrow.furnace.metainfo}.xml
+ mv -v usr/share/metainfo/org.tildearrow.furnace.metainfo.xml
cp -v ../../res/AppRun ./
popd
diff --git a/res/furnace.appdata.xml b/res/furnace.appdata.xml
index 2214cb10d..5271379ab 100644
--- a/res/furnace.appdata.xml
+++ b/res/furnace.appdata.xml
@@ -19,6 +19,11 @@
it also offers DefleMask compatibility, allowing you to import your songs and even export them back for interoperability.
+
+
+ intense
+ mild
+
furnace.desktop
diff --git a/scripts/release-linux-AppImage.sh b/scripts/release-linux-AppImage.sh
new file mode 100755
index 000000000..35be353de
--- /dev/null
+++ b/scripts/release-linux-AppImage.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+# make linux release
+# run on an Ubuntu 16.04 machine or VM for best results.
+
+if [ ! -e /tmp/furnace ]; then
+ ln -s "$PWD" /tmp/furnace || exit 1
+fi
+
+cd /tmp/furnace
+
+if [ ! -e linuxbuild ]; then
+ mkdir linuxbuild || exit 1
+fi
+
+cd linuxbuild
+
+cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Werror" .. || exit 1
+make -j4 || exit 1
+
+cd ..
+
+mkdir -p release/linux/furnace.AppDir || exit 1
+cd linuxbuild
+
+make DESTDIR=/tmp/furnace/release/linux/furnace.AppDir install || exit 1
+
+cd ../release/linux/furnace.AppDir
+
+cp -v ../../../res/logo.png furnace.png || exit 1
+ln -s furnace.png .DirIcon || exit 1
+cp -v ../../../res/furnace.desktop . || exit 1
+#mkdir -p usr/share/metainfo || exit 1
+cp -v ../../../res/furnace.appdata.xml usr/share/metainfo/org.tildearrow.furnace.metainfo.xml || exit 1
+rm usr/share/metainfo/furnace.appdata.xml || exit 1
+cp -v ../../../res/AppRun . || exit 1
+
+#cp /usr/lib/libm.so.6 usr/lib/ || exit 1
+#cp /usr/lib/libstdc++.so.6 usr/lib/ || exit 1
+#cp /usr/lib/libc.so.6 usr/lib/ || exit 1
+#cp /usr/lib/libgcc_s.so.1 usr/lib/ || exit 1
+
+cd usr/bin
+strip -s furnace
+
+cd ../../..
+
+[ -e appimagetool-x86_64.AppImage ] || { wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" && chmod 755 appimagetool-x86_64.AppImage; }
+./appimagetool-x86_64.AppImage --appimage-extract
+ARCH=$(uname -m) ./squashfs-root/AppRun furnace.AppDir
+
+#zip -r furnace.zip LICENSE.txt Furnace*.dmg README.txt papers
diff --git a/scripts/release-linux.sh b/scripts/release-linux.sh
index ecd033140..570712f06 100755
--- a/scripts/release-linux.sh
+++ b/scripts/release-linux.sh
@@ -19,32 +19,42 @@ make -j4 || exit 1
cd ..
-mkdir -p release/linux/furnace.AppDir || exit 1
+mkdir -p release/linux/furnace || exit 1
cd linuxbuild
-make DESTDIR=/tmp/furnace/release/linux/furnace.AppDir install || exit 1
+make DESTDIR=/tmp/furnace/release/linux/furnace install || exit 1
-cd ../release/linux/furnace.AppDir
+cd ../release/linux/furnace
-cp -v ../../../res/logo.png furnace.png || exit 1
-ln -s furnace.png .DirIcon || exit 1
-cp -v ../../../res/furnace.desktop . || exit 1
-#mkdir -p usr/share/metainfo || exit 1
-cp -v ../../../res/furnace.appdata.xml usr/share/metainfo/org.tildearrow.furnace.metainfo.xml || exit 1
-cp -v ../../../res/AppRun . || exit 1
+cp -v ../../../res/logo.png .DirIcon || exit 1
+#cp -v ../../../res/furnace.desktop . || exit 1
-#cp /usr/lib/libm.so.6 usr/lib/ || exit 1
-#cp /usr/lib/libstdc++.so.6 usr/lib/ || exit 1
-#cp /usr/lib/libc.so.6 usr/lib/ || exit 1
-#cp /usr/lib/libgcc_s.so.1 usr/lib/ || exit 1
+cd usr
+
+mv bin/furnace .. || exit 1
+rmdir bin || exit 1
+
+rm -r share/applications
+rm -r share/doc
+mv share/icons ..
+rm -r share/licenses
+rm -r share/metainfo
+
+mv share/furnace/demos ..
+mv share/furnace/instruments ..
+mv share/furnace/wavetables ..
+rmdir share/furnace || exit 1
+rmdir share || exit 1
+
+cd ..
+
+cp ../../../LICENSE . || exit 1
+cp ../../../README.md . || exit 1
+cp -r ../../../papers papers || exit 1
+rmdir usr || exit 1
-cd usr/bin
strip -s furnace
-cd ../../..
+cd ..
-[ -e appimagetool-x86_64.AppImage ] || { wget "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" && chmod 755 appimagetool-x86_64.AppImage; }
-./appimagetool-x86_64.AppImage --appimage-extract
-ARCH=$(uname -m) ./squashfs-root/AppRun furnace.AppDir
-
-#zip -r furnace.zip LICENSE.txt Furnace*.dmg README.txt papers
+tar -zcv -f furnace.tar.gz furnace
From fda24cd9d1e80ee65684d121b3a22c7f886faad6 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Sat, 20 May 2023 21:49:37 -0500
Subject: [PATCH 042/454] update doc authors
---
papers/doc/README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/papers/doc/README.md b/papers/doc/README.md
index 94e9f0b9d..d8a14be24 100644
--- a/papers/doc/README.md
+++ b/papers/doc/README.md
@@ -21,6 +21,7 @@ writers:
- cam900
- host12prog
- WindowxDeveloper
+- polluks
other:
From 095e0a764efbe10439188d3a400c1eac5ccbe113 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Sat, 20 May 2023 22:05:34 -0500
Subject: [PATCH 043/454] k;asgdjhlkfsjadglgksafjd
---
.github/workflows/build.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7563ac6cf..20e267370 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -307,7 +307,7 @@ jobs:
pushd furnace.AppDir
cp -v usr/share/{icons/hicolor/1024x1024/apps/furnace.png,applications/furnace.desktop} ./
ln -s furnace.png .DirIcon
- mv -v usr/share/metainfo/org.tildearrow.furnace.metainfo.xml
+ mv -v usr/share/metainfo/{furnace.appdata,org.tildearrow.furnace.metainfo}.xml
cp -v ../../res/AppRun ./
popd
From 948f948aa4f96f99870d1b659b5e008c9f2faa52 Mon Sep 17 00:00:00 2001
From: Electric Keet
Date: Sat, 20 May 2023 20:12:12 -0700
Subject: [PATCH 044/454] Add Night Market for TI-99/4A to Demo Songs
---
demos/misc/Night_Market_TI994A.fur | Bin 0 -> 6812 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 demos/misc/Night_Market_TI994A.fur
diff --git a/demos/misc/Night_Market_TI994A.fur b/demos/misc/Night_Market_TI994A.fur
new file mode 100644
index 0000000000000000000000000000000000000000..cff985fe5e1f12855e75674b7d78e9c95c299644
GIT binary patch
literal 6812
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
R69DX04FgHchJ?x+{{yOQb9?{*
literal 0
HcmV?d00001
From 85ef4869493022437e902a272dc7138db1053755 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Sat, 20 May 2023 23:20:39 -0500
Subject: [PATCH 045/454] GUI: update credits
---
src/gui/about.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/gui/about.cpp b/src/gui/about.cpp
index 6bacc1a12..fa020b98a 100644
--- a/src/gui/about.cpp
+++ b/src/gui/about.cpp
@@ -78,6 +78,7 @@ const char* aboutLine[]={
"Dippy",
"djtuBIG-MaliceX",
"dumbut",
+ "ElectricKeet",
"EpicTyphlosion",
"FΛDE",
"Forte",
From 2da1fe8821e6f39d6fe5721964cee779c2253a5c Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Sun, 21 May 2023 04:39:36 -0500
Subject: [PATCH 046/454] prepare for patchbay effects
---
CMakeLists.txt | 3 +
papers/format.md | 10 +-
src/engine/effect.h | 189 +++++++++++++++++++++++++++++++++
src/engine/effect/abstract.cpp | 85 +++++++++++++++
src/engine/effect/dummy.cpp | 60 +++++++++++
src/engine/effect/dummy.h | 34 ++++++
src/engine/effectContainer.cpp | 95 +++++++++++++++++
src/engine/engine.h | 32 ++++++
src/engine/song.h | 25 +++++
9 files changed, 528 insertions(+), 5 deletions(-)
create mode 100644 src/engine/effect.h
create mode 100644 src/engine/effect/abstract.cpp
create mode 100644 src/engine/effect/dummy.cpp
create mode 100644 src/engine/effect/dummy.h
create mode 100644 src/engine/effectContainer.cpp
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 78a68aa83..3e2f5ee5b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -556,6 +556,9 @@ src/engine/platform/dummy.cpp
src/engine/export/abstract.cpp
src/engine/export/amigaValidation.cpp
+
+src/engine/effect/abstract.cpp
+src/engine/effect/dummy.cpp
)
if (USE_SNDFILE)
diff --git a/papers/format.md b/papers/format.md
index b22d7b2c7..198f62648 100644
--- a/papers/format.md
+++ b/papers/format.md
@@ -450,7 +450,7 @@ size | description
# patchbay
Furnace dev135 adds a "patchbay" which allows for arbitrary connection of chip outputs to system outputs.
-it eventually will allow connecting outputs to effects and so on.
+it also allows connecting outputs to effects and so on.
a connection is represented as an unsigned int in the following format:
@@ -565,11 +565,11 @@ size | description
| - must be between 32 and 4092.
| - the other slots are reserved for chip/system portsets.
2 | effect ID
- | - 0x00: dummy
- | - 0x01: external (plugin bridge)
+ | - 0x01: dummy
+ | - 0x02: external (plugin bridge)
| - not implemented yet
- | - 0x02: volume
- | - 0x03: filter (circuit)
+ | - 0x03: volume
+ | - 0x04: filter (circuit)
4f | dry/wet balance
2 | reserved
2 | storage version
diff --git a/src/engine/effect.h b/src/engine/effect.h
new file mode 100644
index 000000000..5c735d31d
--- /dev/null
+++ b/src/engine/effect.h
@@ -0,0 +1,189 @@
+/**
+ * Furnace Tracker - multi-system chiptune tracker
+ * Copyright (C) 2021-2023 tildearrow and contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _EFFECT_H
+#define _EFFECT_H
+
+#include
+#include "../ta-utils.h"
+
+class DivEngine;
+
+class DivEffect {
+ protected:
+ DivEngine* parent;
+ public:
+ /**
+ * fill a buffer with sound data.
+ * @param in pointers to input buffers.
+ * @param out pointers to output buffers.
+ * @param len the amount of samples in input and output.
+ */
+ virtual void acquire(float** in, float** out, size_t len);
+
+ /**
+ * reset the state of this effect.
+ */
+ virtual void reset();
+
+ /**
+ * get the number of inputs this effect requests.
+ * @return number of inputs. SHALL NOT be less than zero.
+ */
+ virtual int getInputCount();
+
+ /**
+ * get the number of outputs this effect provides.
+ * @return number of outputs. SHALL NOT be less than one.
+ */
+ virtual int getOutputCount();
+
+ /**
+ * called when the sample rate changes.
+ * @param rate the new sample rate.
+ */
+ virtual void rateChanged(double rate);
+
+ /**
+ * get the value of a parameter.
+ * @param param parameter ID.
+ * @return a String with the value.
+ * @throws std::out_of_range if the parameter ID is out of range.
+ */
+ virtual String getParam(size_t param);
+
+ /**
+ * set the value of a parameter.
+ * @param param parameter ID.
+ * @param value the value.
+ * @return whether the parameter was set successfully.
+ */
+ virtual bool setParam(size_t param, String value);
+
+ /**
+ * get a list of parameters.
+ * @return a C string with a list of parameters, or NULL if there are none.
+ * PARAMETER TYPES
+ *
+ * Parameter
+ * id:type:name:description:[...]
+ * type may be one of the following:
+ * - s: string
+ * - i: integer
+ * - I: integer slider
+ * - r: integer list (radio button)
+ * - R: integer list (combo box)
+ * - h: integer hex
+ * - f: float
+ * - F: float slider
+ * - d: double
+ * - D: double slider
+ * - b: boolean (checkbox)
+ * - t: boolean (toggle button)
+ * - x: X/Y integer
+ * - X: X/Y float
+ * - c: color (RGB)
+ * - C: color (RGBA)
+ * - B: button
+ *
+ * SameLine
+ * !s
+ *
+ * Separator
+ * ---
+ *
+ * Indent/Unindent
+ * > Indent
+ * < Unindent
+ *
+ * TreeNode
+ * >> Begin
+ * << End
+ *
+ * Tabs
+ * >TABBAR BeginTabBar
+ * >TAB:name Begin
+ *
+
+void DivEffect::acquire(float** in, float** out, size_t len) {
+}
+
+void DivEffect::reset() {
+}
+
+int DivEffect::getInputCount() {
+ return 0;
+}
+
+int DivEffect::getOutputCount() {
+ return 0;
+}
+
+void DivEffect::rateChanged(double rate) {
+}
+
+String DivEffect::getParam(size_t param) {
+ throw std::out_of_range("param");
+
+ // unreachable
+ return "";
+}
+
+bool DivEffect::setParam(size_t param, String value) {
+ return false;
+}
+
+const char* DivEffect::getParams() {
+ return NULL;
+}
+
+size_t DivEffect::getParamCount() {
+ return 0;
+}
+
+String DivEffect::getDynamicText(size_t id) {
+ throw std::out_of_range("param");
+
+ // unreachable
+ return "";
+}
+
+bool DivEffect::load(unsigned short version, const unsigned char* data, size_t len) {
+ return false;
+}
+
+unsigned char* DivEffect::save(unsigned short* version, size_t* len) {
+ *len=0;
+ *version=0;
+ return NULL;
+}
+
+bool DivEffect::init(DivEngine* parent, double rate, unsigned short version, const unsigned char* data, size_t len) {
+ return false;
+}
+
+void DivEffect::quit() {
+}
+
+DivEffect::~DivEffect() {
+}
\ No newline at end of file
diff --git a/src/engine/effect/dummy.cpp b/src/engine/effect/dummy.cpp
new file mode 100644
index 000000000..13df4f3f5
--- /dev/null
+++ b/src/engine/effect/dummy.cpp
@@ -0,0 +1,60 @@
+/**
+ * Furnace Tracker - multi-system chiptune tracker
+ * Copyright (C) 2021-2023 tildearrow and contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dummy.h"
+
+void DivEffectDummy::acquire(float** in, float** out, size_t len) {
+ memcpy(out[0],in[0],len*sizeof(float));
+}
+
+void DivEffectDummy::reset() {
+}
+
+int DivEffectDummy::getInputCount() {
+ return 1;
+}
+
+int DivEffectDummy::getOutputCount() {
+ return 1;
+}
+
+const char* DivEffectDummy::getParams() {
+ return NULL;
+}
+
+size_t DivEffectDummy::getParamCount() {
+ return 0;
+}
+
+bool DivEffectDummy::load(unsigned short version, const unsigned char* data, size_t len) {
+ return true;
+}
+
+unsigned char* DivEffectDummy::save(unsigned short* version, size_t* len) {
+ *len=0;
+ *version=0;
+ return NULL;
+}
+
+bool DivEffectDummy::init(DivEngine* parent, double rate, unsigned short version, const unsigned char* data, size_t len) {
+ return false;
+}
+
+void DivEffectDummy::quit() {
+}
\ No newline at end of file
diff --git a/src/engine/effect/dummy.h b/src/engine/effect/dummy.h
new file mode 100644
index 000000000..c8a42919d
--- /dev/null
+++ b/src/engine/effect/dummy.h
@@ -0,0 +1,34 @@
+/**
+ * Furnace Tracker - multi-system chiptune tracker
+ * Copyright (C) 2021-2023 tildearrow and contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "../effect.h"
+
+class DivEffectDummy: public DivEffect {
+ public:
+ void acquire(float** in, float** out, size_t len);
+ void reset();
+ int getInputCount();
+ int getOutputCount();
+ const char* getParams();
+ size_t getParamCount();
+ bool load(unsigned short version, const unsigned char* data, size_t len);
+ unsigned char* save(unsigned short* version, size_t* len);
+ bool init(DivEngine* parent, double rate, unsigned short version, const unsigned char* data, size_t len);
+ void quit();
+};
\ No newline at end of file
diff --git a/src/engine/effectContainer.cpp b/src/engine/effectContainer.cpp
new file mode 100644
index 000000000..d51ddfe57
--- /dev/null
+++ b/src/engine/effectContainer.cpp
@@ -0,0 +1,95 @@
+/**
+ * Furnace Tracker - multi-system chiptune tracker
+ * Copyright (C) 2021-2023 tildearrow and contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "engine.h"
+#include "effect/dummy.h"
+
+void DivEffectContainer::preAcquire(size_t count) {
+ if (!count) return;
+
+ int inCount=effect->getInputCount();
+
+ if (inLengetOutputCount();
+
+ if (outLenacquire(in,out,count);
+}
+
+bool DivEffectContainer::init(DivEffectType effectType, DivEngine* eng, double rate, unsigned short version, const unsigned char* data, size_t len) {
+ switch (effectType) {
+ case DIV_EFFECT_DUMMY:
+ default:
+ effect=new DivEffectDummy;
+ }
+ return effect->init(eng,rate,version,data,len);
+}
+
+void DivEffectContainer::quit() {
+ effect->quit();
+ delete effect;
+ effect=NULL;
+
+ for (int i=0; i midiOuts;
std::vector cmdStream;
std::vector possibleInsTypes;
+ std::vector effectInst;
static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS];
static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS];
static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS];
@@ -441,6 +462,7 @@ class DivEngine {
short tremTable[128];
int reversePitchTable[4096];
int pitchTable[4096];
+ short effectSlotMap[4096];
char c163NameCS[1024];
int midiBaseChan;
bool midiPoly;
@@ -514,6 +536,9 @@ class DivEngine {
void swapChannels(int src, int dest);
void stompChannel(int ch);
+ // recalculate patchbay (UNSAFE)
+ void recalcPatchbay();
+
// change song (UNSAFE)
void changeSong(size_t songIndex);
@@ -1049,6 +1074,12 @@ class DivEngine {
// move system
bool swapSystem(int src, int dest, bool preserveOrder=true);
+ // add effect
+ bool addEffect(DivEffectType which);
+
+ // remove effect
+ bool removeEffect(int index);
+
// write to register on system
void poke(int sys, unsigned int addr, unsigned short val);
@@ -1231,6 +1262,7 @@ class DivEngine {
memset(tremTable,0,128*sizeof(short));
memset(reversePitchTable,0,4096*sizeof(int));
memset(pitchTable,0,4096*sizeof(int));
+ memset(effectSlotMap,-1,4096*sizeof(short));
memset(sysDefs,0,DIV_MAX_CHIP_DEFS*sizeof(void*));
memset(walked,0,8192);
memset(oscBuf,0,DIV_MAX_OUTPUTS*(sizeof(float*)));
diff --git a/src/engine/song.h b/src/engine/song.h
index aff14f48f..fd2d1e07d 100644
--- a/src/engine/song.h
+++ b/src/engine/song.h
@@ -130,6 +130,14 @@ enum DivSystem {
DIV_SYSTEM_PV1000
};
+enum DivEffectType: unsigned short {
+ DIV_EFFECT_NULL=0,
+ DIV_EFFECT_DUMMY,
+ DIV_EFFECT_EXTERNAL,
+ DIV_EFFECT_VOLUME,
+ DIV_EFFECT_FILTER
+};
+
struct DivGroovePattern {
unsigned char val[16];
unsigned char len;
@@ -191,6 +199,21 @@ struct DivAssetDir {
name(n) {}
};
+struct DivEffectStorage {
+ DivEffectType id;
+ unsigned short slot, storageVer;
+ float dryWet;
+ unsigned char* storage;
+ size_t storageLen;
+ DivEffectStorage():
+ id(DIV_EFFECT_NULL),
+ slot(0),
+ storageVer(0),
+ dryWet(1.0f),
+ storage(NULL),
+ storageLen(0) {}
+};
+
struct DivSong {
// version number used for saving the song.
// Furnace will save using the latest possible version,
@@ -366,6 +389,8 @@ struct DivSong {
std::vector waveDir;
std::vector sampleDir;
+ std::vector effects;
+
DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsOPLDrums, nullInsQSound;
DivWavetable nullWave;
DivSample nullSample;
From 8988b52062ff90e0e8ee8ebe9ecbf35b42909453 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Sun, 21 May 2023 05:40:10 -0500
Subject: [PATCH 047/454] The first letter of description shall not upperca
---
src/engine/engine.cpp | 4 ++++
src/engine/sysDef.cpp | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
index 0a00babd8..efe66fc85 100644
--- a/src/engine/engine.cpp
+++ b/src/engine/engine.cpp
@@ -4138,6 +4138,10 @@ void DivEngine::autoPatchbayP() {
BUSY_END;
}
+void DivEngine::recalcPatchbay() {
+
+}
+
bool DivEngine::patchConnect(unsigned int src, unsigned int dest) {
unsigned int armed=(src<<16)|(dest&0xffff);
for (unsigned int i: song.patchbay) {
diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp
index 215c196ec..c590ba107 100644
--- a/src/engine/sysDef.cpp
+++ b/src/engine/sysDef.cpp
@@ -1852,7 +1852,7 @@ void DivEngine::registerSystems() {
sysDefs[DIV_SYSTEM_SFX_BEEPER_QUADTONE]=new DivSysDef(
"ZX Spectrum Beeper (QuadTone Engine)", NULL, 0xca, 0, 5, false, true, 0, false, 1U<
Date: Sun, 21 May 2023 15:24:41 -0500
Subject: [PATCH 048/454] fix
---
papers/doc/7-systems/README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/papers/doc/7-systems/README.md b/papers/doc/7-systems/README.md
index 29c08c2ee..4101aa958 100644
--- a/papers/doc/7-systems/README.md
+++ b/papers/doc/7-systems/README.md
@@ -38,11 +38,11 @@ this is a list of sound chips that Furnace supports, including effects.
- [VERA](vera.md)
- [WonderSwan](wonderswan.md)
- [Virtual Boy](virtual-boy.md)
-- [Yamaha OPLL](opll.md)
+- [Yamaha YM2413 (OPLL)](opll.md)
- [Yamaha OPL](opl.md)
- [Yamaha YM2151](ym2151.md)
- [Yamaha YM2203](ym2203.md)
-- [Yamaha YM2413](opz.md)
+- [Yamaha YM2414 (OPZ)](opz.md)
- [Yamaha YM2608](ym2608.md)
- [Neo Geo/YM2610](ym2610.md)
- [Taito Arcade/YM2610B](ym2610b.md)
From d997a5662468a64be5647d271a9547d254adbf88 Mon Sep 17 00:00:00 2001
From: TheDuccinator <66538032+TheDuccinator@users.noreply.github.com>
Date: Sun, 21 May 2023 16:36:40 -0700
Subject: [PATCH 049/454] Add "Flat Wave Society" to demo songs
yea
---
demos/ay8910/Flat Wave Society.fur | Bin 0 -> 1954 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 demos/ay8910/Flat Wave Society.fur
diff --git a/demos/ay8910/Flat Wave Society.fur b/demos/ay8910/Flat Wave Society.fur
new file mode 100644
index 0000000000000000000000000000000000000000..1c5cb61a7d0462b7958ce4b43db2c8c120b8f4bf
GIT binary patch
literal 1954
zcmV;T2VMAhoZVPmY+OebKKIVtpWVH8uYZ!(=~BF*k;;p~=?{NZ)R+dRR$4dIwoCzm
zt?fom*PBf@Ye(Bvjc;Mwht#c5P(>di9(c}6R&
zXYSoU?>cLexDu0mGiPSbnR8~&nK^eoHM>#1w3^Fji=`Jg*7H+0h$u_#GB-c_+;suM
zvJ9fj-#ksnNsgp>ZW`F8{mbdwl-r2Yuf^>+?roV}U#(=9S2y$7g;MTfzEY*dwfvcl
zT<+qf)k>)hj!&K<(=ZgTt~GU568#Q%(jxj5Fk=(_0XXZR18i@Mqw~F`_qt_kh0u
z?l|UwZvYFxHQ-&~L*N$h4n7}^tr$JUX>jRu()N=SpTGJqV^^4}OYh*HE;BDoJv#NZub-Um5k&byY&eVjsc8Tv
zAOHzifDJf+3nYLfkODlw2hu|G8)Vb`j8Aiyu3+tA{!%47O|#FQpieP5{iDgr)4|~O*_kgAu0*oQZU}o(r#%DL6A=ShDXgPB1ob*
zD2K0Ep!CUZ`(>2Jql2e9KS
z%8pd)1iVzjwWWY!ZA`+E|1u-zj|ve*0Y--P#^VR|hNE?)5Q5UAw(f}Nqj~GjHBT$e
zBG&8~b{qwc<9V?S_s9D;%|5=U-FY-l4T{D%5ALjm(yPlO@{?SS>UuKh9kS1
z;Uo*Lw!rPNKq?CoZHeK??q)d2LQ-3hY+10SKmUPUa%z4+qUw{V`5bYZzV5!#9(onz
zmL_X{7xUGqd$qe8ajBs)$ciS>MBNT65JaqUQ}d}dwjvT$ZM;%RlSNI6m-h@Q@+H5`
zQPe{hK(1*LP1No1211?1Iz)T*L{{{4BO5cDJ{&qBb9D@6N~)u#G8IK(XuRy__Ux~!
zEm&p?kP|XBUu44xS(y;@dZx;YMCTkIYUBxSF$M{lyOt>{R_GGb%7iy`A6D}6i@bb-
zXtLx7B1zZ7#l#9TfpN2-Z3*OeAsXS#zGzhe;qYP!mGjq5FDeKls~H`xf-r#IomI-J-s
zG14)y-8q3;yw)wje_=4jh%av_)1mZ2nF?hxl!;KfVb805Y*$jpgFQ|ixxhGrRnF~~
z3-r5ww0|zJkPBKXn{g`OKj7h0hCUE0B8-Ej&Z8=6uL#^Dn>doVJ%oNC>KAP`hW}_|
zibF7F!4p69M8OkRJ@I3%3p}#qiB(S&JyG>Uo$I0+Ve4LJ>r{$Z5y9T&8UMrH3qA62
zzt}pP-acFN7F%NtIIBZ2##xa8;6`ndt@
zIuj)xxm)8pVs*H_-4U^a#&sm?F#c&fk_U}hUKBXBN}rPcJ=D>zqL9L%O+%!IlK491{<_vw858
oV+K)TIL
Date: Tue, 23 May 2023 19:39:54 -0500
Subject: [PATCH 050/454] a
---
README.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 4f94337a4..83389f3b7 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
-# Furnace (chiptune tracker)
+# Don't star this project until I release 0.6pre5!
+
+the ISSUES count should be 20 before I release pre5 - if stars hit 1000 and I have not fixed bugs then what a shame!

From d7a3cc3049aae353a8c5c60bd98165503e2a732f Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 24 May 2023 01:17:06 -0500
Subject: [PATCH 051/454] QSound: fix forceIns
---
src/engine/platform/qsound.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp
index 2e23abb4e..066ce34bb 100644
--- a/src/engine/platform/qsound.cpp
+++ b/src/engine/platform/qsound.cpp
@@ -466,6 +466,7 @@ int DivPlatformQSound::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
+ chan[c.chan].keyOff=false;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
@@ -609,6 +610,7 @@ void DivPlatformQSound::forceIns() {
for (int i=0; i<19; i++) {
chan[i].insChanged=true;
chan[i].freqChanged=true;
+ chan[i].keyOff=true;
//chan[i].sample=-1;
}
}
From 00108a04f57b6aa7cfcde636a875a5a3dcc63786 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 24 May 2023 01:24:45 -0500
Subject: [PATCH 052/454] PCM DAC: wave interpolation
---
src/engine/platform/pcmdac.cpp | 61 +++++++++++++++++++++++++++++++---
1 file changed, 57 insertions(+), 4 deletions(-)
diff --git a/src/engine/platform/pcmdac.cpp b/src/engine/platform/pcmdac.cpp
index 0b59accb1..283b6e24e 100644
--- a/src/engine/platform/pcmdac.cpp
+++ b/src/engine/platform/pcmdac.cpp
@@ -42,12 +42,65 @@ void DivPlatformPCMDAC::acquire(short** buf, size_t len) {
while (chan[0].audSub>=0x10000) {
chan[0].audSub-=0x10000;
chan[0].audPos+=((!chan[0].useWave) && chan[0].audDir)?-1:1;
+ if (chan[0].audPos>=(int)chan[0].audLen) {
+ chan[0].audPos%=chan[0].audLen;
+ chan[0].audDir=false;
+ }
+ chan[0].audDat[0]=chan[0].audDat[1];
+ chan[0].audDat[1]=chan[0].audDat[2];
+ chan[0].audDat[2]=chan[0].audDat[3];
+ chan[0].audDat[3]=chan[0].audDat[4];
+ chan[0].audDat[4]=chan[0].audDat[5];
+ chan[0].audDat[5]=chan[0].audDat[6];
+ chan[0].audDat[6]=chan[0].audDat[7];
+ chan[0].audDat[7]=(chan[0].ws.output[chan[0].audPos]-0x80)<<8;
}
- if (chan[0].audPos>=(int)chan[0].audLen) {
- chan[0].audPos%=chan[0].audLen;
- chan[0].audDir=false;
+
+ const short s0=chan[0].audDat[0];
+ const short s1=chan[0].audDat[1];
+ const short s2=chan[0].audDat[2];
+ const short s3=chan[0].audDat[3];
+ const short s4=chan[0].audDat[4];
+ const short s5=chan[0].audDat[5];
+ const short s6=chan[0].audDat[6];
+ const short s7=chan[0].audDat[7];
+
+ switch (interp) {
+ case 1: // linear
+ output=s6+((s7-s6)*(chan[0].audSub&0xffff)>>16);
+ break;
+ case 2: { // cubic
+ float* cubicTable=DivFilterTables::getCubicTable();
+ float* t=&cubicTable[((chan[0].audSub&0xffff)>>6)<<2];
+ float result=(float)s4*t[0]+(float)s5*t[1]+(float)s6*t[2]+(float)s7*t[3];
+ if (result<-32768) result=-32768;
+ if (result>32767) result=32767;
+ output=result;
+ break;
+ }
+ case 3: { // sinc
+ float* sincTable=DivFilterTables::getSincTable8();
+ float* t1=&sincTable[(8191-((chan[0].audSub&0xffff)>>3))<<2];
+ float* t2=&sincTable[((chan[0].audSub&0xffff)>>3)<<2];
+ float result=(
+ s0*t2[3]+
+ s1*t2[2]+
+ s2*t2[1]+
+ s3*t2[0]+
+ s4*t1[0]+
+ s5*t1[1]+
+ s6*t1[2]+
+ s7*t1[3]
+ );
+ if (result<-32768) result=-32768;
+ if (result>32767) result=32767;
+ output=result;
+ break;
+ }
+ default: // none
+ output=s7;
+ break;
}
- output=(chan[0].ws.output[chan[0].audPos]-0x80)<<8;
} else {
DivSample* s=parent->getSample(chan[0].sample);
if (s->samples>0) {
From f55dbc73766a1691497a89db610539e919df89a9 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 24 May 2023 01:34:01 -0500
Subject: [PATCH 053/454] AY: fix 5B per-chan osc
---
src/engine/platform/ay.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp
index 13dd2d842..8eaea3fea 100644
--- a/src/engine/platform/ay.cpp
+++ b/src/engine/platform/ay.cpp
@@ -187,9 +187,9 @@ void DivPlatformAY8910::acquire(short** buf, size_t len) {
buf[0][i]=ayBuf[0][0];
buf[1][i]=buf[0][i];
- oscBuf[0]->data[oscBuf[0]->needle++]=sunsoftVolTable[31-(ay->lastIndx&31)]>>3;
- oscBuf[1]->data[oscBuf[1]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>5)&31)]>>3;
- oscBuf[2]->data[oscBuf[2]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>10)&31)]>>3;
+ oscBuf[0]->data[oscBuf[0]->needle++]=sunsoftVolTable[31-(ay->lastIndx&31)]<<3;
+ oscBuf[1]->data[oscBuf[1]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>5)&31)]<<3;
+ oscBuf[2]->data[oscBuf[2]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>10)&31)]<<3;
}
} else {
for (size_t i=0; i
Date: Wed, 24 May 2023 01:51:27 -0500
Subject: [PATCH 054/454] make use of sample loop flag
issue #1109
this MAY BREAK THINGS
if any problems, REVERT THIS ONE
---
src/engine/platform/qsound.cpp | 2 +-
src/engine/platform/segapcm.cpp | 4 ++--
src/engine/platform/snes.cpp | 2 +-
src/engine/vgmOps.cpp | 4 ++--
src/gui/sampleEdit.cpp | 11 ++++++++---
5 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp
index 066ce34bb..b1aeab3e7 100644
--- a/src/engine/platform/qsound.cpp
+++ b/src/engine/platform/qsound.cpp
@@ -319,7 +319,7 @@ void DivPlatformQSound::tick(bool sysTick) {
if (length > 65536 - 16) {
length = 65536 - 16;
}
- if (loopStart == -1 || loopStart >= length) {
+ if (!s->isLoopable()) {
if (i<16) {
qsound_end = offPCM[chan[i].sample] + length + 15;
} else {
diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp
index ead57f800..ce24e2fb0 100644
--- a/src/engine/platform/segapcm.cpp
+++ b/src/engine/platform/segapcm.cpp
@@ -138,7 +138,7 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
rWrite(0x84+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample])&0xff);
rWrite(0x85+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample]>>8)&0xff);
rWrite(6+(i<<3),sampleEndSegaPCM[chan[i].pcm.sample]);
- if (loopStart<0 || loopStart>=actualLength) {
+ if (!s->isLoopable()) {
rWrite(0x86+(i<<3),2+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
} else {
int loopPos=(sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+loopStart;
@@ -156,7 +156,7 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
rWrite(0x84+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample])&0xff);
rWrite(0x85+(i<<3),(sampleOffSegaPCM[chan[i].pcm.sample]>>8)&0xff);
rWrite(6+(i<<3),sampleEndSegaPCM[chan[i].pcm.sample]);
- if (loopStart<0 || loopStart>=actualLength) {
+ if (!s->isLoopable()) {
rWrite(0x86+(i<<3),2+((sampleOffSegaPCM[chan[i].pcm.sample]>>16)<<3));
} else {
int loopPos=(sampleOffSegaPCM[chan[i].pcm.sample]&0xffff)+loopStart;
diff --git a/src/engine/platform/snes.cpp b/src/engine/platform/snes.cpp
index 21ae97b8d..0c9734c29 100644
--- a/src/engine/platform/snes.cpp
+++ b/src/engine/platform/snes.cpp
@@ -222,7 +222,7 @@ void DivPlatformSNES::tick(bool sysTick) {
if (chan[i].audPos>0) {
start=start+MIN(chan[i].audPos,s->lengthBRR-1)/16*9;
}
- if (s->loopStart>=0) {
+ if (s->isLoopable()) {
loop=((s->depth!=DIV_SAMPLE_DEPTH_BRR)?9:0)+start+((s->loopStart/16)*9);
}
} else {
diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp
index 95ba1ca7a..4f764c483 100644
--- a/src/engine/vgmOps.cpp
+++ b/src/engine/vgmOps.cpp
@@ -591,7 +591,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(0x95);
w->writeC(streamID);
w->writeS(write.val); // sample number
- w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0)|(sampleDir[streamID]?0x10:0)); // flags
+ w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0 && sample->isLoopable())|(sampleDir[streamID]?0x10:0)); // flags
if (sample->isLoopable() && !sampleDir[streamID]) {
loopTimer[streamID]=sample->length8;
loopSample[streamID]=write.val;
@@ -610,7 +610,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(0x95);
w->writeC(streamID);
w->writeS(pendingFreq[streamID]); // sample number
- w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0)|(sampleDir[streamID]?0x10:0)); // flags
+ w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0 && sample->isLoopable())|(sampleDir[streamID]?0x10:0)); // flags
if (sample->isLoopable() && !sampleDir[streamID]) {
loopTimer[streamID]=sample->length8;
loopSample[streamID]=pendingFreq[streamID];
diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp
index ab92f3d8c..1888aff1d 100644
--- a/src/gui/sampleEdit.cpp
+++ b/src/gui/sampleEdit.cpp
@@ -219,12 +219,17 @@ void FurnaceGUI::drawSampleEdit() {
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
if (doLoop) {
sample->loop=true;
- sample->loopStart=0;
- sample->loopEnd=sample->samples;
+ if (sample->loopStart<0) {
+ sample->loopStart=0;
+ }
+ if (sample->loopEnd<0) {
+ sample->loopEnd=sample->samples;
+ }
} else {
sample->loop=false;
+ /*
sample->loopStart=-1;
- sample->loopEnd=sample->samples;
+ sample->loopEnd=sample->samples;*/
}
updateSampleTex=true;
if (e->getSampleFormatMask()&(1U<
Date: Wed, 24 May 2023 01:58:12 -0500
Subject: [PATCH 055/454] GUI: prevent exit from being clicked by accident
issue #361
---
src/gui/gui.cpp | 19 +++++++++++++++++++
src/gui/gui.h | 2 +-
2 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp
index 1f2d48794..91f0d3b55 100644
--- a/src/gui/gui.cpp
+++ b/src/gui/gui.cpp
@@ -3791,6 +3791,7 @@ bool FurnaceGUI::loop() {
}
}
if (ImGui::BeginMenu("open recent")) {
+ exitDisabledTimer=1;
for (int i=0; i<(int)recentFile.size(); i++) {
String item=recentFile[i];
if (ImGui::MenuItem(item.c_str())) {
@@ -3837,6 +3838,7 @@ bool FurnaceGUI::loop() {
}
ImGui::Separator();
if (ImGui::BeginMenu("export audio...")) {
+ exitDisabledTimer=1;
if (ImGui::MenuItem("one file")) {
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
}
@@ -3855,6 +3857,7 @@ bool FurnaceGUI::loop() {
ImGui::EndMenu();
}
if (ImGui::BeginMenu("export VGM...")) {
+ exitDisabledTimer=1;
ImGui::Text("settings:");
if (ImGui::BeginCombo("format version",fmt::sprintf("%d.%.2x",vgmExportVersion>>8,vgmExportVersion&0xff).c_str())) {
for (int i=0; i<7; i++) {
@@ -3943,6 +3946,7 @@ bool FurnaceGUI::loop() {
}
if (numZSMCompat > 0) {
if (ImGui::BeginMenu("export ZSM...")) {
+ exitDisabledTimer=1;
ImGui::Text("Commander X16 Zsound Music File");
if (ImGui::InputInt("Tick Rate (Hz)",&zsmExportTickRate,1,2)) {
if (zsmExportTickRate<1) zsmExportTickRate=1;
@@ -3963,6 +3967,7 @@ bool FurnaceGUI::loop() {
}
if (numAmiga && settings.iCannotWait) {
if (ImGui::BeginMenu("export Amiga validation data...")) {
+ exitDisabledTimer=1;
ImGui::Text(
"this is NOT ROM export! only use for making sure the\n"
"Furnace Amiga emulator is working properly by\n"
@@ -3993,6 +3998,7 @@ bool FurnaceGUI::loop() {
}
}
if (ImGui::BeginMenu("export command stream...")) {
+ exitDisabledTimer=1;
ImGui::Text(
"this option exports a text or binary file which\n"
"contains a dump of the internal command stream\n"
@@ -4010,6 +4016,7 @@ bool FurnaceGUI::loop() {
}
ImGui::Separator();
if (ImGui::BeginMenu("add chip...")) {
+ exitDisabledTimer=1;
DivSystem picked=systemPicker();
if (picked!=DIV_SYSTEM_NULL) {
if (!e->addSystem(picked)) {
@@ -4026,6 +4033,7 @@ bool FurnaceGUI::loop() {
ImGui::EndMenu();
}
if (ImGui::BeginMenu("configure chip...")) {
+ exitDisabledTimer=1;
for (int i=0; isong.systemLen; i++) {
if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSP%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true);
@@ -4035,6 +4043,7 @@ bool FurnaceGUI::loop() {
ImGui::EndMenu();
}
if (ImGui::BeginMenu("change chip...")) {
+ exitDisabledTimer=1;
ImGui::Checkbox("Preserve channel positions",&preserveChanPos);
for (int i=0; isong.systemLen; i++) {
if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
@@ -4054,6 +4063,7 @@ bool FurnaceGUI::loop() {
ImGui::EndMenu();
}
if (ImGui::BeginMenu("remove chip...")) {
+ exitDisabledTimer=1;
ImGui::Checkbox("Preserve channel positions",&preserveChanPos);
for (int i=0; isong.systemLen; i++) {
if (ImGui::MenuItem(fmt::sprintf("%d. %s##_SYSR%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
@@ -4070,6 +4080,7 @@ bool FurnaceGUI::loop() {
}
ImGui::EndMenu();
}
+ ImGui::BeginDisabled(exitDisabledTimer);
ImGui::Separator();
if (ImGui::MenuItem("restore backup",BIND_FOR(GUI_ACTION_OPEN_BACKUP))) {
doAction(GUI_ACTION_OPEN_BACKUP);
@@ -4082,7 +4093,10 @@ bool FurnaceGUI::loop() {
quit=true;
}
}
+ ImGui::EndDisabled();
ImGui::EndMenu();
+ } else {
+ exitDisabledTimer=0;
}
if (ImGui::BeginMenu("edit")) {
ImGui::Text("...");
@@ -5818,6 +5832,10 @@ bool FurnaceGUI::loop() {
WAKE_UP;
}
+ if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
+ exitDisabledTimer=0;
+ }
+
wheelX=0;
wheelY=0;
wantScrollList=false;
@@ -6566,6 +6584,7 @@ FurnaceGUI::FurnaceGUI():
oldBeat(-1),
oldBar(-1),
curGroove(-1),
+ exitDisabledTimer(0),
soloTimeout(0.0f),
exportFadeOut(5.0),
editControlsOpen(true),
diff --git a/src/gui/gui.h b/src/gui/gui.h
index 880748645..d4b3e4eca 100644
--- a/src/gui/gui.h
+++ b/src/gui/gui.h
@@ -1607,7 +1607,7 @@ class FurnaceGUI {
int curIns, curWave, curSample, curOctave, curOrder, prevIns, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan,orderEditMode, orderCursor;
int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, newSongCategory, latchTarget;
int wheelX, wheelY, dragSourceX, dragSourceXFine, dragSourceY, dragDestinationX, dragDestinationXFine, dragDestinationY, oldBeat, oldBar;
- int curGroove;
+ int curGroove, exitDisabledTimer;
float soloTimeout;
double exportFadeOut;
From da16494f7f994042d33465a6bd36dc8afefc86eb Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 24 May 2023 02:10:39 -0500
Subject: [PATCH 056/454] Revert "a"
This reverts commit 40c4632879b0eb944f775f2821969ff2e7e55e50.
---
README.md | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 83389f3b7..4f94337a4 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,4 @@
-# Don't star this project until I release 0.6pre5!
-
-the ISSUES count should be 20 before I release pre5 - if stars hit 1000 and I have not fixed bugs then what a shame!
+# Furnace (chiptune tracker)

From 4b12f440fafc5e5e3509ba56334bf2cf134ec225 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 24 May 2023 02:12:42 -0500
Subject: [PATCH 057/454] variable set but not used
---
README.md | 2 +-
src/engine/vgmOps.cpp | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 4f94337a4..07b6edd1a 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ the biggest multi-system chiptune tracker ever made!
check out the [Releases](https://github.com/tildearrow/furnace/releases) page. available for Windows, macOS and Linux (AppImage).
-[see here](https://nightly.link/tildearrow/furnace/workflows/build/master) for unstable developer builds.
+[see here](https://nightly.link/tildearrow/furnace/workflows/build/master) for the latest unstable build.
## features
diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp
index 4f764c483..8f867322a 100644
--- a/src/engine/vgmOps.cpp
+++ b/src/engine/vgmOps.cpp
@@ -1626,6 +1626,9 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
}
}
+ // variable set but not used?
+ logV("howManyChips: %d",howManyChips);
+
// write chips and stuff
w->writeI(hasSN);
w->writeI(hasOPLL);
From 0fd5483b5917955ce144528fc92f5f4e13f0e1b8 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 24 May 2023 02:38:43 -0500
Subject: [PATCH 058/454] SNES: fix BRR once again
---
src/engine/sample.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp
index 2f8e7b997..ecd9752ef 100644
--- a/src/engine/sample.cpp
+++ b/src/engine/sample.cpp
@@ -1130,8 +1130,9 @@ void DivSample::render(unsigned int formatMask) {
}
}
if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_BRR)) { // BRR
- if (!initInternal(DIV_SAMPLE_DEPTH_BRR,samples)) return;
- brrEncode(data16,dataBRR,samples,loop?loopStart:-1,brrEmphasis);
+ int sampleCount=loop?loopEnd:samples;
+ if (!initInternal(DIV_SAMPLE_DEPTH_BRR,sampleCount)) return;
+ brrEncode(data16,dataBRR,sampleCount,loop?loopStart:-1,brrEmphasis);
}
if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_VOX)) { // VOX
if (!initInternal(DIV_SAMPLE_DEPTH_VOX,samples)) return;
From c7b24f706fc7fa90abb3a4fe295624818255510f Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 24 May 2023 02:54:36 -0500
Subject: [PATCH 059/454] GUI: improve sample type conversion
---
src/engine/sample.cpp | 34 ++++++++++++++++++++++++++++++++++
src/engine/sample.h | 7 +++++++
src/gui/sampleEdit.cpp | 3 +--
3 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp
index ecd9752ef..346f9a12c 100644
--- a/src/engine/sample.cpp
+++ b/src/engine/sample.cpp
@@ -699,6 +699,40 @@ bool DivSample::insert(unsigned int pos, unsigned int length) {
return false;
}
+void DivSample::convert(DivSampleDepth newDepth) {
+ render();
+ depth=newDepth;
+ switch (depth) {
+ case DIV_SAMPLE_DEPTH_1BIT:
+ setSampleCount((samples+7)&(~7));
+ break;
+ case DIV_SAMPLE_DEPTH_1BIT_DPCM:
+ setSampleCount((1+((((samples+7)/8)+15)&(~15)))<<3);
+ break;
+ case DIV_SAMPLE_DEPTH_YMZ_ADPCM:
+ setSampleCount(((lengthZ+3)&(~0x03))*2);
+ break;
+ case DIV_SAMPLE_DEPTH_QSOUND_ADPCM: // QSound ADPCM
+ setSampleCount((samples+1)&(~1));
+ break;
+ case DIV_SAMPLE_DEPTH_ADPCM_A: // ADPCM-A
+ setSampleCount((samples+1)&(~1));
+ break;
+ case DIV_SAMPLE_DEPTH_ADPCM_B: // ADPCM-B
+ setSampleCount((samples+1)&(~1));
+ break;
+ case DIV_SAMPLE_DEPTH_BRR: // BRR
+ setSampleCount(16*(lengthBRR/9));
+ break;
+ case DIV_SAMPLE_DEPTH_VOX: // VOX
+ setSampleCount((samples+1)&(~1));
+ break;
+ default:
+ break;
+ }
+ render();
+}
+
#define RESAMPLE_BEGIN \
if (samples<1) return true; \
int finalCount=(double)samples*(tRate/sRate); \
diff --git a/src/engine/sample.h b/src/engine/sample.h
index ae9172e3a..c2f08ced6 100644
--- a/src/engine/sample.h
+++ b/src/engine/sample.h
@@ -269,6 +269,13 @@ struct DivSample {
*/
bool resample(double sRate, double tRate, int filter);
+ /**
+ * convert sample depth.
+ * @warning do not attempt to do this outside of a synchronized block!
+ * @param newDepth the new depth.
+ */
+ void convert(DivSampleDepth newDepth);
+
/**
* initialize the rest of sample formats for this sample.
*/
diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp
index 1888aff1d..e0f079ba5 100644
--- a/src/gui/sampleEdit.cpp
+++ b/src/gui/sampleEdit.cpp
@@ -257,8 +257,7 @@ void FurnaceGUI::drawSampleEdit() {
if (ImGui::Selectable(sampleDepths[i])) {
sample->prepareUndo(true);
e->lockEngine([this,sample,i]() {
- sample->render();
- sample->depth=(DivSampleDepth)i;
+ sample->convert((DivSampleDepth)i);
e->renderSamples();
});
updateSampleTex=true;
From 3b688774fe36bfa9c3beedd89d54b5e5a45373c3 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 24 May 2023 03:33:51 -0500
Subject: [PATCH 060/454] move loop when deleting in sample editor
---
src/engine/sample.cpp | 29 ++++++++++++++++++++++++++++-
src/gui/sampleEdit.cpp | 2 +-
2 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp
index 346f9a12c..b178c6e87 100644
--- a/src/engine/sample.cpp
+++ b/src/engine/sample.cpp
@@ -584,7 +584,34 @@ bool DivSample::strip(unsigned int begin, unsigned int end) {
if (begin>samples) begin=samples;
if (end>samples) end=samples;
int count=samples-(end-begin);
- if (count<=0) return resize(0);
+ if (count<=0) {
+ loopStart=-1;
+ loopEnd=-1;
+ loop=false;
+ return resize(0);
+ }
+ if (loopStart>(int)begin && loopEnd<(int)end) {
+ loopStart=-1;
+ loopEnd=-1;
+ loop=false;
+ } else {
+ if (loopStart<(int)end && loopStart>(int)begin) {
+ loopStart=end;
+ }
+ if (loopStart>(int)begin && loopEnd>(int)begin) {
+ loopStart-=end-begin;
+ loopEnd-=end-begin;
+ if (loopEnd<0) loopEnd=0;
+ if (loopStart<0) loopStart=0;
+ } else if (loopEnd>(int)begin) {
+ loopEnd=begin;
+ }
+ }
+ if (loopStart>loopEnd) {
+ loopStart=-1;
+ loopEnd=-1;
+ loop=false;
+ }
if (depth==DIV_SAMPLE_DEPTH_8BIT) {
if (data8!=NULL) {
signed char* oldData8=data8;
diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp
index e0f079ba5..9d0fcc19b 100644
--- a/src/gui/sampleEdit.cpp
+++ b/src/gui/sampleEdit.cpp
@@ -215,7 +215,7 @@ void FurnaceGUI::drawSampleEdit() {
}
popToggleColors();
ImGui::TableNextColumn();
- bool doLoop=(sample->isLoopable());
+ bool doLoop=(sample->loop);
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
if (doLoop) {
sample->loop=true;
From 6d0f6bf50fe959baae8680b70308eef21c7e8383 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 24 May 2023 04:05:14 -0500
Subject: [PATCH 061/454] move loop when trimming in sample editor
---
src/engine/sample.cpp | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp
index b178c6e87..57bf7e09b 100644
--- a/src/engine/sample.cpp
+++ b/src/engine/sample.cpp
@@ -656,6 +656,16 @@ bool DivSample::trim(unsigned int begin, unsigned int end) {
int count=end-begin;
if (count==0) return true;
if (begin==0 && end==samples) return true;
+ if (((int)beginloopEnd && (int)end>loopEnd)) {
+ loopStart=-1;
+ loopEnd=-1;
+ loop=false;
+ } else {
+ loopStart-=begin;
+ loopEnd-=begin;
+ if (loopStart<0) loopStart=0;
+ if (loopEnd>count) loopEnd=count;
+ }
if (depth==DIV_SAMPLE_DEPTH_8BIT) {
if (data8!=NULL) {
signed char* oldData8=data8;
From 4c069178e726e54f18b4b5280556a0883710367b Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 24 May 2023 04:08:28 -0500
Subject: [PATCH 062/454] fix secret arp speed parameter
---
src/engine/engine.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
index efe66fc85..9f70aef00 100644
--- a/src/engine/engine.cpp
+++ b/src/engine/engine.cpp
@@ -2207,6 +2207,7 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
prevOrder=0;
prevRow=0;
stepPlay=0;
+ if (curSubSong!=NULL) curSubSong->arpLen=1;
int prevDrift, prevMidiClockDrift, prevMidiTimeDrift;
prevDrift=clockDrift;
prevMidiClockDrift=midiClockDrift;
From b24b22dfd9fba2391ede75ff2682727caa88c841 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 24 May 2023 04:19:45 -0500
Subject: [PATCH 063/454] release v0.6pre5
the thousand-star release
---
android/app/build.gradle | 4 +--
android/app/src/main/AndroidManifest.xml | 4 +--
res/Info.plist | 6 ++--
src/engine/engine.h | 4 +--
src/gui/gui.cpp | 2 +-
src/gui/gui.h | 4 +--
src/gui/tutorial.cpp | 44 ++++++++++++++++++++----
7 files changed, 49 insertions(+), 19 deletions(-)
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 3ceb1765e..483ea52c4 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -15,8 +15,8 @@ android {
}
minSdkVersion 21
targetSdkVersion 26
- versionCode 143
- versionName "0.6pre4"
+ versionCode 158
+ versionName "0.6pre5"
externalNativeBuild {
cmake {
arguments "-DANDROID_APP_PLATFORM=android-21", "-DANDROID_STL=c++_static"
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 8bad4fe2e..3deb416b9 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -1,8 +1,8 @@
diff --git a/res/Info.plist b/res/Info.plist
index 39a9ac844..026051de2 100644
--- a/res/Info.plist
+++ b/res/Info.plist
@@ -15,17 +15,17 @@
CFBundleInfoDictionaryVersion
6.0
CFBundleLongVersionString
- 0.6pre4
+ 0.6pre5
CFBundleName
Furnace
CFBundlePackageType
APPL
CFBundleShortVersionString
- 0.6pre4
+ 0.6pre5
CFBundleSignature
????
CFBundleVersion
- 0.6pre4
+ 0.6pre5
NSHumanReadableCopyright
NSHighResolutionCapable
diff --git a/src/engine/engine.h b/src/engine/engine.h
index a83c25f94..0c780f91e 100644
--- a/src/engine/engine.h
+++ b/src/engine/engine.h
@@ -54,8 +54,8 @@
#define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock();
#define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false;
-#define DIV_VERSION "dev157"
-#define DIV_ENGINE_VERSION 157
+#define DIV_VERSION "0.6pre5"
+#define DIV_ENGINE_VERSION 158
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02
diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp
index 91f0d3b55..4f5e54ff1 100644
--- a/src/gui/gui.cpp
+++ b/src/gui/gui.cpp
@@ -5095,7 +5095,7 @@ bool FurnaceGUI::loop() {
ImGui::EndPopup();
}
- //drawTutorial();
+ drawTutorial();
ImVec2 newSongMinSize=mobileUI?ImVec2(canvasW-(portrait?0:(60.0*dpiScale)),canvasH-60.0*dpiScale):ImVec2(400.0f*dpiScale,200.0f*dpiScale);
ImVec2 newSongMaxSize=ImVec2(canvasW-((mobileUI && !portrait)?(60.0*dpiScale):0),canvasH-(mobileUI?(60.0*dpiScale):0));
diff --git a/src/gui/gui.h b/src/gui/gui.h
index d4b3e4eca..fd30c734f 100644
--- a/src/gui/gui.h
+++ b/src/gui/gui.h
@@ -1590,12 +1590,12 @@ class FurnaceGUI {
struct Tutorial {
int userComesFrom;
bool introPlayed;
- bool welcome;
+ bool protoWelcome;
bool taken[GUI_TUTORIAL_MAX];
Tutorial():
userComesFrom(0),
introPlayed(false),
- welcome(false) {
+ protoWelcome(false) {
memset(taken,0,GUI_TUTORIAL_MAX*sizeof(bool));
}
} tutorial;
diff --git a/src/gui/tutorial.cpp b/src/gui/tutorial.cpp
index c3dad67bd..71ad3ca40 100644
--- a/src/gui/tutorial.cpp
+++ b/src/gui/tutorial.cpp
@@ -223,26 +223,28 @@ void FurnaceGUI::initTutorial() {
void FurnaceGUI::syncTutorial() {
// tutorial.userComesFrom=e->getConfInt("tutUserComesFrom",0);
tutorial.introPlayed=e->getConfBool("tutIntroPlayed",false);
-// tutorial.welcome=e->getConfBool("tutWelcome",false);
+ tutorial.protoWelcome=e->getConfBool("tutProtoWelcome2",false);
}
void FurnaceGUI::commitTutorial() {
// e->setConf("tutUserComesFrom",tutorial.userComesFrom);
e->setConf("tutIntroPlayed",tutorial.introPlayed);
-// e->setConf("tutWelcome",tutorial.welcome);
+ e->setConf("tutProtoWelcome2",tutorial.protoWelcome);
}
void FurnaceGUI::activateTutorial(FurnaceGUITutorials which) {
- if (tutorial.welcome && !tutorial.taken[which] && !ImGui::IsPopupOpen((const char*)NULL,ImGuiPopupFlags_AnyPopupId|ImGuiPopupFlags_AnyPopupLevel) && curTutorial==-1 && introPos>=10.0) {
+ /*
+ if (tutorial.protoWelcome && !tutorial.taken[which] && !ImGui::IsPopupOpen((const char*)NULL,ImGuiPopupFlags_AnyPopupId|ImGuiPopupFlags_AnyPopupLevel) && curTutorial==-1 && introPos>=10.0) {
logV("activating tutorial %d.",which);
curTutorial=which;
curTutorialStep=0;
}
+ */
}
void FurnaceGUI::drawTutorial() {
// welcome
- if (!tutorial.welcome) {
+ if (!tutorial.protoWelcome) {
ImGui::OpenPopup("Welcome");
}
if (ImGui::BeginPopupModal("Welcome",NULL,ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoTitleBar)) {
@@ -253,7 +255,35 @@ void FurnaceGUI::drawTutorial() {
ImGui::Text("welcome to Furnace, the biggest open-source chiptune tracker!");
- ImGui::TextWrapped("get ready for the tutorial, which will teach you how to use it.");
+ ImGui::TextWrapped(
+ "did I say that 0.6pre5 will have a tutorial? well, it doesn't...\n"
+ "the reason is because 0.6pre5 fixes a critical bug which may cause config loss in some machines.\n"
+ "furthermore, it dramatically improves the backup system. couldn't put this version on hold anymore."
+ );
+
+ ImGui::Separator();
+
+ ImGui::TextWrapped("here are some tips to get you started:");
+
+ ImGui::TextWrapped(
+ "- add an instrument by clicking on + in Instruments\n"
+ "- click on the pattern view to focus it\n"
+ "- channel columns have the following, in this order: note, instrument, volume and effects\n"
+ "- hit space bar while on the pattern to toggle Edit Mode\n"
+ "- click on the pattern or use arrow keys to move the cursor\n"
+ "- values (instrument, volume, effects and effect values) are in hexadecimal\n"
+ "- hit enter to play/stop the song\n"
+ "- extend the song by adding more orders in the Orders window\n"
+ "- click on the Orders matrix to change the patterns of a channel (left click increases; right click decreases)"
+ );
+
+ ImGui::TextWrapped(
+ "if you need help, you may:\n"
+ "- read the (incomplete) manual: https://github.com/tildearrow/furnace/blob/master/papers/doc/README.md\n"
+ "- ask for help in Discussions (https://github.com/tildearrow/furnace/discussions) or the Furnace Discord (https://discord.gg/EfrwT2wq7z)"
+ );
+
+ ImGui::Separator();
ImGui::TextWrapped(
"there are two interface modes: Basic, and Advanced.\n"
@@ -265,13 +295,13 @@ void FurnaceGUI::drawTutorial() {
if (ImGui::Button("Start in Basic Mode")) {
basicMode=true;
- tutorial.welcome=true;
+ tutorial.protoWelcome=true;
commitTutorial();
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Start in Advanced Mode")) {
basicMode=false;
- tutorial.welcome=true;
+ tutorial.protoWelcome=true;
commitTutorial();
ImGui::CloseCurrentPopup();
}
From 61b9a88f8c57a03d41abbdaed66c44ec267e6100 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 24 May 2023 07:18:45 -0500
Subject: [PATCH 064/454] update readme
---
README.md | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 07b6edd1a..316deee74 100644
--- a/README.md
+++ b/README.md
@@ -66,9 +66,12 @@ check out the [Releases](https://github.com/tildearrow/furnace/releases) page. a
- Family Noraebang (OPLL)
- SID (6581/8580) used in Commodore 64
- Mikey used in Atari Lynx
- - ZX Spectrum beeper (SFX-like engine)
+ - ZX Spectrum beeper
+ - SFX-like engine
+ - QuadTone engine
- Pokémon Mini
- Commodore PET
+ - Casio PV-1000
- TIA used in Atari 2600
- POKEY used in Atari 8-bit computers
- Game Boy
@@ -124,8 +127,8 @@ check out the [Releases](https://github.com/tildearrow/furnace/releases) page. a
[](https://repology.org/project/furnace/versions)
some people have provided packages for Unix/Unix-like distributions. here's a list.
- - **Arch Linux**: [furnace](https://archlinux.org/packages/community/x86_64/furnace/) is now in the community repo!
- - **FreeBSD**: [a package in ports](https://www.freshports.org/audio/furnace/) is available courtesy of ehaupt.
+ - **Arch Linux**: [furnace](https://archlinux.org/packages/extra/x86_64/furnace/) is now in the official repositories!
+ - **FreeBSD**: [a package in ports](https://www.freshports.org/audio/furnace/) is available courtesy of ehaupt (warning: 0.5.8!).
- **Nix**: [package](https://search.nixos.org/packages?channel=unstable&show=furnace&from=0&size=50&sort=relevance&type=packages&query=furnace) thanks to OPNA2608.
- **openSUSE**: [a package](https://software.opensuse.org/package/furnace) is available, courtesy of fpesari.
From e07c14db811f9b91188dde145c16e482c293cb97 Mon Sep 17 00:00:00 2001
From: YohananDiamond
Date: Wed, 24 May 2023 12:47:20 -0300
Subject: [PATCH 065/454] Add option to prevent piano input
---
src/gui/gui.cpp | 4 ++++
src/gui/gui.h | 1 +
src/gui/piano.cpp | 3 ++-
3 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp
index 1f2d48794..e9390dff9 100644
--- a/src/gui/gui.cpp
+++ b/src/gui/gui.cpp
@@ -5954,6 +5954,7 @@ bool FurnaceGUI::init() {
pianoOptions=e->getConfBool("pianoOptions",pianoOptions);
pianoSharePosition=e->getConfBool("pianoSharePosition",pianoSharePosition);
pianoOptionsSet=e->getConfBool("pianoOptionsSet",pianoOptionsSet);
+ pianoReadonly=e->getConfBool("pianoReadonly",false);
pianoOffset=e->getConfInt("pianoOffset",pianoOffset);
pianoOffsetEdit=e->getConfInt("pianoOffsetEdit",pianoOffsetEdit);
pianoView=e->getConfInt("pianoView",pianoView);
@@ -6389,6 +6390,7 @@ void FurnaceGUI::commitState() {
e->setConf("pianoOptions",pianoOptions);
e->setConf("pianoSharePosition",pianoSharePosition);
e->setConf("pianoOptionsSet",pianoOptionsSet);
+ e->setConf("pianoReadonly",pianoReadonly);
e->setConf("pianoOffset",pianoOffset);
e->setConf("pianoOffsetEdit",pianoOffsetEdit);
e->setConf("pianoView",pianoView);
@@ -6835,6 +6837,7 @@ FurnaceGUI::FurnaceGUI():
pianoOptions(true),
pianoSharePosition(false),
pianoOptionsSet(false),
+ pianoReadonly(false),
pianoOffset(6),
pianoOffsetEdit(9),
pianoView(PIANO_LAYOUT_AUTOMATIC),
@@ -6844,6 +6847,7 @@ FurnaceGUI::FurnaceGUI():
pianoOctavesEdit(4),
pianoOptions(false),
pianoSharePosition(true),
+ pianoReadonly(false),
pianoOffset(6),
pianoOffsetEdit(6),
pianoView(PIANO_LAYOUT_STANDARD),
diff --git a/src/gui/gui.h b/src/gui/gui.h
index 880748645..0fc45f70a 100644
--- a/src/gui/gui.h
+++ b/src/gui/gui.h
@@ -1916,6 +1916,7 @@ class FurnaceGUI {
bool pianoOptions, pianoSharePosition, pianoOptionsSet;
float pianoKeyHit[180];
bool pianoKeyPressed[180];
+ bool pianoReadonly;
int pianoOffset, pianoOffsetEdit;
int pianoView, pianoInputPadMode;
diff --git a/src/gui/piano.cpp b/src/gui/piano.cpp
index baae316d1..4842d05cf 100644
--- a/src/gui/piano.cpp
+++ b/src/gui/piano.cpp
@@ -123,6 +123,7 @@ void FurnaceGUI::drawPiano() {
pianoInputPadMode=PIANO_INPUT_PAD_SPLIT_VISIBLE;
}
ImGui::Checkbox("Share play/edit offset/range",&pianoSharePosition);
+ ImGui::Checkbox("Read-only (can't input notes)",&pianoReadonly);
ImGui::EndPopup();
}
@@ -223,7 +224,7 @@ void FurnaceGUI::drawPiano() {
//ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID("pianoDisplay"))) {
bool canInput=false;
- if (ImGui::ItemHoverable(rect,ImGui::GetID("pianoDisplay"))) {
+ if (!pianoReadonly && ImGui::ItemHoverable(rect,ImGui::GetID("pianoDisplay"))) {
canInput=true;
ImGui::InhibitInertialScroll();
}
From 4d36bd23365ea67af48ffe2256cab07780c06320 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Wed, 24 May 2023 14:25:30 -0500
Subject: [PATCH 066/454] VGM export; fix loop trail when loop is off
---
src/engine/vgmOps.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp
index 8f867322a..eb10521a7 100644
--- a/src/engine/vgmOps.cpp
+++ b/src/engine/vgmOps.cpp
@@ -2098,6 +2098,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
if (nextTick(false,true)) {
if (trailing) beenOneLoopAlready=true;
trailing=true;
+ if (!loop) countDown=0;
for (int i=0; i
Date: Wed, 24 May 2023 22:14:36 -0500
Subject: [PATCH 067/454] stop reading config if there's a zero
most likely corrupt file
---
extern/imgui_patched/imgui.cpp | 5 ++++-
src/engine/config.cpp | 6 +++++-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/extern/imgui_patched/imgui.cpp b/extern/imgui_patched/imgui.cpp
index 4d57efc02..e2552fe2b 100644
--- a/extern/imgui_patched/imgui.cpp
+++ b/extern/imgui_patched/imgui.cpp
@@ -12478,9 +12478,12 @@ bool ImGui::LoadIniSettingsFromDisk(const char* ini_filename, bool redundancy)
if (!file_data) continue;
for (size_t j=0; j
Date: Wed, 24 May 2023 22:15:49 -0500
Subject: [PATCH 068/454] update release scripts to strip before packing
instead of after build
this way I at least hope I can use addr2line to retrieve the address
of a call in case of crash
---
scripts/release-win32.sh | 3 ++-
scripts/release-win64.sh | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/scripts/release-win32.sh b/scripts/release-win32.sh
index 9faf9c2fa..df603dbab 100755
--- a/scripts/release-win32.sh
+++ b/scripts/release-win32.sh
@@ -17,7 +17,6 @@ cd win32build
# TODO: potential Arch-ism?
i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Werror" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=ON .. || exit 1
make -j8 || exit 1
-#i686-w64-mingw32-strip -s furnace.exe || exit 1
cd ..
@@ -32,6 +31,8 @@ cp -r ../../demos demos || exit 1
cp -r ../../instruments instruments || exit 1
cp -r ../../wavetables wavetables || exit 1
+i686-w64-mingw32-strip -s furnace.exe || exit 1
+
zip -r furnace.zip LICENSE.txt furnace.exe README.txt papers demos instruments wavetables
furName=$(git describe --tags | sed "s/v0/0/")
diff --git a/scripts/release-win64.sh b/scripts/release-win64.sh
index b349e571f..e1678eda1 100755
--- a/scripts/release-win64.sh
+++ b/scripts/release-win64.sh
@@ -17,7 +17,6 @@ cd winbuild
# TODO: potential Arch-ism?
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
cd ..
@@ -32,6 +31,8 @@ cp -r ../../demos demos || exit 1
cp -r ../../instruments instruments || exit 1
cp -r ../../wavetables wavetables || exit 1
+x86_64-w64-mingw32-strip -s furnace.exe || exit 1
+
zip -r furnace.zip LICENSE.txt furnace.exe README.txt papers demos instruments wavetables
furName=$(git describe --tags | sed "s/v0/0/")
From 46d86ff335fab34eb5367eb720f40cea3ce5fed7 Mon Sep 17 00:00:00 2001
From: tildearrow
Date: Thu, 25 May 2023 04:10:42 -0500
Subject: [PATCH 069/454] update readme
---
README.md | 106 ++++++++++++++++++++++++++---------------
papers/screenshot3.png | Bin 0 -> 404659 bytes
2 files changed, 67 insertions(+), 39 deletions(-)
create mode 100644 papers/screenshot3.png
diff --git a/README.md b/README.md
index 316deee74..7f2a34cf2 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
# Furnace (chiptune tracker)
-
+
the biggest multi-system chiptune tracker ever made!
-[downloads](#downloads) | [discussion/help](#quick-references) | [developer info](#developer-info) | [unofficial packages](#unofficial-packages) | [FAQ](#frequently-asked-questions)
+[downloads](#downloads) | [discussion/help](#quick-references) | [developer info](#developer-info) | [Unix/Linux packages](#packages) | [FAQ](#frequently-asked-questions)
---
## downloads
@@ -83,7 +83,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.7)
+ - loads .dmf modules from all versions (beta 1 to 1.1.9)
- saves .dmf modules - both modern and legacy
- Furnace doubles as a module downgrader
- loads/saves .dmp instruments and .dmw wavetables as well
@@ -119,18 +119,19 @@ check out the [Releases](https://github.com/tildearrow/furnace/releases) page. a
---
# quick references
- - **discussion**: see the [Discussions](https://github.com/tildearrow/furnace/discussions) section, or (preferably) the [official Discord server](https://discord.gg/EfrwT2wq7z).
- - **help**: check out the [documentation](papers/doc/README.md). it's mostly incomplete, but has details on effects.
+- **discussion**: see the [Discussions](https://github.com/tildearrow/furnace/discussions) section, or the [official Discord server](https://discord.gg/EfrwT2wq7z).
+- **help**: check out the [documentation](papers/doc/README.md). it's incomplete, but has details on effects.
-## unofficial packages
+## packages
[](https://repology.org/project/furnace/versions)
some people have provided packages for Unix/Unix-like distributions. here's a list.
- - **Arch Linux**: [furnace](https://archlinux.org/packages/extra/x86_64/furnace/) is now in the official repositories!
- - **FreeBSD**: [a package in ports](https://www.freshports.org/audio/furnace/) is available courtesy of ehaupt (warning: 0.5.8!).
- - **Nix**: [package](https://search.nixos.org/packages?channel=unstable&show=furnace&from=0&size=50&sort=relevance&type=packages&query=furnace) thanks to OPNA2608.
- - **openSUSE**: [a package](https://software.opensuse.org/package/furnace) is available, courtesy of fpesari.
+
+- **Arch Linux**: [furnace](https://archlinux.org/packages/extra/x86_64/furnace/) is in the official repositories.
+- **FreeBSD**: [a package in ports](https://www.freshports.org/audio/furnace/) is available courtesy of ehaupt (warning: 0.5.8!).
+- **Nix**: [package](https://search.nixos.org/packages?channel=unstable&show=furnace&from=0&size=50&sort=relevance&type=packages&query=furnace) thanks to OPNA2608.
+- **openSUSE**: [a package](https://software.opensuse.org/package/furnace) is available, courtesy of fpesari.
---
# developer info
@@ -144,7 +145,9 @@ if you can't download these artifacts (because GitHub requires you to be logged
## dependencies
- CMake
+- Git (for cloning the repository)
- JACK (optional, macOS/Linux only)
+- a C/C++ compiler (e.g. Visual Studio or MinGW on Windows, Xcode (the command-line tools are enough) on macOS or GCC on Linux)
if building under Windows or macOS, no additional dependencies are required.
otherwise, you may also need the following:
@@ -181,10 +184,32 @@ from the developer tools command prompt:
mkdir build
cd build
cmake ..
+```
+
+then open the solution file in Visual Studio and build.
+
+alternatively, do:
+
+```
msbuild ALL_BUILD.vcxproj
```
-### macOS and Linux
+### Windows using MinGW
+
+setting up MinGW is a bit more complicated. two benefits are a faster, hotter Furnace, and Windows XP support.
+
+however, one huge drawback is lack of backtrace support, so you'll have to use gdb when diagnosing a crash.
+
+```
+mkdir build
+cd build
+cmake -G "MinGW Makefiles" ..
+mingw32-make
+```
+
+you may use "MSYS Makefiles" instead, depending on how you installed MinGW.
+
+### macOS, Linux and other Unix/Unix-like
```
mkdir build
@@ -192,7 +217,16 @@ cd build
cmake ..
make
```
-Alternatively, build scripts are provided in the `scripts/` folder in the root of the repository.
+
+on macOS you may do the following instead:
+
+```
+mkdir build
+cd build
+cmake -G Xcode ..
+```
+
+...and then load the project on Xcode or type `xcodebuild`.
### CMake options
@@ -223,6 +257,8 @@ Available options:
## console usage
+(note: if on Windows, type `furnace.exe` instead, or `Debug\furnace.exe` on MSVC)
+
```
./furnace
```
@@ -241,23 +277,17 @@ this will play a compatible file.
this will play a compatible file and enable the commands view.
-**note that these commands only actually work in Linux environments. on other command lines, such as Windows' Command Prompt, or MacOS Terminal, it may not work correctly.**
+**note that console mode may not work correctly on Windows. you may have to quit using the Task Manager.**
---
# frequently asked questions
-> woah! 50 sound chips?! I can't believe it!
-
-yup, it's real.
-
-> where's the manual?
-
-see [papers/](papers/doc/README.md). it's kind of incomplete, but at least the sound chips section is there.
-
> it doesn't open under macOS!
this is due to Apple's application signing policy. a workaround is to right click on the Furnace app icon and select Open.
+> it says "Furnace" is damaged and can't be opened!
+
**as of Monterey, this workaround no longer works (especially on ARM).** yeah, Apple has decided to be strict on the matter.
if you happen to be on that version, use this workaround instead (on a Terminal):
@@ -269,24 +299,25 @@ xattr -d com.apple.quarantine /path/to/Furnace.app
you may need to log out and/or reboot after doing this.
+> where's the manual?
+
+see [papers/](papers/doc/README.md). it's kind of incomplete, but at least the sound chips section is there.
+
+> is there a tutorial?
+
+sadly, the in-program tutorial isn't ready yet. however, [a video tutorial is available on YouTube](https://youtube.com/playlist?list=PLCELB6AsTZUnwv0PC5AAGHjvg47F44YQ1), made by Spinning Square Waves.
+
+> I've lost my song!
+
+Furnace keeps backups of the songs you've worked on before. go to **file > restore backup**.
+
> .spc export?
**not yet!** coming in 0.7 though, eventually...
-> how do I use C64 absolute filter/duty?
+> ROM export?
-on Instrument Editor in the C64 tab there are two options to toggle these.
-also provided are two effects:
-
-- `3xxx`: set fine duty.
-- `4xxx`: set fine cutoff. `xxx` range is 000-7ff.
-additionally, you can change the cutoff and/or duty as a macro inside an instrument by clicking the `absolute cutoff macro` and/or `absolute duty macro` checkbox at the bottom of the instrument. (for the filter, you also need to click the checkbox that says `volume macro is cutoff macro`.)
-
-> how do I use PCM on a PCM-capable chip?
-
-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.
+**not yet!** coming in 0.7 though, eventually...
> my .dmf song sounds odd at a certain point
@@ -296,10 +327,6 @@ Furnace's .dmf compatibility isn't perfect and it's mostly because DefleMask doe
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?
-
-right click on the channel name.
-
---
# footnotes
@@ -312,4 +339,5 @@ 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, .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.
+Furnace is NOT affiliated with Delek or DefleMask in any form, regardless of its ability to load and save the .dmf, .dmp and .dmw file formats.
+additionally, Furnace does not intend to replace DefleMask, nor any other program.
diff --git a/papers/screenshot3.png b/papers/screenshot3.png
new file mode 100644
index 0000000000000000000000000000000000000000..da5983669e5e2eebd7ee9a410e274a5ffb626bcc
GIT binary patch
literal 404659
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