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

This commit is contained in:
cam900 2022-11-15 11:52:30 +09:00
commit 46880634b4
50 changed files with 2492 additions and 1960 deletions

View file

@ -308,6 +308,7 @@ endif()
set(ENGINE_SOURCES set(ENGINE_SOURCES
src/log.cpp src/log.cpp
src/baseutils.cpp
src/fileutils.cpp src/fileutils.cpp
src/utfutils.cpp src/utfutils.cpp
@ -572,6 +573,7 @@ src/gui/guiConst.cpp
src/gui/about.cpp src/gui/about.cpp
src/gui/channels.cpp src/gui/channels.cpp
src/gui/chanOsc.cpp src/gui/chanOsc.cpp
src/gui/clock.cpp
src/gui/compatFlags.cpp src/gui/compatFlags.cpp
src/gui/cursor.cpp src/gui/cursor.cpp
src/gui/dataList.cpp src/gui/dataList.cpp

View file

@ -1,4 +1,4 @@
# Furnace Tracker # Furnace (chiptune tracker)
![screenshot](papers/screenshot2.png) ![screenshot](papers/screenshot2.png)

BIN
demos/SeeingRed.fur Normal file

Binary file not shown.

Binary file not shown.

BIN
demos/splashingwater.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -3,7 +3,7 @@
a backwards-compatible successor to the AY-3-8910, with increased volume resolution, duty cycle control, three envelopes and highly configurable noise generator. a backwards-compatible successor to the AY-3-8910, with increased volume resolution, duty cycle control, three envelopes and highly configurable noise generator.
sadly, this soundchip has only ever observed minimal success, and has remained rather obscure since. sadly, this soundchip has only ever observed minimal success, and has remained rather obscure since.
it is known for being used in the Covox Sound Master, which didn't sell well either. it is best known for being used in the Covox Sound Master, which didn't sell well either. It also observed very minimal success in Merit's CRT-250 machines, but only as a replacement for the AY-3-8910.
emulation of this chip in Furnace is now complete thanks to community efforts and hardware testing, which an MSX board called Darky has permitted. emulation of this chip in Furnace is now complete thanks to community efforts and hardware testing, which an MSX board called Darky has permitted.

View file

@ -2,8 +2,6 @@
one of two chips that powered the Sega Genesis. It is a six-channel, four-operator FM synthesizer. Channel #6 can be turned into 8-bit PCM player. one of two chips that powered the Sega Genesis. It is a six-channel, four-operator FM synthesizer. Channel #6 can be turned into 8-bit PCM player.
For 0.6pre1, Furnace can now support advanced YM2612 features that [Fractal](https://gitlab.com/Natsumi/Fractal-Sound) sound driver adds: two software-mixed PCM channels (variable pitch, sample offsets, max 13.7 khz rate) and CSM - ch3 special mode feature that can be abused to produce rudimentary speech synthesis.
# effects # effects
- `10xy`: set LFO parameters. - `10xy`: set LFO parameters.

View file

@ -32,6 +32,8 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
the format versions are: the format versions are:
- 123: Furnace dev123
- 122: Furnace dev122
- 121: Furnace dev121 - 121: Furnace dev121
- 120: Furnace dev120 - 120: Furnace dev120
- 119: Furnace dev119 - 119: Furnace dev119
@ -1096,7 +1098,11 @@ size | description
| - 9: BRR (SNES) | - 9: BRR (SNES)
| - 10: VOX | - 10: VOX
| - 16: 16-bit PCM | - 16: 16-bit PCM
3 | reserved 1 | loop direction (>=123) or reserved
| - 0: forward
| - 0: backward
| - 0: ping-pong
2 | reserved
4 | loop start 4 | loop start
| - -1 means no loop | - -1 means no loop
4 | loop end 4 | loop end

89
src/baseutils.cpp Normal file
View file

@ -0,0 +1,89 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This 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 "baseutils.h"
#include <string.h>
const char* base64Table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string taEncodeBase64(const std::string& data) {
std::string ret;
ret.reserve((2+data.size()*4)/3);
unsigned int groupOfThree=0;
unsigned char pos=0;
for (const char& i: data) {
groupOfThree|=((unsigned char)i)<<((2-pos)<<3);
if (++pos>=3) {
pos=0;
ret+=base64Table[(groupOfThree>>18)&63];
ret+=base64Table[(groupOfThree>>12)&63];
ret+=base64Table[(groupOfThree>>6)&63];
ret+=base64Table[groupOfThree&63];
groupOfThree=0;
}
}
if (pos==2) {
ret+=base64Table[(groupOfThree>>18)&63];
ret+=base64Table[(groupOfThree>>12)&63];
ret+=base64Table[(groupOfThree>>6)&63];
ret+='=';
} else if (pos==1) {
ret+=base64Table[(groupOfThree>>18)&63];
ret+=base64Table[(groupOfThree>>12)&63];
ret+="==";
}
return ret;
}
std::string taDecodeBase64(const char* buf) {
std::string data;
unsigned int groupOfThree=0;
signed char pos=18;
for (const char* i=buf; *i; i++) {
unsigned char nextVal=0;
if ((*i)=='/') {
nextVal=63;
} else if ((*i)=='+') {
nextVal=62;
} else if ((*i)>='0' && (*i)<='9') {
nextVal=52+((*i)-'0');
} else if ((*i)>='a' && (*i)<='z') {
nextVal=26+((*i)-'a');
} else if ((*i)>='A' && (*i)<='Z') {
nextVal=((*i)-'A');
} else {
nextVal=0;
}
groupOfThree|=nextVal<<pos;
pos-=6;
if (pos<0) {
pos=18;
if ((groupOfThree>>16)&0xff) data+=(groupOfThree>>16)&0xff;
if ((groupOfThree>>8)&0xff) data+=(groupOfThree>>8)&0xff;
if (groupOfThree&0xff) data+=groupOfThree&0xff;
groupOfThree=0;
}
}
return data;
}

28
src/baseutils.h Normal file
View file

@ -0,0 +1,28 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This 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 _BASEUTILS_H
#define _BASEUTILS_H
#include <string>
std::string taEncodeBase64(const std::string& data);
std::string taDecodeBase64(const char* str);
#endif

View file

@ -19,6 +19,7 @@
#include "config.h" #include "config.h"
#include "../ta-log.h" #include "../ta-log.h"
#include "../baseutils.h"
#include "../fileutils.h" #include "../fileutils.h"
#include <fmt/printf.h> #include <fmt/printf.h>
@ -48,41 +49,9 @@ String DivConfig::toString() {
return ret; return ret;
} }
const char* base64Table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
String DivConfig::toBase64() { String DivConfig::toBase64() {
String data=toString(); String data=toString();
String ret; return taEncodeBase64(data);
ret.reserve((2+data.size()*4)/3);
unsigned int groupOfThree=0;
unsigned char pos=0;
for (char& i: data) {
groupOfThree|=((unsigned char)i)<<((2-pos)<<3);
if (++pos>=3) {
pos=0;
ret+=base64Table[(groupOfThree>>18)&63];
ret+=base64Table[(groupOfThree>>12)&63];
ret+=base64Table[(groupOfThree>>6)&63];
ret+=base64Table[groupOfThree&63];
groupOfThree=0;
}
}
if (pos==2) {
ret+=base64Table[(groupOfThree>>18)&63];
ret+=base64Table[(groupOfThree>>12)&63];
ret+=base64Table[(groupOfThree>>6)&63];
ret+='=';
} else if (pos==1) {
ret+=base64Table[(groupOfThree>>18)&63];
ret+=base64Table[(groupOfThree>>12)&63];
ret+="==";
}
logV("toBase64: %s",ret);
return ret;
} }
void DivConfig::parseLine(const char* line) { void DivConfig::parseLine(const char* line) {
@ -143,38 +112,7 @@ bool DivConfig::loadFromMemory(const char* buf) {
} }
bool DivConfig::loadFromBase64(const char* buf) { bool DivConfig::loadFromBase64(const char* buf) {
String data; String data=taDecodeBase64(buf);
unsigned int groupOfThree=0;
signed char pos=18;
for (const char* i=buf; *i; i++) {
unsigned char nextVal=0;
if ((*i)=='/') {
nextVal=63;
} else if ((*i)=='+') {
nextVal=62;
} else if ((*i)>='0' && (*i)<='9') {
nextVal=52+((*i)-'0');
} else if ((*i)>='a' && (*i)<='z') {
nextVal=26+((*i)-'a');
} else if ((*i)>='A' && (*i)<='Z') {
nextVal=((*i)-'A');
} else {
nextVal=0;
}
groupOfThree|=nextVal<<pos;
pos-=6;
if (pos<0) {
pos=18;
if ((groupOfThree>>16)&0xff) data+=(groupOfThree>>16)&0xff;
if ((groupOfThree>>8)&0xff) data+=(groupOfThree>>8)&0xff;
if (groupOfThree&0xff) data+=groupOfThree&0xff;
groupOfThree=0;
}
}
logV("fromBase64: %s",data);
return loadFromMemory(data.c_str()); return loadFromMemory(data.c_str());
} }

View file

@ -1390,10 +1390,14 @@ String DivEngine::decodeSysDesc(String desc) {
return newDesc.toBase64(); return newDesc.toBase64();
} }
void DivEngine::initSongWithDesc(const char* description) { void DivEngine::initSongWithDesc(const char* description, bool inBase64) {
int chanCount=0; int chanCount=0;
DivConfig c; DivConfig c;
c.loadFromBase64(description); if (inBase64) {
c.loadFromBase64(description);
} else {
c.loadFromMemory(description);
}
int index=0; int index=0;
for (; index<32; index++) { for (; index<32; index++) {
song.system[index]=systemFromFileFur(c.getInt(fmt::sprintf("id%d",index),0)); song.system[index]=systemFromFileFur(c.getInt(fmt::sprintf("id%d",index),0));
@ -1414,7 +1418,7 @@ void DivEngine::initSongWithDesc(const char* description) {
song.systemLen=index; song.systemLen=index;
} }
void DivEngine::createNew(const char* description, String sysName) { void DivEngine::createNew(const char* description, String sysName, bool inBase64) {
quitDispatch(); quitDispatch();
BUSY_BEGIN; BUSY_BEGIN;
saveLock.lock(); saveLock.lock();
@ -1422,7 +1426,7 @@ void DivEngine::createNew(const char* description, String sysName) {
song=DivSong(); song=DivSong();
changeSong(0); changeSong(0);
if (description!=NULL) { if (description!=NULL) {
initSongWithDesc(description); initSongWithDesc(description,inBase64);
} }
if (sysName=="") { if (sysName=="") {
song.systemName=getSongSystemLegacyName(song,!getConfInt("noMultiSystem",0)); song.systemName=getSongSystemLegacyName(song,!getConfInt("noMultiSystem",0));
@ -2358,6 +2362,9 @@ void DivEngine::reset() {
firstTick=false; firstTick=false;
shallStop=false; shallStop=false;
shallStopSched=false; shallStopSched=false;
pendingMetroTick=0;
elapsedBars=0;
elapsedBeats=0;
nextSpeed=speed1; nextSpeed=speed1;
divider=60; divider=60;
if (curSubSong->customTempo) { if (curSubSong->customTempo) {
@ -2548,6 +2555,14 @@ int DivEngine::getRow() {
return prevRow; return prevRow;
} }
int DivEngine::getElapsedBars() {
return elapsedBars;
}
int DivEngine::getElapsedBeats() {
return elapsedBeats;
}
size_t DivEngine::getCurrentSubSong() { size_t DivEngine::getCurrentSubSong() {
return curSubSongIndex; return curSubSongIndex;
} }
@ -2673,12 +2688,17 @@ void DivEngine::unmuteAll() {
BUSY_END; BUSY_END;
} }
int DivEngine::addInstrument(int refChan) { int DivEngine::addInstrument(int refChan, DivInstrumentType fallbackType) {
if (song.ins.size()>=256) return -1; if (song.ins.size()>=256) return -1;
BUSY_BEGIN; BUSY_BEGIN;
DivInstrument* ins=new DivInstrument; DivInstrument* ins=new DivInstrument;
int insCount=(int)song.ins.size(); int insCount=(int)song.ins.size();
DivInstrumentType prefType=getPreferInsType(refChan); DivInstrumentType prefType;
if (refChan<0) {
prefType=fallbackType;
} else {
prefType=getPreferInsType(refChan);
}
switch (prefType) { switch (prefType) {
case DIV_INS_OPLL: case DIV_INS_OPLL:
*ins=song.nullInsOPLL; *ins=song.nullInsOPLL;
@ -2692,8 +2712,10 @@ int DivEngine::addInstrument(int refChan) {
default: default:
break; break;
} }
if (sysOfChan[refChan]==DIV_SYSTEM_QSOUND) { if (refChan>=0) {
*ins=song.nullInsQSound; if (sysOfChan[refChan]==DIV_SYSTEM_QSOUND) {
*ins=song.nullInsQSound;
}
} }
ins->name=fmt::sprintf("Instrument %d",insCount); ins->name=fmt::sprintf("Instrument %d",insCount);
if (prefType!=DIV_INS_NULL) { if (prefType!=DIV_INS_NULL) {

View file

@ -47,8 +47,8 @@
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false; #define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev122" #define DIV_VERSION "dev125"
#define DIV_ENGINE_VERSION 122 #define DIV_ENGINE_VERSION 125
// for imports // for imports
#define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02 #define DIV_VERSION_FC 0xff02
@ -351,14 +351,14 @@ class DivEngine {
bool midiOutClock; bool midiOutClock;
int midiOutMode; int midiOutMode;
int softLockCount; int softLockCount;
int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed; int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats;
size_t curSubSongIndex; size_t curSubSongIndex;
double divider; double divider;
int cycles; int cycles;
double clockDrift; double clockDrift;
int stepPlay; int stepPlay;
int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch; int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch;
unsigned char extValue; unsigned char extValue, pendingMetroTick;
unsigned char speed1, speed2; unsigned char speed1, speed2;
short tempoAccum; short tempoAccum;
DivStatusView view; DivStatusView view;
@ -382,9 +382,9 @@ class DivEngine {
std::vector<String> midiOuts; std::vector<String> midiOuts;
std::vector<DivCommand> cmdStream; std::vector<DivCommand> cmdStream;
std::vector<DivInstrumentType> possibleInsTypes; std::vector<DivInstrumentType> possibleInsTypes;
DivSysDef* sysDefs[256]; static DivSysDef* sysDefs[256];
DivSystem sysFileMapFur[256]; static DivSystem sysFileMapFur[256];
DivSystem sysFileMapDMF[256]; static DivSystem sysFileMapDMF[256];
struct SamplePreview { struct SamplePreview {
double rate; double rate;
@ -440,8 +440,6 @@ class DivEngine {
void reset(); void reset();
void playSub(bool preserveDrift, int goalRow=0); void playSub(bool preserveDrift, int goalRow=0);
void convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivSystem sys);
bool loadDMF(unsigned char* file, size_t len); bool loadDMF(unsigned char* file, size_t len);
bool loadFur(unsigned char* file, size_t len); bool loadFur(unsigned char* file, size_t len);
bool loadMod(unsigned char* file, size_t len); bool loadMod(unsigned char* file, size_t len);
@ -469,7 +467,7 @@ class DivEngine {
bool deinitAudioBackend(bool dueToSwitchMaster=false); bool deinitAudioBackend(bool dueToSwitchMaster=false);
void registerSystems(); void registerSystems();
void initSongWithDesc(const char* description); void initSongWithDesc(const char* description, bool inBase64=true);
void exchangeIns(int one, int two); void exchangeIns(int one, int two);
void swapChannels(int src, int dest); void swapChannels(int src, int dest);
@ -503,7 +501,7 @@ class DivEngine {
// parse old system setup description // parse old system setup description
String decodeSysDesc(String desc); String decodeSysDesc(String desc);
// start fresh // start fresh
void createNew(const char* description, String sysName); void createNew(const char* description, String sysName, bool inBase64=true);
// load a file. // load a file.
bool load(unsigned char* f, size_t length); bool load(unsigned char* f, size_t length);
// save as .dmf. // save as .dmf.
@ -532,10 +530,14 @@ class DivEngine {
void notifyWaveChange(int wave); void notifyWaveChange(int wave);
// get system IDs // get system IDs
DivSystem systemFromFileFur(unsigned char val); static DivSystem systemFromFileFur(unsigned char val);
unsigned char systemToFileFur(DivSystem val); static unsigned char systemToFileFur(DivSystem val);
DivSystem systemFromFileDMF(unsigned char val); static DivSystem systemFromFileDMF(unsigned char val);
unsigned char systemToFileDMF(DivSystem val); static unsigned char systemToFileDMF(DivSystem val);
// convert old flags
static void convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivSystem sys);
// benchmark (returns time in seconds) // benchmark (returns time in seconds)
double benchmarkPlayback(); double benchmarkPlayback();
@ -709,6 +711,10 @@ class DivEngine {
// get current row // get current row
int getRow(); int getRow();
// get beat/bar
int getElapsedBars();
int getElapsedBeats();
// get current subsong // get current subsong
size_t getCurrentSubSong(); size_t getCurrentSubSong();
@ -753,7 +759,7 @@ class DivEngine {
bool isExporting(); bool isExporting();
// add instrument // add instrument
int addInstrument(int refChan=0); int addInstrument(int refChan=0, DivInstrumentType fallbackType=DIV_INS_STD);
// add instrument from pointer // add instrument from pointer
int addInstrumentPtr(DivInstrument* which); int addInstrumentPtr(DivInstrument* which);
@ -1058,6 +1064,8 @@ class DivEngine {
lastLoopPos(0), lastLoopPos(0),
exportLoopCount(0), exportLoopCount(0),
nextSpeed(3), nextSpeed(3),
elapsedBars(0),
elapsedBeats(0),
curSubSongIndex(0), curSubSongIndex(0),
divider(60), divider(60),
cycles(0), cycles(0),
@ -1073,6 +1081,7 @@ class DivEngine {
cmdsPerSecond(0), cmdsPerSecond(0),
globalPitch(0), globalPitch(0),
extValue(0), extValue(0),
pendingMetroTick(0),
speed1(3), speed1(3),
speed2(3), speed2(3),
tempoAccum(0), tempoAccum(0),

View file

@ -2383,11 +2383,16 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (isNewSample) { if (isNewSample) {
sample->centerRate=reader.readI(); sample->centerRate=reader.readI();
sample->depth=(DivSampleDepth)reader.readC(); sample->depth=(DivSampleDepth)reader.readC();
if (ds.version>=123) {
sample->loopMode=(DivSampleLoopMode)reader.readC();
} else {
sample->loopMode=DIV_SAMPLE_LOOP_FORWARD;
reader.readC();
}
// reserved // reserved
reader.readC(); reader.readC();
reader.readC(); reader.readC();
reader.readC();
sample->loopStart=reader.readI(); sample->loopStart=reader.readI();
sample->loopEnd=reader.readI(); sample->loopEnd=reader.readI();
@ -2611,6 +2616,21 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
} }
} }
// ExtCh compat flag
if (ds.version<125) {
for (int i=0; i<ds.systemLen; i++) {
if (ds.system[i]==DIV_SYSTEM_YM2612_EXT ||
ds.system[i]==DIV_SYSTEM_YM2612_FRAC_EXT ||
ds.system[i]==DIV_SYSTEM_YM2610_EXT ||
ds.system[i]==DIV_SYSTEM_YM2610_FULL_EXT ||
ds.system[i]==DIV_SYSTEM_YM2610B_EXT ||
ds.system[i]==DIV_SYSTEM_OPN_EXT ||
ds.system[i]==DIV_SYSTEM_PC98_EXT) {
ds.systemFlags[i].set("noExtMacros",true);
}
}
}
if (active) quitDispatch(); if (active) quitDispatch();
BUSY_BEGIN_SOFT; BUSY_BEGIN_SOFT;
saveLock.lock(); saveLock.lock();
@ -2946,7 +2966,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
writeFxCol(fxTyp,fxVal); writeFxCol(fxTyp,fxVal);
break; break;
case 12: // set vol case 12: // set vol
data[row][3]=fxVal; data[row][3]=MIN(0x40,fxVal);
break; break;
case 13: // break to row (BCD) case 13: // break to row (BCD)
writeFxCol(fxTyp,((fxVal>>4)*10)+(fxVal&15)); writeFxCol(fxTyp,((fxVal>>4)*10)+(fxVal&15));
@ -4624,9 +4644,9 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
w->writeI(sample->rate); w->writeI(sample->rate);
w->writeI(sample->centerRate); w->writeI(sample->centerRate);
w->writeC(sample->depth); w->writeC(sample->depth);
w->writeC(sample->loopMode);
w->writeC(0); // reserved w->writeC(0); // reserved
w->writeC(0); w->writeC(0);
w->writeC(0);
w->writeI(sample->loop?sample->loopStart:-1); w->writeI(sample->loop?sample->loopStart:-1);
w->writeI(sample->loop?sample->loopEnd:-1); w->writeI(sample->loop?sample->loopEnd:-1);

View file

@ -285,7 +285,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
} else { } else {
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
chan[i].baseFreq=NOTE_FNUM_BLOCK(parent->calcArp(chan[i].note,chan[i].std.arp.val),11); chan[i].baseFreq=NOTE_FNUM_BLOCK(parent->calcArp(chan[i].note,chan[i].std.arp.val),11);
} }
@ -1224,6 +1224,7 @@ void DivPlatformGenesis::setFlags(const DivConfig& flags) {
break; break;
} }
ladder=flags.getBool("ladderEffect",false); ladder=flags.getBool("ladderEffect",false);
noExtMacros=flags.getBool("noExtMacros",false);
OPN2_SetChipType(ladder?ym3438_mode_ym2612:0); OPN2_SetChipType(ladder?ym3438_mode_ym2612:0);
if (useYMFM) { if (useYMFM) {
if (fm_ymfm!=NULL) delete fm_ymfm; if (fm_ymfm!=NULL) delete fm_ymfm;

View file

@ -116,7 +116,7 @@ class DivPlatformGenesis: public DivPlatformOPN {
int softPCMTimer; int softPCMTimer;
bool extMode, softPCM, useYMFM; bool extMode, softPCM, noExtMacros, useYMFM;
bool ladder; bool ladder;
unsigned char dacVolTable[128]; unsigned char dacVolTable[128];

View file

@ -52,6 +52,15 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
chan[2].state.ams=ins->fm.ams; chan[2].state.ams=ins->fm.ams;
chan[2].state.op[ordch]=ins->fm.op[ordch]; chan[2].state.op[ordch]=ins->fm.op[ordch];
} }
if (noExtMacros) {
opChan[ch].macroInit(NULL);
} else {
opChan[ch].macroInit(ins);
}
if (!opChan[ch].std.vol.will) {
opChan[ch].outVol=opChan[ch].vol;
}
unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
DivInstrumentFM::Operator& op=chan[2].state.op[ordch]; DivInstrumentFM::Operator& op=chan[2].state.op[ordch];
@ -60,7 +69,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
rWrite(baseAddr+0x40,127); rWrite(baseAddr+0x40,127);
} else { } else {
if (opChan[ch].insChanged) { if (opChan[ch].insChanged) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127)); rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
} }
} }
if (opChan[ch].insChanged) { if (opChan[ch].insChanged) {
@ -81,6 +90,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
opChan[ch].portaPause=false; opChan[ch].portaPause=false;
opChan[ch].note=c.value;
opChan[ch].freqChanged=true; opChan[ch].freqChanged=true;
} }
opChan[ch].keyOn=true; opChan[ch].keyOn=true;
@ -92,14 +102,28 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
opChan[ch].keyOn=false; opChan[ch].keyOn=false;
opChan[ch].active=false; opChan[ch].active=false;
break; break;
case DIV_CMD_NOTE_OFF_ENV:
if (noExtMacros) break;
opChan[ch].keyOff=true;
opChan[ch].keyOn=false;
opChan[ch].active=false;
opChan[ch].std.release();
break;
case DIV_CMD_ENV_RELEASE:
if (noExtMacros) break;
opChan[ch].std.release();
break;
case DIV_CMD_VOLUME: { case DIV_CMD_VOLUME: {
opChan[ch].vol=c.value; opChan[ch].vol=c.value;
if (!opChan[ch].std.vol.has) {
opChan[ch].outVol=c.value;
}
unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
DivInstrumentFM::Operator& op=chan[2].state.op[ordch]; DivInstrumentFM::Operator& op=chan[2].state.op[ordch];
if (isOpMuted[ch]) { if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127); rWrite(baseAddr+0x40,127);
} else { } else {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127)); rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
} }
break; break;
} }
@ -210,7 +234,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
if (isOpMuted[ch]) { if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127); rWrite(baseAddr+0x40,127);
} else if (KVS(2,c.value)) { } else if (KVS(2,c.value)) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127)); rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
} else { } else {
rWrite(baseAddr+0x40,op.tl); rWrite(baseAddr+0x40,op.tl);
} }
@ -393,8 +417,8 @@ void DivPlatformGenesisExt::muteChannel(int ch, bool mute) {
rWrite(baseAddr+0x40,127); rWrite(baseAddr+0x40,127);
immWrite(baseAddr+0x40,127); immWrite(baseAddr+0x40,127);
} else if (KVS(2,ordch)) { } else if (KVS(2,ordch)) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].vol&0x7f,127)); rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].vol&0x7f,127)); immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
} else { } else {
rWrite(baseAddr+0x40,op.tl); rWrite(baseAddr+0x40,op.tl);
immWrite(baseAddr+0x40,op.tl); immWrite(baseAddr+0x40,op.tl);
@ -438,6 +462,91 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
} }
} }
if (extMode && !noExtMacros) for (int i=0; i<4; i++) {
opChan[i].std.next();
if (opChan[i].std.vol.had) {
opChan[i].outVol=VOL_SCALE_LOG_BROKEN(opChan[i].vol,MIN(127,opChan[i].std.vol.val),127);
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]];
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]];
if (isOpMuted[i]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[i].outVol&0x7f,127));
}
}
if (opChan[i].std.arp.had) {
if (!opChan[i].inPorta) {
opChan[i].baseFreq=NOTE_FNUM_BLOCK(parent->calcArp(opChan[i].note,opChan[i].std.arp.val),11);
}
opChan[i].freqChanged=true;
}
if (opChan[i].std.pitch.had) {
if (opChan[i].std.pitch.mode) {
opChan[i].pitch2+=opChan[i].std.pitch.val;
CLAMP_VAR(opChan[i].pitch2,-32768,32767);
} else {
opChan[i].pitch2=opChan[i].std.pitch.val;
}
opChan[i].freqChanged=true;
}
// param macros
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]];
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]];
DivMacroInt::IntOp& m=opChan[i].std.op[orderedOps[i]];
if (m.am.had) {
op.am=m.am.val;
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
}
if (m.ar.had) {
op.ar=m.ar.val;
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
}
if (m.dr.had) {
op.dr=m.dr.val;
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
}
if (m.mult.had) {
op.mult=m.mult.val;
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
}
if (m.rr.had) {
op.rr=m.rr.val;
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
if (m.sl.had) {
op.sl=m.sl.val;
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
}
if (m.tl.had) {
op.tl=127-m.tl.val;
if (isOpMuted[i]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[i].outVol&0x7f,127));
}
}
if (m.rs.had) {
op.rs=m.rs.val;
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
}
if (m.dt.had) {
op.dt=m.dt.val;
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
}
if (m.d2r.had) {
op.d2r=m.d2r.val;
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
}
if (m.ssg.had) {
op.ssgEnv=m.ssg.val;
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
}
DivPlatformGenesis::tick(sysTick); DivPlatformGenesis::tick(sysTick);
bool writeNoteOn=false; bool writeNoteOn=false;
@ -527,7 +636,7 @@ void DivPlatformGenesisExt::forceIns() {
if (isOpMuted[j]) { if (isOpMuted[j]) {
rWrite(baseAddr+0x40,127); rWrite(baseAddr+0x40,127);
} else if (KVS(i,j)) { } else if (KVS(i,j)) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].vol&0x7f,127)); rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
} else { } else {
rWrite(baseAddr+0x40,op.tl); rWrite(baseAddr+0x40,op.tl);
} }
@ -601,7 +710,9 @@ void DivPlatformGenesisExt::reset() {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
opChan[i]=DivPlatformGenesisExt::OpChannel(); opChan[i]=DivPlatformGenesisExt::OpChannel();
opChan[i].std.setEngine(parent);
opChan[i].vol=127; opChan[i].vol=127;
opChan[i].outVol=127;
} }
// channel 3 mode // channel 3 mode

View file

@ -25,11 +25,15 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
struct OpChannel { struct OpChannel {
DivMacroInt std; DivMacroInt std;
unsigned char freqH, freqL; unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins; int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins, note;
signed char konCycles; signed char konCycles;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, mask; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, mask;
int vol; int vol, outVol;
unsigned char pan; unsigned char pan;
void macroInit(DivInstrument* which) {
std.init(which);
pitch2=0;
}
OpChannel(): OpChannel():
freqH(0), freqH(0),
freqL(0), freqL(0),
@ -39,6 +43,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
pitch2(0), pitch2(0),
portaPauseFreq(0), portaPauseFreq(0),
ins(-1), ins(-1),
note(0),
active(false), active(false),
insChanged(true), insChanged(true),
freqChanged(false), freqChanged(false),
@ -48,6 +53,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
inPorta(false), inPorta(false),
mask(true), mask(true),
vol(0), vol(0),
outVol(0),
pan(3) {} pan(3) {}
}; };
OpChannel opChan[4]; OpChannel opChan[4];

View file

@ -587,7 +587,17 @@ bool DivPlatformPCSpeaker::keyOffAffectsArp(int ch) {
} }
void DivPlatformPCSpeaker::setFlags(const DivConfig& flags) { void DivPlatformPCSpeaker::setFlags(const DivConfig& flags) {
chipClock=COLOR_NTSC/3.0; switch (flags.getInt("clockSel",0)) {
case 1: // PC-98
chipClock=38400*52;
break;
case 2: // PC-98
chipClock=38400*64;
break;
default: // IBM PC
chipClock=COLOR_NTSC/3.0;
break;
}
rate=chipClock/PCSPKR_DIVIDER; rate=chipClock/PCSPKR_DIVIDER;
speakerType=flags.getInt("speakerType",0)&3; speakerType=flags.getInt("speakerType",0)&3;
oscBuf->rate=rate; oscBuf->rate=rate;

View file

@ -129,7 +129,7 @@ void DivPlatformSMS::tick(bool sysTick) {
if (chan[i].outVol<0) chan[i].outVol=0; if (chan[i].outVol<0) chan[i].outVol=0;
// old formula // old formula
// ((chan[i].vol&15)*MIN(15,chan[i].std.vol.val))>>4; // ((chan[i].vol&15)*MIN(15,chan[i].std.vol.val))>>4;
rWrite(0,0x90|(i<<5)|(isMuted[i]?15:(15-(chan[i].outVol&15)))); chan[i].writeVol=true;
} }
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
@ -235,6 +235,12 @@ void DivPlatformSMS::tick(bool sysTick) {
chan[3].freqChanged=false; chan[3].freqChanged=false;
updateSNMode=false; updateSNMode=false;
} }
for (int i=0; i<4; i++) {
if (chan[i].writeVol) {
rWrite(0,0x90|(i<<5)|(isMuted[i]?15:(15-(chan[i].outVol&15))));
chan[i].writeVol=false;
}
}
} }
int DivPlatformSMS::dispatch(DivCommand c) { int DivPlatformSMS::dispatch(DivCommand c) {
@ -249,9 +255,11 @@ int DivPlatformSMS::dispatch(DivCommand c) {
chan[c.chan].actualNote=c.value; chan[c.chan].actualNote=c.value;
} }
chan[c.chan].active=true; chan[c.chan].active=true;
if (!parent->song.brokenOutVol2) { //if (!parent->song.brokenOutVol2) {
rWrite(0,0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15)))); chan[c.chan].writeVol=true;
} chan[c.chan].outVol=chan[c.chan].vol;
//rWrite(0,0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15))));
//}
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol; chan[c.chan].outVol=chan[c.chan].vol;
@ -276,7 +284,9 @@ int DivPlatformSMS::dispatch(DivCommand c) {
if (!chan[c.chan].std.vol.has) { if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value; chan[c.chan].outVol=c.value;
} }
if (chan[c.chan].active) rWrite(0,0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15)))); if (chan[c.chan].active) {
chan[c.chan].writeVol=true;
}
} }
break; break;
case DIV_CMD_GET_VOLUME: case DIV_CMD_GET_VOLUME:
@ -356,7 +366,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
void DivPlatformSMS::muteChannel(int ch, bool mute) { void DivPlatformSMS::muteChannel(int ch, bool mute) {
isMuted[ch]=mute; isMuted[ch]=mute;
if (chan[ch].active) rWrite(0,0x90|ch<<5|(isMuted[ch]?15:(15-(chan[ch].outVol&15)))); if (chan[ch].active) chan[ch].writeVol=true;
} }
void DivPlatformSMS::forceIns() { void DivPlatformSMS::forceIns() {

View file

@ -31,7 +31,7 @@ extern "C" {
class DivPlatformSMS: public DivDispatch { class DivPlatformSMS: public DivDispatch {
struct Channel { struct Channel {
int freq, baseFreq, pitch, pitch2, note, actualNote, ins; int freq, baseFreq, pitch, pitch2, note, actualNote, ins;
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, writeVol;
signed char vol, outVol; signed char vol, outVol;
DivMacroInt std; DivMacroInt std;
void macroInit(DivInstrument* which) { void macroInit(DivInstrument* which) {
@ -52,6 +52,7 @@ class DivPlatformSMS: public DivDispatch {
keyOn(false), keyOn(false),
keyOff(false), keyOff(false),
inPorta(false), inPorta(false),
writeVol(false),
vol(15), vol(15),
outVol(15) {} outVol(15) {}
}; };

View file

@ -952,6 +952,7 @@ void DivPlatformYM2203::setFlags(const DivConfig& flags) {
ayDiv=16; ayDiv=16;
break; break;
} }
noExtMacros=flags.getBool("noExtMacros",false);
rate=fm->sample_rate(chipClock); rate=fm->sample_rate(chipClock);
for (int i=0; i<6; i++) { for (int i=0; i<6; i++) {
oscBuf[i]->rate=rate; oscBuf[i]->rate=rate;

View file

@ -91,7 +91,7 @@ class DivPlatformYM2203: public DivPlatformOPN {
DivPlatformAY8910* ay; DivPlatformAY8910* ay;
unsigned char sampleBank; unsigned char sampleBank;
bool extMode; bool extMode, noExtMacros;
unsigned char prescale; unsigned char prescale;
friend void putDispatchChip(void*,int); friend void putDispatchChip(void*,int);

View file

@ -1394,6 +1394,7 @@ void DivPlatformYM2608::setFlags(const DivConfig& flags) {
ayDiv=32; ayDiv=32;
break; break;
} }
noExtMacros=flags.getBool("noExtMacros",false);
rate=fm->sample_rate(chipClock); rate=fm->sample_rate(chipClock);
for (int i=0; i<16; i++) { for (int i=0; i<16; i++) {
oscBuf[i]->rate=rate; oscBuf[i]->rate=rate;

View file

@ -106,7 +106,7 @@ class DivPlatformYM2608: public DivPlatformOPN {
unsigned char writeRSSOff, writeRSSOn; unsigned char writeRSSOff, writeRSSOn;
int globalRSSVolume; int globalRSSVolume;
bool extMode; bool extMode, noExtMacros;
unsigned char prescale; unsigned char prescale;
double NOTE_OPNB(int ch, int note); double NOTE_OPNB(int ch, int note);

View file

@ -141,7 +141,7 @@ template<int ChanNum> class DivPlatformYM2610Base: public DivPlatformOPN {
unsigned char sampleBank; unsigned char sampleBank;
bool extMode; bool extMode, noExtMacros;
unsigned char writeADPCMAOff, writeADPCMAOn; unsigned char writeADPCMAOff, writeADPCMAOn;
int globalADPCMAVolume; int globalADPCMAVolume;
@ -269,6 +269,7 @@ template<int ChanNum> class DivPlatformYM2610Base: public DivPlatformOPN {
chipClock=8000000.0; chipClock=8000000.0;
break; break;
} }
noExtMacros=flags.getBool("noExtMacros",false);
rate=chipClock/16; rate=chipClock/16;
for (int i=0; i<ChanNum; i++) { for (int i=0; i<ChanNum; i++) {
oscBuf[i]->rate=rate; oscBuf[i]->rate=rate;

View file

@ -975,6 +975,20 @@ void DivEngine::nextRow() {
printf("| %.2x:%s | \x1b[1;33m%3d%s\x1b[m\n",curOrder,pb1,curRow,pb3); printf("| %.2x:%s | \x1b[1;33m%3d%s\x1b[m\n",curOrder,pb1,curRow,pb3);
} }
if (curSubSong->hilightA>0) {
if ((curRow%curSubSong->hilightA)==0) {
pendingMetroTick=1;
elapsedBeats++;
}
}
if (curSubSong->hilightB>0) {
if ((curRow%curSubSong->hilightB)==0) {
pendingMetroTick=2;
elapsedBars++;
elapsedBeats=0;
}
}
prevOrder=curOrder; prevOrder=curOrder;
prevRow=curRow; prevRow=curRow;
@ -1608,16 +1622,6 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
// 2. check whether we gonna tick // 2. check whether we gonna tick
if (cycles<=0) { if (cycles<=0) {
// we have to tick // we have to tick
if (!freelance && stepPlay!=-1 && subticks==1) {
unsigned int realPos=size-(runLeftG>>MASTER_CLOCK_PREC);
if (realPos>=size) realPos=size-1;
if (curSubSong->hilightA>0) {
if ((curRow%curSubSong->hilightA)==0 && ticks==1) metroTick[realPos]=1;
}
if (curSubSong->hilightB>0) {
if ((curRow%curSubSong->hilightB)==0 && ticks==1) metroTick[realPos]=2;
}
}
if (nextTick()) { if (nextTick()) {
lastLoopPos=size-(runLeftG>>MASTER_CLOCK_PREC); lastLoopPos=size-(runLeftG>>MASTER_CLOCK_PREC);
logD("last loop pos: %d for a size of %d and runLeftG of %d",lastLoopPos,size,runLeftG); logD("last loop pos: %d for a size of %d and runLeftG of %d",lastLoopPos,size,runLeftG);
@ -1634,6 +1638,12 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
} }
} }
} }
if (pendingMetroTick) {
unsigned int realPos=size-(runLeftG>>MASTER_CLOCK_PREC);
if (realPos>=size) realPos=size-1;
metroTick[realPos]=pendingMetroTick;
pendingMetroTick=0;
}
} else { } else {
// 3. tick the clock and fill buffers as needed // 3. tick the clock and fill buffers as needed
if (cycles<runLeftG) { if (cycles<runLeftG) {

View file

@ -23,6 +23,10 @@
#include "song.h" #include "song.h"
#include "../ta-log.h" #include "../ta-log.h"
DivSysDef* DivEngine::sysDefs[256];
DivSystem DivEngine::sysFileMapFur[256];
DivSystem DivEngine::sysFileMapDMF[256];
DivSystem DivEngine::systemFromFileFur(unsigned char val) { DivSystem DivEngine::systemFromFileFur(unsigned char val) {
return sysFileMapFur[val]; return sysFileMapFur[val];
} }

View file

@ -94,10 +94,12 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
if (VERA >= 0) disCont[VERA].dispatch->toggleRegisterDump(true); if (VERA >= 0) disCont[VERA].dispatch->toggleRegisterDump(true);
if (YM >= 0) { if (YM >= 0) {
disCont[YM].dispatch->toggleRegisterDump(true); disCont[YM].dispatch->toggleRegisterDump(true);
zsm.writeYM(0x18,0); // initialize the LFO freq to 0 // emit LFO initialization commands
// note - I think there's a bug where Furnace writes AMD/PMD=max zsm.writeYM(0x18,0); // freq = 0
// that shouldn't be there, requiring this initialization that shouldn't zsm.writeYM(0x19,0x7F); // AMD = 7F
// be there for ZSM. zsm.writeYM(0x19,0xFF); // PMD = 7F
// TODO: incorporate the Furnace meta-command for init data and filter
// out writes to otherwise-unused channels.
} }
while (!done) { while (!done) {

View file

@ -66,6 +66,7 @@ const char* aboutLine[]={
"BlueElectric05", "BlueElectric05",
"breakthetargets", "breakthetargets",
"brickblock369", "brickblock369",
"Burnt Fishy",
"CaptainMalware", "CaptainMalware",
"DeMOSic", "DeMOSic",
"DevEd", "DevEd",
@ -91,6 +92,7 @@ const char* aboutLine[]={
"Raijin", "Raijin",
"SnugglyBun", "SnugglyBun",
"SuperJet Spade", "SuperJet Spade",
"TakuikaNinja",
"TheDuccinator", "TheDuccinator",
"theloredev", "theloredev",
"TheRealHedgehogSonic", "TheRealHedgehogSonic",
@ -153,7 +155,6 @@ const char* aboutLine[]={
"MSM6295 emulator by cam900", "MSM6295 emulator by cam900",
"", "",
"greetings to:", "greetings to:",
"Fractal Sound team",
"NEOART Costa Rica", "NEOART Costa Rica",
"all members of Deflers of Noice!", "all members of Deflers of Noice!",
"", "",

116
src/gui/clock.cpp Normal file
View file

@ -0,0 +1,116 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "imgui_internal.h"
#include "imgui.h"
void FurnaceGUI::drawClock() {
if (nextWindow==GUI_WINDOW_CLOCK) {
clockOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!clockOpen) return;
if (ImGui::Begin("Clock",&clockOpen,globalWinFlags)) {
int row=e->getRow();
int elapsedBars=e->getElapsedBars();
int elapsedBeats=e->getElapsedBeats();
bool playing=e->isPlaying();
if (clockShowRow) {
ImGui::PushFont(bigFont);
ImGui::Text("%.3d:%.3d",e->getOrder(),row);
ImGui::PopFont();
}
if (clockShowBeat) {
ImGui::PushFont(bigFont);
ImGui::Text("%.3d:%.1d",elapsedBars,elapsedBeats+1);
ImGui::PopFont();
}
if (clockShowMetro) {
ImDrawList* dl=ImGui::GetWindowDrawList();
ImGuiWindow* window=ImGui::GetCurrentWindow();
ImVec2 size=ImGui::GetContentRegionAvail();
size.y=12.0f*dpiScale;
ImVec2 minArea=window->DC.CursorPos;
ImVec2 maxArea=ImVec2(
minArea.x+size.x,
minArea.y+size.y
);
ImRect rect=ImRect(minArea,maxArea);
/*ImRect inRect=rect;
inRect.Min.x+=dpiScale;
inRect.Min.y+=dpiScale;
inRect.Max.x-=dpiScale;
inRect.Max.y-=dpiScale;*/
ImGuiStyle& style=ImGui::GetStyle();
ImGui::ItemSize(size,style.FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID("metroQ"))) {
int h1=e->curSubSong->hilightA;
int h2=e->curSubSong->hilightB;
if (h1>0 && h2>0) {
int beats=(h2+(h1-1))/h1;
if (beats<0) beats=1;
if (beats>16) beats=16;
if (playing) {
if (elapsedBeats!=oldBeat || elapsedBars!=oldBar) {
if (elapsedBeats>15) elapsedBeats=15;
clockMetroTick[elapsedBeats]=1.0f;
oldBeat=elapsedBeats;
oldBar=elapsedBars;
}
} else {
oldBeat=-1;
oldBar=-1;
}
for (int i=0; i<beats; i++) {
ImVec2 minB=ImLerp(minArea,maxArea,ImVec2((float)i/(float)beats,0.0f));
ImVec2 maxB=ImLerp(minArea,maxArea,ImVec2((float)(i+1)/(float)beats,1.0f));
ImVec4 col=ImLerp(uiColors[GUI_COLOR_CLOCK_BEAT_LOW],uiColors[GUI_COLOR_CLOCK_BEAT_HIGH],clockMetroTick[i]);
dl->AddQuadFilled(
ImLerp(minB,maxB,ImVec2(0.35f,0.0f)),
ImLerp(minB,maxB,ImVec2(0.9f,0.0f)),
ImLerp(minB,maxB,ImVec2(0.65f,1.0f)),
ImLerp(minB,maxB,ImVec2(0.1f,1.0f)),
ImGui::GetColorU32(col)
);
if (elapsedBeats==i && playing) {
clockMetroTick[i]-=0.1f*ImGui::GetIO().DeltaTime*60.0f;
if (clockMetroTick[i]<0.3f) clockMetroTick[i]=0.3f;
} else {
clockMetroTick[i]-=0.1f*ImGui::GetIO().DeltaTime*60.0f;
if (clockMetroTick[i]<0.0f) clockMetroTick[i]=0.0f;
}
}
}
}
}
if (clockShowTime) {
int totalTicks=e->getTotalTicks();
int totalSeconds=e->getTotalSeconds();
ImGui::PushFont(bigFont);
ImGui::Text("%.2d:%.2d.%.2d",(totalSeconds/60),totalSeconds%60,totalTicks/10000);
ImGui::PopFont();
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SPOILER;
ImGui::End();
}

View file

@ -60,6 +60,11 @@ void FurnaceGUI::drawInsList(bool asChild) {
} }
ImGui::EndPopup(); ImGui::EndPopup();
} }
} else {
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
displayInsTypeList=true;
displayInsTypeListMakeInsSample=-1;
}
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(ICON_FA_FILES_O "##InsClone")) { if (ImGui::Button(ICON_FA_FILES_O "##InsClone")) {

View file

@ -1291,14 +1291,54 @@ void FurnaceGUI::doAction(int what) {
break; break;
case GUI_ACTION_SAMPLE_MAKE_INS: { case GUI_ACTION_SAMPLE_MAKE_INS: {
if (curSample<0 || curSample>=(int)e->song.sample.size()) break; if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
// determine instrument type
std::vector<DivInstrumentType> tempTypeList=e->getPossibleInsTypes();
makeInsTypeList.clear();
for (DivInstrumentType& i: tempTypeList) {
if (i==DIV_INS_PCE ||
i==DIV_INS_MSM6258 ||
i==DIV_INS_MSM6295 ||
i==DIV_INS_ADPCMA ||
i==DIV_INS_ADPCMB ||
i==DIV_INS_SEGAPCM ||
i==DIV_INS_QSOUND ||
i==DIV_INS_YMZ280B ||
i==DIV_INS_RF5C68 ||
i==DIV_INS_MULTIPCM ||
i==DIV_INS_MIKEY ||
i==DIV_INS_X1_010 ||
i==DIV_INS_SWAN ||
i==DIV_INS_AY ||
i==DIV_INS_AY8930 ||
i==DIV_INS_VRC6 ||
i==DIV_INS_SU ||
i==DIV_INS_SNES ||
i==DIV_INS_ES5506) {
makeInsTypeList.push_back(i);
}
}
if (makeInsTypeList.size()>1) {
displayInsTypeList=true;
displayInsTypeListMakeInsSample=curSample;
break;
}
DivInstrumentType insType=DIV_INS_AMIGA;
if (!makeInsTypeList.empty()) {
insType=makeInsTypeList[0];
}
DivSample* sample=e->song.sample[curSample]; DivSample* sample=e->song.sample[curSample];
curIns=e->addInstrument(cursor.xCoarse); curIns=e->addInstrument(cursor.xCoarse);
if (curIns==-1) { if (curIns==-1) {
showError("too many instruments!"); showError("too many instruments!");
} else { } else {
e->song.ins[curIns]->type=DIV_INS_AMIGA; e->song.ins[curIns]->type=insType;
e->song.ins[curIns]->name=sample->name; e->song.ins[curIns]->name=sample->name;
e->song.ins[curIns]->amiga.initSample=curSample; e->song.ins[curIns]->amiga.initSample=curSample;
if (insType!=DIV_INS_AMIGA) e->song.ins[curIns]->amiga.useSample=true;
nextWindow=GUI_WINDOW_INS_EDIT; nextWindow=GUI_WINDOW_INS_EDIT;
MARK_MODIFIED; MARK_MODIFIED;
wavePreviewInit=true; wavePreviewInit=true;

View file

@ -242,6 +242,7 @@ void FurnaceGUI::drawMobileControls() {
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Settings")) { if (ImGui::Button("Settings")) {
mobileMenuOpen=false; mobileMenuOpen=false;
settingsOpen=true;
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Log")) { if (ImGui::Button("Log")) {

View file

@ -2651,8 +2651,11 @@ int _processEvent(void* instance, SDL_Event* event) {
int FurnaceGUI::processEvent(SDL_Event* ev) { int FurnaceGUI::processEvent(SDL_Event* ev) {
#ifdef IS_MOBILE #ifdef IS_MOBILE
if (ev->type==SDL_APP_WILLENTERBACKGROUND) { if (ev->type==SDL_APP_TERMINATING) {
// TODO: save "last state" and potentially suspend engine // TODO: save last song state here
} else if (ev->type==SDL_APP_WILLENTERBACKGROUND) {
commitState();
e->saveConf();
} }
#endif #endif
if (ev->type==SDL_KEYDOWN) { if (ev->type==SDL_KEYDOWN) {
@ -2958,7 +2961,11 @@ bool FurnaceGUI::detectOutOfBoundsWindow() {
} }
bool FurnaceGUI::loop() { bool FurnaceGUI::loop() {
#ifdef IS_MOBILE
bool doThreadedInput=true;
#else
bool doThreadedInput=!settings.noThreadedInput; bool doThreadedInput=!settings.noThreadedInput;
#endif
if (doThreadedInput) { if (doThreadedInput) {
logD("key input: event filter"); logD("key input: event filter");
SDL_SetEventFilter(_processEvent,this); SDL_SetEventFilter(_processEvent,this);
@ -3620,6 +3627,7 @@ bool FurnaceGUI::loop() {
if (ImGui::MenuItem("oscilloscope (master)",BIND_FOR(GUI_ACTION_WINDOW_OSCILLOSCOPE),oscOpen)) oscOpen=!oscOpen; if (ImGui::MenuItem("oscilloscope (master)",BIND_FOR(GUI_ACTION_WINDOW_OSCILLOSCOPE),oscOpen)) oscOpen=!oscOpen;
if (ImGui::MenuItem("oscilloscope (per-channel)",BIND_FOR(GUI_ACTION_WINDOW_CHAN_OSC),chanOscOpen)) chanOscOpen=!chanOscOpen; if (ImGui::MenuItem("oscilloscope (per-channel)",BIND_FOR(GUI_ACTION_WINDOW_CHAN_OSC),chanOscOpen)) chanOscOpen=!chanOscOpen;
if (ImGui::MenuItem("volume meter",BIND_FOR(GUI_ACTION_WINDOW_VOL_METER),volMeterOpen)) volMeterOpen=!volMeterOpen; if (ImGui::MenuItem("volume meter",BIND_FOR(GUI_ACTION_WINDOW_VOL_METER),volMeterOpen)) volMeterOpen=!volMeterOpen;
if (ImGui::MenuItem("clock",BIND_FOR(GUI_ACTION_WINDOW_CLOCK),clockOpen)) clockOpen=!clockOpen;
if (ImGui::MenuItem("register view",BIND_FOR(GUI_ACTION_WINDOW_REGISTER_VIEW),regViewOpen)) regViewOpen=!regViewOpen; if (ImGui::MenuItem("register view",BIND_FOR(GUI_ACTION_WINDOW_REGISTER_VIEW),regViewOpen)) regViewOpen=!regViewOpen;
if (ImGui::MenuItem("log viewer",BIND_FOR(GUI_ACTION_WINDOW_LOG),logOpen)) logOpen=!logOpen; if (ImGui::MenuItem("log viewer",BIND_FOR(GUI_ACTION_WINDOW_LOG),logOpen)) logOpen=!logOpen;
if (ImGui::MenuItem("statistics",BIND_FOR(GUI_ACTION_WINDOW_STATS),statsOpen)) statsOpen=!statsOpen; if (ImGui::MenuItem("statistics",BIND_FOR(GUI_ACTION_WINDOW_STATS),statsOpen)) statsOpen=!statsOpen;
@ -3747,6 +3755,7 @@ bool FurnaceGUI::loop() {
} }
globalWinFlags=0; globalWinFlags=0;
drawSettings();
drawDebug(); drawDebug();
drawLog(); drawLog();
} else { } else {
@ -3782,6 +3791,7 @@ bool FurnaceGUI::loop() {
drawChannels(); drawChannels();
drawPatManager(); drawPatManager();
drawSysManager(); drawSysManager();
drawClock();
drawRegView(); drawRegView();
drawLog(); drawLog();
drawEffectList(); drawEffectList();
@ -4399,6 +4409,11 @@ bool FurnaceGUI::loop() {
ImGui::OpenPopup("Import Raw Sample"); ImGui::OpenPopup("Import Raw Sample");
} }
if (displayInsTypeList) {
displayInsTypeList=false;
ImGui::OpenPopup("InsTypeList");
}
if (displayExporting) { if (displayExporting) {
displayExporting=false; displayExporting=false;
ImGui::OpenPopup("Rendering..."); ImGui::OpenPopup("Rendering...");
@ -4428,9 +4443,14 @@ bool FurnaceGUI::loop() {
ImGui::EndPopup(); ImGui::EndPopup();
} }
ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(canvasW,canvasH)); 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));
ImGui::SetNextWindowSizeConstraints(newSongMinSize,newSongMaxSize);
if (ImGui::BeginPopupModal("New Song",NULL,ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) { if (ImGui::BeginPopupModal("New Song",NULL,ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) {
ImGui::SetWindowPos(ImVec2(((canvasW)-ImGui::GetWindowSize().x)*0.5,((canvasH)-ImGui::GetWindowSize().y)*0.5)); ImGui::SetWindowPos(ImVec2(((canvasW)-ImGui::GetWindowSize().x)*0.5,((canvasH)-ImGui::GetWindowSize().y)*0.5));
if (ImGui::GetWindowSize().x<newSongMinSize.x || ImGui::GetWindowSize().y<newSongMinSize.y) {
ImGui::SetWindowSize(newSongMinSize,ImGuiCond_Always);
}
drawNewSong(); drawNewSong();
ImGui::EndPopup(); ImGui::EndPopup();
} }
@ -4774,6 +4794,31 @@ bool FurnaceGUI::loop() {
ImGui::EndPopup(); ImGui::EndPopup();
} }
if (ImGui::BeginPopup("InsTypeList",ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) {
char temp[1024];
for (DivInstrumentType& i: makeInsTypeList) {
strncpy(temp,insTypes[i],1023);
if (ImGui::MenuItem(temp)) {
// create ins
curIns=e->addInstrument(-1,i);
if (curIns==-1) {
showError("too many instruments!");
} else {
if (displayInsTypeListMakeInsSample>=0 && displayInsTypeListMakeInsSample<(int)e->song.sample.size()) {
e->song.ins[curIns]->type=i;
e->song.ins[curIns]->name=e->song.sample[displayInsTypeListMakeInsSample]->name;
e->song.ins[curIns]->amiga.initSample=displayInsTypeListMakeInsSample;
if (i!=DIV_INS_AMIGA) e->song.ins[curIns]->amiga.useSample=true;
nextWindow=GUI_WINDOW_INS_EDIT;
wavePreviewInit=true;
}
MARK_MODIFIED;
}
}
}
ImGui::EndPopup();
}
// TODO: // TODO:
// - multiple selection // - multiple selection
// - replace instrument // - replace instrument
@ -5020,6 +5065,7 @@ bool FurnaceGUI::init() {
channelsOpen=e->getConfBool("channelsOpen",false); channelsOpen=e->getConfBool("channelsOpen",false);
patManagerOpen=e->getConfBool("patManagerOpen",false); patManagerOpen=e->getConfBool("patManagerOpen",false);
sysManagerOpen=e->getConfBool("sysManagerOpen",false); sysManagerOpen=e->getConfBool("sysManagerOpen",false);
clockOpen=e->getConfBool("clockOpen",false);
regViewOpen=e->getConfBool("regViewOpen",false); regViewOpen=e->getConfBool("regViewOpen",false);
logOpen=e->getConfBool("logOpen",false); logOpen=e->getConfBool("logOpen",false);
effectListOpen=e->getConfBool("effectListOpen",false); effectListOpen=e->getConfBool("effectListOpen",false);
@ -5333,15 +5379,10 @@ bool FurnaceGUI::init() {
return true; return true;
} }
bool FurnaceGUI::finish() { void FurnaceGUI::commitState() {
if (!mobileUI) { if (!mobileUI) {
ImGui::SaveIniSettingsToDisk(finalLayoutPath); ImGui::SaveIniSettingsToDisk(finalLayoutPath);
} }
ImGui_ImplSDLRenderer_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
SDL_DestroyRenderer(sdlRend);
SDL_DestroyWindow(sdlWin);
e->setConf("configVersion",(int)DIV_ENGINE_VERSION); e->setConf("configVersion",(int)DIV_ENGINE_VERSION);
@ -5383,6 +5424,7 @@ bool FurnaceGUI::finish() {
e->setConf("channelsOpen",channelsOpen); e->setConf("channelsOpen",channelsOpen);
e->setConf("patManagerOpen",patManagerOpen); e->setConf("patManagerOpen",patManagerOpen);
e->setConf("sysManagerOpen",sysManagerOpen); e->setConf("sysManagerOpen",sysManagerOpen);
e->setConf("clockOpen",clockOpen);
e->setConf("regViewOpen",regViewOpen); e->setConf("regViewOpen",regViewOpen);
e->setConf("logOpen",logOpen); e->setConf("logOpen",logOpen);
e->setConf("effectListOpen",effectListOpen); e->setConf("effectListOpen",effectListOpen);
@ -5454,6 +5496,15 @@ bool FurnaceGUI::finish() {
e->setConf(key,recentFile[i]); e->setConf(key,recentFile[i]);
} }
} }
}
bool FurnaceGUI::finish() {
commitState();
ImGui_ImplSDLRenderer_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
SDL_DestroyRenderer(sdlRend);
SDL_DestroyWindow(sdlWin);
for (int i=0; i<DIV_MAX_CHANS; i++) { for (int i=0; i<DIV_MAX_CHANS; i++) {
delete oldPat[i]; delete oldPat[i];
@ -5485,6 +5536,7 @@ FurnaceGUI::FurnaceGUI():
zsmExportLoop(true), zsmExportLoop(true),
vgmExportPatternHints(false), vgmExportPatternHints(false),
vgmExportDirectStream(false), vgmExportDirectStream(false),
displayInsTypeList(false),
portrait(false), portrait(false),
injectBackUp(false), injectBackUp(false),
mobileMenuOpen(false), mobileMenuOpen(false),
@ -5505,6 +5557,7 @@ FurnaceGUI::FurnaceGUI():
zsmExportTickRate(60), zsmExportTickRate(60),
macroPointSize(16), macroPointSize(16),
waveEditStyle(0), waveEditStyle(0),
displayInsTypeListMakeInsSample(-1),
mobileMenuPos(0.0f), mobileMenuPos(0.0f),
autoButtonSize(0.0f), autoButtonSize(0.0f),
curSysSection(NULL), curSysSection(NULL),
@ -5571,6 +5624,8 @@ FurnaceGUI::FurnaceGUI():
dragSourceY(0), dragSourceY(0),
dragDestinationX(0), dragDestinationX(0),
dragDestinationY(0), dragDestinationY(0),
oldBeat(-1),
oldBar(-1),
exportFadeOut(5.0), exportFadeOut(5.0),
editControlsOpen(true), editControlsOpen(true),
ordersOpen(true), ordersOpen(true),
@ -5603,6 +5658,12 @@ FurnaceGUI::FurnaceGUI():
spoilerOpen(false), spoilerOpen(false),
patManagerOpen(false), patManagerOpen(false),
sysManagerOpen(false), sysManagerOpen(false),
clockOpen(false),
clockShowReal(true),
clockShowRow(true),
clockShowBeat(true),
clockShowMetro(true),
clockShowTime(true),
selecting(false), selecting(false),
selectingFull(false), selectingFull(false),
dragging(false), dragging(false),
@ -5631,7 +5692,6 @@ FurnaceGUI::FurnaceGUI():
curWindowLast(GUI_WINDOW_NOTHING), curWindowLast(GUI_WINDOW_NOTHING),
curWindowThreadSafe(GUI_WINDOW_NOTHING), curWindowThreadSafe(GUI_WINDOW_NOTHING),
lastPatternWidth(0.0f), lastPatternWidth(0.0f),
nextDesc(NULL),
latchNote(-1), latchNote(-1),
latchIns(-2), latchIns(-2),
latchVol(-1), latchVol(-1),

View file

@ -243,6 +243,10 @@ enum FurnaceGUIColors {
GUI_COLOR_PIANO_KEY_BOTTOM_ACTIVE, GUI_COLOR_PIANO_KEY_BOTTOM_ACTIVE,
GUI_COLOR_PIANO_KEY_TOP_ACTIVE, GUI_COLOR_PIANO_KEY_TOP_ACTIVE,
GUI_COLOR_CLOCK_TEXT,
GUI_COLOR_CLOCK_BEAT_LOW,
GUI_COLOR_CLOCK_BEAT_HIGH,
GUI_COLOR_LOGLEVEL_ERROR, GUI_COLOR_LOGLEVEL_ERROR,
GUI_COLOR_LOGLEVEL_WARNING, GUI_COLOR_LOGLEVEL_WARNING,
GUI_COLOR_LOGLEVEL_INFO, GUI_COLOR_LOGLEVEL_INFO,
@ -285,6 +289,7 @@ enum FurnaceGUIWindows {
GUI_WINDOW_CHAN_OSC, GUI_WINDOW_CHAN_OSC,
GUI_WINDOW_SUBSONGS, GUI_WINDOW_SUBSONGS,
GUI_WINDOW_FIND, GUI_WINDOW_FIND,
GUI_WINDOW_CLOCK,
GUI_WINDOW_SPOILER GUI_WINDOW_SPOILER
}; };
@ -425,6 +430,7 @@ enum FurnaceGUIActions {
GUI_ACTION_WINDOW_CHAN_OSC, GUI_ACTION_WINDOW_CHAN_OSC,
GUI_ACTION_WINDOW_SUBSONGS, GUI_ACTION_WINDOW_SUBSONGS,
GUI_ACTION_WINDOW_FIND, GUI_ACTION_WINDOW_FIND,
GUI_ACTION_WINDOW_CLOCK,
GUI_ACTION_COLLAPSE_WINDOW, GUI_ACTION_COLLAPSE_WINDOW,
GUI_ACTION_CLOSE_WINDOW, GUI_ACTION_CLOSE_WINDOW,
@ -893,12 +899,22 @@ struct Gradient2D {
} }
}; };
struct FurnaceGUISysDefChip {
DivSystem sys;
int vol, pan;
const char* flags;
FurnaceGUISysDefChip(DivSystem s, int v, int p, const char* f):
sys(s),
vol(v),
pan(p),
flags(f) {}
};
struct FurnaceGUISysDef { struct FurnaceGUISysDef {
const char* name; const char* name;
std::vector<int> definition; String definition;
FurnaceGUISysDef(const char* n, std::initializer_list<int> def): FurnaceGUISysDef(const char* n, std::initializer_list<int> def);
name(n), definition(def) { FurnaceGUISysDef(const char* n, std::initializer_list<FurnaceGUISysDefChip> def);
}
}; };
struct FurnaceGUISysCategory { struct FurnaceGUISysCategory {
@ -945,6 +961,12 @@ struct FurnaceGUIMacroDesc {
} }
}; };
struct FurnaceGUIMacroEditState {
int selectedMacro;
FurnaceGUIMacroEditState():
selectedMacro(0) {}
};
enum FurnaceGUIFindQueryModes { enum FurnaceGUIFindQueryModes {
GUI_QUERY_IGNORE=0, GUI_QUERY_IGNORE=0,
GUI_QUERY_MATCH, GUI_QUERY_MATCH,
@ -1033,9 +1055,11 @@ class FurnaceGUI {
std::vector<DivSystem> sysSearchResults; std::vector<DivSystem> sysSearchResults;
std::vector<FurnaceGUISysDef> newSongSearchResults; std::vector<FurnaceGUISysDef> newSongSearchResults;
std::deque<String> recentFile; std::deque<String> recentFile;
std::vector<DivInstrumentType> makeInsTypeList;
bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, vgmExportPatternHints; bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, vgmExportPatternHints;
bool vgmExportDirectStream; bool vgmExportDirectStream, displayInsTypeList;
bool portrait, injectBackUp, mobileMenuOpen; bool portrait, injectBackUp, mobileMenuOpen;
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu; bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly; bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
@ -1046,6 +1070,7 @@ class FurnaceGUI {
int zsmExportTickRate; int zsmExportTickRate;
int macroPointSize; int macroPointSize;
int waveEditStyle; int waveEditStyle;
int displayInsTypeListMakeInsSample;
float mobileMenuPos, autoButtonSize; float mobileMenuPos, autoButtonSize;
const int* curSysSection; const int* curSysSection;
@ -1216,6 +1241,7 @@ class FurnaceGUI {
int persistFadeOut; int persistFadeOut;
int exportLoops; int exportLoops;
double exportFadeOut; double exportFadeOut;
int macroLayout;
unsigned int maxUndoSteps; unsigned int maxUndoSteps;
String mainFontPath; String mainFontPath;
String patFontPath; String patFontPath;
@ -1346,6 +1372,7 @@ class FurnaceGUI {
persistFadeOut(1), persistFadeOut(1),
exportLoops(0), exportLoops(0),
exportFadeOut(0.0), exportFadeOut(0.0),
macroLayout(0),
maxUndoSteps(100), maxUndoSteps(100),
mainFontPath(""), mainFontPath(""),
patFontPath(""), patFontPath(""),
@ -1367,7 +1394,7 @@ class FurnaceGUI {
int curIns, curWave, curSample, curOctave, curOrder, prevIns, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan, soloTimeout, orderEditMode, orderCursor; int curIns, curWave, curSample, curOctave, curOrder, prevIns, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan, soloTimeout, orderEditMode, orderCursor;
int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory, latchTarget; int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory, latchTarget;
int wheelX, wheelY, dragSourceX, dragSourceY, dragDestinationX, dragDestinationY; int wheelX, wheelY, dragSourceX, dragSourceY, dragDestinationX, dragDestinationY, oldBeat, oldBar;
double exportFadeOut; double exportFadeOut;
@ -1375,7 +1402,10 @@ class FurnaceGUI {
bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen; bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen; bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen;
bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen; bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen, clockOpen;
bool clockShowReal, clockShowRow, clockShowBeat, clockShowMetro, clockShowTime;
float clockMetroTick[16];
SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd; SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd;
bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI; bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI;
@ -1387,7 +1417,7 @@ class FurnaceGUI {
float patChanX[DIV_MAX_CHANS+1]; float patChanX[DIV_MAX_CHANS+1];
float patChanSlideY[DIV_MAX_CHANS+1]; float patChanSlideY[DIV_MAX_CHANS+1];
float lastPatternWidth; float lastPatternWidth;
const int* nextDesc; String nextDesc;
String nextDescName; String nextDescName;
OperationMask opMaskDelete, opMaskPullDelete, opMaskInsert, opMaskPaste, opMaskTransposeNote, opMaskTransposeValue; OperationMask opMaskDelete, opMaskPullDelete, opMaskInsert, opMaskPaste, opMaskTransposeNote, opMaskTransposeValue;
@ -1511,6 +1541,8 @@ class FurnaceGUI {
int macroLoopDragLen; int macroLoopDragLen;
bool macroLoopDragActive; bool macroLoopDragActive;
FurnaceGUIMacroEditState macroEditStateFM, macroEditStateOP[4], macroEditStateMacros;
ImVec2 waveDragStart; ImVec2 waveDragStart;
ImVec2 waveDragAreaSize; ImVec2 waveDragAreaSize;
int* waveDragTarget; int* waveDragTarget;
@ -1675,7 +1707,8 @@ class FurnaceGUI {
void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel); void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel);
void drawMacros(std::vector<FurnaceGUIMacroDesc>& macros); void drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float availableWidth, int index);
void drawMacros(std::vector<FurnaceGUIMacroDesc>& macros, FurnaceGUIMacroEditState& state);
void actualWaveList(); void actualWaveList();
void actualSampleList(); void actualSampleList();
@ -1717,6 +1750,7 @@ class FurnaceGUI {
void drawSubSongs(); void drawSubSongs();
void drawFindReplace(); void drawFindReplace();
void drawSpoiler(); void drawSpoiler();
void drawClock();
void parseKeybinds(); void parseKeybinds();
void promptKey(int which); void promptKey(int which);
@ -1736,6 +1770,7 @@ class FurnaceGUI {
void syncSettings(); void syncSettings();
void commitSettings(); void commitSettings();
void commitState();
void processDrags(int dragX, int dragY); void processDrags(int dragX, int dragY);
void processPoint(SDL_Event& ev); void processPoint(SDL_Event& ev);

View file

@ -513,6 +513,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0), D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0),
D("WINDOW_SUBSONGS", "Subsongs", 0), D("WINDOW_SUBSONGS", "Subsongs", 0),
D("WINDOW_FIND", "Find/Replace", FURKMOD_CMD|SDLK_f), D("WINDOW_FIND", "Find/Replace", FURKMOD_CMD|SDLK_f),
D("WINDOW_CLOCK", "Clock", 0),
D("COLLAPSE_WINDOW", "Collapse/expand current window", 0), D("COLLAPSE_WINDOW", "Collapse/expand current window", 0),
D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE), D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE),
@ -874,6 +875,10 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_PIANO_KEY_BOTTOM_ACTIVE,"",ImVec4(0.5f,0.5f,0.5f,1.0f)), D(GUI_COLOR_PIANO_KEY_BOTTOM_ACTIVE,"",ImVec4(0.5f,0.5f,0.5f,1.0f)),
D(GUI_COLOR_PIANO_KEY_TOP_ACTIVE,"",ImVec4(0.4f,0.4f,0.4f,1.0f)), D(GUI_COLOR_PIANO_KEY_TOP_ACTIVE,"",ImVec4(0.4f,0.4f,0.4f,1.0f)),
D(GUI_COLOR_CLOCK_TEXT,"",ImVec4(1.0f,1.0f,1.0f,1.0f)),
D(GUI_COLOR_CLOCK_BEAT_LOW,"",ImVec4(0.1f,0.13f,0.25f,1.0f)),
D(GUI_COLOR_CLOCK_BEAT_HIGH,"",ImVec4(0.5f,0.8f,1.0f,1.0f)),
D(GUI_COLOR_LOGLEVEL_ERROR,"",ImVec4(1.0f,0.2f,0.2f,1.0f)), D(GUI_COLOR_LOGLEVEL_ERROR,"",ImVec4(1.0f,0.2f,0.2f,1.0f)),
D(GUI_COLOR_LOGLEVEL_WARNING,"",ImVec4(1.0f,1.0f,0.2f,1.0f)), D(GUI_COLOR_LOGLEVEL_WARNING,"",ImVec4(1.0f,1.0f,0.2f,1.0f)),
D(GUI_COLOR_LOGLEVEL_INFO,"",ImVec4(0.4f,1.0f,0.4f,1.0f)), D(GUI_COLOR_LOGLEVEL_INFO,"",ImVec4(0.4f,1.0f,0.4f,1.0f)),

File diff suppressed because it is too large Load diff

View file

@ -107,7 +107,7 @@ void FurnaceGUI::drawNewSong() {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ImGui::Selectable(i.name,false,ImGuiSelectableFlags_DontClosePopups)) { if (ImGui::Selectable(i.name,false,ImGuiSelectableFlags_DontClosePopups)) {
nextDesc=i.definition.data(); nextDesc=i.definition;
nextDescName=i.name; nextDescName=i.name;
accepted=true; accepted=true;
} }
@ -129,7 +129,7 @@ void FurnaceGUI::drawNewSong() {
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} else { } else {
unsigned int selection=rand()%newSystemCat->systems.size(); unsigned int selection=rand()%newSystemCat->systems.size();
nextDesc=newSystemCat->systems[selection].definition.data(); nextDesc=newSystemCat->systems[selection].definition;
nextDescName=newSystemCat->systems[selection].name; nextDescName=newSystemCat->systems[selection].name;
accepted=true; accepted=true;
} }
@ -143,16 +143,7 @@ void FurnaceGUI::drawNewSong() {
} }
if (accepted) { if (accepted) {
// TODO: remove after porting all presets to new format e->createNew(nextDesc.c_str(),nextDescName,false);
String oldDescFormat;
for (const int* i=nextDesc; *i; i+=4) {
oldDescFormat+=fmt::sprintf("%d ",e->systemToFileFur((DivSystem)i[0]));
oldDescFormat+=fmt::sprintf("%d ",i[1]);
oldDescFormat+=fmt::sprintf("%d ",i[2]);
oldDescFormat+=fmt::sprintf("%d ",i[3]);
}
String oldDesc=e->decodeSysDesc(oldDescFormat.c_str());
e->createNew(oldDesc.c_str(),nextDescName);
undoHist.clear(); undoHist.clear();
redoHist.clear(); redoHist.clear();
curFileName=""; curFileName="";

View file

@ -219,11 +219,14 @@ void FurnaceGUI::drawPiano() {
// render piano // render piano
//ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); //ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID("pianoDisplay"))) { if (ImGui::ItemAdd(rect,ImGui::GetID("pianoDisplay"))) {
ImGui::ItemHoverable(rect,ImGui::GetID("pianoDisplay")); bool canInput=false;
if (ImGui::ItemHoverable(rect,ImGui::GetID("pianoDisplay"))) {
canInput=true;
}
if (view) { if (view) {
int notes=oct*12; int notes=oct*12;
// evaluate input // evaluate input
for (TouchPoint& i: activePoints) { if (canInput) for (TouchPoint& i: activePoints) {
if (rect.Contains(ImVec2(i.x,i.y))) { if (rect.Contains(ImVec2(i.x,i.y))) {
int note=(((i.x-rect.Min.x)/(rect.Max.x-rect.Min.x))*notes)+12*off; int note=(((i.x-rect.Min.x)/(rect.Max.x-rect.Min.x))*notes)+12*off;
if (note<0) continue; if (note<0) continue;

File diff suppressed because it is too large Load diff

View file

@ -197,10 +197,12 @@ double getScaleFactor(const char* driverHint) {
// SDL fallback // SDL fallback
#ifdef ANDROID #ifdef ANDROID
float dpiScaleF=192.0f; float dpiScaleF=160.0f;
if (SDL_GetDisplayDPI(0,&dpiScaleF,NULL,NULL)==0) { if (SDL_GetDisplayDPI(0,&dpiScaleF,NULL,NULL)==0) {
ret=round(dpiScaleF/192.0f); ret=dpiScaleF/160.0f;
if (ret<1) ret=1; if (ret<1) ret=1;
logI("dpiScaleF: %f",dpiScaleF);
logI("ret: %f",ret);
} }
#else #else

View file

@ -33,14 +33,14 @@
#define DEFAULT_NOTE_KEYS "5:7;6:4;7:3;8:16;10:6;11:8;12:24;13:10;16:11;17:9;18:26;19:28;20:12;21:17;22:1;23:19;24:23;25:5;26:14;27:2;28:21;29:0;30:100;31:13;32:15;34:18;35:20;36:22;38:25;39:27;43:100;46:101;47:29;48:31;53:102;" #define DEFAULT_NOTE_KEYS "5:7;6:4;7:3;8:16;10:6;11:8;12:24;13:10;16:11;17:9;18:26;19:28;20:12;21:17;22:1;23:19;24:23;25:5;26:14;27:2;28:21;29:0;30:100;31:13;32:15;34:18;35:20;36:22;38:25;39:27;43:100;46:101;47:29;48:31;53:102;"
#if defined(_WIN32) || defined(__APPLE__) #if defined(_WIN32) || defined(__APPLE__) || defined(IS_MOBILE)
#define POWER_SAVE_DEFAULT 1 #define POWER_SAVE_DEFAULT 1
#else #else
// currently off on Linux/other due to Mesa catch-up behavior. // currently off on Linux/other due to Mesa catch-up behavior.
#define POWER_SAVE_DEFAULT 0 #define POWER_SAVE_DEFAULT 0
#endif #endif
#if defined(__HAIKU__) #if defined(__HAIKU__) || defined(IS_MOBILE)
// NFD doesn't support Haiku // NFD doesn't support Haiku
#define SYS_FILE_DIALOG_DEFAULT 0 #define SYS_FILE_DIALOG_DEFAULT 0
#else #else
@ -239,6 +239,12 @@ void FurnaceGUI::drawSettings() {
nextWindow=GUI_WINDOW_NOTHING; nextWindow=GUI_WINDOW_NOTHING;
} }
if (!settingsOpen) return; if (!settingsOpen) return;
if (mobileUI) {
ImVec2 setWindowPos=ImVec2(0,0);
ImVec2 setWindowSize=ImVec2(canvasW,canvasH);
ImGui::SetNextWindowPos(setWindowPos);
ImGui::SetNextWindowSize(setWindowSize);
}
if (ImGui::Begin("Settings",&settingsOpen,ImGuiWindowFlags_NoDocking|globalWinFlags)) { if (ImGui::Begin("Settings",&settingsOpen,ImGuiWindowFlags_NoDocking|globalWinFlags)) {
if (!settingsOpen) { if (!settingsOpen) {
settingsOpen=true; settingsOpen=true;
@ -332,7 +338,7 @@ void FurnaceGUI::drawSettings() {
settings.initialSys.set("pan0",0); settings.initialSys.set("pan0",0);
settings.initialSys.set("flags0",""); settings.initialSys.set("flags0","");
settings.initialSys.set("id1",e->systemToFileFur(DIV_SYSTEM_SMS)); settings.initialSys.set("id1",e->systemToFileFur(DIV_SYSTEM_SMS));
settings.initialSys.set("vol1",64); settings.initialSys.set("vol1",32);
settings.initialSys.set("pan1",0); settings.initialSys.set("pan1",0);
settings.initialSys.set("flags1",""); settings.initialSys.set("flags1","");
settings.initialSysName="Sega Genesis/Mega Drive"; settings.initialSysName="Sega Genesis/Mega Drive";
@ -343,11 +349,15 @@ void FurnaceGUI::drawSettings() {
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::InputText("##InitSysName",&settings.initialSysName); ImGui::InputText("##InitSysName",&settings.initialSysName);
int sysCount=0;
int doRemove=-1;
for (size_t i=0; settings.initialSys.getInt(fmt::sprintf("id%d",i),0); i++) { for (size_t i=0; settings.initialSys.getInt(fmt::sprintf("id%d",i),0); i++) {
DivSystem sysID=e->systemFromFileFur(settings.initialSys.getInt(fmt::sprintf("id%d",i),0)); DivSystem sysID=e->systemFromFileFur(settings.initialSys.getInt(fmt::sprintf("id%d",i),0));
signed char sysVol=settings.initialSys.getInt(fmt::sprintf("vol%d",i),0); signed char sysVol=settings.initialSys.getInt(fmt::sprintf("vol%d",i),0);
signed char sysPan=settings.initialSys.getInt(fmt::sprintf("pan%d",i),0); signed char sysPan=settings.initialSys.getInt(fmt::sprintf("pan%d",i),0);
sysCount=i+1;
//bool doRemove=false; //bool doRemove=false;
bool doInvert=sysVol&128; bool doInvert=sysVol&128;
signed char vol=sysVol&127; signed char vol=sysVol&127;
@ -373,7 +383,7 @@ void FurnaceGUI::drawSettings() {
ImGui::SameLine(); ImGui::SameLine();
//ImGui::BeginDisabled(settings.initialSys.size()<=4); //ImGui::BeginDisabled(settings.initialSys.size()<=4);
if (ImGui::Button(ICON_FA_MINUS "##InitSysRemove")) { if (ImGui::Button(ICON_FA_MINUS "##InitSysRemove")) {
//doRemove=true; doRemove=i;
} }
//ImGui::EndDisabled(); //ImGui::EndDisabled();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale)); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale));
@ -398,17 +408,31 @@ void FurnaceGUI::drawSettings() {
} }
ImGui::PopID(); ImGui::PopID();
/*if (doRemove && settings.initialSys.size()>=8) {
settings.initialSys.erase(settings.initialSys.begin()+i,settings.initialSys.begin()+i+4);
i-=4;
}*/
} }
if (ImGui::Button(ICON_FA_PLUS "##InitSysAdd")) { if (doRemove>=0 && sysCount>1) {
/*settings.initialSys.push_back(DIV_SYSTEM_YM2612); for (int i=doRemove; i<sysCount-1; i++) {
settings.initialSys.push_back(64); int sysID=settings.initialSys.getInt(fmt::sprintf("id%d",i+1),0);
settings.initialSys.push_back(0); int sysVol=settings.initialSys.getInt(fmt::sprintf("vol%d",i+1),0);
settings.initialSys.push_back(0);*/ int sysPan=settings.initialSys.getInt(fmt::sprintf("pan%d",i+1),0);
String sysFlags=settings.initialSys.getString(fmt::sprintf("flags%d",i+1),"");
settings.initialSys.set(fmt::sprintf("id%d",i),sysID);
settings.initialSys.set(fmt::sprintf("vol%d",i),sysVol);
settings.initialSys.set(fmt::sprintf("pan%d",i),sysPan);
settings.initialSys.set(fmt::sprintf("flags%d",i),sysFlags);
}
settings.initialSys.remove(fmt::sprintf("id%d",sysCount-1));
settings.initialSys.remove(fmt::sprintf("vol%d",sysCount-1));
settings.initialSys.remove(fmt::sprintf("pan%d",sysCount-1));
settings.initialSys.remove(fmt::sprintf("flags%d",sysCount-1));
}
if (sysCount<32) if (ImGui::Button(ICON_FA_PLUS "##InitSysAdd")) {
settings.initialSys.set(fmt::sprintf("id%d",sysCount),(int)e->systemToFileFur(DIV_SYSTEM_YM2612));
settings.initialSys.set(fmt::sprintf("vol%d",sysCount),64);
settings.initialSys.set(fmt::sprintf("pan%d",sysCount),0);
settings.initialSys.set(fmt::sprintf("flags%d",sysCount),"");
} }
ImGui::Separator(); ImGui::Separator();
@ -522,6 +546,7 @@ void FurnaceGUI::drawSettings() {
ImGui::SetTooltip("saves power by lowering the frame rate to 2fps when idle.\nmay cause issues under Mesa drivers!"); ImGui::SetTooltip("saves power by lowering the frame rate to 2fps when idle.\nmay cause issues under Mesa drivers!");
} }
#ifndef IS_MOBILE
bool noThreadedInputB=settings.noThreadedInput; bool noThreadedInputB=settings.noThreadedInput;
if (ImGui::Checkbox("Disable threaded input (restart after changing!)",&noThreadedInputB)) { if (ImGui::Checkbox("Disable threaded input (restart after changing!)",&noThreadedInputB)) {
settings.noThreadedInput=noThreadedInputB; settings.noThreadedInput=noThreadedInputB;
@ -537,6 +562,7 @@ void FurnaceGUI::drawSettings() {
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("remembers the window's last position on startup."); ImGui::SetTooltip("remembers the window's last position on startup.");
} }
#endif
bool blankInsB=settings.blankIns; bool blankInsB=settings.blankIns;
if (ImGui::Checkbox("New instruments are blank",&blankInsB)) { if (ImGui::Checkbox("New instruments are blank",&blankInsB)) {
@ -1318,6 +1344,23 @@ void FurnaceGUI::drawSettings() {
settings.susPosition=1; settings.susPosition=1;
} }
ImGui::Text("Macro editor layout:");
if (ImGui::RadioButton("Unified##mel0",settings.macroLayout==0)) {
settings.macroLayout=0;
}
if (ImGui::RadioButton("Mobile##mel1",settings.macroLayout==1)) {
settings.macroLayout=1;
}
if (ImGui::RadioButton("Grid##mel2",settings.macroLayout==2)) {
settings.macroLayout=2;
}
if (ImGui::RadioButton("Single (with list)##mel3",settings.macroLayout==3)) {
settings.macroLayout=3;
}
if (ImGui::RadioButton("Single (combo box)##mel4",settings.macroLayout==4)) {
settings.macroLayout=4;
}
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Namco 163 chip name"); ImGui::Text("Namco 163 chip name");
@ -2397,6 +2440,7 @@ void FurnaceGUI::syncSettings() {
settings.persistFadeOut=e->getConfInt("persistFadeOut",1); settings.persistFadeOut=e->getConfInt("persistFadeOut",1);
settings.exportLoops=e->getConfInt("exportLoops",0); settings.exportLoops=e->getConfInt("exportLoops",0);
settings.exportFadeOut=e->getConfDouble("exportFadeOut",0.0); settings.exportFadeOut=e->getConfDouble("exportFadeOut",0.0);
settings.macroLayout=e->getConfInt("macroLayout",0);
clampSetting(settings.mainFontSize,2,96); clampSetting(settings.mainFontSize,2,96);
clampSetting(settings.patFontSize,2,96); clampSetting(settings.patFontSize,2,96);
@ -2503,6 +2547,7 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.centerPattern,0,1); clampSetting(settings.centerPattern,0,1);
clampSetting(settings.ordersCursor,0,1); clampSetting(settings.ordersCursor,0,1);
clampSetting(settings.persistFadeOut,0,1); clampSetting(settings.persistFadeOut,0,1);
clampSetting(settings.macroLayout,0,4);
if (settings.exportLoops<0.0) settings.exportLoops=0.0; if (settings.exportLoops<0.0) settings.exportLoops=0.0;
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0; if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
@ -2675,6 +2720,7 @@ void FurnaceGUI::commitSettings() {
e->setConf("persistFadeOut",settings.persistFadeOut); e->setConf("persistFadeOut",settings.persistFadeOut);
e->setConf("exportLoops",settings.exportLoops); e->setConf("exportLoops",settings.exportLoops);
e->setConf("exportFadeOut",settings.exportFadeOut); e->setConf("exportFadeOut",settings.exportFadeOut);
e->setConf("macroLayout",settings.macroLayout);
// colors // colors
for (int i=0; i<GUI_COLOR_MAX; i++) { for (int i=0; i<GUI_COLOR_MAX; i++) {
@ -3082,6 +3128,14 @@ void FurnaceGUI::popAccentColors() {
#define SYSTEM_PAT_FONT_PATH_1 "/System/Library/Fonts/SFNSMono.ttf" #define SYSTEM_PAT_FONT_PATH_1 "/System/Library/Fonts/SFNSMono.ttf"
#define SYSTEM_PAT_FONT_PATH_2 "/System/Library/Fonts/Courier New.ttf" #define SYSTEM_PAT_FONT_PATH_2 "/System/Library/Fonts/Courier New.ttf"
#define SYSTEM_PAT_FONT_PATH_3 "/System/Library/Fonts/Courier New.ttf" #define SYSTEM_PAT_FONT_PATH_3 "/System/Library/Fonts/Courier New.ttf"
#elif defined(ANDROID)
#define SYSTEM_FONT_PATH_1 "/system/fonts/Roboto-Regular.ttf"
#define SYSTEM_FONT_PATH_2 "/system/fonts/DroidSans.ttf"
#define SYSTEM_FONT_PATH_3 "/system/fonts/DroidSans.ttf"
// ???
#define SYSTEM_PAT_FONT_PATH_1 "/system/fonts/RobotoMono-Regular.ttf"
#define SYSTEM_PAT_FONT_PATH_2 "/system/fonts/DroidSansMono.ttf"
#define SYSTEM_PAT_FONT_PATH_3 "/system/fonts/CutiveMono.ttf"
#else #else
#define SYSTEM_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" #define SYSTEM_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
#define SYSTEM_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSans.ttf" #define SYSTEM_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSans.ttf"

View file

@ -32,6 +32,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
case DIV_SYSTEM_YM2612_FRAC_EXT: { case DIV_SYSTEM_YM2612_FRAC_EXT: {
int clockSel=flags.getInt("clockSel",0); int clockSel=flags.getInt("clockSel",0);
bool ladder=flags.getBool("ladderEffect",0); bool ladder=flags.getBool("ladderEffect",0);
bool noExtMacros=flags.getBool("noExtMacros",false);
if (ImGui::RadioButton("NTSC (7.67MHz)",clockSel==0)) { if (ImGui::RadioButton("NTSC (7.67MHz)",clockSel==0)) {
clockSel=0; clockSel=0;
@ -56,11 +57,17 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
if (ImGui::Checkbox("Enable DAC distortion",&ladder)) { if (ImGui::Checkbox("Enable DAC distortion",&ladder)) {
altered=true; altered=true;
} }
if (type==DIV_SYSTEM_YM2612_EXT || type==DIV_SYSTEM_YM2612_FRAC_EXT) {
if (ImGui::Checkbox("Disable ExtCh FM macros (compatibility)",&noExtMacros)) {
altered=true;
}
}
if (altered) { if (altered) {
e->lockSave([&]() { e->lockSave([&]() {
flags.set("clockSel",clockSel); flags.set("clockSel",clockSel);
flags.set("ladderEffect",ladder); flags.set("ladderEffect",ladder);
flags.set("noExtMacros",noExtMacros);
}); });
} }
break; break;
@ -439,6 +446,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
case DIV_SYSTEM_YM2610B: case DIV_SYSTEM_YM2610B:
case DIV_SYSTEM_YM2610B_EXT: { case DIV_SYSTEM_YM2610B_EXT: {
int clockSel=flags.getInt("clockSel",0); int clockSel=flags.getInt("clockSel",0);
bool noExtMacros=flags.getBool("noExtMacros",false);
if (ImGui::RadioButton("8MHz (Neo Geo MVS)",clockSel==0)) { if (ImGui::RadioButton("8MHz (Neo Geo MVS)",clockSel==0)) {
clockSel=0; clockSel=0;
@ -449,9 +457,16 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
altered=true; altered=true;
} }
if (type==DIV_SYSTEM_YM2610_EXT || type==DIV_SYSTEM_YM2610_FULL_EXT || type==DIV_SYSTEM_YM2610B_EXT) {
if (ImGui::Checkbox("Disable ExtCh FM macros (compatibility)",&noExtMacros)) {
altered=true;
}
}
if (altered) { if (altered) {
e->lockSave([&]() { e->lockSave([&]() {
flags.set("clockSel",clockSel); flags.set("clockSel",clockSel);
flags.set("noExtMacros",noExtMacros);
}); });
} }
break; break;
@ -668,8 +683,23 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
break; break;
} }
case DIV_SYSTEM_PCSPKR: { case DIV_SYSTEM_PCSPKR: {
int clockSel=flags.getInt("clockSel",0);
int speakerType=flags.getInt("speakerType",0); int speakerType=flags.getInt("speakerType",0);
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("1.19MHz (PC)",clockSel==0)) {
clockSel=0;
altered=true;
}
if (ImGui::RadioButton("1.99MHz (PC-98)",clockSel==1)) {
clockSel=1;
altered=true;
}
if (ImGui::RadioButton("2.46MHz (PC-98)",clockSel==2)) {
clockSel=2;
altered=true;
}
ImGui::Text("Speaker type:"); ImGui::Text("Speaker type:");
if (ImGui::RadioButton("Unfiltered",speakerType==0)) { if (ImGui::RadioButton("Unfiltered",speakerType==0)) {
speakerType=0; speakerType=0;
@ -690,6 +720,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
if (altered) { if (altered) {
e->lockSave([&]() { e->lockSave([&]() {
flags.set("clockSel",clockSel);
flags.set("speakerType",speakerType); flags.set("speakerType",speakerType);
}); });
} }
@ -805,6 +836,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
case DIV_SYSTEM_OPN_EXT: { case DIV_SYSTEM_OPN_EXT: {
int clockSel=flags.getInt("clockSel",0); int clockSel=flags.getInt("clockSel",0);
int prescale=flags.getInt("prescale",0); int prescale=flags.getInt("prescale",0);
bool noExtMacros=flags.getBool("noExtMacros",false);
ImGui::Text("Clock rate:"); ImGui::Text("Clock rate:");
if (ImGui::RadioButton("3.58MHz (NTSC)",clockSel==0)) { if (ImGui::RadioButton("3.58MHz (NTSC)",clockSel==0)) {
@ -845,10 +877,17 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
altered=true; altered=true;
} }
if (type==DIV_SYSTEM_OPN_EXT) {
if (ImGui::Checkbox("Disable ExtCh FM macros (compatibility)",&noExtMacros)) {
altered=true;
}
}
if (altered) { if (altered) {
e->lockSave([&]() { e->lockSave([&]() {
flags.set("clockSel",clockSel); flags.set("clockSel",clockSel);
flags.set("prescale",prescale); flags.set("prescale",prescale);
flags.set("noExtMacros",noExtMacros);
}); });
} }
break; break;
@ -857,6 +896,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
case DIV_SYSTEM_PC98_EXT: { case DIV_SYSTEM_PC98_EXT: {
int clockSel=flags.getInt("clockSel",0); int clockSel=flags.getInt("clockSel",0);
int prescale=flags.getInt("prescale",0); int prescale=flags.getInt("prescale",0);
bool noExtMacros=flags.getBool("noExtMacros",false);
ImGui::Text("Clock rate:"); ImGui::Text("Clock rate:");
if (ImGui::RadioButton("8MHz (Arcade)",clockSel==0)) { if (ImGui::RadioButton("8MHz (Arcade)",clockSel==0)) {
@ -881,10 +921,17 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
altered=true; altered=true;
} }
if (type==DIV_SYSTEM_PC98_EXT) {
if (ImGui::Checkbox("Disable ExtCh FM macros (compatibility)",&noExtMacros)) {
altered=true;
}
}
if (altered) { if (altered) {
e->lockSave([&]() { e->lockSave([&]() {
flags.set("clockSel",clockSel); flags.set("clockSel",clockSel);
flags.set("prescale",prescale); flags.set("prescale",prescale);
flags.set("noExtMacros",noExtMacros);
}); });
} }
break; break;