mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-27 06:53:01 +00:00
Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt
* 'master' of https://github.com/tildearrow/furnace: sysDef refactor, part 2 sysDef refactor, part 1 - PLEASE READ NOTE prepare for macroInt preview in instrument editor GUI: fix #400 fix metronome in low-latency mode # Conflicts: # src/engine/dispatch.h # src/engine/dispatchContainer.cpp # src/engine/instrument.h # src/engine/song.h # src/engine/sysDef.cpp # src/gui/dataList.cpp # src/gui/gui.h # src/gui/guiConst.cpp
This commit is contained in:
commit
0209ebda03
22 changed files with 1333 additions and 1591 deletions
|
@ -197,7 +197,7 @@ size | description
|
|||
| - 0xa6: Neo Geo extended (YM2610) - 17 channels
|
||||
| - 0xa7: OPLL drums (YM2413) - 11 channels
|
||||
| - 0xa8: Atari Lynx - 4 channels
|
||||
| - 0xa9: SegaPCM (for Deflemask Compatibility) - 5 channels
|
||||
| - 0xa9: SegaPCM (for DefleMask compatibility) - 5 channels
|
||||
| - 0xaa: MSM6295 - 4 channels
|
||||
| - 0xab: MSM6258 - 1 channel
|
||||
| - 0xac: Commander X16 (VERA) - 17 channels
|
||||
|
@ -209,8 +209,12 @@ size | description
|
|||
| - 0xb2: Yamaha Y8950 - 10 channels
|
||||
| - 0xb3: Yamaha Y8950 drums - 12 channels
|
||||
| - 0xb4: Konami SCC+ - 5 channels
|
||||
| - 0xb5: tildearrow Sound Unit - 8 channels
|
||||
| - 0xde: YM2610B extended - 19 channels
|
||||
| - 0xe0: QSound - 19 channels
|
||||
| - 0xfd: Dummy System - 8 channels
|
||||
| - 0xfe: reserved for development
|
||||
| - 0xff: reserved for development
|
||||
| - (compound!) means that the system is composed of two or more chips,
|
||||
| and has to be flattened.
|
||||
32 | sound chip volumes
|
||||
|
@ -328,6 +332,10 @@ size | description
|
|||
| - 24: VERA
|
||||
| - 25: X1-010
|
||||
| - 26: VRC6 (saw)
|
||||
| - 27: ES5506
|
||||
| - 28: MultiPCM
|
||||
| - 29: SNES
|
||||
| - 30: Sound Unit
|
||||
1 | reserved
|
||||
STR | instrument name
|
||||
--- | **FM instrument data**
|
||||
|
|
|
@ -35,74 +35,77 @@
|
|||
// names as strings for the commands (and other debug stuff).
|
||||
//
|
||||
// if you miss it, the program will crash or misbehave at some point.
|
||||
//
|
||||
// the comments are: (arg1, arg2) -> val
|
||||
// not all commands have a return value
|
||||
enum DivDispatchCmds {
|
||||
DIV_CMD_NOTE_ON=0,
|
||||
DIV_CMD_NOTE_ON=0, // (note)
|
||||
DIV_CMD_NOTE_OFF,
|
||||
DIV_CMD_NOTE_OFF_ENV,
|
||||
DIV_CMD_ENV_RELEASE,
|
||||
DIV_CMD_INSTRUMENT,
|
||||
DIV_CMD_VOLUME,
|
||||
DIV_CMD_GET_VOLUME,
|
||||
DIV_CMD_GET_VOLMAX,
|
||||
DIV_CMD_NOTE_PORTA,
|
||||
DIV_CMD_PITCH,
|
||||
DIV_CMD_PANNING,
|
||||
DIV_CMD_LEGATO,
|
||||
DIV_CMD_PRE_PORTA,
|
||||
DIV_CMD_PRE_NOTE, // used in C64
|
||||
DIV_CMD_INSTRUMENT, // (ins, force)
|
||||
DIV_CMD_VOLUME, // (vol)
|
||||
DIV_CMD_GET_VOLUME, // () -> vol
|
||||
DIV_CMD_GET_VOLMAX, // () -> volMax
|
||||
DIV_CMD_NOTE_PORTA, // (target, speed) -> 2 if target reached
|
||||
DIV_CMD_PITCH, // (pitch)
|
||||
DIV_CMD_PANNING, // (pan)
|
||||
DIV_CMD_LEGATO, // (note)
|
||||
DIV_CMD_PRE_PORTA, // (inPorta, isPortaOrSlide)
|
||||
DIV_CMD_PRE_NOTE, // used in C64 (note)
|
||||
|
||||
DIV_CMD_SAMPLE_MODE,
|
||||
DIV_CMD_SAMPLE_FREQ,
|
||||
DIV_CMD_SAMPLE_BANK,
|
||||
DIV_CMD_SAMPLE_POS,
|
||||
DIV_CMD_SAMPLE_MODE, // (enabled)
|
||||
DIV_CMD_SAMPLE_FREQ, // (frequency)
|
||||
DIV_CMD_SAMPLE_BANK, // (bank)
|
||||
DIV_CMD_SAMPLE_POS, // (pos)
|
||||
|
||||
DIV_CMD_FM_HARD_RESET,
|
||||
DIV_CMD_FM_LFO,
|
||||
DIV_CMD_FM_LFO_WAVE,
|
||||
DIV_CMD_FM_TL,
|
||||
DIV_CMD_FM_AR,
|
||||
DIV_CMD_FM_FB,
|
||||
DIV_CMD_FM_MULT,
|
||||
DIV_CMD_FM_EXTCH,
|
||||
DIV_CMD_FM_AM_DEPTH,
|
||||
DIV_CMD_FM_PM_DEPTH,
|
||||
DIV_CMD_FM_HARD_RESET, // (enabled)
|
||||
DIV_CMD_FM_LFO, // (speed)
|
||||
DIV_CMD_FM_LFO_WAVE, // (waveform)
|
||||
DIV_CMD_FM_TL, // (op, value)
|
||||
DIV_CMD_FM_AR, // (op, value)
|
||||
DIV_CMD_FM_FB, // (value)
|
||||
DIV_CMD_FM_MULT, // (op, value)
|
||||
DIV_CMD_FM_EXTCH, // (enabled)
|
||||
DIV_CMD_FM_AM_DEPTH, // (depth)
|
||||
DIV_CMD_FM_PM_DEPTH, // (depth)
|
||||
|
||||
DIV_CMD_GENESIS_LFO,
|
||||
DIV_CMD_GENESIS_LFO, // unused?
|
||||
|
||||
DIV_CMD_ARCADE_LFO,
|
||||
DIV_CMD_ARCADE_LFO, // unused?
|
||||
|
||||
DIV_CMD_STD_NOISE_FREQ,
|
||||
DIV_CMD_STD_NOISE_MODE,
|
||||
DIV_CMD_STD_NOISE_FREQ, // (freq)
|
||||
DIV_CMD_STD_NOISE_MODE, // (mode)
|
||||
|
||||
DIV_CMD_WAVE,
|
||||
DIV_CMD_WAVE, // (waveform)
|
||||
|
||||
DIV_CMD_GB_SWEEP_TIME,
|
||||
DIV_CMD_GB_SWEEP_DIR,
|
||||
DIV_CMD_GB_SWEEP_TIME, // (time)
|
||||
DIV_CMD_GB_SWEEP_DIR, // (direction)
|
||||
|
||||
DIV_CMD_PCE_LFO_MODE,
|
||||
DIV_CMD_PCE_LFO_SPEED,
|
||||
DIV_CMD_PCE_LFO_MODE, // (mode)
|
||||
DIV_CMD_PCE_LFO_SPEED, // (speed)
|
||||
|
||||
DIV_CMD_NES_SWEEP,
|
||||
DIV_CMD_NES_SWEEP, // (direction, value)
|
||||
|
||||
DIV_CMD_C64_CUTOFF,
|
||||
DIV_CMD_C64_RESONANCE,
|
||||
DIV_CMD_C64_FILTER_MODE,
|
||||
DIV_CMD_C64_RESET_TIME,
|
||||
DIV_CMD_C64_RESET_MASK,
|
||||
DIV_CMD_C64_FILTER_RESET,
|
||||
DIV_CMD_C64_DUTY_RESET,
|
||||
DIV_CMD_C64_EXTENDED,
|
||||
DIV_CMD_C64_FINE_DUTY,
|
||||
DIV_CMD_C64_FINE_CUTOFF,
|
||||
DIV_CMD_C64_CUTOFF, // (value)
|
||||
DIV_CMD_C64_RESONANCE, // (value)
|
||||
DIV_CMD_C64_FILTER_MODE, // (value)
|
||||
DIV_CMD_C64_RESET_TIME, // (value)
|
||||
DIV_CMD_C64_RESET_MASK, // (mask)
|
||||
DIV_CMD_C64_FILTER_RESET, // (value)
|
||||
DIV_CMD_C64_DUTY_RESET, // (value)
|
||||
DIV_CMD_C64_EXTENDED, // (value)
|
||||
DIV_CMD_C64_FINE_DUTY, // (value)
|
||||
DIV_CMD_C64_FINE_CUTOFF, // (value)
|
||||
|
||||
DIV_CMD_AY_ENVELOPE_SET,
|
||||
DIV_CMD_AY_ENVELOPE_LOW,
|
||||
DIV_CMD_AY_ENVELOPE_HIGH,
|
||||
DIV_CMD_AY_ENVELOPE_SLIDE,
|
||||
DIV_CMD_AY_NOISE_MASK_AND,
|
||||
DIV_CMD_AY_NOISE_MASK_OR,
|
||||
DIV_CMD_AY_AUTO_ENVELOPE,
|
||||
DIV_CMD_AY_IO_WRITE,
|
||||
DIV_CMD_AY_NOISE_MASK_AND, // (value)
|
||||
DIV_CMD_AY_NOISE_MASK_OR, // (value)
|
||||
DIV_CMD_AY_AUTO_ENVELOPE, // (value)
|
||||
DIV_CMD_AY_IO_WRITE, // (port, value)
|
||||
DIV_CMD_AY_AUTO_PWM,
|
||||
|
||||
DIV_CMD_FDS_MOD_DEPTH,
|
||||
|
@ -111,13 +114,13 @@ enum DivDispatchCmds {
|
|||
DIV_CMD_FDS_MOD_POS,
|
||||
DIV_CMD_FDS_MOD_WAVE,
|
||||
|
||||
DIV_CMD_SAA_ENVELOPE,
|
||||
DIV_CMD_SAA_ENVELOPE, // (value)
|
||||
|
||||
DIV_CMD_AMIGA_FILTER,
|
||||
DIV_CMD_AMIGA_AM,
|
||||
DIV_CMD_AMIGA_PM,
|
||||
DIV_CMD_AMIGA_FILTER, // (enabled)
|
||||
DIV_CMD_AMIGA_AM, // (enabled)
|
||||
DIV_CMD_AMIGA_PM, // (enabled)
|
||||
|
||||
DIV_CMD_LYNX_LFSR_LOAD,
|
||||
DIV_CMD_LYNX_LFSR_LOAD, // (value)
|
||||
|
||||
DIV_CMD_QSOUND_ECHO_FEEDBACK,
|
||||
DIV_CMD_QSOUND_ECHO_DELAY,
|
||||
|
@ -146,16 +149,16 @@ enum DivDispatchCmds {
|
|||
DIV_CMD_N163_GLOBAL_WAVE_LOADLEN,
|
||||
DIV_CMD_N163_GLOBAL_WAVE_LOADMODE,
|
||||
|
||||
DIV_CMD_ES5506_FILTER_MODE,
|
||||
DIV_CMD_ES5506_FILTER_K1,
|
||||
DIV_CMD_ES5506_FILTER_K2,
|
||||
DIV_CMD_ES5506_ENVELOPE_COUNT,
|
||||
DIV_CMD_ES5506_ENVELOPE_LVRAMP,
|
||||
DIV_CMD_ES5506_ENVELOPE_RVRAMP,
|
||||
DIV_CMD_ES5506_ENVELOPE_K1RAMP,
|
||||
DIV_CMD_ES5506_ENVELOPE_K2RAMP,
|
||||
DIV_CMD_ES5506_FILTER_MODE, // (value)
|
||||
DIV_CMD_ES5506_FILTER_K1, // (value)
|
||||
DIV_CMD_ES5506_FILTER_K2, // (value)
|
||||
DIV_CMD_ES5506_ENVELOPE_COUNT, // (count)
|
||||
DIV_CMD_ES5506_ENVELOPE_LVRAMP, // (ramp)
|
||||
DIV_CMD_ES5506_ENVELOPE_RVRAMP, // (ramp)
|
||||
DIV_CMD_ES5506_ENVELOPE_K1RAMP, // (ramp, slowdown)
|
||||
DIV_CMD_ES5506_ENVELOPE_K2RAMP, // (ramp, slowdown)
|
||||
|
||||
DIV_ALWAYS_SET_VOLUME,
|
||||
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
|
||||
|
||||
DIV_CMD_MAX
|
||||
};
|
||||
|
@ -210,6 +213,7 @@ struct DivRegWrite {
|
|||
};
|
||||
|
||||
class DivEngine;
|
||||
class DivMacroInt;
|
||||
|
||||
class DivDispatch {
|
||||
protected:
|
||||
|
@ -223,11 +227,13 @@ class DivDispatch {
|
|||
/**
|
||||
* the rate the samples are provided.
|
||||
* the engine shall resample to the output rate.
|
||||
* you have to initialize this one during init() or setFlags().
|
||||
*/
|
||||
int rate;
|
||||
|
||||
/**
|
||||
* the actual chip's clock.
|
||||
* you have to initialize this one during init() or setFlags().
|
||||
*/
|
||||
int chipClock;
|
||||
|
||||
|
@ -263,6 +269,12 @@ class DivDispatch {
|
|||
* @return a pointer, or NULL.
|
||||
*/
|
||||
virtual void* getChanState(int chan);
|
||||
|
||||
/**
|
||||
* get the DivMacroInt of a chanmel.
|
||||
* @return a pointer, or NULL.
|
||||
*/
|
||||
virtual DivMacroInt* getChanMacroInt(int chan);
|
||||
|
||||
/**
|
||||
* get the register pool of this dispatch.
|
||||
|
@ -427,17 +439,30 @@ class DivDispatch {
|
|||
/**
|
||||
* quit the DivDispatch.
|
||||
*/
|
||||
virtual void quit();
|
||||
virtual void quit();
|
||||
|
||||
virtual ~DivDispatch();
|
||||
virtual ~DivDispatch();
|
||||
};
|
||||
|
||||
// pitch calculation:
|
||||
// - a DivDispatch usually contains four variables per channel:
|
||||
// - baseFreq: this changes on new notes, legato, arpeggio and slides.
|
||||
// - pitch: this changes with DIV_CMD_PITCH (E5xx/04xy).
|
||||
// - freq: this is the result of combining baseFreq and pitch using DivEngine::calcFreq().
|
||||
// - freqChanged: whether baseFreq and/or pitch have changed, and a frequency recalculation is required on the next tick.
|
||||
// - the following definitions will help you calculate baseFreq.
|
||||
// - to use them, define CHIP_DIVIDER and/or CHIP_FREQBASE in your code (not in the header though!).
|
||||
#define NOTE_PERIODIC(x) round(parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true))
|
||||
#define NOTE_PERIODIC_NOROUND(x) parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true)
|
||||
#define NOTE_FREQUENCY(x) parent->calcBaseFreq(chipClock,CHIP_FREQBASE,x,false)
|
||||
|
||||
// this is a special case definition. only use it for f-num/block-based chips.
|
||||
#define NOTE_FNUM_BLOCK(x,bits) parent->calcBaseFreqFNumBlock(chipClock,CHIP_FREQBASE,x,bits)
|
||||
|
||||
// these are here for convenience.
|
||||
// it is encouraged to use these, since you get an exact value this way.
|
||||
// - NTSC colorburst: 3.58MHz
|
||||
// - PAL colorburst: 4.43MHz
|
||||
#define COLOR_NTSC (315000000.0/88.0)
|
||||
#define COLOR_PAL (283.75*15625.0+25.0)
|
||||
|
||||
|
|
|
@ -315,6 +315,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_ES5506:
|
||||
dispatch=new DivPlatformES5506;
|
||||
break;
|
||||
case DIV_SYSTEM_DUMMY:
|
||||
dispatch=new DivPlatformDummy;
|
||||
break;
|
||||
default:
|
||||
logW("this system is not supported yet! using dummy platform.");
|
||||
dispatch=new DivPlatformDummy;
|
||||
|
|
|
@ -1470,6 +1470,10 @@ bool DivEngine::addWaveFromFile(const char* path) {
|
|||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
if (len==(SIZE_MAX>>1)) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
if (len==0) {
|
||||
fclose(f);
|
||||
return false;
|
||||
|
@ -1662,6 +1666,14 @@ int DivEngine::addSampleFromFile(const char* path) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (len==(SIZE_MAX>>1)) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
lastError="file is invalid!";
|
||||
delete sample;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fseek(f,0,SEEK_SET)<0) {
|
||||
fclose(f);
|
||||
BUSY_END;
|
||||
|
@ -2457,6 +2469,9 @@ bool DivEngine::deinitAudioBackend() {
|
|||
#endif
|
||||
|
||||
bool DivEngine::init() {
|
||||
// register systems
|
||||
if (!systemsRegistered) registerSystems();
|
||||
|
||||
// init config
|
||||
#ifdef _WIN32
|
||||
configPath=getWinConfigPath();
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "../audio/taAudio.h"
|
||||
#include "blip_buf.h"
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
|
@ -43,8 +44,8 @@
|
|||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev87"
|
||||
#define DIV_ENGINE_VERSION 87
|
||||
#define DIV_VERSION "dev88"
|
||||
#define DIV_ENGINE_VERSION 88
|
||||
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
|
@ -177,6 +178,91 @@ struct DivDispatchContainer {
|
|||
dcOffCompensation(false) {}
|
||||
};
|
||||
|
||||
typedef std::function<bool(int,unsigned char,unsigned char)> EffectProcess;
|
||||
|
||||
struct DivSysDef {
|
||||
const char* name;
|
||||
const char* nameJ;
|
||||
unsigned char id;
|
||||
unsigned char id_DMF;
|
||||
int channels;
|
||||
bool isFM, isSTD, isCompound;
|
||||
unsigned int vgmVersion;
|
||||
const char* chanNames[DIV_MAX_CHANS];
|
||||
const char* chanShortNames[DIV_MAX_CHANS];
|
||||
int chanTypes[DIV_MAX_CHANS];
|
||||
// 0: primary
|
||||
// 1: alternate (usually PCM)
|
||||
DivInstrumentType chanInsType[DIV_MAX_CHANS][2];
|
||||
EffectProcess effectFunc;
|
||||
EffectProcess postEffectFunc;
|
||||
DivSysDef(
|
||||
const char* sysName, const char* sysNameJ, unsigned char fileID, unsigned char fileID_DMF, int chans,
|
||||
bool isFMChip, bool isSTDChip, unsigned int vgmVer, bool compound,
|
||||
std::initializer_list<const char*> chNames,
|
||||
std::initializer_list<const char*> chShortNames,
|
||||
std::initializer_list<int> chTypes,
|
||||
std::initializer_list<DivInstrumentType> chInsType1,
|
||||
std::initializer_list<DivInstrumentType> chInsType2={},
|
||||
EffectProcess fxHandler=NULL,
|
||||
EffectProcess postFxHandler=NULL):
|
||||
name(sysName),
|
||||
nameJ(sysNameJ),
|
||||
id(fileID),
|
||||
id_DMF(fileID_DMF),
|
||||
channels(chans),
|
||||
isFM(isFMChip),
|
||||
isSTD(isSTDChip),
|
||||
isCompound(compound),
|
||||
vgmVersion(vgmVer),
|
||||
effectFunc(fxHandler),
|
||||
postEffectFunc(postFxHandler) {
|
||||
memset(chanNames,0,DIV_MAX_CHANS*sizeof(void*));
|
||||
memset(chanShortNames,0,DIV_MAX_CHANS*sizeof(void*));
|
||||
memset(chanTypes,0,DIV_MAX_CHANS*sizeof(int));
|
||||
memset(chanInsType,0,DIV_MAX_CHANS*2*sizeof(DivInstrumentType));
|
||||
|
||||
int index=0;
|
||||
for (const char* i: chNames) {
|
||||
chanNames[index++]=i;
|
||||
if (index>=DIV_MAX_CHANS) break;
|
||||
}
|
||||
|
||||
index=0;
|
||||
for (const char* i: chShortNames) {
|
||||
chanShortNames[index++]=i;
|
||||
if (index>=DIV_MAX_CHANS) break;
|
||||
}
|
||||
|
||||
index=0;
|
||||
for (int i: chTypes) {
|
||||
chanTypes[index++]=i;
|
||||
if (index>=DIV_MAX_CHANS) break;
|
||||
}
|
||||
|
||||
index=0;
|
||||
for (DivInstrumentType i: chInsType1) {
|
||||
chanInsType[index++][0]=i;
|
||||
if (index>=DIV_MAX_CHANS) break;
|
||||
}
|
||||
|
||||
index=0;
|
||||
for (DivInstrumentType i: chInsType2) {
|
||||
chanInsType[index++][1]=i;
|
||||
if (index>=DIV_MAX_CHANS) break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
enum DivChanTypes {
|
||||
DIV_CH_FM=0,
|
||||
DIV_CH_PULSE=1,
|
||||
DIV_CH_NOISE=2,
|
||||
DIV_CH_WAVE=3,
|
||||
DIV_CH_PCM=4,
|
||||
DIV_CH_OP=5
|
||||
};
|
||||
|
||||
class DivEngine {
|
||||
DivDispatchContainer disCont[32];
|
||||
TAAudio* output;
|
||||
|
@ -204,6 +290,7 @@ class DivEngine {
|
|||
bool skipping;
|
||||
bool midiIsDirect;
|
||||
bool lowLatency;
|
||||
bool systemsRegistered;
|
||||
int softLockCount;
|
||||
int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed;
|
||||
double divider;
|
||||
|
@ -230,6 +317,9 @@ class DivEngine {
|
|||
std::vector<String> midiIns;
|
||||
std::vector<String> midiOuts;
|
||||
std::vector<DivCommand> cmdStream;
|
||||
DivSysDef* sysDefs[256];
|
||||
DivSystem sysFileMapFur[256];
|
||||
DivSystem sysFileMapDMF[256];
|
||||
|
||||
struct SamplePreview {
|
||||
int sample;
|
||||
|
@ -300,6 +390,8 @@ class DivEngine {
|
|||
bool initAudioBackend();
|
||||
bool deinitAudioBackend();
|
||||
|
||||
void registerSystems();
|
||||
|
||||
void exchangeIns(int one, int two);
|
||||
|
||||
public:
|
||||
|
@ -432,9 +524,6 @@ class DivEngine {
|
|||
// get sys name
|
||||
const char* getSystemName(DivSystem sys);
|
||||
|
||||
// get sys chips
|
||||
const char* getSystemChips(DivSystem sys);
|
||||
|
||||
// get japanese system name
|
||||
const char* getSystemNameJ(DivSystem sys);
|
||||
|
||||
|
@ -720,7 +809,7 @@ class DivEngine {
|
|||
// quit dispatch
|
||||
void quitDispatch();
|
||||
|
||||
// initialize the engine. optionally provide an output file name.
|
||||
// initialize the engine.
|
||||
bool init();
|
||||
|
||||
// terminate the engine.
|
||||
|
@ -765,6 +854,7 @@ class DivEngine {
|
|||
skipping(false),
|
||||
midiIsDirect(false),
|
||||
lowLatency(false),
|
||||
systemsRegistered(false),
|
||||
softLockCount(0),
|
||||
subticks(0),
|
||||
ticks(0),
|
||||
|
@ -834,6 +924,12 @@ class DivEngine {
|
|||
memset(vibTable,0,64*sizeof(short));
|
||||
memset(reversePitchTable,0,4096*sizeof(int));
|
||||
memset(pitchTable,0,4096*sizeof(int));
|
||||
memset(sysDefs,0,256*sizeof(void*));
|
||||
|
||||
for (int i=0; i<256; i++) {
|
||||
sysFileMapFur[i]=DIV_SYSTEM_NULL;
|
||||
sysFileMapDMF[i]=DIV_SYSTEM_NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -1101,9 +1101,11 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
return false;
|
||||
}
|
||||
|
||||
logD("systems:");
|
||||
for (int i=0; i<32; i++) {
|
||||
unsigned char sysID=reader.readC();
|
||||
ds.system[i]=systemFromFileFur(sysID);
|
||||
logD("- %d: %.2x (%s)",i,sysID,getSystemName(ds.system[i]));
|
||||
if (sysID!=0 && systemToFileFur(ds.system[i])==0) {
|
||||
logE("unrecognized system ID %.2x",ds.system[i]);
|
||||
lastError=fmt::sprintf("unrecognized system ID %.2x!",ds.system[i]);
|
||||
|
@ -2036,6 +2038,8 @@ bool DivEngine::load(unsigned char* f, size_t slen) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!systemsRegistered) registerSystems();
|
||||
|
||||
// step 1: try loading as a zlib-compressed file
|
||||
logD("trying zlib...");
|
||||
try {
|
||||
|
|
|
@ -1220,6 +1220,11 @@ std::vector<DivInstrument*> DivEngine::instrumentFromFile(const char* path) {
|
|||
fclose(f);
|
||||
return ret;
|
||||
}
|
||||
if (len==(SIZE_MAX>>1)) {
|
||||
lastError=strerror(errno);
|
||||
fclose(f);
|
||||
return ret;
|
||||
}
|
||||
if (len==0) {
|
||||
lastError=strerror(errno);
|
||||
fclose(f);
|
||||
|
|
|
@ -55,7 +55,11 @@ enum DivInstrumentType: unsigned short {
|
|||
DIV_INS_X1_010=25,
|
||||
DIV_INS_VRC6_SAW=26,
|
||||
DIV_INS_ES5506=27,
|
||||
DIV_INS_MULTIPCM=28,
|
||||
DIV_INS_SNES=29,
|
||||
DIV_INS_SU=30,
|
||||
DIV_INS_MAX,
|
||||
DIV_INS_NULL
|
||||
};
|
||||
|
||||
// FM operator structure:
|
||||
|
|
|
@ -29,6 +29,10 @@ void* DivDispatch::getChanState(int chan) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
DivMacroInt* DivDispatch::getChanMacroInt(int chan) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned char* DivDispatch::getRegisterPool() {
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -22,15 +22,20 @@
|
|||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#define CHIP_FREQBASE 2048
|
||||
|
||||
void DivPlatformDummy::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
for (size_t i=start; i<start+len; i++) {
|
||||
bufL[i]=0;
|
||||
int out=0;
|
||||
for (unsigned char j=0; j<chans; j++) {
|
||||
if (chan[j].active) {
|
||||
if (!isMuted[j]) bufL[i]+=(((signed short)chan[j].pos)*chan[j].amp*chan[j].vol)>>13;
|
||||
if (!isMuted[j]) out+=(((signed short)chan[j].pos)*chan[j].amp*chan[j].vol)>>12;
|
||||
chan[j].pos+=chan[j].freq;
|
||||
}
|
||||
}
|
||||
if (out<-32768) out=-32768;
|
||||
if (out>32767) out=32767;
|
||||
bufL[i]=out;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,8 +46,8 @@ void DivPlatformDummy::muteChannel(int ch, bool mute) {
|
|||
void DivPlatformDummy::tick(bool sysTick) {
|
||||
for (unsigned char i=0; i<chans; i++) {
|
||||
if (sysTick) {
|
||||
chan[i].amp-=3;
|
||||
if (chan[i].amp<16) chan[i].amp=16;
|
||||
chan[i].amp-=7;
|
||||
if (chan[i].amp<15) chan[i].amp=15;
|
||||
}
|
||||
|
||||
if (chan[i].freqChanged) {
|
||||
|
@ -60,7 +65,7 @@ int DivPlatformDummy::dispatch(DivCommand c) {
|
|||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON:
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=65.6f*pow(2.0f,((float)c.value/12.0f));
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
|
@ -80,8 +85,28 @@ int DivPlatformDummy::dispatch(DivCommand c) {
|
|||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>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) return 2;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=65.6f*pow(2.0f,((float)c.value/12.0f));
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
|
@ -108,6 +133,7 @@ int DivPlatformDummy::init(DivEngine* p, int channels, int sugRate, unsigned int
|
|||
isMuted[i]=false;
|
||||
}
|
||||
rate=65536;
|
||||
chipClock=65536;
|
||||
chans=channels;
|
||||
reset();
|
||||
return channels;
|
||||
|
|
|
@ -23,8 +23,7 @@
|
|||
// used when a DivDispatch for a system is not found.
|
||||
class DivPlatformDummy: public DivDispatch {
|
||||
struct Channel {
|
||||
unsigned short freq, baseFreq;
|
||||
short pitch;
|
||||
int freq, baseFreq, pitch;
|
||||
unsigned short pos;
|
||||
bool active, freqChanged;
|
||||
unsigned char vol;
|
||||
|
|
261
src/engine/platform/sound/su.cpp
Normal file
261
src/engine/platform/sound/su.cpp
Normal file
|
@ -0,0 +1,261 @@
|
|||
#include "su.h"
|
||||
#include <string.h>
|
||||
|
||||
#define minval(a,b) (((a)<(b))?(a):(b))
|
||||
#define maxval(a,b) (((a)>(b))?(a):(b))
|
||||
|
||||
void SoundUnit::NextSample(short* l, short* r) {
|
||||
for (int i=0; i<8; i++) {
|
||||
if (chan[i].vol==0 && !chan[i].flags.swvol) {fns[i]=0; continue;}
|
||||
if (chan[i].flags.pcm) {
|
||||
ns[i]=pcm[chan[i].pcmpos];
|
||||
} else switch (chan[i].flags.shape) {
|
||||
case 0:
|
||||
ns[i]=(((cycle[i]>>15)&127)>chan[i].duty)*127;
|
||||
break;
|
||||
case 1:
|
||||
ns[i]=cycle[i]>>14;
|
||||
break;
|
||||
case 2:
|
||||
ns[i]=SCsine[(cycle[i]>>14)&255];
|
||||
break;
|
||||
case 3:
|
||||
ns[i]=SCtriangle[(cycle[i]>>14)&255];
|
||||
break;
|
||||
case 4: case 5:
|
||||
ns[i]=(lfsr[i]&1)*127;
|
||||
break;
|
||||
case 6:
|
||||
ns[i]=((((cycle[i]>>15)&127)>chan[i].duty)*127)^(short)SCsine[(cycle[i]>>14)&255];
|
||||
break;
|
||||
case 7:
|
||||
ns[i]=((((cycle[i]>>15)&127)>chan[i].duty)*127)^(short)SCtriangle[(cycle[i]>>14)&255];
|
||||
break;
|
||||
}
|
||||
|
||||
if (chan[i].flags.pcm) {
|
||||
if (chan[i].freq>0x8000) {
|
||||
pcmdec[i]+=0x8000;
|
||||
} else {
|
||||
pcmdec[i]+=chan[i].freq;
|
||||
}
|
||||
if (pcmdec[i]>=32768) {
|
||||
pcmdec[i]-=32768;
|
||||
if (chan[i].pcmpos<chan[i].pcmbnd) {
|
||||
chan[i].pcmpos++;
|
||||
chan[i].wc++;
|
||||
if (chan[i].pcmpos==chan[i].pcmbnd) {
|
||||
if (chan[i].flags.pcmloop) {
|
||||
chan[i].pcmpos=chan[i].pcmrst;
|
||||
}
|
||||
}
|
||||
chan[i].pcmpos&=(SOUNDCHIP_PCM_SIZE-1);
|
||||
} else if (chan[i].flags.pcmloop) {
|
||||
chan[i].pcmpos=chan[i].pcmrst;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ocycle[i]=cycle[i];
|
||||
if (chan[i].flags.shape==5) {
|
||||
switch ((chan[i].duty>>4)&3) {
|
||||
case 0:
|
||||
cycle[i]+=chan[i].freq*1-(chan[i].freq>>3);
|
||||
break;
|
||||
case 1:
|
||||
cycle[i]+=chan[i].freq*2-(chan[i].freq>>3);
|
||||
break;
|
||||
case 2:
|
||||
cycle[i]+=chan[i].freq*4-(chan[i].freq>>3);
|
||||
break;
|
||||
case 3:
|
||||
cycle[i]+=chan[i].freq*8-(chan[i].freq>>3);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
cycle[i]+=chan[i].freq;
|
||||
}
|
||||
if ((cycle[i]&0xf80000)!=(ocycle[i]&0xf80000)) {
|
||||
if (chan[i].flags.shape==4) {
|
||||
lfsr[i]=(lfsr[i]>>1|(((lfsr[i]) ^ (lfsr[i] >> 2) ^ (lfsr[i] >> 3) ^ (lfsr[i] >> 5) ) & 1)<<31);
|
||||
} else {
|
||||
switch ((chan[i].duty>>4)&3) {
|
||||
case 0:
|
||||
lfsr[i]=(lfsr[i]>>1|(((lfsr[i] >> 3) ^ (lfsr[i] >> 4) ) & 1)<<5);
|
||||
break;
|
||||
case 1:
|
||||
lfsr[i]=(lfsr[i]>>1|(((lfsr[i] >> 2) ^ (lfsr[i] >> 3) ) & 1)<<5);
|
||||
break;
|
||||
case 2:
|
||||
lfsr[i]=(lfsr[i]>>1|(((lfsr[i]) ^ (lfsr[i] >> 2) ^ (lfsr[i] >> 3) ) & 1)<<5);
|
||||
break;
|
||||
case 3:
|
||||
lfsr[i]=(lfsr[i]>>1|(((lfsr[i]) ^ (lfsr[i] >> 2) ^ (lfsr[i] >> 3) ^ (lfsr[i] >> 5) ) & 1)<<5);
|
||||
break;
|
||||
}
|
||||
if ((lfsr[i]&63)==0) {
|
||||
lfsr[i]=0xaaaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].flags.restim) {
|
||||
if (--rcycle[i]<=0) {
|
||||
cycle[i]=0;
|
||||
rcycle[i]=chan[i].restimer;
|
||||
lfsr[i]=0xaaaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
fns[i]=ns[i]*chan[i].vol*2;
|
||||
if (chan[i].flags.fmode!=0) {
|
||||
int ff=chan[i].cutoff;
|
||||
nslow[i]=nslow[i]+(((ff)*nsband[i])>>16);
|
||||
nshigh[i]=fns[i]-nslow[i]-(((256-chan[i].reson)*nsband[i])>>8);
|
||||
nsband[i]=(((ff)*nshigh[i])>>16)+nsband[i];
|
||||
fns[i]=(((chan[i].flags.fmode&1)?(nslow[i]):(0))+((chan[i].flags.fmode&2)?(nshigh[i]):(0))+((chan[i].flags.fmode&4)?(nsband[i]):(0)));
|
||||
}
|
||||
nsL[i]=(fns[i]*SCpantabL[(unsigned char)chan[i].pan])>>8;
|
||||
nsR[i]=(fns[i]*SCpantabR[(unsigned char)chan[i].pan])>>8;
|
||||
oldfreq[i]=chan[i].freq;
|
||||
oldflags[i]=chan[i].flags.flags;
|
||||
if (chan[i].flags.swvol) {
|
||||
if (--swvolt[i]<=0) {
|
||||
swvolt[i]=chan[i].swvol.speed;
|
||||
if (chan[i].swvol.dir) {
|
||||
chan[i].vol+=chan[i].swvol.amt;
|
||||
if (chan[i].vol>chan[i].swvol.bound && !chan[i].swvol.loop) {
|
||||
chan[i].vol=chan[i].swvol.bound;
|
||||
}
|
||||
if (chan[i].vol&0x80) {
|
||||
if (chan[i].swvol.loop) {
|
||||
if (chan[i].swvol.loopi) {
|
||||
chan[i].swvol.dir=!chan[i].swvol.dir;
|
||||
chan[i].vol=0xff-chan[i].vol;
|
||||
} else {
|
||||
chan[i].vol&=~0x80;
|
||||
}
|
||||
} else {
|
||||
chan[i].vol=0x7f;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
chan[i].vol-=chan[i].swvol.amt;
|
||||
if (chan[i].vol&0x80) {
|
||||
if (chan[i].swvol.loop) {
|
||||
if (chan[i].swvol.loopi) {
|
||||
chan[i].swvol.dir=!chan[i].swvol.dir;
|
||||
chan[i].vol=-chan[i].vol;
|
||||
} else {
|
||||
chan[i].vol&=~0x80;
|
||||
}
|
||||
} else {
|
||||
chan[i].vol=0x0;
|
||||
}
|
||||
}
|
||||
if (chan[i].vol<chan[i].swvol.bound && !chan[i].swvol.loop) {
|
||||
chan[i].vol=chan[i].swvol.bound;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].flags.swfreq) {
|
||||
if (--swfreqt[i]<=0) {
|
||||
swfreqt[i]=chan[i].swfreq.speed;
|
||||
if (chan[i].swfreq.dir) {
|
||||
if (chan[i].freq>(0xffff-chan[i].swfreq.amt)) {
|
||||
chan[i].freq=0xffff;
|
||||
} else {
|
||||
chan[i].freq=(chan[i].freq*(0x80+chan[i].swfreq.amt))>>7;
|
||||
if ((chan[i].freq>>8)>chan[i].swfreq.bound) {
|
||||
chan[i].freq=chan[i].swfreq.bound<<8;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (chan[i].freq<chan[i].swfreq.amt) {
|
||||
chan[i].freq=0;
|
||||
} else {
|
||||
chan[i].freq=(chan[i].freq*(0xff-chan[i].swfreq.amt))>>8;
|
||||
if ((chan[i].freq>>8)<chan[i].swfreq.bound) {
|
||||
chan[i].freq=chan[i].swfreq.bound<<8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].flags.swcut) {
|
||||
if (--swcutt[i]<=0) {
|
||||
swcutt[i]=chan[i].swcut.speed;
|
||||
if (chan[i].swcut.dir) {
|
||||
if (chan[i].cutoff>(0xffff-chan[i].swcut.amt)) {
|
||||
chan[i].cutoff=0xffff;
|
||||
} else {
|
||||
chan[i].cutoff+=chan[i].swcut.amt;
|
||||
if ((chan[i].cutoff>>8)>chan[i].swcut.bound) {
|
||||
chan[i].cutoff=chan[i].swcut.bound<<8;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (chan[i].cutoff<chan[i].swcut.amt) {
|
||||
chan[i].cutoff=0;
|
||||
} else {
|
||||
chan[i].cutoff=((2048-(unsigned int)chan[i].swcut.amt)*(unsigned int)chan[i].cutoff)>>11;
|
||||
if ((chan[i].cutoff>>8)<chan[i].swcut.bound) {
|
||||
chan[i].cutoff=chan[i].swcut.bound<<8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].flags.resosc) {
|
||||
cycle[i]=0;
|
||||
rcycle[i]=chan[i].restimer;
|
||||
ocycle[i]=0;
|
||||
chan[i].flags.resosc=0;
|
||||
}
|
||||
}
|
||||
tnsL=(nsL[0]+nsL[1]+nsL[2]+nsL[3]+nsL[4]+nsL[5]+nsL[6]+nsL[7])>>2;
|
||||
tnsR=(nsR[0]+nsR[1]+nsR[2]+nsR[3]+nsR[4]+nsR[5]+nsR[6]+nsR[7])>>2;
|
||||
|
||||
*l=minval(32767,maxval(-32767,tnsL));
|
||||
*r=minval(32767,maxval(-32767,tnsR));
|
||||
}
|
||||
|
||||
void SoundUnit::Init() {
|
||||
Reset();
|
||||
memset(pcm,0,SOUNDCHIP_PCM_SIZE);
|
||||
for (int i=0; i<256; i++) {
|
||||
SCsine[i]=sin((i/128.0f)*M_PI)*127;
|
||||
SCtriangle[i]=(i>127)?(255-i):(i);
|
||||
SCpantabL[i]=127;
|
||||
SCpantabR[i]=127;
|
||||
}
|
||||
for (int i=0; i<128; i++) {
|
||||
SCpantabL[i]=127-i;
|
||||
SCpantabR[128+i]=i-1;
|
||||
}
|
||||
SCpantabR[128]=0;
|
||||
}
|
||||
|
||||
void SoundUnit::Reset() {
|
||||
for (int i=0; i<8; i++) {
|
||||
cycle[i]=0;
|
||||
resetfreq[i]=0;
|
||||
voldcycles[i]=0;
|
||||
volicycles[i]=0;
|
||||
fscycles[i]=0;
|
||||
sweep[i]=0;
|
||||
ns[i]=0;
|
||||
swvolt[i]=1;
|
||||
swfreqt[i]=1;
|
||||
swcutt[i]=1;
|
||||
lfsr[i]=0xaaaa;
|
||||
}
|
||||
memset(chan,0,sizeof(SUChannel)*8);
|
||||
}
|
||||
|
||||
void SoundUnit::Write(unsigned char addr, unsigned char data) {
|
||||
((unsigned char*)chan)[addr]=data;
|
||||
}
|
||||
|
||||
SoundUnit::SoundUnit() {
|
||||
Init();
|
||||
}
|
93
src/engine/platform/sound/su.h
Normal file
93
src/engine/platform/sound/su.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define SOUNDCHIP_PCM_SIZE 8192
|
||||
|
||||
class SoundUnit {
|
||||
signed char SCsine[256];
|
||||
signed char SCtriangle[256];
|
||||
signed char SCpantabL[256];
|
||||
signed char SCpantabR[256];
|
||||
unsigned int ocycle[8];
|
||||
unsigned int cycle[8];
|
||||
int rcycle[8];
|
||||
unsigned int lfsr[8];
|
||||
signed char ns[8];
|
||||
int fns[8];
|
||||
int nsL[8];
|
||||
int nsR[8];
|
||||
int nslow[8];
|
||||
int nshigh[8];
|
||||
int nsband[8];
|
||||
int tnsL, tnsR;
|
||||
unsigned short oldfreq[8];
|
||||
unsigned short oldflags[8];
|
||||
public:
|
||||
unsigned short resetfreq[8];
|
||||
unsigned short voldcycles[8];
|
||||
unsigned short volicycles[8];
|
||||
unsigned short fscycles[8];
|
||||
unsigned char sweep[8];
|
||||
unsigned short swvolt[8];
|
||||
unsigned short swfreqt[8];
|
||||
unsigned short swcutt[8];
|
||||
unsigned short pcmdec[8];
|
||||
struct SUChannel {
|
||||
unsigned short freq;
|
||||
signed char vol;
|
||||
signed char pan;
|
||||
union {
|
||||
unsigned short flags;
|
||||
struct {
|
||||
unsigned char shape: 3;
|
||||
unsigned char pcm: 1;
|
||||
unsigned char ring: 1;
|
||||
unsigned char fmode: 3;
|
||||
unsigned char resosc: 1;
|
||||
unsigned char resfilt: 1;
|
||||
unsigned char pcmloop: 1;
|
||||
unsigned char restim: 1;
|
||||
unsigned char swfreq: 1;
|
||||
unsigned char swvol: 1;
|
||||
unsigned char swcut: 1;
|
||||
unsigned char padding: 1;
|
||||
};
|
||||
} flags;
|
||||
unsigned short cutoff;
|
||||
unsigned char duty;
|
||||
unsigned char reson;
|
||||
unsigned short pcmpos;
|
||||
unsigned short pcmbnd;
|
||||
unsigned short pcmrst;
|
||||
struct {
|
||||
unsigned short speed;
|
||||
unsigned char amt: 7;
|
||||
unsigned char dir: 1;
|
||||
unsigned char bound;
|
||||
} swfreq;
|
||||
struct {
|
||||
unsigned short speed;
|
||||
unsigned char amt: 5;
|
||||
unsigned char dir: 1;
|
||||
unsigned char loop: 1;
|
||||
unsigned char loopi: 1;
|
||||
unsigned char bound;
|
||||
} swvol;
|
||||
struct {
|
||||
unsigned short speed;
|
||||
unsigned char amt: 7;
|
||||
unsigned char dir: 1;
|
||||
unsigned char bound;
|
||||
} swcut;
|
||||
unsigned short wc;
|
||||
unsigned short restimer;
|
||||
} chan[8];
|
||||
signed char pcm[SOUNDCHIP_PCM_SIZE];
|
||||
void Write(unsigned char addr, unsigned char data);
|
||||
void NextSample(short* l, short* r);
|
||||
void Init();
|
||||
void Reset();
|
||||
SoundUnit();
|
||||
};
|
|
@ -1977,7 +1977,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
// 2. check whether we gonna tick
|
||||
if (cycles<=0) {
|
||||
// we have to tick
|
||||
if (!freelance && stepPlay!=-1) {
|
||||
if (!freelance && stepPlay!=-1 && subticks==1) {
|
||||
unsigned int realPos=size-(runLeftG>>MASTER_CLOCK_PREC);
|
||||
if (realPos>=size) realPos=size-1;
|
||||
if (song.hilightA>0) {
|
||||
|
|
|
@ -96,7 +96,16 @@ enum DivSystem {
|
|||
DIV_SYSTEM_SEGAPCM_COMPAT,
|
||||
DIV_SYSTEM_X1_010,
|
||||
DIV_SYSTEM_BUBSYS_WSG,
|
||||
DIV_SYSTEM_ES5506
|
||||
DIV_SYSTEM_OPL4,
|
||||
DIV_SYSTEM_OPL4_DRUMS,
|
||||
DIV_SYSTEM_ES5506,
|
||||
DIV_SYSTEM_Y8950,
|
||||
DIV_SYSTEM_Y8950_DRUMS,
|
||||
DIV_SYSTEM_SCC_PLUS,
|
||||
DIV_SYSTEM_SOUND_UNIT,
|
||||
DIV_SYSTEM_MSM6295,
|
||||
DIV_SYSTEM_MSM6258,
|
||||
DIV_SYSTEM_DUMMY
|
||||
};
|
||||
|
||||
struct DivSong {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -191,6 +191,18 @@ void FurnaceGUI::drawInsList() {
|
|||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_ES5506]);
|
||||
name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i);
|
||||
break;
|
||||
case DIV_INS_MULTIPCM:
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MULTIPCM]);
|
||||
name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i);
|
||||
break;
|
||||
case DIV_INS_SNES:
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SNES]);
|
||||
name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i);
|
||||
break;
|
||||
case DIV_INS_SU:
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SU]);
|
||||
name=fmt::sprintf(ICON_FA_MICROCHIP " %.2X: %s##_INS%d",i,ins->name,i);
|
||||
break;
|
||||
default:
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]);
|
||||
name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i);
|
||||
|
|
|
@ -505,9 +505,11 @@ bool FurnaceGUI::CWVSliderInt(const char* label, const ImVec2& size, int* v, int
|
|||
}
|
||||
|
||||
const char* FurnaceGUI::getSystemName(DivSystem which) {
|
||||
/*
|
||||
if (settings.chipNames) {
|
||||
return e->getSystemChips(which);
|
||||
}
|
||||
*/
|
||||
return e->getSystemName(which);
|
||||
}
|
||||
|
||||
|
@ -1286,6 +1288,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
);
|
||||
break;
|
||||
case GUI_FILE_INS_OPEN:
|
||||
prevIns=-3;
|
||||
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
|
||||
hasOpened=fileDialog->openLoad(
|
||||
"Load Instrument",
|
||||
|
@ -1298,6 +1301,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
std::vector<DivInstrument*> instruments=e->instrumentFromFile(path);
|
||||
if (!instruments.empty()) {
|
||||
e->loadTempIns(instruments[0]);
|
||||
if (curIns!=-2) {
|
||||
prevIns=curIns;
|
||||
}
|
||||
curIns=-2;
|
||||
}
|
||||
for (DivInstrument* i: instruments) delete i;
|
||||
|
@ -2666,6 +2672,7 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::Separator();
|
||||
if (ImGui::BeginMenu("add system...")) {
|
||||
for (int j=0; availableSystems[j]; j++) {
|
||||
if (!settings.hiddenSystems && (availableSystems[j]==DIV_SYSTEM_YMU759 || availableSystems[j]==DIV_SYSTEM_DUMMY || availableSystems[j]==DIV_SYSTEM_SOUND_UNIT)) continue;
|
||||
sysAddOption((DivSystem)availableSystems[j]);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
|
@ -2683,6 +2690,7 @@ bool FurnaceGUI::loop() {
|
|||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
|
||||
for (int j=0; availableSystems[j]; j++) {
|
||||
if (!settings.hiddenSystems && (availableSystems[j]==DIV_SYSTEM_YMU759 || availableSystems[j]==DIV_SYSTEM_DUMMY || availableSystems[j]==DIV_SYSTEM_SOUND_UNIT)) continue;
|
||||
sysChangeOption(i,(DivSystem)availableSystems[j]);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
|
@ -2937,6 +2945,9 @@ bool FurnaceGUI::loop() {
|
|||
workingDirLayout=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
||||
break;
|
||||
}
|
||||
if (prevIns!=-3) {
|
||||
curIns=prevIns;
|
||||
}
|
||||
if (fileDialog->accepted()) {
|
||||
fileName=fileDialog->getFileName();
|
||||
if (fileName!="") {
|
||||
|
@ -3791,6 +3802,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
curSample(0),
|
||||
curOctave(3),
|
||||
curOrder(0),
|
||||
prevIns(0),
|
||||
oldRow(0),
|
||||
oldOrder(0),
|
||||
oldOrder1(0),
|
||||
|
@ -3878,6 +3890,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
lockLayout(false),
|
||||
editOptsVisible(false),
|
||||
latchNibble(false),
|
||||
nonLatchNibble(false),
|
||||
curWindow(GUI_WINDOW_NOTHING),
|
||||
nextWindow(GUI_WINDOW_NOTHING),
|
||||
nextDesc(NULL),
|
||||
|
|
|
@ -146,6 +146,9 @@ enum FurnaceGUIColors {
|
|||
GUI_COLOR_INSTR_X1_010,
|
||||
GUI_COLOR_INSTR_VRC6_SAW,
|
||||
GUI_COLOR_INSTR_ES5506,
|
||||
GUI_COLOR_INSTR_MULTIPCM,
|
||||
GUI_COLOR_INSTR_SNES,
|
||||
GUI_COLOR_INSTR_SU,
|
||||
GUI_COLOR_INSTR_UNKNOWN,
|
||||
|
||||
GUI_COLOR_CHANNEL_FM,
|
||||
|
@ -845,6 +848,7 @@ class FurnaceGUI {
|
|||
int absorbInsInput;
|
||||
int eventDelay;
|
||||
int moveWindowTitle;
|
||||
int hiddenSystems;
|
||||
unsigned int maxUndoSteps;
|
||||
String mainFontPath;
|
||||
String patFontPath;
|
||||
|
@ -922,6 +926,7 @@ class FurnaceGUI {
|
|||
absorbInsInput(0),
|
||||
eventDelay(0),
|
||||
moveWindowTitle(0),
|
||||
hiddenSystems(0),
|
||||
maxUndoSteps(100),
|
||||
mainFontPath(""),
|
||||
patFontPath(""),
|
||||
|
@ -932,7 +937,7 @@ class FurnaceGUI {
|
|||
|
||||
char finalLayoutPath[4096];
|
||||
|
||||
int curIns, curWave, curSample, curOctave, curOrder, 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 wheelX, wheelY;
|
||||
|
||||
|
@ -950,7 +955,7 @@ class FurnaceGUI {
|
|||
|
||||
SelectionPoint selStart, selEnd, cursor;
|
||||
bool selecting, curNibble, orderNibble, followOrders, followPattern, changeAllOrders;
|
||||
bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, lockLayout, editOptsVisible, latchNibble;
|
||||
bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
|
||||
FurnaceGUIWindows curWindow, nextWindow;
|
||||
float peak[2];
|
||||
float patChanX[DIV_MAX_CHANS+1];
|
||||
|
|
|
@ -107,7 +107,10 @@ const char* insTypes[DIV_INS_MAX]={
|
|||
"VERA",
|
||||
"X1-010",
|
||||
"VRC6 (saw)",
|
||||
"ES5506"
|
||||
"ES5506",
|
||||
"MultiPCM",
|
||||
"SNES",
|
||||
"Sound Unit",
|
||||
};
|
||||
|
||||
const char* sampleDepths[DIV_SAMPLE_DEPTH_MAX]={
|
||||
|
@ -517,7 +520,10 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
|
|||
D(GUI_COLOR_INSTR_VERA,"",ImVec4(0.4f,0.6f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_X1_010,"",ImVec4(0.3f,0.5f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_VRC6_SAW,"",ImVec4(0.8f,0.3f,0.0f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_ES5506,"",ImVec4(1.0f,0.5f,0.5f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_ES5506,"",ImVec4(0.5f,0.5f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_MULTIPCM,"",ImVec4(1.0f,0.8f,0.1f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_SNES,"",ImVec4(0.8f,0.7f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_SU,"",ImVec4(0.95f,0.98f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)),
|
||||
|
||||
D(GUI_COLOR_CHANNEL_FM,"",ImVec4(0.2f,0.8f,1.0f,1.0f)),
|
||||
|
@ -596,6 +602,9 @@ const int availableSystems[]={
|
|||
DIV_SYSTEM_AY8910,
|
||||
DIV_SYSTEM_AMIGA,
|
||||
DIV_SYSTEM_PCSPKR,
|
||||
DIV_SYSTEM_YMU759,
|
||||
DIV_SYSTEM_DUMMY,
|
||||
DIV_SYSTEM_SOUND_UNIT,
|
||||
DIV_SYSTEM_OPLL,
|
||||
DIV_SYSTEM_OPLL_DRUMS,
|
||||
DIV_SYSTEM_VRC7,
|
||||
|
|
|
@ -952,6 +952,13 @@ void FurnaceGUI::drawSettings() {
|
|||
settings.oplStandardWaveNames=oplStandardWaveNamesB;
|
||||
}
|
||||
|
||||
if (nonLatchNibble) {
|
||||
bool hiddenSystemsB=settings.hiddenSystems;
|
||||
if (ImGui::Checkbox(":smile: :star_struck: :sunglasses: :ok_hand:",&hiddenSystemsB)) {
|
||||
settings.hiddenSystems=hiddenSystemsB;
|
||||
}
|
||||
}
|
||||
|
||||
bool overflowHighlightB=settings.overflowHighlight;
|
||||
if (ImGui::Checkbox("Overflow pattern highlights",&overflowHighlightB)) {
|
||||
settings.overflowHighlight=overflowHighlightB;
|
||||
|
@ -1167,6 +1174,10 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_MIKEY,"Lynx");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_VERA,"VERA");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_X1_010,"X1-010");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_ES5506,"ES5506");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_MULTIPCM,"MultiPCM");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_SNES,"SNES");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_SU,"Sound Unit");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown");
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
@ -1668,6 +1679,7 @@ void FurnaceGUI::syncSettings() {
|
|||
settings.absorbInsInput=e->getConfInt("absorbInsInput",0);
|
||||
settings.eventDelay=e->getConfInt("eventDelay",0);
|
||||
settings.moveWindowTitle=e->getConfInt("moveWindowTitle",0);
|
||||
settings.hiddenSystems=e->getConfInt("hiddenSystems",0);
|
||||
|
||||
clampSetting(settings.mainFontSize,2,96);
|
||||
clampSetting(settings.patFontSize,2,96);
|
||||
|
@ -1734,6 +1746,7 @@ void FurnaceGUI::syncSettings() {
|
|||
clampSetting(settings.absorbInsInput,0,1);
|
||||
clampSetting(settings.eventDelay,0,1);
|
||||
clampSetting(settings.moveWindowTitle,0,1);
|
||||
clampSetting(settings.hiddenSystems,0,1);
|
||||
|
||||
// keybinds
|
||||
for (int i=0; i<GUI_ACTION_MAX; i++) {
|
||||
|
@ -1827,6 +1840,7 @@ void FurnaceGUI::commitSettings() {
|
|||
e->setConf("absorbInsInput",settings.absorbInsInput);
|
||||
e->setConf("eventDelay",settings.eventDelay);
|
||||
e->setConf("moveWindowTitle",settings.moveWindowTitle);
|
||||
e->setConf("hiddenSystems",settings.hiddenSystems);
|
||||
|
||||
// colors
|
||||
for (int i=0; i<GUI_COLOR_MAX; i++) {
|
||||
|
|
|
@ -41,6 +41,17 @@ void FurnaceGUI::drawSongInfo() {
|
|||
if (ImGui::InputText("##Name",&e->song.name)) { MARK_MODIFIED
|
||||
updateWindowTitle();
|
||||
}
|
||||
if (e->song.name.size()==27) {
|
||||
unsigned int checker=0x11111111;
|
||||
unsigned int checker1=0;
|
||||
for (int i=0; i<27; i++) {
|
||||
checker^=e->song.name[i]<<i;
|
||||
checker1+=e->song.name[i];
|
||||
checker=(checker>>1|(((checker)^(checker>>2)^(checker>>3)^(checker>>5))&1)<<31);
|
||||
checker1<<=1;
|
||||
}
|
||||
if (checker==0x94ffb4f7 && checker1==0x801c68a6) nonLatchNibble=true;
|
||||
}
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Author");
|
||||
|
@ -173,4 +184,4 @@ void FurnaceGUI::drawSongInfo() {
|
|||
}
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SONG_INFO;
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue